linux添加系统调用(4.x版本)
Contents
由于本学期开设了操作系统课程, 需要我们编译
linux内核, 以及添加系统调用. 但是, 老师给的教程是基于2.x版本的内核, 在GCC版本较高时, 编译内核基本是没法进行的.同时, 由于使用的宿主系统如果直接更换内核, 可能会对用户造成困扰, 因此, 本教程中的所有操作均不需要对原系统有所修改, 系统调用的测试均放在
qemu这个虚拟机中.如果大家对于
Linux不熟悉, 新建和编辑文件可以使用gedit编辑器下面列出了主机的几个比较重要的环境
- 将要编译的内核版本
4.10.3(2017-03-15稳定版) - 宿主Linux版本
4.10.2-1-ARCH - 编译器版本
gcc 6.3.1 qemu版本2.8.0busybox版本1.26
本教程的使用时间是有限制的, 如果未来
Linux内核结构与此不同, 请勿进行使用, 或者联系笔者将此教程撤下, 以免误人子弟
Linux内核编译
内核下载

- 下载完成后解压
1 | xz -d linux-4.10.3.tar.xz |

- 编译及启动
若你使用的是
Ubuntu, 那么编译过程中出现的所有问题可以直接百度解决
1 | make menuconfig # 默认就好, 精简可以加快编译速度 |
笔者大概用了15分钟 (CPU: i5-4200M)
使用qemu启动我们编译好的内核
我们刚刚编译完成的只是一个
linux内核, 它具有很强大的功能, 但是只有内核是无法称之为系统的, 这里, 我们使用busybox制作根文件系统, 为了方便大家, 我在这里直接提供一份我已经制作好的系统
从这里下载world.img
qemu简单使用(此时我们在内核目录中)
1 | qemu-system-x86_64 -kernel arch/x86_64/boot/bzImage \ |
- 在此解释下使用的参数
1 | -kernel arch/x86_64/boot/bzImage # 指定内核 |
启动成功之后如图所示, 简易的linux系统就这样搭建完成了.

添加系统调用
定义新的系统调用
- 确保你在内核目录
1 | mkdir $KERNEL/hello |
- 自行新建文件
hello.c,hello.c内容如下图所示(cat查看文件内容)
version_changed: 2019-08-30 感谢一位学弟指正, 在hello.c中定义sys_hello是有问题的, 而应该使用
SYSCALL_DEFINE的形式, 这个问题与CVE-2009-0029相关, 我并没有深入了解. 不过, 自己定义的系统调用最好也和其他内核的系统调用保持一致.请把下面代码中的
asmlinkage long sys_hello(int arg0)修改为SYSCALL_DEFINE1(hello, arg0)

添加到系统调用表中
- 修改
syscall_64.tbl
在4.10.3中, 该文件在arch/x86/entry/syscalls/syscall_64.tbl中
此时我们在文件末尾添加我们自己的系统调用

- 修改
sysclass.h
在4.10.3中, 该文件在include/linux/syscalls.h中
也是在末尾添加我们的系统调用

添加到内核编译的Makefile中
- 在
hello目录下新建Makefile,Makefile内容如下

- 修改全局主
Makefile

全局主
Makefile就是内核目录下的Makefile, 大概是在900多行, 有这样一句话
1 | core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ |
现在我们将其改为如下, 也就是添加了hello文件夹
1 | core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ hello/ |
重新编译内核
做完以上的工作就可以重新编译内核了, 可以直接
make -j4, 不需要make menuconfig. 本来由于上一次的编译有中间文件, 此次会很快. 可惜添加了系统调用之后基本整个内核都要重新编译了, 希望大家有耐心
检验我们新添加了系统调用
使用系统调用的程序, 首先我们要将程序加入到根文件夹中, 才可以通过
qemu虚拟机进行调用执行. 因此, 我们首先将根文件系统挂载在本地目录, 然后将编译好的测试程序添加到根文件系统中, 也就是拷贝到该目录中
在宿主机上挂载world.img文件
1 | mkdir world_mount # 新建文件夹, 稍后会将程序拷贝到此文件夹 |
如图所示

编写程序实现系统调用
- 系统调用程序
刚才我们添加的系统调用号为
551, 并且接受一个int参数, 所以, 此处函数syscall(551, 24), 若有疑问可以查阅之前的hello.c文件 相对比之下更容易理解,call.c文件如下所示
1 |
|
- 保存后可以直接编译
1 | gcc -o call call.c -static |
- 拷贝程序到根文件系统中
1 | sudo cp call world_mount/ |
- 使用
qemu启动系统
1 | qemu-system-x86_64 -kernel arch/x86_64/boot/bzImage \ |
这时, 启动后的系统如下图所示, 同时, 使用
./call运行程序, 由于我们的系统调用中使用了printk函数, 会由内核输出信息, 可以通过dmesg进行查看, 这时, 可以看到系统打印出来了内容

- 最终的目录结构大概是这样的
1 | . |
参考资料
LICENSE:
Copyright © 2016 corvo. 未经许可不得用于商业用途. 转载请注明出处.