如何实现一个系统调用
在 Linux 4.19 内核中实现一个系统调用,主要分为以下四个步骤。以 x86_64 架构为例,假设我们要实现一个名为 sys_mysyscall 的简单系统调用:
- 实现系统调用函数
在内核源码中编写你的功能代码。通常可以放在 kernel 目录下,或者在特定模块中。 使用 SYSCALL_DEFINE 宏来定义函数,这有助于处理安全检查和参数传递。
文件:sys.c (或新建一个文件)
/* 定义一个接收 2 个参数的系统调用 */
SYSCALL_DEFINE2(mysyscall, int, a, int, b)
{
int result = a + b;
/* printk 会将信息输出到内核日志缓存区,可通过 dmesg 查看 */
printk("============= [MySyscall] Kernel Hello! a=%d, b=%d =============\n", a, b);
return result;
}
注:SYSCALL_DEFINEx 中的 x 代表参数个数(0-6)。
- 注册系统调用号
你需要为新的系统调用分配一个唯一的编号。在 x86_64 架构中,这在系统调用表中完成。
文件:syscall_64.tbl 找到文件末尾,添加一行:
# [系统调用号] [ABI] [名称] [内核函数入口]
548 common mysyscall __x64_sys_mysyscall
注:548 是顺延的下一个可用编号。
- 添加函数原型声明
在内核头文件中声明该系统调用,以便其他内核组件可以识别。
文件:syscalls.h 在文件末尾(#endif 之前)添加:
asmlinkage long sys_mysyscall(int a, int b);
- 编译并测试
重新编译内核:执行 make -j$(nproc) 并更新启动镜像。
编写用户态测试程序:
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#define SYS_MYSYS 548 // 必须与 syscall_64.tbl 中的编号一致
int main() {
long ret = syscall(SYS_MYSYS);
if (ret == 0) {
printf("System call executed successfully! Check 'dmesg' for output.\n");
} else {
perror("System call failed");
}
return 0;
}
关键点总结
架构差异:不同架构(如 ARM, x86)的系统调用表路径不同。
参数传递:内核态与用户态通信时,指针必须使用 __user 修饰,并使用 copy_from_user 或 copy_to_user 进行安全拷贝。
返回值:系统调用通常返回 long 类型,成功返回 0 或正数,失败返回负的错误码(如 -EFAULT)。
你可以通过 dmesg 命令查看内核输出的 Hello from kernel space! 日志来验证是否成功。

浙公网安备 33010602011771号