以前虽然知道原理,但是没真的实践过,这次就留个记录吧

获取内核源码

apt-get提供了一个非常便捷的方式下载内核源码

apt-get source linux-image-$(uname -r)

完成后将会在你的当前目录下产出对应的压缩包及解压后的目录linux-x.x

向 syscall table 添加入口

用你最喜欢的编辑器打开 arch/x86/entry/syscalls/syscall_64.tbl,追加一行

322     64      execveat                stub_execveat
323     common  userfaultfd             sys_userfaultfd
324     common  membarrier              sys_membarrier
325     common  mlock2                  sys_mlock2
326     common  hello                   sys_hello      <---- 加在这一行
实现 syscall

打开 include/linux/syscalls.h,追加一行

asmlinkage long sys_membarrier(int cmd, int flags);

asmlinkage long sys_mlock2(unsigned long start, size_t len, int flags);

asmlinkage long sys_hello(void);  <---- 加这一行

在内核源码根目录下

mkdir hello
touch hello/hello.c
touch hello/Makefile

打开 hello/hello.c,写入

#include <linux/kernel.h>

asmlinkage long sys_hello(void)
{
    printk("Hello world\n");
    return 0;
}

打开 hello/Makefile,写入

obj-y := hello.o

打开 Makefile,将

core-y          += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/

改为

core-y          += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ hello/
编译内核

make menuconfig

如果你不需要改参数的话直接保存退出就好了

接着开始编译

make -j4  #根据你的逻辑CPU数调整

编译完成后安装

sudo make modules_install install

重启咯,注意下grub选单,免得引导的内核版本不是这次编译的

PS: 如果之前没编译过内核,编译过程中可能会报错,一般都是缺依赖,对应装一下就好

测试

随便在哪新建一个 test.c,内容如下

#include <stdio.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
 
#define __NR_hello 326
 
long hello_syscall(void)
{
    return syscall(__NR_hello);
}
 
int main(int argc, char *argv[])
{
    long int a = hello_syscall();
    printf("System call returned %ld\n", a);
    return 0;
}

然后

gcc test.c
./a.out

这时候应该输出

System call returned 0

那怎么知道确实执行了添加的系统调用呢?从dmesg里找,因为printk会把信息打印到这里

dmesg | tail -n 100

就能看到 hello 系统调用的输出

[  137.317782] Hello world