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()-&gt;pid<br>ticks / tickslock<br>sleep(&amp;ticks, &amp;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()-&gt;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()-&gt;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(&amp;rf, &amp;wf)<br>fdalloc(rf)<br>fdalloc(wf)<br>copyout()<br>myproc()-&gt;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-&gt;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