# Fork System Call ## Overview The `fork()` system call duplicates the calling process, creating a new child process with an independent copy of the parent's address space. ## System Call Interface - **Number**: `SYS_FORK` (3) - **Arguments**: None - **Returns**: Child PID in the parent, 0 in the child, -1 on error ## Implementation ### Address Space Cloning `paging_clone_directory_from(src_pd_phys)` performs a deep copy of a process's page directory: 1. **Kernel-space entries** (no `PAGE_USER` flag): shared directly between parent and child. Both processes see the same kernel mappings. 2. **User-space entries** (`PAGE_USER` flag set): fully deep-copied. For each user-space page directory entry: - A new page table is allocated - Each present user page has a new physical page allocated and the content copied byte-for-byte - This ensures parent and child have completely independent memory ### Register State The child receives a copy of the parent's register state at the time of the `INT 0x80` syscall, with `EAX` set to 0. This means the child resumes execution at the instruction immediately following the `INT 0x80` that triggered fork. ### Process Exit and Waitpid `process_exit()` was refactored to support multi-process scenarios: - When a process exits, it scans for any process blocked on `waitpid()` for its PID and unblocks it, setting the waiter's saved `EAX` to the exit code. - If another process is ready, `process_switch_to_user()` is called to directly context-switch via an assembly stub that loads the full register set and performs `iret`. - If no processes remain, the system halts. `sys_waitpid()` supports blocking: - If the child is already a zombie, it reaps immediately - Otherwise, the caller is marked `PROCESS_BLOCKED` and the scheduler is invoked to switch to another process - When the child exits, the parent is unblocked with the exit code ### Assembly Support `process_switch_to_user` in `interrupts.S` loads a full `registers_t` struct and performs `iret` to enter user mode. This is used when `process_exit()` needs to context-switch outside the normal ISR return path. ## Syscall Flow ``` User: INT 0x80 (EAX=SYS_FORK) → ISR stub pushes registers → isr_handler → syscall_handler → sys_fork(regs) → process_fork(regs) → Clone page directory with deep user-page copy → Copy current interrupt frame to child (EAX=0) → Return child PID to parent (via EAX) → ISR stub pops registers, iret → Parent continues with EAX=child_pid → [Timer interrupt] → scheduler picks child → Child starts with EAX=0 ``` ## Testing The `fork-test` application validates fork by: 1. Calling `SYS_FORK` 2. Parent prints "Parent" and calls `SYS_WAITPID` 3. Child prints "Child" and exits with code 7 4. Parent reaps child, prints "Reaped", exits with code 0