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 0x80
on x86) or uses thesyscall
instruction (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_64
on x86)
3. Syscall Dispatching
- Syscall Number:
- The syscall number is stored in a register (e.g.,
RAX
on x86,R7
on 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.,
buf
inread()
) 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
ioctl
command)
- Perform the requested operation (e.g., read from a file, send an
File Operations (read
/write
):
- File Descriptor Resolution:
- Convert
fd
to astruct file
usingfdget()
. - Check file permissions (
FMODE_READ
/FMODE_WRITE
).
- Convert
- Driver Interaction:
- Call the
read
/write
method from the file’sfile_operations
struct. - Example: For
/dev/mydevice
, this invokes the driver’s.read
function.
- Call the
I/O Control (ioctl
):
- The
ioctl
syscall (sys_ioctl()
) calls the driver’s.unlocked_ioctl
method. !../3-Resource/Platform/IOCTL in Kernel Device Drivers#3. Integrate into file_operations
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:
- Character Device Management in Kernel Drivers
- IOCTL in Kernel Device Drivers
5. Signal Handling (Ctrl+C)
- Ctrl+C sends a
SIGINT
to 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
) deliversSIGINT
to 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()
resolvesfd
to astruct file
.- Calls
vfs_read()
, which invokes the driver’s.read
method. - 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_operations
struct ties syscalls to driver functions. - User/Kernel Boundary: Use
copy_to_user
/copy_from_user
to safely exchange data. - Signals: Delivered via
send_signal
and handled during syscall return.