新增 Linux 系统调用

问题描述

新增一个 Linux 系统调用,实现功能:计算一个数字三次方,并打印出来。
具体实现时有三个内容:

  • 需要重新编译 Linux 内核;
  • 增加一个 Linux 的系统调用;
  • 另外写一个程序进行调用。

环境

Windows 10 上 VMware Workstation 14 Pro,Ubuntu(64-bit) 18.04
原内核版本:4.15.0
编译内核版本:4.9.109
:若内核版本差距较大,在本文中涉及到的文件可能与实际不同)

详细步骤

前期准备

在 VMware Workstation 上安装 Ubuntu 操作系统。具体步骤可以看这里
要注意的是,在安装虚拟机前,给系统预留的磁盘空间应该为 40 GB 或更大,否则在解压缩内核文件或编译过程中都会报错。假如你的计算机是多核的,那可以相应地给虚拟机设置多个处理器,这会大大节约后面的内核编译时间。

装好系统后,开机,利用快捷键 Ctrl+Alt+T 打开终端。输入命令 uname -a ,查看该虚拟机系统的初始内核版本,这里是 4.15.0

www.kernel.org 上下载另外一个不同版本的内核,这里的版本号为 4.9.109。最好选择与初始版本接近的内核来编译。
su 命令进入 root,依次输入以下命令:

1
2
3
4
mv linux-4.9.109.tar.xz '/usr/src'
mv '/usr/src'
xz -d '/usr/src/linux-4.9.109.tar.xz'
tar -xv -f '/usr/src/linux-4.9.109.tar'

移动并且解压缩我们下载的内核文件。
于是我们得到了 Linux 的内核文件,接下来的操作都在此文件夹(*/usr/src/linux-4.9.109*)中进行。

添加系统调用

接下来是修改内核文件。用 gedit 文本编辑器修改 kernel/sys.c 文件,先是加入 linkage.h 头文件:

然后加入系统调用函数,注意要绕过条件编译,也就是 # 间的内容:

修改好了,保存关闭文件。

用 gedit 文本编辑器修改 arch/x86/include/asm/syscalls.h 文件,添加系统调用函数的声明,保存并关闭。如下图:

用 gedit 文本编辑器打开 arch/x86/entry/syscalls/syscall_64.tbl 文件,由于安装了64位系统,所以修改此文件。发现文件中的系统调用编号到 331为止,于是添上 332 号,注意不可以抢占其他已经存在的调用号,按照文件开头注释中标注的 “ ” 格式加入新增内容,如下图所示:

配置和编译内核

首先配置编译环境,在 root 状态下输入以下四条命令,安装编译所必要的软件包。

1
2
3
4
apt-get install libncurses5-dev libssl-dev
apt-get install build-essential openssl
apt-get install zlibc minizip
apt-get install libidn11-dev libidn11

如果在安装过程中出现错误,可以输入 apt-get update 命令更新,再继续安装。

如果是第一次编译,那就使用 make menuconfig 进入配置菜单(如下图),否则输入 make mrproper 清除之前的编译配置。

直接选择默认配置选项,使用键盘上的方向键选择 Exit 退出保存即可。

接下来开始编译内核,命令为 make ,如果在第一步时给虚拟机设置了多个内核,则可以在后面加上 -j** 表示内核个数),可以加快编译速度。如果不知道系统的内核数量,使用命令 lscpu 查看,可以看见我这里的系统是双核。

在编译过程中,要随时注意报错提示,及时解决。不过假如之前的步骤没有做错,大概率是没有错误的。如果有,请善用搜索引擎。
编译时间视机器性能而定。漫长的编译结束后,使用 make modules_installmake install 两个命令安装内核模块和内核

测试结果

重启系统,在开机时的虚拟机模式下,不停地按 Shift 进入 BIOS 界面:

选择第二个高级选项,这里可以看见我们的新内核了!选择进入新编译好的内核版本 4.9.109

在终端里再次查看内核,已经是更新后的版本了:

编写一个测试程序,其中的 332 就是系统调用号,后面的 3 是传入的参数:

1
2
3
4
5
6
7
8
#include <stdio.h>
#include <stdlib.h>

int main(){
long t=syscall(332,3);
printf("%ld\n",t);
return 0;
}

使用 gcc-o my test.c 命令编译程序,使用 ./my 命令运行,结果如下:

计算结果正确!你还可以继续修改传入的参数反复验证结果。

到这里,我们就成功地添加了一个 Linux 的系统调用。

参考资料:

  1. ubuntu16.04系统下的kernel 4.10的内核编译以及新增系统调用
  2. Linux添加系统调用
  3. 如何编译 Linux 内核
  4. Adding a New System Call