基于Linux虚拟机的操作系统实验
计算机综合实验-操作系统
一、课程任务与实验设备、开发工具链
1. 课程任务
本次课程设计以 Linux 内核为实践载体,围绕操作系统核心机制完成四个模块:(1)在虚拟机中完成 Linux 操作系统的裁剪与编译;(2)实现自定义系统调用扩展;(3)实现文件管理系统调用,获取指定文件的大小与权限;(4)实现进程管理系统调用,查询指定进程的 PID、PPID、状态与优先级。
2. 实验设备与开发工具链
宿主机:Windows 10/11(64 位);虚拟机软件:VMware Workstation Pro 17;客户机系统:Ubuntu 22.04 LTS,分配 4 核 CPU、4GB 内存、50GB 磁盘,网络采用 NAT 模式。
Linux 内核源码:Linux 6.1.60(LTS)。
开发工具链:gcc、g++、make、libncurses-dev、bison、flex、libssl-dev、libelf-dev、bc,以及 open-vm-tools。
二、总体设计思路
实验按照“环境搭建 → 内核裁剪与编译 → 自定义系统调用 → 文件管理系统调用 → 进程管理系统调用”的顺序展开。前一步为后一步提供基础:只有先编译出可修改、可启动的 6.1.60 内核,后面的自定义系统调用才有运行环境。
整体设计围绕两条主线。第一,用户态与内核态的隔离与交互:应用程序不能直接访问硬件,必须通过系统调用陷入内核,由内核完成服务后再返回结果,因此三个自定义功能都实现为系统调用。第二,操作系统的资源抽象:文件被抽象为 inode 元数据,通过 VFS 统一访问;进程被抽象为 task_struct(进程控制块 PCB),内核通过它管理进程状态、父子关系和优先级。
三个自定义系统调用分别编号为 451(获取系统运行时间)、452(获取文件大小与权限)、453(查询进程信息)。它们共用同一套扩展流程:编写内核服务函数、修改 Makefile、在 syscall_64.tbl 中登记编号、在 syscalls.h 中声明,然后重新编译内核、安装并重启验证。
三、调试过程与实现
(一)任务一:Linux 内核裁剪与编译
1. 实验步骤
在 Ubuntu 22.04 中安装内核编译依赖;下载并解压 Linux 6.1.60 源码到 /usr/src;复制当前系统配置文件为 .config;执行 make menuconfig 裁剪无用驱动和文件系统,并清空内核签名证书路径;保存配置后依次执行 make -j$(nproc)、make modules_install、make install、update-grub,最后重启验证内核版本。
2. 实验结果
重启后在 GRUB 中选择 6.1.60 内核,执行 uname -r 输出 6.1.60,说明裁剪后的新内核编译、安装和启动成功。

图 1 内核编译并完成更新

图 2 uname -r 显示 6.1.60,新内核启动成功
(二)任务二:自定义系统调用 sys_my_get_uptime
1. 实验步骤
在 kernel/mysyscall/ 下新建 mysyscall.c,使用 SYSCALL_DEFINE1 实现 sys_my_get_uptime;修改 kernel/mysyscall/Makefile 和 kernel/Makefile 把该文件加入编译;在 syscall_64.tbl 中登记 451 号;在 syscalls.h 末尾添加函数声明;重新编译、安装并重启内核。用户态编写 test_syscall.c,通过 syscall(451, &uptime) 调用并打印结果。
2. 实现原理
用户态程序通过系统调用号陷入内核,内核根据系统调用表跳转到对应服务函数。服务函数用 ktime_get_boottime() 获取系统启动以来的单调时间并换算为秒;由于返回数据要写入用户态内存,必须使用 copy_to_user 完成安全拷贝,不能直接解引用用户指针。最后用 printk 输出内核日志,通过 dmesg 验证用户态与内核态结果一致。
3. 实验结果
用户态输出“Success! System Uptime: 158 seconds.”,dmesg 中出现 Syscall [my_get_uptime] 日志,功能验证通过。

图 3 test_syscall 运行结果与日志
(三)任务三:文件管理系统调用 sys_my_file_info
1. 实验步骤
首先编写 file_ops_demo.c,用 open、write、read、close、unlink 演示文件的创建、写入、读取和删除。然后在 kernel/mysyscall/ 下新增 myfileinfo.c,实现 sys_my_file_info;在 syscall_64.tbl 中登记 452 号并更新头文件与 Makefile;重新编译安装内核后,用 test_file_info.c 查询 /etc/passwd 的大小和权限。(用户和内核的字段需要保持一致)
2. 实现原理
Linux 通过 VFS 为不同文件系统提供统一接口,文件元数据存放在 inode 中。内核服务函数先用 user_path_at() 按路径解析出文件的 path,再用 vfs_getattr() 获取 kstat 结构,从中取出 size 和 mode,最后通过 copy_to_user 返回用户态。运行结果 mode=0100644 中, 10 表示普通文件类型, 644 是权限位。
3. 实验结果
file_ops_demo 成功完成写入与读取;test_file_info 输出 /etc/passwd 的大小和 mode=0100644,dmesg 中出现对应日志。

图 4 file_ops_demo 运行结果

图5 my_file_info加入第452行

图6 用户态输出与 dmesg 日志
(四)任务四:进程管理系统调用 sys_my_proc_info
1. 实验步骤
先编写 process_ops_demo.c,用 fork 创建子进程、用 execlp 替换子进程程序、用 waitpid 回收子进程并观察 PID 与 PPID 的关系。随后在 kernel/mysyscall/ 下新增 myprocinfo.c,实现 sys_my_proc_info;登记 453 号并更新头文件与 Makefile;重新编译安装内核后,用 test_proc_info.c 查询当前进程的信息。
2. 实现原理
task_struct 是 Linux 中的进程控制块,保存进程的 PID、父进程指针、状态、优先级等信息。sys_my_proc_info 先用 find_vpid() 和 pid_task() 按 PID 找到 task_struct,再读取 pid、real_parent->pid、__state 和 static_prio。查询期间必须持有 rcu_read_lock():目标进程可能在查询过程中退出并被销毁,RCU 锁可以避免访问已释放内存(use-after-free),保证内核安全。
3. 实验结果
test_proc_info 输出 PID、PPID、state=0、prio=120,与 /proc 中的进程信息一致;dmesg 中出现 Syscall [my_proc_info] 日志。

图 7 process_ops_demo 运行结果

图 8 检查自定义系统调用

图9 my_proc_info 用户态输出与 dmesg 日志
四、实验总结与心得体会
1. 遇到的困难和解决方法
(1)make menuconfig 提示找不到 ncurses 包:原因是终端图形化配置界面依赖 ncurses 库,安装 libncurses-dev 和 pkg-config 后问题解决。
(2)编译完成后重启,uname -r 仍显示发行版内核 6.8:原因是 GRUB 默认启动版本号更高的内核,进入 GRUB 的 Advanced options for Ubuntu 手动选择 6.1.60 后解决,也可通过修改 /etc/default/grub 把新内核设为默认项。
(3)编译时报 Error 2:查看日志发现源码中 printk 的字符串被换行拆成两行,编译器报缺少字符串结束符。把字符串合并为一行后编译通过。
(4)重启后调用返回 Function not implemented:通过 /proc/kallsyms 检查发现运行中的内核没有该函数符号,说明安装的是未包含修改的旧内核镜像;重新编译并安装后问题解决。这说明内核符号表是判断改动是否真正编入内核的可靠依据。
(5)dmesg 提示 Operation not permitted:普通用户没有读取内核日志的权限,使用 sudo dmesg 解决。
2. 实验收获
通过本次实验,我对操作系统的“接口”有了更具体的认识。系统调用是用户态程序访问内核服务的唯一合法通道,每一次 open、fork 最终都要经过系统调用进入内核;内核态代码必须谨慎处理用户指针和并发,copy_to_user、RCU 等机制并不是理论,而是保证系统安全的底线。
实验也让我理解了文件与进程的统一抽象:文件通过 VFS 和 inode 描述,进程通过 task_struct(PCB)描述。三个自定义系统调用虽然功能不同,但注册流程完全一致,这种“重复”让我真正掌握了内核扩展方法,也学会了通过 /proc/kallsyms、dmesg 等工具定位问题。
此外,内核实验的调试过程非常考验耐心。遇到报错时不能只看最后的 Error 2,而要回到日志中找到真正的错误行;遇到结果不符时也不能靠反复重启,而要从内核版本、编译产物和符号表逐层排查。这是本次实验最大的收获之一。
参考文献
[1] 计算机系统工程综合实践课程组. 计算机系统工程综合实践实验指导[Z]. 北京: 中国农业大学, 2025.
[2] 清华大学开源软件镜像站. Linux kernel 镜像[EB/OL]. [2026-09-13]. Index of /pub/linux/kernel/.
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)