1

Linux添加系统调用

分享一下从编译内核到Linux添加系统调用的过程吧。

一、准备

我使用的是ubuntu 16.04 LTS,下载的是4.4.25的内核。
首先要注意硬盘至少需要30g,不然会爆掉...别问我怎么知道的。内存分配的稍微大点就好。

内核下载地址

二、编译内核

编译内核其实很简单~

我们先切换到root用户,把下载好的内核压缩文件放到/usr/src下,然后进入这个目录解压

xz -d linux-4.4.25.tar.xz
tar -xvf linux-4.4.25.tar

然后如果不是第一次编译或者编译出错需要重新编译最好都执行一下make mrproper
执行这条命令需要先安装一个包

apt-get install libncurses5-dev

我们是刚下载的的干净的内核源码包,所以不用执行make mrproper这条命令。

make mrproper -Remove all generated files + config + various back files

make clean - Remove most generated files but keep the config and enough build support to build external modules

所以我们可以知道make mrproper和make clean的区别主要就是前者会删除所有的编译生成文件,而后者会保留配置文件.config,还有足够的扩展模块的支持。所以其实想清除编译残留数据的话执行make clean就可以了。实际上make mrproper一开始就会调用make clean的。

好了 下面我们要配置内核选项了

make menuconfig

这个就去网上找具体点的吧 选项太多
会出来一个图形界面 配置好exit会提示save即可。


之后就是激动人心的编译内核时刻
我们先安装一个包

apt-get install libssl-dev

生成启动映像文件

make bzImage

然后去看个番什么的等吧 不过可能要多看几集

编译完成之后继续编译模块

make  modules

注意我们一直是在/usr/src/linux-4.4.25这个文件夹下操作的,这次时间就更长了。我的配置一般i5而且是虚拟机,加上编译内核一共可能要几个小时,所以去打会球吧=。=

模块编译好之后的步骤都很快了,
安装模块

make modules_install

然后要建立载入虚拟内存盘的编在内核中的根文件系统镜像

mkinitramfs 4.4.25 -o /boot/initrd-4.4.25.img

配置grub引导

update-grub2

最后重启系统就大功告成了

reboot

最后可以通过uname -a查看内核版本

三、添加新的系统调用

1.分配系统调用号
先去查看一下系统的调用号使用到多少了,查找一下系统调用表
/usr/src/linux-4.4.25/arch/x86/entry/syscalls/syscall_64.tbl
我的版本使用到了325,所以我新的系统调用用326号。注意文件里要看属于x64的系统调用号。
然后我们修改/usr/include/asm-generic/unistd.h设置系统调用号
添加

  #define __NR_saulcall 326
  __SYSYCALL(__NR_saulcall,sys_saulcall)

并且将后面的__NR_syscalls的号加1。

ps: 起名困难症 不过一看到call就想起better call saul~ 就用了这个

2.修改系统调用表,关联调用号与调用的服务例程地址
/usr/src/linux-4.4.25/arch/x86/entry/syscalls/syscall_64.tbl

326       64        saulcall   sys_saulcall

3.编写服务例程
要为我们的服务写程序了
/usr/src/linux-4.4.25/kernel/sys.c
我这里是一个简单的实现读取进程的nice值和修改进程nice值的服务
参数flag为1时修改nice
参数为0时读取nice

SYSCALL_DEFINE3(saulcall, pid_t, pid, int, flag, int, nicevalue){    
    int error = 0;
    struct task_struct *p;
    for(p = &init_task;(p = next_task(p)) != &init_task;){    
        if(p->pid == pid){
            if(flag == 0){
                printk("the process's nice = %d",task_nice(p));
            } else if(flag == 1){
                printk("the process changed to %d",nicevalue);
                set_user_nice(p,nicevalue);
            } else {
                error = -EFAULT;
            }
            return error;
        }
    }
    error = -EFAULT;
    return error;
}

SYSCALL_DEFINE3的3即为参数个数,其他就不过多解释了,程序轻喷....

4.重新编译内核
不重新编译无法生效...等吧=。=

5.编写用户态程序测试
下面就可以写程序测试啦
得引入下头文件

#include <linux/unistd.h>
#include <sys/syscall.h>

然后可以选择一个进程调用我们的服务修改或者读取nice值啦,
由于使用了printk,我们要用dmesg查看输出信息。
可以使用top -p pid查看指定进程的信息。

提供一个小demo:
首先我们可以查找一个指定进程的pid,可以用pgrep加指定进程名,
也可以用top命令显示各个进程资源占用,然后找一个进程的pid来实验我们的系统调用,比方说我先打开火狐浏览器,然后假设用top命令看到firefox的pid为4325,程序就可以如下来写

#include <linux/unistd.h>
#include <sys/syscall.h>
int main()
{
    syscall(__NR_saulcall,4325,1,15); // 修改4325进程的nice值为15
    return 0;
}

再次使用Top查看firefox进程的nice值可以看到已经被更改成我们想要的数值。

over.


lunaticf
136 声望7 粉丝