Syscall dispatch
When a user program invokes a system call, the ecall instruction traps into the kernel. The trap handler reads the syscall number from the trapframe and dispatches through a table to the appropriate sys_* wrapper. Argument helpers extract integers, addresses, or strings from the trapframe or user memory.
--- config: layout: dagre --- flowchart TB classDef user fill:#FFFFFF,stroke:#111,stroke-width:3px,color:#111 classDef dispatch fill:#E9F1FF,stroke:#111,stroke-width:2px,color:#111 classDef helper fill:#F7E6CC,stroke:#111,stroke-width:2px,color:#111 classDef state fill:#F8F8F8,stroke:#111,stroke-width:2px,color:#111 classDef wrapper fill:#EDE7D4,stroke:#111,stroke-width:2px,color:#111 USER["user-space syscall stub<br/><br/>ecall"]:::user TRAP["trap path<br/><br/>enters kernel"]:::user subgraph DISPATCH["kernel/syscall.c: syscall dispatch layer"] direction TB SYSCALL["syscall()<br/><br/>p = myproc()<br/>num = p->trapframe->a7"]:::dispatch TABLE["syscalls[] table<br/><br/>SYS_* number<br/>maps to<br/>sys_* wrapper"]:::dispatch RET["return path<br/><br/>p->trapframe->a0 = syscalls[num]()"]:::dispatch UNKNOWN["unknown syscall<br/><br/>print error<br/>return -1 in a0"]:::dispatch end subgraph ARGUMENTS["syscall argument helpers"] direction TB ARGRAW["argraw(n)<br/><br/>read a0-a5<br/>from trapframe"]:::helper ARGINT["argint(n, &x)<br/><br/>32-bit integer argument"]:::helper ARGADDR["argaddr(n, &addr)<br/><br/>user virtual address argument"]:::helper ARGSTR["argstr(n, buf, max)<br/><br/>string argument"]:::helper FETCHADDR["fetchaddr(addr, &x)<br/><br/>fetch uint64<br/>from user memory"]:::helper FETCHSTR["fetchstr(addr, buf, max)<br/><br/>fetch NUL-terminated string<br/>from user memory"]:::helper end subgraph FIRST_STATE["first touched process / memory state"] direction LR PROC["struct proc<br/><br/>myproc()"]:::state TF["trapframe<br/><br/>a0-a7"]:::state PAGETABLE["proc->pagetable"]:::state COPYIN["copyin()"]:::state COPYINSTR["copyinstr()"]:::state end subgraph WRAPPERS["sys_* wrapper families"] direction TB PROCESS["process lifecycle wrappers"]:::wrapper ADDRSPACE["address-space wrapper"]:::wrapper TIME["time / blocking wrappers"]:::wrapper FD["file descriptor wrappers"]:::wrapper PIPE["pipe / IPC wrapper"]:::wrapper PATHFS["pathname / filesystem wrappers"]:::wrapper EXEC["exec / program-image wrapper"]:::wrapper end USER --> TRAP --> SYSCALL SYSCALL --> PROC PROC --> TF SYSCALL --> TABLE TABLE -->|valid SYS_* number| RET TABLE -->|invalid SYS_* number| UNKNOWN RET --> PROCESS RET --> ADDRSPACE RET --> TIME RET --> FD RET --> PIPE RET --> PATHFS RET --> EXEC ARGINT --> ARGRAW ARGADDR --> ARGRAW ARGSTR --> ARGADDR ARGSTR --> FETCHSTR ARGRAW --> TF FETCHADDR --> PROC FETCHADDR --> PAGETABLE FETCHADDR --> COPYIN FETCHSTR --> PROC FETCHSTR --> PAGETABLE FETCHSTR --> COPYINSTR
Touchpoints
Each sys_* wrapper touches specific kernel subsystems as it performs its work. The following diagram groups them by domain and shows the first functions and data structures each wrapper reaches.
--- config: layout: dagre --- flowchart TB subgraph PROCESS["process lifecycle / control wrapper"] direction TB P_WRAP["process lifecycle / control<br><br>create / exit / wait / identify / signal / pause"] SYS_FORK["sys_fork()"] SYS_EXIT["sys_exit(status)"] SYS_WAIT["sys_wait(addr)"] SYS_KILL["sys_kill(pid)"] SYS_GETPID["sys_getpid()"] SYS_PAUSE["sys_pause(n)"] P_TOUCH["first touch points<br><br>kfork()<br>kexit(status)<br>kwait(addr)<br>kkill(pid)<br>myproc()->pid<br>ticks / tickslock<br>sleep(&ticks, &tickslock)<br>killed(myproc())"] end subgraph ADDR["address-space wrapper"] direction TB A_WRAP["address-space / heap growth<br><br>process memory size boundary"] SYS_SBRK["sys_sbrk(n, type)"] A_TOUCH["first touch points<br><br>argint()<br>myproc()->sz<br>growproc(n)"] end subgraph TIMEBLOCK["time query wrapper"] direction TB T_WRAP["time query<br><br>read kernel tick counter"] SYS_UPTIME["sys_uptime()"] T_TOUCH["first touch points<br><br>ticks<br>tickslock"] end subgraph FDWRAP["file descriptor wrapper"] direction TB FD_WRAP["file descriptor / open-file wrapper<br><br>operate on existing FDs<br>or duplicate / close them"] SYS_DUP["sys_dup(fd)"] SYS_READ["sys_read(fd, addr, n)"] SYS_WRITE["sys_write(fd, addr, n)"] SYS_CLOSE["sys_close(fd)"] SYS_FSTAT["sys_fstat(fd, stataddr)"] FD_TOUCH["first touch points<br><br>argfd()<br>fdalloc()<br>myproc()->ofile[]<br>filedup()<br>fileread()<br>filewrite()<br>fileclose()<br>filestat()"] end subgraph PIPEWRAP["pipe / IPC wrapper"] direction TB PIPE_WRAP["pipe / IPC<br><br>creates two connected file endpoints"] SYS_PIPE["sys_pipe(fdarray)"] PIPE_TOUCH["first touch points<br><br>argaddr()<br>pipealloc(&rf, &wf)<br>fdalloc(rf)<br>fdalloc(wf)<br>copyout()<br>myproc()->ofile[]"] end subgraph PATHFSWRAP["pathname / filesystem wrapper"] direction TB FS_WRAP["pathname / filesystem<br><br>names, directories, inodes, transactions"] SYS_OPEN["sys_open(path, omode)"] SYS_LINK["sys_link(old, new)"] SYS_UNLINK["sys_unlink(path)"] SYS_MKDIR["sys_mkdir(path)"] SYS_MKNOD["sys_mknod(path, major, minor)"] SYS_CHDIR["sys_chdir(path)"] FS_TOUCH["first touch points<br><br>argstr()<br>argint()<br>begin_op() / end_op()<br>namei()<br>nameiparent()<br>create()<br>ilock()<br>iunlockput()<br>iupdate()<br>dirlink()<br>dirlookup()<br>proc->cwd"] end subgraph EXECWRAP["exec / program image wrapper"] direction TB E_WRAP["exec / program image<br><br>replace current process image"] SYS_EXEC["sys_exec(path, argv)"] E_TOUCH["first touch points<br><br>argstr(path)<br>argaddr(argv)<br>fetchaddr(argv[i])<br>fetchstr(argv[i])<br>kalloc()<br>kfree()<br>kexec(path, argv)"] E_NOTE["ELF loading starts after wrapper handoff<br>"] end P_WRAP --> SYS_FORK & SYS_EXIT & SYS_WAIT & SYS_KILL & SYS_GETPID & SYS_PAUSE SYS_FORK --> P_TOUCH SYS_EXIT --> P_TOUCH SYS_WAIT --> P_TOUCH SYS_KILL --> P_TOUCH SYS_GETPID --> P_TOUCH SYS_PAUSE --> P_TOUCH A_WRAP --> SYS_SBRK SYS_SBRK --> A_TOUCH T_WRAP --> SYS_UPTIME SYS_UPTIME --> T_TOUCH FD_WRAP --> SYS_DUP & SYS_READ & SYS_WRITE & SYS_CLOSE & SYS_FSTAT SYS_DUP --> FD_TOUCH SYS_READ --> FD_TOUCH SYS_WRITE --> FD_TOUCH SYS_CLOSE --> FD_TOUCH SYS_FSTAT --> FD_TOUCH PIPE_WRAP --> SYS_PIPE SYS_PIPE --> PIPE_TOUCH FS_WRAP --> SYS_OPEN & SYS_LINK & SYS_UNLINK & SYS_MKDIR & SYS_MKNOD & SYS_CHDIR SYS_OPEN --> FS_TOUCH SYS_LINK --> FS_TOUCH SYS_UNLINK --> FS_TOUCH SYS_MKDIR --> FS_TOUCH SYS_MKNOD --> FS_TOUCH SYS_CHDIR --> FS_TOUCH E_WRAP --> SYS_EXEC SYS_EXEC --> E_TOUCH E_TOUCH -. handoff .-> E_NOTE ROOT["syscall wrapper layer<br><br>all SYS_* entries from syscall.h"] --> P_WRAP & A_WRAP & T_WRAP & FD_WRAP & PIPE_WRAP & FS_WRAP & E_WRAP P_WRAP:::family SYS_FORK:::syscall SYS_EXIT:::syscall SYS_WAIT:::syscall SYS_KILL:::syscall SYS_GETPID:::syscall SYS_PAUSE:::syscall P_TOUCH:::touch A_WRAP:::family SYS_SBRK:::syscall A_TOUCH:::touch T_WRAP:::family SYS_UPTIME:::syscall T_TOUCH:::touch FD_WRAP:::family SYS_DUP:::syscall SYS_READ:::syscall SYS_WRITE:::syscall SYS_CLOSE:::syscall SYS_FSTAT:::syscall FD_TOUCH:::touch PIPE_WRAP:::family SYS_PIPE:::syscall PIPE_TOUCH:::touch FS_WRAP:::family SYS_OPEN:::syscall SYS_LINK:::syscall SYS_UNLINK:::syscall SYS_MKDIR:::syscall SYS_MKNOD:::syscall SYS_CHDIR:::syscall FS_TOUCH:::touch E_WRAP:::family SYS_EXEC:::syscall E_TOUCH:::touch E_NOTE:::note ROOT:::family classDef family fill:#E9F1FF,stroke:#111,stroke-width:3px,color:#111 classDef syscall fill:#EDE7D4,stroke:#111,stroke-width:2px,color:#111 classDef touch fill:#F8F8F8,stroke:#111,stroke-width:2px,color:#111 classDef note fill:#FFFFFF,stroke:#555,stroke-width:1px,color:#111