diff --git a/5-ShellP3/.dshlib.c.swp b/5-ShellP3/.dshlib.c.swp new file mode 100644 index 0000000000000000000000000000000000000000..42a51fa50c4d5b89873c2b580b7a2bace6f2ac48 Binary files /dev/null and b/5-ShellP3/.dshlib.c.swp differ diff --git a/5-ShellP3/bats/student_test.sh b/5-ShellP3/bats/student_test.sh index 638bc341446f7580a80c2aff52971b8023407ea8..248994b98dd90520a1c8385e4ee051bb7a310ecb 100755 --- a/5-ShellP3/bats/student_test.sh +++ b/5-ShellP3/bats/student_test.sh @@ -4,11 +4,68 @@ # # Create your unit tests suit in this file -@test "Example: check ls runs without errors" { +@test "ls | grep .c " { run ./dsh <<EOF -ls +ls | grep ".c" EOF # Assertions [ "$status" -eq 0 ] } + +@test "ls | wc" { + run "./dsh" << EOF +ls | wc +EOF + + stripped_output=$(echo "$output" | tr -d '[:space:]\t\n\r\f\v') + + expected_output="8870dsh3>dsh3>cmdloopreturned0" + + echo "Captured stdout:" + echo "Output: $output" + echo "Exit Status: $status" + echo "${stripped_output} -> ${expected_output}" + + [ "$stripped_output" = "$expected_output" ] + + [ "$status" -eq 0 ] +} + +@test "properly exits" { + run "./dsh" << EOF +exit +EOF + + stripped_output=$(echo "$output" | tr -d '[:space:]\t\n\r\f\v') + + expected_output="dsh3>exiting...cmdloopreturned0" + + echo "Captured stdout:" + echo "Output: $output" + echo "Exit Status: $status" + echo "${stripped_output} -> ${expected_output}" + + [ "$stripped_output" = "$expected_output" ] + + [ "$status" -eq 0 ] +} + +@test "echo hello world | wc & exit" { + run "./dsh" << EOF +echo hello world | wc +exit +EOF + stripped_output=$(echo "$output" | tr -d '[:space:]\t\n\r\f\v') + + expected_output="1212dsh3>dsh3>exiting...cmdloopreturned0" + + echo "Captured stdout:" + echo "Output: $output" + echo "Exit Status: $status" + echo "${stripped_output} -> ${expected_output}" + + [ "$stripped_output" = "$expected_output" ] + + [ "$status" -eq 0] +} diff --git a/5-ShellP3/dragon.txt b/5-ShellP3/dragon.txt new file mode 100644 index 0000000000000000000000000000000000000000..a9177fd41bb14d2fa0fd439742e74d3f61f60d2d --- /dev/null +++ b/5-ShellP3/dragon.txt @@ -0,0 +1,38 @@ + @%%%% + %%%%%% + %%%%%% + % %%%%%%% @ + %%%%%%%%%% %%%%%%% + %%%%%%% %%%%@ %%%%%%%%%%%%@ %%%%%% @%%%% + %%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%% %%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%% %%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%@ @%%%%%%%%%%%%%%%%%% %% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%@ + %%%%%%%%@ %%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%% %% + %%%%%%%%%%%%% %%@%%%%%%%%%%%% %%%%%%%%%%% %%%%%%%%%%%% @% + %%%%%%%%%% %%% %%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%% % %%%%%%%%%%%%% %%%%%%%%%%%%@%%%%%%%%%%% +%%%%%%%%%@ % %%%%%%%%%%%%% @%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%@ %%@%%%%%%%%%%%% @%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%@ %%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%% %%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% +%%%%%%%%%@ @%%%%%%%%%%%%%% %%%%%%%%%%%%@ %%%% %%%%%%%%%%%%%%%%% %%%%%%%% +%%%%%%%%%% %%%%%%%%%%%%%%%%% %%%%%%%%%%%%% %%%%%%%%%%%%%%%%%% %%%%%%%%% +%%%%%%%%%@%%@ %%%%%%%%%%%%%%%%@ %%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%% %% + %%%%%%%%%% % %%%%%%%%%%%%%%@ %%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%% %% + %%%%%%%%%%%% @ %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% + %%%%%%%%%%%%% %% % %@ %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% + %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%% @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% + @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%% %%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%% @%%%%%%%%% + %%%%%%%%%%%%%%%%%%%% @%@% @%%%%%%%%%%%%%%%%%% %%% + %%%%%%%%%%%%%%% %%%%%%%%%% %%%%%%%%%%%%%%% % + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%% + %%%%%%%%%%%%%%%%%%%%%%%%%% %%%% %%% %%%%%%%%%% %%%@ + %%%%%%%%%%%%%%%%%%% %%%%%% %% %%%%%%%%%%%%%@ + %%%%%%%@ diff --git a/5-ShellP3/dsh b/5-ShellP3/dsh new file mode 100755 index 0000000000000000000000000000000000000000..97964bae366d2d39c9679ddcb6f5bc27f7b68a81 Binary files /dev/null and b/5-ShellP3/dsh differ diff --git a/5-ShellP3/dshlib.c b/5-ShellP3/dshlib.c index 0e6738ed007037b9f293051f058931ad8cb98ced..4db5396084f28d06fefabbe573f7923331e7455e 100644 --- a/5-ShellP3/dshlib.c +++ b/5-ShellP3/dshlib.c @@ -6,9 +6,356 @@ #include <unistd.h> #include <fcntl.h> #include <sys/wait.h> - +#include <errno.h> #include "dshlib.h" +int numInstanceOf(char *str, const char c) { + int count = 0; + while (*str != '\0') { + if (*str == c) count++; + str++; + } + return count; +} + +void print_dragon(){ + // TODO implement + FILE *dragon = fopen("dragon.txt", "r"); + if (dragon == NULL) { + return; + } + + char *s = NULL; + size_t nbyte; + ssize_t nchar; + + while (1) { + nchar = getline(&s, &nbyte, dragon); + if (nchar == -1) { // end of file reached + break; + } + if (nchar == 0) { + continue; + } + if (s == NULL) { // out of memory + exit(1); + } + if (s[nchar - 1] == '\n') { + s[nchar - 1] = '\0'; // remove newline + nchar--; // newline removed + } + + printf("%s\n", s); + } + free(s); + fclose(dragon); +} + +char* trim_whitespace(char *str) { + int start = 0; + while (isspace((unsigned char)str[start])) { + start++; + } + + int end = strlen(str) - 1; + while (end > start && isspace((unsigned char)str[end])) { + end--; + } + + int j = 0; + for (int i = start; i <= end; i++) { + str[j++] = str[i]; + } + str[j] = '\0'; + + return str; +} + +int alloc_cmd_buff(cmd_buff_t *cmd_buff) { + if (cmd_buff == NULL) return ERR_MEMORY; + + cmd_buff->argc = 0; + + cmd_buff->argv = (char**)malloc(CMD_ARGV_MAX * sizeof(char *)); + if (cmd_buff->argv == NULL) return ERR_MEMORY; + + for (int i = 0; i < CMD_ARGV_MAX; i++) { + cmd_buff->argv[i] = (char *)malloc(ARG_MAX * sizeof(char)); + if (cmd_buff->argv[i] == NULL) { + for (int j = 0; j < i; j++) free(cmd_buff->argv[j]); + free(cmd_buff->argv); + return ERR_MEMORY; + } + } + + cmd_buff->_cmd_buffer = (char *)malloc(SH_CMD_MAX * sizeof(char)); + if (cmd_buff->_cmd_buffer == NULL) { + for (int i = 0; i < CMD_ARGV_MAX; i++) free(cmd_buff->argv[i]); + free(cmd_buff->argv); + return ERR_MEMORY; + } + + return OK; +} + +void free_cmd_buff(cmd_buff_t *cmd_buff) { + if (cmd_buff != NULL) { + if (cmd_buff->_cmd_buffer != NULL) { + free(cmd_buff->_cmd_buffer); + cmd_buff->_cmd_buffer = NULL; + } + + if (cmd_buff->argv != NULL) { + for (int i = 0; i < CMD_ARGV_MAX; i++) { + if (cmd_buff->argv[i] != NULL) { + free(cmd_buff->argv[i]); + cmd_buff->argv[i] = NULL; + } + } + free(cmd_buff->argv); + cmd_buff->argv = NULL; + } + memset(cmd_buff, 0, sizeof(cmd_buff_t)); + } +} + +void clear_cmd_buff(cmd_buff_t *cmd_buff) { + if (cmd_buff != NULL) { + cmd_buff->argc = 0; + if (cmd_buff->_cmd_buffer) memset(cmd_buff->_cmd_buffer, 0, strlen(cmd_buff->_cmd_buffer)); + for (int i = 0; i < CMD_ARGV_MAX; i++) cmd_buff->argv[i] = NULL; + } +} + +int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) { + if ((int)strlen(cmd_line) > SH_CMD_MAX) return ERR_CMD_OR_ARGS_TOO_BIG; + + if ((int)strlen(cmd_line) == 0) return WARN_NO_CMDS; + + if (cmd_buff->_cmd_buffer != NULL) { + free(cmd_buff->_cmd_buffer); + cmd_buff->_cmd_buffer = strdup(trim_whitespace(cmd_line)); + } else return ERR_MEMORY; + + char *token = cmd_buff->_cmd_buffer; + bool quotes = false; + char *p = NULL; + + while (*token) { + if (*token == DOUBLE_QUOTE_CHAR) { + quotes = !quotes; + if (quotes) p = token + 1; + else *token = '\0'; + } else if (!quotes && (*token == SPACE_CHAR || *token == '\t')) { + *token = '\0'; + + if (p != NULL) { + cmd_buff->argv[cmd_buff->argc++] = p; + p = NULL; + } + } else if (p == NULL) { + p = token; + } + token++; + } + + if (p != NULL) { + if (cmd_buff->argc >= CMD_ARGV_MAX - 1) return ERR_CMD_OR_ARGS_TOO_BIG; + cmd_buff->argv[cmd_buff->argc++] = p; + } + + cmd_buff->argv[cmd_buff->argc] = NULL; + return OK; +} + +int alloc_cmd_list(command_list_t *clist, int rc) { + if (clist == NULL) return ERR_MEMORY; + clist->num = 0; + clist->commands = (cmd_buff_t **)calloc(CMD_MAX, sizeof(cmd_buff_t *)); + if (clist->commands == NULL) return ERR_MEMORY; + + for (int i = 0; i < CMD_MAX; i++) { + cmd_buff_t *cmd = (cmd_buff_t *)malloc(sizeof(cmd_buff_t)); + if (cmd == NULL) { + rc = ERR_MEMORY; + break; + } + + memset(cmd, 0, sizeof(cmd_buff_t)); + + if ((rc = alloc_cmd_buff(cmd)) != OK_EXIT) { + free(cmd); + break; + } + clist->commands[i] = cmd; + } + + return rc; +} + +int build_cmd_list(char *cmd_line, command_list_t *clist, int rc) { + if (numInstanceOf(cmd_line, PIPE_CHAR) > CMD_MAX-1) return ERR_TOO_MANY_COMMANDS; + + if ((int)strlen(cmd_line) > SH_CMD_MAX) return ERR_CMD_OR_ARGS_TOO_BIG; + + char *outer_saveptr = NULL; + + char *outer_token = strtok_r(cmd_line, PIPE_STRING, &outer_saveptr); + + while (outer_token != NULL) { + if (clist->num > CMD_MAX) return ERR_TOO_MANY_COMMANDS; + + cmd_buff_t *cmd = malloc(sizeof(cmd_buff_t)); + + if ((rc = alloc_cmd_buff(cmd)) != OK) { + free(cmd); + return rc; + } + + if ((rc = build_cmd_buff(outer_token, cmd)) != OK) { + free(cmd); + return rc; + } + + clist->commands[clist->num] = cmd; + clist->num++; + + outer_token = strtok_r(NULL, PIPE_STRING, &outer_saveptr); + } + + return OK; +} + +void free_cmd_list(command_list_t *clist) { + if (clist != NULL) { + if (clist->commands != NULL) { + for (int i = 0; i < CMD_MAX; i++) { + if (clist->commands[i] != NULL) { + free_cmd_buff(clist->commands[i]); + free(clist->commands[i]); + clist->commands[i] = NULL; + } + } + free(clist->commands); + clist->commands = NULL; + } + } +} + +void clear_cmd_list(command_list_t *clist) { + if (clist != NULL) { + clist->num = 0; + for (int i = 0; i < CMD_MAX; i++) clear_cmd_buff(clist->commands[i]); + } +} + +int exec_pipeline(command_list_t *clist) { + int pipes[clist->num - 1][2]; + pid_t pids[clist->num]; + + for (int i = 0; i < clist->num - 1; i++) { + if (pipe(pipes[i]) == -1) { + perror("pipe"); + exit(EXIT_FAILURE); + } + } + + for (int i = 0; i < clist->num; i++) { + pids[i] = fork(); + if (pids[i] == -1) { + perror("fork"); + exit(EXIT_FAILURE); + } + + if (pids[i] == 0) { + if (i > 0) dup2(pipes[i-1][0], STDIN_FILENO); + + if (i < clist->num - 1) dup2(pipes[i][1], STDOUT_FILENO); + + for (int j = 0; j < clist->num - 1; j++) { + close(pipes[j][0]); + close(pipes[j][1]); + } + + execvp(clist->commands[i]->argv[0], clist->commands[i]->argv); + perror("execvp"); + exit(EXIT_FAILURE); + } + } + + for (int i = 0; i < clist->num - 1; i++) { + close(pipes[i][0]); + close(pipes[i][1]); + } + + for (int i = 0; i < clist->num; i++) waitpid(pids[i], NULL, 0); + + return OK; +} + +int exec_one_cmd(command_list_t *clist, int rc) { + cmd_buff_t *cmd = clist->commands[0]; + if (strcmp(cmd->argv[0], "dragon") == 0) { + print_dragon(); + return OK; + } + + if (strcmp(cmd->argv[0], "rc") == 0) { + printf("%d\n", rc); + return OK; + } + + if (strcmp(cmd->argv[0], EXIT_CMD) == 0) { + printf("exiting...\n"); + return(OK_EXIT); + } + + cmd->argv[cmd->argc] = 0; + + if (strcmp(cmd->argv[0], "cd") == 0) chdir(cmd->argv[1]); + else { + int f_result, c_result; + f_result = fork(); + + if (f_result < 0) perror("fork failed"); + else if (f_result == 0) { + rc = execvp(cmd->argv[0], cmd->argv); + perror("execvp failed"); + exit(EXIT_FAILURE); + } else { + wait(&c_result); + } + } + return OK; +} + +int exec_cmd(command_list_t *clist, int rc) { + if (clist == NULL) return ERR_MEMORY; + + if (rc == ERR_TOO_MANY_COMMANDS) { + printf(CMD_ERR_PIPE_LIMIT, CMD_MAX); + return rc; + } + + char *cmd = clist->commands[0]->argv[0]; + if (!cmd) return ERR_MEMORY; + + if (clist->num == 1) { + if ((rc = exec_one_cmd(clist, rc)) != OK) { + if (rc == OK_EXIT) { + clear_cmd_list(clist); + return rc; + } else return ERR_EXEC_CMD; + } + } + + if (clist->num > 1) { + if ((rc = exec_pipeline(clist)) != OK) return ERR_EXEC_CMD; + } + + return OK; +} + /* * Implement your exec_local_cmd_loop function by building a loop that prompts the * user for input. Use the SH_PROMPT constant from dshlib.h and then @@ -54,6 +401,42 @@ */ int exec_local_cmd_loop() { - - return OK; + char *cmd_line = (char *)malloc(ARG_MAX * sizeof(char)); + int rc = OK; + command_list_t *clist = (command_list_t *)malloc(sizeof(command_list_t)); + + if ((rc = alloc_cmd_list(clist, rc)) != OK) { + free(cmd_line); + return rc; + } + + while(1){ + printf("%s", SH_PROMPT); + if (fgets(cmd_line, ARG_MAX, stdin) == NULL){ + printf("\n"); + break; + } + //remove the trailing \n from cmd_buff + cmd_line[strcspn(cmd_line,"\n")] = '\0'; + + //IMPLEMENT THE REST OF THE REQUIREMENTS + if (strlen(cmd_line) == 0) { + printf(CMD_WARN_NO_CMD); + continue; + } + + if ((rc = build_cmd_list(cmd_line, clist, rc)) != OK) break; + + if ((rc = exec_cmd(clist, rc)) != OK) { + if (rc == OK_EXIT) rc = OK; + break; + } + + clear_cmd_list(clist); + } + + free(cmd_line); + free_cmd_list(clist); + free(clist); + return rc; } diff --git a/5-ShellP3/dshlib.h b/5-ShellP3/dshlib.h index 0f949dcfd6284795a93293c206215d78a32ac94a..25aec28c8c2a004a2168736194dd42eec967422a 100644 --- a/5-ShellP3/dshlib.h +++ b/5-ShellP3/dshlib.h @@ -1,7 +1,6 @@ #ifndef __DSHLIB_H__ #define __DSHLIB_H__ - //Constants for command structure sizes #define EXE_MAX 64 #define ARG_MAX 256 @@ -18,11 +17,16 @@ typedef struct command typedef struct cmd_buff { - int argc; - char *argv[CMD_ARGV_MAX]; - char *_cmd_buffer; + int argc; // up to CMD_MAX + char **argv; // CMD_ARG_MAX + char *_cmd_buffer; // SCH_CMD_MAX } cmd_buff_t; +typedef struct command_list{ + int num; + cmd_buff_t **commands; // CMD_MAX +} command_list_t; + /* WIP - Move to next assignment #define N_ARG_MAX 15 //MAX number of args for a command typedef struct command{ @@ -33,15 +37,12 @@ typedef struct command{ }command_t; */ -typedef struct command_list{ - int num; - cmd_buff_t commands[CMD_MAX]; -}command_list_t; - //Special character #defines #define SPACE_CHAR ' ' +#define SPACE_STRING " " #define PIPE_CHAR '|' #define PIPE_STRING "|" +#define DOUBLE_QUOTE_CHAR '"' #define SH_PROMPT "dsh3> " #define EXIT_CMD "exit" @@ -58,13 +59,27 @@ typedef struct command_list{ #define OK_EXIT -7 //prototypes +int numInstanceOf(char *, const char); +char *trim_whitespace(char *str); +extern void print_dragon(); + +//cmd_buff int alloc_cmd_buff(cmd_buff_t *cmd_buff); -int free_cmd_buff(cmd_buff_t *cmd_buff); -int clear_cmd_buff(cmd_buff_t *cmd_buff); +void free_cmd_buff(cmd_buff_t *cmd_buff); +void clear_cmd_buff(cmd_buff_t *cmd_buff); int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff); -int close_cmd_buff(cmd_buff_t *cmd_buff); -int build_cmd_list(char *cmd_line, command_list_t *clist); -int free_cmd_list(command_list_t *cmd_lst); + +//command_list_t +int alloc_cmd_list(command_list_t *clist, int rc); +int build_cmd_list(char *cmd_line, command_list_t *clist, int rc); +void free_cmd_list(command_list_t *clist); +void clear_cmd_list(command_list_t *clist); + +//main execution context +int exec_local_cmd_loop(); +int exec_pipeline(command_list_t *clist); +int exec_cd(cmd_buff_t *cmd_buff, int rc); +int exec_cmd(command_list_t *clist, int rc); //built in command stuff typedef enum { @@ -77,13 +92,6 @@ typedef enum { Built_In_Cmds match_command(const char *input); Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd); -//main execution context -int exec_local_cmd_loop(); -int exec_cmd(cmd_buff_t *cmd); -int execute_pipeline(command_list_t *clist); - - - //output constants #define CMD_OK_HEADER "PARSED COMMAND LINE - TOTAL COMMANDS %d\n" diff --git a/5-ShellP3/questions.md b/5-ShellP3/questions.md new file mode 100644 index 0000000000000000000000000000000000000000..23623ace965546d094713da784d498c0951c09dd --- /dev/null +++ b/5-ShellP3/questions.md @@ -0,0 +1,38 @@ +### Questions on Fork, Exec, and Linux Signals + +1. **Can you think of why we use `fork()`/`execvp()` instead of just calling `execvp()` directly? What value do you think the `fork()` provides?** + +Since fork creates a separate child process, it allows the command given as input to run separately from the shell without it replacing the previous process that was running. It allows the parents process to "wait" for the child process to finish or continuing allowing for user inputs. + +2. **What happens if the `fork()` system call fails? How does your implementation handle this scenario?** + +If 'fork()' fails, i.e returning -1, is exits with 'EXIT_FAILURE' meaning it didn't execute correctly. + +3. **How does `execvp()` find the command to execute? What system environment variable plays a role in this process?** + +Since the first argument for 'execvp()' is the name of a file, I woudl assume that the function looks for the executable in each directory in the path to the executable and attempts to execute it. If it doesn't exist in the path, then (at least in my code) it exits. + +4. **What is the purpose of calling `wait()` in the parent process after forking? What would happen if we didn’t call it?** + +5. **In the referenced demo code, we used `WEXITSTATUS()`. What information does this provide, and why is it important?** + +It gives you the exit status of a child process, which is important if we wanted to know whether a child process exited properly or with error. + +6. **Describe how your implementation of `build_cmd_buff()` handles quoted arguments. Why is this necessary?** + +Since 'build_cmd_list()' splits the stdin into tokens by the pipe character, i can call 'build_cmd_buff()' on each token. When I do, it goes through the token character by character until it comes across a double quote. If it does, it keeps everything between the first quote and second quote as one token, rather than splitting it by space or tab as would be handled normally. This is important for commands like 'echo' or 'grep' where text included inbetween quotes is supposed to be kept together and handled as one argument. + +7. **What changes did you make to your parsing logic compared to the previous assignment? Were there any unexpected challenges in refactoring your old code?** + +Barely any changes. The only real refactoring I did from the previous assignments was to merge 'build_cmd_buff()' and the code to parse by pipe together, which required more removing code than adding code. + +8. **For this question, you need to do some research on Linux signals. You can use this Google search to get started.** + + - **What is the purpose of signals in a Linux system, and how do they differ from other forms of interprocess communication (IPC)?** + Unlike other IPCs, signals can be received by the system at any point in time. Their purpose is to "notify" processes about events, such as process termination or execution. + - **Find and describe three commonly used signals (e.g., `SIGKILL`, `SIGTERM`, `SIGINT`). What are their typical use cases?** + SIGKILL - kills a process immediately, forcefully terminating it without clean up: example: kill -9 <pid> + SIGTERM - requests for a process to be terminated, allowing clean up; example: 'kill <pid>' + SIGINT - interupts a currently running process; example: 'CTRL + C' + - **What happens when a process receives `SIGSTOP`? Can it be caught or ignored like `SIGINT`? Why or why not?** + Both causes a process to pause however SIGSTOP (CTRL + Z) is forceful and must be adhered to whereas SIGINT can be handled by the process and subsequently ignored.