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.0
busybox
版本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. 未经许可不得用于商业用途. 转载请注明出处.