系統調用核心原理
- 用戶態 → 核心態:通過軟中斷
int 0x80 (x86) 或 syscall 指令 (x86_64) 觸發異常,CPU 自動切換特權級。
- 參數傳遞:系統調用號放入
EAX/RAX,參數依次放入 EBX, ECX, EDX, ESI, EDI, EBP (x86) 或 RDI, RSI, RDX, R10, R8, R9 (x86_64)。
- 內核入口:
entry.S 中 system_call 保存現場 (SAVE_ALL),校驗調用號,查表 sys_call_table 跳轉處理函數,返回值存入 EAX,恢復現場 (RESTORE_REGS) 執行 iret 返回用戶態。
添加系統調用步驟 (以 x86_64 為例)
- 定義系統調用號:
// arch/x86/entry/syscalls/syscall_64.tbl
548 common mycall sys_mycall
- 聲明內核函數原型:
// include/linux/syscalls.h
asmlinkage long sys_mycall(int arg1, char __user *arg2);
- 實現系統調用:
// kernel/sys_mycall.c
#include
#include
#include
SYSCALL_DEFINE2(mycall, int, arg1, char __user *, arg2) {
char buf[128];
if (copy_from_user(buf, arg2, sizeof(buf)-1))
return -EFAULT;
buf[sizeof(buf)-1] = '\0';
printk(KERN_INFO "mycall: arg1=%d, arg2=%s\n", arg1, buf);
return 0;
}
- 編譯內核:
make menuconfig # 確保選中 General setup -> [*] Enable system call auditing 等
make -j$(nproc)
make modules_install install
- 用戶空間調用:
// user_test.c
#include
#include
#include
#define __NR_mycall 548
int main() {
long ret = syscall(__NR_mycall, 42, "Hello Kernel");
printf("syscall returned: %ld\n", ret);
return 0;
}
關鍵數據結構
系統調用表 (sys_call_table)
// arch/x86/kernel/sys_call_table.S (簡化)
.data
ENTRY(sys_call_table)
.long sys_restart_syscall /* 0 */
.long sys_exit /* 1 */
.long sys_fork /* 2 */
.long sys_read /* 3 */
.long sys_write /* 4 */
.long sys_open /* 5 */
...
.long sys_mycall /* 548 */
現場保存/恢復宏 (entry.S)
#define SAVE_ALL \
cld; \
pushq %rax; pushq %rdx; pushq %rcx; pushq %rbx; \
pushq %rsi; pushq %rdi; pushq %rbp; pushq %r8; \
pushq %r9; pushq %r10; pushq %r11; pushq %r12; \
pushq %r13; pushq %r14; pushq %r15; \
movq %rsp, %rdi; /* 將 pt_regs 指針傳給 C 處理函數 */
#define RESTORE_ALL \
popq %r15; popq %r14; popq %r13; popq %r12; \
popq %r11; popq %r10; popq %r9; popq %r8; \
popq %rbp; popq %rdi; popq %rsi; popq %rdx; \
popq %rcx; popq %rbx; popq %rax; \
iretq
現代內核注意事項 (5.10+)
- syscall 表分離:
syscall_64.tbl 自動生成 sys_call_table 和 syscalls.h,無需手動編輯彙編文件。
- SYSCALL_DEFINE 宏:自動處理參數拷貝、兼容層、追蹤點,推薦使用
SYSCALL_DEFINE2(name, type1, arg1, type2, arg2)。
- 兼容性:32 位程序在 64 位內核調用需在
syscall_32.tbl 同步添加,並實現 compat_sys_xxx。
- 安全性:嚴格使用
copy_from_user/to_user 訪問用戶指針,校驗指釖有效性 (access_ok)。
驗證與調試
# 查看系統調用是否註冊
grep mycall /proc/kallsyms
# 內核日志
dmesg | grep mycall
# 追蹤系統調用
strace -e mycall ./user_test