学校学习操作系统,上机使用的环境是Ubuntu 10.4,内核上也就是Linux 2.6,但是现在Linux内核版本已经4.x了啊喂。主要更难过的是,上机给的示例代码在4.x的机子上根本就没法运行,因为系统调用的接口已经变了。就这样我也就忍了,自己找找新的接口定义也就完了。

结果接下来要求添加一个系统调用,这回好了,添加系统调用的文件夹直接就不存在。到网上去找也是各种最高才到3.x的教程,简直没有办法能做下去,幸而再后来还是找到了一两篇中文的教材,没至于沦落到啃英文手册的地步,不过看来自己是非总结一下不可了。

本机环境是

1
2
$ uname -r
4.13.4-gentoo

首先需要进到/usr/src/linux/,并确保你有root权限。

系统调用函数

这里需要修改的是include/linux/syscalls.h,这里的最后加上你需要的系统调用的函数申明,比如:

1
asmlinkage long sys_mycall(int number);

需要加在最后一行#endif的前面。

其中asmlinkage是一个宏,定义在include/linux/linkage.h中。它的意思是使C使用栈的方式来传递参数,而不是像一般的C程序一样使用寄存器。这样取用参数就可以通过push和pop语句直接进行了,不需要直接寄存器来传值。这种方式据说在很高度优化的程序当中存在着,无论怎么说,大部分系统调用都有这么一个前缀。

添加系统调用应该需要在kernel文件夹中新建一个文件,然后在里面写入函数,但是为了简单起见(不用修改Makefile),就只修改kernel/sys.c这个文件即可。在文件的最后加上这么一个函数:

1
2
3
4
asmlinkage long sys_mycall(int number) {
printk("This is my call.\n");
return 0;
}

这个函数唯一的功能就是使用printk函数输出了一句话。

在系统调用表中添加函数

arch/x86/entry/syscalls/syscall_64.tbl,这个文件对应2.6内核中的系统调用表arch/x86/kernel/syscall_table_32.S。64是因为对应了64位的操作系统。

在文件尾部按照前面的格式添上你的系统调用就好了,注意第一个数字是系统调用号,不要覆盖原有的系统调用。

1
2
3
4
331	common	pkey_free		sys_pkey_free
332 common statx sys_statx

333 64 mycall sys_mycall

系统调用号需要记一下,等一下测试的时候会用到。

编译内核

在这之后,回到/usr/src/linux,输入以下指令:

1
2
3
4
make oldconfig
make -j8
make modules_install
make istall

重启

1
reboot

然后你就已经在内核中添加了你自己的系统调用了。

测试你的代码

1
2
3
4
5
6
#include <uninstd.h>

int main() {
symcall(333, 10);
return 0;
}

这会使用你新加入的系统调用号为548的系统调用。传入参数10。这样就会调用sys_mycall函数里面的printk它输出的内容可以使用dmesg来看到,但是如果不想跟其他系统调用输出的内容混在一起,最好使用grep:

1
dmesg | grep "my call"

如果一切正确,你应该能看到你的输出。

如果你传到系统调用的是一个指针,请务必小心,这可能会引起一个错误。因为内存的地址跟程序里的地址是不一样的。