【教程】操作系统实验一 - Linux 内核编译及添加系统调用
添加一个系统调用,实现对指定进程的 nice 值的修改或读取功能,并返回进程最新的 nice 值及优先级 prio。
视频教程地址:https://www.bilibili.com/video/av47274857
源码地址:https://github.com/leslievan/Operator_System/tree/master/Operator_System_Lab1
以下内容全部在 Ubuntu 18.04
下操作,使用其他发行版的同学可在此基础上自行修改。
实验内容
- 添加一个系统调用,实现对指定进程的 nice 值的修改或读取功能,并返回进程最新的 nice 值及优先级 prio。
- 写一个简单的应用程序测试添加的系统调用。
- 若程序中调用了 Linux 的内核函数,要求深入阅读相关函数源码。
实验步骤
获取内核源码
从 The Linux Kernel Archives 获取 Linux 的内核源码,任意选择一个版本,建议使用 longterm
版本。
戳这里下载 linux-4.19.113.tar.xz
。
解压内核源码
可以通过命令行或图形界面进行解压:
-
右键单击内核压缩包,点击解压
-
或在终端键入如下命令检查文件是否存在。(系统语言为中文的请将
Downloads
替换为下载
)。1 2
$ cd ~/Downloads $ tar xvJf linux-4.19.25.tar.xz
tar
命令参数解释参见附录 1-tar.
修改内核
修改系统调用表
根据上一步内核源码解压目录,定位系统调用表:
|
|
定位到 common/64
的最后一条,此处是334 common rseq __x64_sys_rseq
,在下面添加335 64 mysetnice __x64_sys_mysetnice
,系统调用号不固定,只需唯一即可。
申明系统调用服务例程原型
根据内核源码解压目录,定位系统调用头文件:
|
|
定位到最后一行,在 endif
前面添加系统调用原型声明:
|
|
实现系统调用服务例程
上一步对函数进行了声明,这里给函数一个具体的实现。
需要实现的任务为:
添加一个系统调用,对指定进程的 nice 值的修改及读取的功能,同时返回进程最新的 nice 值及优先级 prio。
分解为四步:
- 根据进程号 pid 找到相应的进程控制块 PCB(因为进程控制块中记录了用于描述进程情况及控制进程运行所需要的全部信息,nice 值和优先级正是其中的一部分);
- 根据 PCB 对相应进程的 nice 值和优先级 prio 进行读取;
- 根据 PCB 对相应进程的 nice 值进行修改;
- 重新从 PCB 中读取修改后 nice 值和 prio 值;
- 将得到的 nice 值和优先级 prio 进行返回。
根据内核源码解压目录,定位系统调用头文件:
|
|
定位到最后一行,在 endif
前面添加系统调用服务例程实现代码:
|
|
需要用到的几个内核函数:
-
struct pid *find_get_pid(pid_t nr)
根据进程标识符号返回相应的进程标识符
-
struct task_struct *pid_task(struct pid *pid, enum pid_type type)
根据进程标识符和进程类型返回进程控制块
-
int task_prio(const struct task_struct *p)
返回该 PCB 的 prio 参数
-
static inline int task_nice(const struct task_struct *p)
返回该 PCB 的 nice 参数
-
void set_user_nice(struct task_struct *p, long nice)
修改 PCB 的 nice 参数
-
static __always_inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
将变量从内核空间复制到用户空间
内核函数具体实现见附录 2
编译内核
安装依赖
以下是一些需要用到的包,用 apt/apt-get 进行安装,命令行输入:
|
|
确认参数
定位内核源码解压目录,命令行运行:
|
|
命令行运行 make
,开始编译前的参数确认:
|
|
出现如下所示界面后,左右键移动下方光标选中 Save
,按 Enter
结束。
开始编译
编译内核需要耗费的时间较长,建议通电进行。
定位内核源码解压目录,命令行运行:
|
|
-j4
表示使用四线程进行编译,这个过程大概持续一个小时,后面的重定向将错误信息输出到了 error.log
这个文件里面,方便之后进行错误排查。
make
命令默认是指编译所有,包括内核和模块,所以不需要再重新使用make modules
进行模块的编译(至少我并没有在这个地方受到困扰)。
如果碰到问题请查阅附录 3。
安装内核
等待内核、模块均编译完成,开始安装内核,分为两步:
-
安装模块
1 2
# 大约持续十几分钟到几十分钟不等 $ sudo make modules_install
-
替换内核
1 2
# 大约持续几分钟到十几分钟不等 $ sudo make install
替换完成后重启你的电脑,准备下一步的测试,如果电脑打不开可以参考附录 3。
测试
完成编译工作后,需要编写一个用户态程序,测试系统调用是否正常工作,这里直接给出 demo.c
,请自行查阅理解。
|
|
命令行使用 gcc
进行编译,根据提示信息输入 pid
、flag
和 nicevalue
进行测试。
|
|