Kernel Internals & OS Construction

the system-call table

When a program traps into the kernel asking for service, the kernel arrives at a single entry point but must somehow route hundreds of different requests — open a file, get the time, create a process — each to the right piece of code. The system-call table is how it does that: a simple numbered array where slot N holds the address of the kernel function that handles system call number N. Think of it as the keypad menu on an office phone system: you do not need a separate phone line for every department; you dial one number, press the extension, and the switchboard connects you to the right desk.

Concretely, the table is an array of function pointers indexed by the system-call number. The user side and kernel side agree in advance on the numbering (read is 0, write is 1, openat is 257, and so on, on x86-64 Linux). When the trap instruction lands in the kernel, the dispatch code reads the number the user placed in a register, uses it as an index into the table, and calls the function stored there — turning a number into the right action in essentially one array lookup, which is O(1). The numbering is treated as a stable contract: once a number is assigned to a call it is never reused for something else, because countless already-compiled programs depend on it.

It matters because this table is the kernel's master directory of services and a famous security target: rootkits historically hijacked the table, swapping in their own pointers so that a normal call (like reading a directory) secretly runs malicious code that hides files — which is why modern kernels keep the table read-only and watch for tampering. A common misconception is that a system call is found by name at runtime; it is found by number, and the human-readable name is just a convenience in the source code and the libc wrapper.

Imagine the table as: table[0] = sys_read, table[1] = sys_write, table[2] = sys_open, .... A program wanting to write puts 1 in the call-number register and traps. The dispatcher computes table[1], finds sys_write, and calls it. Add a new call to the kernel and you simply assign it the next free number and put its function in that slot.

A number from user space indexes into an array of handler pointers — dispatch in one lookup.

System-call numbers are architecture-specific: the same name (say write) can have different numbers on x86-64, ARM64, and 32-bit x86. Code that hard-codes a number for one architecture will call the wrong handler on another — which is why programs use the named libc wrappers, not raw numbers.

Also called
syscall tablesys_call_tabledispatch table系統呼叫分派表