Step 0 简述

1. 运行环境

• 平台:Google Cloud Platform
• Linux 发行版:Ubuntu 20.04 LTS
• 配置:Intel Skylake:e2-highcpu-16(16 个 vCPU,16 GB 内存)
• Linux 源码版本:version=5.4.80

2. 添加系统调用函数说明

在官方内核的基础上,添加一个自定义的系统调用 setnice,功能如下:
1. 按需修改指定进程的 nice 数值;
2. 读取该进程的 nice 与优先级(priority)的数值。

Step 1 准备工作

1. 下载 Linux 源码

version=’5.4.80’
host=’https://cdn.kernel.org/pub/linux/
wget $host/kernel/v5.x/linux-$version.tar.{xz,sign}
Linux 添加系统调用函数 - 图4

2. 验证签名

看到 Good signature 即证明源码未损坏
gpg —locate-keys torvalds@kernel.org gregkh@kernel.org # download and import keys
gpg —verify linux-$version.tar.sign # verify the .tar archive against the signature
Linux 添加系统调用函数 - 图5

3. 解包源码

tar —extract —file=linux-$version.tar & progress —monitor —pid $!
cd linux-5.4.80/
图略

4. 修改内核版本号

修改内核版本号,方便后续验证编译成果。
make nconfig # append your custom local version string
Linux 添加系统调用函数 - 图6

Linux 添加系统调用函数 - 图7

Step 2 添加系统调用

以下修改均在kernel-build/linux-5.4.80下
cd ~/kernel-build/linux-$version # kernel source code directory

1. 添加函数原型

cd include/linux/
vim syscalls.h

添加
asmlinkage long sys_setnice(pid_t pid, bool set, int niceval, int user *nice, int user *prio);
Linux 添加系统调用函数 - 图8

2. 添加系统调用表

cd include/uapi/asm-generic/
vim unistd.h

末尾追加表项,NR_syscalls 也要相应添加
#define
NR_setnice 436
SYSCALL(NR_setnice,sys_setnice)

undef NR_syscalls
#define
NR_syscalls 437
Linux 添加系统调用函数 - 图9

3. x86 系统调用表

cd arch/x86/entry/syscalls/
# 按对应格式在列表末尾追加表项
vim syscall_64.tbl
# 添加
436 common setnice __x64_sys_setnice

vim syscall_32.tbl
# 添加
436 i386 setnice sys_setnice __ia32_sys_setnice
Linux 添加系统调用函数 - 图10

Linux 添加系统调用函数 - 图11

4. 添加入口点

cd kernel/

vim sys.c
# 添加如下代码

/
Set/get nice value and get priority value of a process with given ID;
Set new nice value only if parameter set is true;
Get values of nice and priority of the process at last.
/
SYSCALL_DEFINE5(setnice, pid_t, pid, bool, set, int, niceval,
int __user
, nice, int __user , prio)
{
int tmp;
struct task_struct
tsk = find_get_task_by_vpid(pid);
if (tsk == NULL)
return -ESRCH; // no such process

  1. if (set == true) // set new nice value<br /> set_user_nice(tsk, niceval);
  2. tmp = task_nice(tsk); // get nice value<br /> if (copy_to_user(nice, &tmp, sizeof(tmp)))<br /> return -EFAULT; // bad address
  3. tmp = task_prio(tsk); // get priority value<br /> if (copy_to_user(prio, &tmp, sizeof(tmp)))<br /> return -EFAULT; // bad address
  4. return 0;<br />}<br />![](https://cdn.nlark.com/yuque/0/2020/png/1571172/1606726187765-2f36d976-1ce4-4ff3-bd1f-fd651de622bd.png#height=234&width=420)<br /> <br />

Step 3. 编译内核

1. 编译内核模块

使用 time 命令可为编译计时;使用 —jobs=X 选项可并行工作以加快编译,其中 X 为处理器线程数,对应虚拟机的配置。
time make —jobs=16 # compile kernel
编译完成:
Linux 添加系统调用函数 - 图12

2. 安装内核模块

make modules_install # compile kernel modules
运行结束:
Linux 添加系统调用函数 - 图13

3. 安装内核

make install
Linux 添加系统调用函数 - 图14

4. 配置 grub 引导程序

配置 grub 引导程序 并 重启
update-grub2
reboot
Linux 添加系统调用函数 - 图15

Step 4. 测试系统调用

1. 查看版本号

重启后版本号后缀发生改变,说明已切换到新内核。
Linux 添加系统调用函数 - 图16

2. 运行测试程序

编译运行下述程序:
#include // syscall()
#include // pid_t, getpid()
#include // scanf(), printf()

define _NR_setnice 436 // syscall name and number_

int main(void)
{
printf(“[ Test program for syscall setnice ]\n”);
printf(“—————————————————————-\n”);
printf(“Will test on this process itself by default\n”);
printf(“or anohter process (need input process id).\n”);
printf(“—————————————————————-\n”);
printf(“:: Test on this process itself? [y/n] “);

  1. pid_t pid; _// pid number_<br /> char choice;<br /> scanf("%c%*c", &choice); _// choose process to test_<br /> **if** (choice != 'y') {<br /> printf(":: Process ID: ");<br /> scanf("%d", &pid);<br /> }<br /> **else** {<br /> pid = getpid();<br /> printf("Process ID: %d\n", pid);<br /> }<br /> choice = 'g'; _// get nice value at first time_<br /> printf("Original values:\n");<br /> int count = 1;<br /> **do** {<br /> int err, niceval, nice, prio;<br /> int set = (choice != 'g');<br /> **if** (set) {<br /> printf(":: New nice [-20, 19]: ");<br /> scanf("%d%*c", &niceval);<br /> }<br /> **if** (err = syscall(__NR_setnice, pid, set, niceval, &nice, &prio)) {<br /> printf("Error %d, test terminated.\n", err);<br /> **return** err;<br /> }
  2. printf("Nice: %d\n", nice);<br /> printf("Priority: %d\n", prio);<br /> printf(":: [%d] Set/get nice value? (Ctrl+C to exit) [s/g] ", count++);<br /> } **while** (scanf("%c%*c", &choice) != EOF);<br />}<br />cd cprogram/<br />touch test_setnice.c<br />vim test_setnice.c<br /># 将上述程序复制进去<br />gcc test_setnice.c -o test<br />./test<br />运行结果如下:<br />![](https://cdn.nlark.com/yuque/0/2020/png/1571172/1606726189555-5a59bb00-a6c0-47f2-93d5-348a2e0a3f30.png#height=270&width=420)<br />

参考资料

Linux 内核编译及添加系统调用
Adding a New System Call — The Linux Kernel documentation