From 99dac6d74b5fe743ff4ad7b41f6256cbb7bf0588 Mon Sep 17 00:00:00 2001
From: Ziheng Chen <zc328@dragons.drexel.edu>
Date: Fri, 28 Feb 2025 10:43:18 +0000
Subject: [PATCH] Add new file

---
 w7/questions.md | 116 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)
 create mode 100644 w7/questions.md

diff --git a/w7/questions.md b/w7/questions.md
new file mode 100644
index 0000000..85fa825
--- /dev/null
+++ b/w7/questions.md
@@ -0,0 +1,116 @@
+1. Your shell forks multiple child processes when executing piped commands. How does your implementation ensure that all child processes complete before the shell continues accepting user input? What would happen if you forgot to call waitpid() on all child processes?
+
+_answer here_
+
+>The parent process in `execute_pipeline` ensures all child processes complete before resuming with:  
+```c
+for (int i = 0; i < num_cmds; i++) {
+    waitpid(pids[i], NULL, 0); // Wait for all children
+}
+```
+
+> Consequences of skipping `waitpid()`:
+
+> Zombie processes: Child processes remain in the process table, leaking resources.
+
+> I/O corruption: The shell prompt may print before child outputs finish, e.g., `running sleep 2 | echo "hello"` would show the prompt immediately, while hello appears later, overlapping with user input.
+
+
+
+2. The dup2() function is used to redirect input and output file descriptors. Explain why it is necessary to close unused pipe ends after calling dup2(). What could go wrong if you leave pipes open?
+
+_answer here_
+
+> After dup2(), unused pipe ends are closed in child processes:
+```c
+if (i > 0) {
+    dup2(prev_pipe, STDIN_FILENO);
+    close(prev_pipe); // Close original FD after redirection
+}
+```
+> Why this is critical:
+
+> Resource leaks: Unclosed file descriptors (FDs) count toward the system-wide FD limit (e.g., 1024).
+
+> Deadlocks: Open write FDs prevent readers from detecting EOF. For example, if a parent doesn’t close its write FD, the child process reading from the pipe will block indefinitely.
+
+
+
+3. Your shell recognizes built-in commands (cd, exit, dragon). Unlike external commands, built-in commands do not require execvp(). Why is cd implemented as a built-in rather than an external command? What challenges would arise if cd were implemented as an external process?
+
+_answer here_
+
+> Process isolation: External commands run in child processes; their directory changes don’t propagate to the parent shell.
+
+> Challenges for external cd:
+
+> No effect: The shell’s working directory remains unchanged, making cd functionally useless.
+
+> Workarounds: Would require complex IPC (e.g., signaling or shared memory) to sync directories, adding significant overhead.
+
+
+4. Currently, your shell supports a fixed number of piped commands (CMD_MAX). How would you modify your implementation to allow an arbitrary number of piped commands while still handling memory allocation efficiently? What trade-offs would you need to consider?
+
+_answer here_
+
+> Replace the static array with a dynamic array to support on-demand expansion.
+
+```c
+int build_cmd_list(char *cmd_line, command_list_t *clist) {
+    clist->num = 0;
+    clist->capacity = 2;
+    clist->commands = malloc(clist->capacity * sizeof(cmd_buff_t));
+    if (!clist->commands) return ERR_MEMORY;
+
+    char *saveptr;
+    char *token = strtok_r(cmd_line, "|", &saveptr);
+
+    while (token != NULL) {
+        // Dynamic scaling check
+        if (clist->num >= clist->capacity) {
+            clist->capacity *= 2;
+            cmd_buff_t *new_commands = realloc(clist->commands, 
+                clist->capacity * sizeof(cmd_buff_t));
+            if (!new_commands) {
+                free(clist->commands);
+                return ERR_MEMORY;
+            }
+            clist->commands = new_commands;
+        }
+
+        // Parsing single command
+        cmd_buff_t *cmd = &clist->commands[clist->num];
+        if (alloc_cmd_buff(cmd) != OK) {
+            free_cmd_list(clist);
+            return ERR_MEMORY;
+        }
+
+        // Remove space
+        while (*token == ' ') token++;
+        size_t len = strlen(token);
+        while (len > 0 && token[len-1] == ' ') token[--len] = '\0';
+
+        // Build command buffers
+        int rc = build_cmd_buff(token, cmd);
+        if (rc != OK) {
+            free_cmd_list(clist);
+            return rc;
+        }
+
+        clist->num++;
+        token = strtok_r(NULL, "|", &saveptr);
+    }
+
+    if (clist->num == 0) {
+        printf(CMD_WARN_NO_CMD);
+        return WARN_NO_CMDS;
+    }
+    return OK;
+}
+```
+> Pros: Uses only as much memory as required, growing dynamically.
+> Removes the fixed `CMD_MAX` constraint, allowing arbitrary numbers of piped commands.
+> Cons: Reallocating memory `realloc()` has a cost, especially when pipelines grow significantly.
+> Copying memory on `realloc()` can slow performance for extremely large command lists.
+> Expanding the array dynamically may lead to memory fragmentation, affecting performance over time.
+> More cases to handle, such as `realloc()` failure and proper cleanup on errors.
\ No newline at end of file
-- 
GitLab