1. System Call Basics
System calls (syscalls) are the interface for user-space programs to request services from the kernel. Examples include:
- File I/O:
read(),write(),open(),close(). - Device Control:
ioctl(). - Signal Handling:
kill(),signal().
2. System Call Table and Registration
Syscall Table:
- A table (
sys_call_table) maps syscall numbers to handler functions. - Architecture-Specific:
- x86: Defined in
arch/x86/entry/syscalls/syscall_64.tbl. - ARM: Defined in
arch/arm/tools/syscall.tbl.
- x86: Defined in
- Registration:
- Syscalls are registered at compile time using macros like
SYSCALL_DEFINE(e.g.,SYSCALL_DEFINE3(write, ...)forwrite()). - For custom syscalls (rare and discouraged), you would:
- Add an entry to the syscall table.
- Define the handler using
SYSCALL_DEFINE. - Recompile the kernel (or use modules for dynamic insertion).
- Syscalls are registered at compile time using macros like
3. Flow of System Calls
1. User-Space Invocation
- The libc wrapper (e.g.,
read(),ioctl()) triggers a software interrupt (int 0x80on x86) or uses thesyscallinstruction (modern x86/ARM).
// User-space code
fd = open("/dev/mydevice", O_RDWR); // Syscall 1: open()
read(fd, buf, 100); // Syscall 2: read()
ioctl(fd, MY_CMD, arg); // Syscall 3: ioctl()
close(fd); // Syscall 4: close()
2. Transition to Kernel Mode
- Switches to kernel mode (ring 0 on x86, EL1 on ARM).
- Saves user-space registers (e.g., RIP, RSP, EFLAGS).
- Jumps to the kernel’s syscall entry point (e.g.,
entry_SYSCALL_64on x86)
3. Syscall Dispatching
- Syscall Number:
- The syscall number is stored in a register (e.g.,
RAXon x86,R7on ARM). - Example:
__NR_read(syscall number forread()).
- The syscall number is stored in a register (e.g.,
- Syscall Table:
- The kernel uses
sys_call_table(array of function pointers) to find the handler. - Example:
sys_call_table[__NR_read]points tosys_read().
- The kernel uses
4. Handler Execution in Process Context
Generic Steps for All Syscalls:
- Argument Validation:
- Check pointers (e.g.,
bufinread()) usingaccess_ok() - Copy arguments from user space with
copy_from_user()orget_user()
- Check pointers (e.g.,
- Kernel Function Execution:
- Perform the requested operation (e.g., read from a file, send an
ioctlcommand)
- Perform the requested operation (e.g., read from a file, send an
File Operations (read/write):
- File Descriptor Resolution:
- Convert
fdto astruct fileusingfdget(). - Check file permissions (
FMODE_READ/FMODE_WRITE).
- Convert
- Driver Interaction:
- Call the
read/writemethod from the file’sfile_operationsstruct. - Example: For
/dev/mydevice, this invokes the driver’s.readfunction.
- Call the
I/O Control (ioctl):
- The
ioctlsyscall (sys_ioctl()) calls the driver’s.unlocked_ioctlmethod.
5. Return to User Space:
- Result is stored in
eax/r0, and the kernel restores user registers - Execute
iret(x86) or exception return (ARM) to resume user-mode execution.
4. Device File Operations
Character devices (e.g., /dev/char_dev) expose operations via file_operations:
struct file_operations {
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long);
// ...
};
Examples:
- [3-Resource/Platform/Character Device Management in Kernel Drivers|Character Device Management in Kernel Drivers]({< ref “/posts/3-resource/platform/character-device-management-in-kernel-drivers|character-device-management-in-kernel-drivers/” >}})
- [3-Resource/Platform/IOCTL in Kernel Device Drivers|IOCTL in Kernel Device Drivers]({< ref “/posts/3-resource/platform/ioctl-in-kernel-device-drivers|ioctl-in-kernel-device-drivers/” >}})
5. Signal Handling (Ctrl+C)
- Ctrl+C sends a
SIGINTto the foreground process. - Kernel Flow:
- The terminal driver (e.g.,
tty_io.c) receives the interrupt. - The kernel’s signal-handling code (
kernel/signal.c) deliversSIGINTto the process. - The process’s signal handler (if registered via
signal()orsigaction()) is invoked.
- The terminal driver (e.g.,
- Key APIs:
send_signal(): Kernel function to queue a signal.do_signal(): Handles signal delivery during return to user space.
6. Example: Tracing the read() Syscall
- User-Space:
read(fd, buf, 100); // Invokes syscall via libc
- Kernel-Space:
sys_read()resolvesfdto astruct file.- Calls
vfs_read(), which invokes the driver’s.readmethod. - Driver copies data from device to kernel buffer, then to user space using
copy_to_user().
7. Key Kernel APIs and Modules
APIs for Syscalls:
SYSCALL_DEFINE{0-6}: Define syscall handlers (e.g.,SYSCALL_DEFINE3(read, ...)).copy_to_user()/copy_from_user(): Safely copy data between kernel and user space.get_user()/put_user(): Access single values in user memory.
APIs for Device Drivers:
register_chrdev(): Register a character device.unregister_chrdev(): Unregister a device.class_create()/device_create(): Create device nodes in/dev.
APIs for Signals:
kill_pid(): Send a signal to a processsigaction(): User-space API to register signal handlers
8. Critical Concepts to Know
- Syscall Table: Architecture-specific table mapping syscall numbers to handlers.
- Process Context vs. Interrupt Context:
- Syscalls run in process context (can sleep).
- Hardware interrupts run in interrupt context (atomic).
- Device File Operations:
file_operationsstruct ties syscalls to driver functions. - User/Kernel Boundary: Use
copy_to_user/copy_from_userto safely exchange data. - Signals: Delivered via
send_signaland handled during syscall return.