一、实验目的
掌握进程结构及进程管理原理
二、实验内容
编写一个模块,将它作为Linux内核空间的扩展来执行,并报告模块加载时内核的当前进程信息,进一步了解用户空间和内核空间的概念。
任务A 查看系统已加载模块。写一个小的模块打印"hello world"。
任务B 设计模块遍历进程描述符链表,打印出系统的进程数目、当前进程,并尽量多地打印每个进程的信息。例如:进程PID,进程状态等。
任务C 使用模块实现一个系统调用(也实现遍历进程链表的功能),并编写一个用户程序测试。体会利用模块从而带来的不用重新编译内核、调试方便等好处。
三、 实验 步骤
部分A:编写"hello world"模块
(1)首先创建工作目录。打开终端,依次输入命令行"mkdir test_1""cd test_1"。如图1所示:

图 1
(2)命令行输入 vim lmon.c创建模块源文件,文件名为lmon.c。如图2所示:

图 2
(3)编写Makefile。如图3所示

图 3
(4)编译模块,输入命令"make"。
这里出现编译错误,显示为格式错误,Makefile 对于缩进非常敏感,它必须使用 Tab 键而不是空格来缩进命令。更改后,make编译结果如图4所示:

图 4
(5)加载模块。输入命令行"sudo insmod lmon.ko""dmesg | tail"查看输出结果。如图5所示,由结果看到是我们程序运行的预期结果

图 5
(6)卸载模块。输入命令行"sudo rmmod lmon.ko""dmesg | tail"查看日志输出。看到我们第二个卸载模块的返回函数结果显示在下面,如图6所示:

图 6
部分B:设计模块遍历进程描述符链表,打印出系统的进程数目、当前进程,并尽量多地打印每个进程的信息。例如:进程PID,进程状态等。
(1)使用命令行:Vim wmon.c,编写代码文件,如图7所示:

图 7
(2)编写Makefile,然后输入make命令查看编译结果,如图8,图9所示

图 8

图 9
(3)查看日志 dmesg 可以看到进程的PID、进程名称、父进程id、当前状态,如图10所示

图 10
部分C:实现系统调用
(1)创建工作目录。首先打开终端,选择打开内核文件,输入命令行"cd linux-5.15.1/",然后依次输入"mkdir ~/syscall_module","cd ~/syscall_module",创建工作目录和打开工作目录。然后创建内核模块文件:创建一个新的 C 文件syscall_module.c,用于实现内核模块和系统调用功能。该模块的功能是列出所有进程的 PID、名称和状态。如图11所示:

图 11
(2)创建Makefile。如图12所示:

图12
(3)编译模块。输入"make"如图13所示:

图 13
(4)创建一个用户程序文件test_syscall.c用来测试。如图14所示:

图 14
(5)编译用户测试程序,命令行输入"gcc -o test_syscall test_syscall.c",然后运行用户程序,输入"./test_syscall"。程序会自动调用 insmod 命令加载 syscall_module.ko,并遍历进程链表,输出进程的信息。如图15所示:

图 15
这里回车后选择查看日志,确认模块的输出。如图16所示:

图 16
如图所示,日志并未输出预期的结果。从 dmesg 输出来看,内核模块确实已经成功加载并执行了 traverse_process_list 函数,遍历进程链表的日志信息已经输出。但没有显示出进程的具体信息。这可能是因为内核日志没有正确刷新,或者输出信息被覆盖。这里选择使用 dmesg -c 清除缓存后再查看,输入命令行"sudo dmesg -c"。如图17所示:

图 17
四、实验 体会
内核模块的灵活性:通过模块化设计,可以在不重新编译整个内核的情况下,快速测试和调试内核代码。这种灵活性大大提高了开发效率。
用户空间与内核空间的理解:在实验中,通过直接操作内核空间,深入理解了它与用户空间之间的区别和联系。特别是在进程管理方面,内核空间提供了对进程信息的直接访问,而用户空间则需要通过系统调用来获取这些信息。
调试技巧:在实验过程中遇到日志未输出预期结果的问题,学会了使用 dmesg -c 清除缓存以查看最新的内核日志。这种调试技巧对于后续的内核开发非常有帮助。
进程管理的重要性:通过遍历进程链表,观察到系统中各个进程的状态和信息,增强了对Linux进程管理机制的理解,认识到其在系统性能和资源管理中的关键作用。
在后续实验中,可以考虑增加更多的进程信息输出,例如CPU使用率、内存占用等,以更全面地了解系统状态。
进一步探索如何在用户空间与内核空间之间高效地传递数据,优化模块与用户程序的交互方式。
通过本实验的实践,掌握了Linux内核模块的基本编写流程,增强了对进程管理的理解,为进一步的内核开发奠定了基础。