一 实验目的
(1)理解 Linux 系统处理系统调用的流程。
(2)掌握增加与调用系统调用的方法。
(3)理解 Linux 的内核模块和编译方法。
二 实验内容
2.1 任务一
向现有 Linux 内核加入一个新的系统调用,实现一个新的内核函数 mycall()。内核函数的具体功能不作要求,可以是输出一行特定的字符 (如学号和姓名)、返回系统当前时间、 返回一个特定的值等。
2.2 任务二
用编译内核的方法,将其加入内核源码并编译。
2.3 任务三
使用新的编译后的内核,编写测试程序测试该系统调用。
三 实验过程及结果
(1)虚拟机扩容。由于预装的系统分配的资源较少,为保证编译内核时有充足资源和空间,建议先增加处理器内核数量和硬盘容量。
【实验步骤】:以下是一个简单的方法:
1. 在虚拟机设置中修改处理器内核数量以及硬盘容量(60GB 以上)
2. 在 CD/DVD 中勾选启动时连接,并指定 Ubuntu 镜像的 ISO 映像文件。
3. 重启虚拟机,按 F2 键进入 BIOS 设置。在 Boot 选项卡中,将 CD-ROM Drive 移动至启动顺序的第一位。
4. 重新启动虚拟机后,选择 "Try Ubuntu" 以进入 live 系统
5. 搜索GParted并打开,对要扩展的分区右键选择 "Resize/Move" 以扩容。
6. 关机后取消勾选 CD/DVD 的启动时连接选项。
7. 开机后,运行 df -h 命令验证扩容是否成功。经验证,扩容成功。
(2)下载 Linux 内核文件。在 https://www.kernel.org/中下载 Linux 内核文件压缩包,考虑兼容性,建议使用 5.10.199 版本。将压缩包解压到/usr/src 目录下。为方便后续操作,可以先获取 root 权限。
【实验步骤】:
1. 在网站下载Linux 内核文件压缩包
2. 检查是否下载成功
3. 获取root权限,输入命令,将压缩包解压到/usr/src 目录下
4. 正在解压中
5. 检查是否解压成功
(3)编写系统调用服务程序
【实验步骤】:
1. 切换至解压后的内核目录,在kernel/sys.c中添加自定义函数 SYSCALL_DEFINE(mycall, ...){}
2. 声明系统调用服务例程:在 include/linux/syscalls.h 头文件中添加函数声明 asmlinkage long sys_mycall(…);
3. 修改系统调用表:在 arch/x86/entry/syscalls/syscall_64.tbl 中修改系统调用表,添 加系统调用号。
(4)编译内核,依次执行以下命令:
【实验步骤】:
1. sudo apt-get install libncurses5-dev make openssl libssl-dev bison flex
2. sudo apt-get install dwarves #安装依赖包
3. make mrproper #清空以前的编译信息
4. make menuconfig #生成配置清单文件
保存文件配置文件
5. scripts/config --disable SYSTEM_TRUSTED_KEYS
scripts/config --disable SYSTEM_REVOCATION_KEYS
6. make -j16 #多线程编译内核
出现报错:No rule to make target ‘y‘, needed by ‘certs/x509_certificate_list‘
[解决方案]:编辑.config文件
修改CONFIG_SYSTEM_TRUSTED_KEYS的值,将其赋空值。
修改CONFIG_SYSTEM_REVOCATION_KEYS的值,也将其赋空值。
1)在vim编辑器中使用如下命令,可以替换y值为空值
2)重新编译内核(整个过程大概花费五个小时)
7. make modules_install #模块命令
8. make install #安装内核
(5)切换到新的 Linux 内核。需要进行以下操作:
【实验步骤】:
1. 修改 /boot/grub/grub.cfg 文件,注释掉 set timeout_style = hidden 并将 set timeout 的值设置为 10。
2. 重新启动虚拟机,在 GRUB 引导菜单中选择新的 Linux 内核版本。
3. 登录后,输入 uname -r 命令确认是否已切换到新的内核。
(6)编写程序测试系统调用。
【实验步骤】:
1. 编写一个 c 或 cpp 测试程序test.c
2. 编译test.c程序并输出test文件,使用ls命令检查是否编译成功
3. 运行该程序
4. 该程序将调用syscall()并输出相应信息,我们使用root权限,输入dmesg命令查 看输出信息,测试新增加的系统调用是否可用
[结果分析]:我们发现,在输出信息的最后一行输出了我们定义的系统调用函数的内容,表明我们新增加的系统调用可用,测试成功!
四 实验总结
- 通过这次实验,我学会了如何对虚拟机进行扩容来满足我们编译内核的资源需求。这包括增加处理器内核数量、扩展磁盘容量以及修改CD/DVD设置等操作,以确保我们有足够的资源和空间进行内核编译的操作。
- 通过这次实验,我学会了在Linux系统中如何下载文件,并通过tar命令将文件解压到目的目录上,注意在进行解压操作之前,我们要先获取超级用户权限。这是内核编译的第一步,为后续的操作奠定了基础。
- 通过这次实验,我还学会了如何编写系统调用服务程序。这包括在内核源代码中添加自定义的系统调用服务函数,以及在头文件中声明相应的系统调用服务例程。修改系统调用表是为了让系统能够识别我们新增的系统调用。在本次实验中,我们学会了如何在特定的文件中注册我们自定义的系统调用号,从而使系统能够正确地调用我们的功能。
- 安装必要的依赖包并进行内核编译是本次实验的重要一步。我们学习了如何使用命令行工具安装所需的编译依赖包,以及如何执行一系列的编译命令来生成新的内核。在内核编译的过程中,可能会出现各种各样的错误,我们需要不断地去查找资料,不断地尝试和纠错,并需要耐心地等待,这是我们编译过程所积累的重要经验。
- 我还学会了如何切换内核系统,如何在引导时选择新的 Linux 内核版本,从而切换到我们编译的新内核。通过运行 uname -r 命令,可以帮助我们来确认切换是否执行成功。
- 最后,我们编写了一个测试程序来验证我们新增加的系统调用功能是否可用。这是确认我们的系统调用已成功添加并能够在新内核中正常工作的重要步骤。我学会了在超级用户权限中,使用dmesg命令可以查看我们系统调用的输出信息,来验证我们编写的系统调用函数是否运行成功。