Linux添加系统调用总结
最近在做操作系统的课程设计,其中一题就是给linux系统添加一个copy功能的系统调用,折腾之后总结一下:
其实一开始我是不打算写的,但是发现老师的课设指导实在是太过时了,linux内核这都更新到4.4了,而老师的指导是2.4的,真是差得太多了,涉及到的文件现在已经找不到了,而google出来的最新也就3.16,所以我还是总结一下。
什么是系统调用?(http://blog.csdn.net/m6830098/article/details/9056457)
Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己的应用程序中调用它们。从某种角度来看,系统调用和普通的函数调用非常相似。区别仅仅在令在自己的应用程序中调用它们。从某种角度来看,系统调用和普通的函数调用非常相似。区别仅仅在于,系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,运行于用户态。二者在使用方式上也有相似之处。Linux系统的核心部分即是Linux内核,是一系列设备的驱动程序。系统调用是Linux内核提供的功能十分强大的一系列的函数。这些函数是在内核中实现的,它们是应用程序和内核交互的接口,系统调用在Linux系统中发挥着巨大的作用,如果没有系统调用,那么应用程序就失去了内核的支持。
下载内核源代码
可以在github或https://www.kernel.org/上进行下载。我下载的是linux-4.4.4.tar.xz解压
tar -xvf linux-4.4.4.tar.xz添加系统调用号
vim ./arch/x86/entry/syscalls/syscall_64.tbl(32位系统是syscall_32.tbl)
我在后面添加了两个系统调用函数,sys_hello和sys_mycopy,326和327就是系统调用号。声明系统调用函数原型
vim include/linux/syscalls.h
其中的asmlinkage用在大多数的系统调用中。有一些情况下是需要明确的告诉编译器,我们是使用stack来传递参数的,比如X86中的系统调用,是先将参数压入stack以后调用sys_*函数的,所以所有的sys_*函数都有asmlinkage来告诉编译器不要使用寄存器来编译。添加系统调用函数的定义
vim kernel/sys.c
注意此处的sys_hello参数表中如果没有指明void,编译时是会报错的。
另外,在内核中创建对文件的操作和在用户空间中对文件的操作有些许不同,下面是几个函数的对应关系。用户空间 内核 open sys_open close sys_close read sys_read write sys_write 系统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数,它默认会认为来自用户空间,在write()函数中,为了保护内核空间,一般会用get_fs()得到的值来和USER_DS进行比较,从而防止用户空间程序“蓄意”破坏内核空间;
get_ds: 获得kernel的内存访问地址范围
get_fs: 取得当前的地址访问限制值
set_fs: 设置当前的地址访问限制值
在进行文件操作时,暂时将访问限制值设置为内核的内存访问范围,然后再修改为原来的值。编译内核
sudo menuconfig 直接选择save,然后退出即可,生成.config
sudo make 编译开始,我是双核四线程,等待了不到20分钟。
sudo make modules_install 安装模块
sudo make install 安装内核
(此处大多数系统就已经成功了,跳至第10步)
但是我的archlinux在执行make insatll中提示我说没有找到LILO程序,无法添加到我的grub引导之中,进而有了第七步。将生成的内核文件(arch/x86_64/boot/bzImage),复制进/boot中,注意保存原有内核文件。
添加引导到grub
编辑grub的配置文件/boot/grub/grub.cfg,添加如下内容
注意linux /bzImage中的bzImage就是我们生成的内核文件,如果你没有将boot单独分区,则应该写为linux /boot/bzImage
图中的配置并非所有都是必需,比如if段就可以不要,root=”hd0, msdos2”,表示第一块硬盘的第二个分区(boot分区,如果没有将boot单独分区,请指定为/分区);其中root=UUID=….部分可以用root=/dev/sda2代替。重启,选择进入新的内核。
测试系统调用
- sys_hello
syscall的参数为系统调用号。
由于sys_hello里面有一条printk语句,该语句会将此输出作为系统的日志,通过dmesg命令查看。
由于日志并没有立即输出,我又执行了一次测试程序,日志便将两次的都输出了。 - sys_copy
不在叙述,注意给系统调用传递的参数直接加在syscall函数的参数表中就行了,例如syscall(327, from_file, to_file)
- sys_hello
以上。