【操作系统】实验七:显示进程列表
实验7 显示进程列表
练习目的:编写一个模块,将它作为Linux内核空间的扩展来执行,并报告模块加载时内核的当前进程信息,进一步了解用户空间和内核空间的概念。
7.1 进程
进程是任何多道程序设计的操作系统中的基本概念。为了管理进程,内核必须对每个进程所做的事情进行清楚的描述。例如,内核必须知道进程的优先级,它是正在CPU上运行还是因某些事件被阻塞,给它分配什么样的地址空间,允许它访问哪个文件等等。这正是进程描述符(process descriptor)的作用进程描述符都是task_struct 类型结构,它的域包含了与一个进程相关的所有信息。因为进程描述符中存放了那么多信息,所以它是相当复杂的。它本身不仅包含了很多域,而且一些域还包括了指向其他数据结构的指针,依此类推。
为了对给定类型的进程(例如,在可运行状态的所有进程)进行有效的搜索,内核建立了几个进程链表。有一个双向循环链表(参见图7.1)把所有现有的进程联系起来,我们叫它为进程链表(process list)。每个进程的prev_task和next_task域用来实现链表。init_task指向描述符链表的头。另外,宏current总是指向CPU的当前进程。

图7.1 进程链表
7.2 用户空间和内核空间
模块运行在所谓的内核空间(Kernel space)里,而应用程序运行在所谓的用户空间里(User space)中,这个概念是操作系统理论的基础之一。
我们通常将执行模式称作内核空间和用户空间。这两个术语不仅说明两种模式具有不同的优先权等级,而且还说明每个模式都有自己的内存映射,即自己的地址空间。
每当应用程序执行系统系统调用和被中断挂起时,Unix将执行模式从用户空间切换到内核空间,执行系统调用的内核代码运行在进程上下文中,它代表调用进程执行操作,因此能够访问进程地址空间的所有数据;而处理硬件中断的内核代码则和进程是异步的,因而与任何一个特定进程都无关。

图7.2 用户空间和内核空间切换的示例
7.3 问题陈述
设计并实现一个模块,遍历进程描述符链表,打印出系统的进程数目、当前进程,并尽量多地打印每个进程的信息。
7.3.1部分A
查看系统已加载模块。写一个小的模块打印“hello world”。
7.3.2部分B
设计模块遍历进程描述符链表,打印出系统的进程数目、当前进程,并尽量多地打印每个进程的信息。例如:进程PID,进程状态等。
7.3.3部分C(可选)
使用模块实现一个系统调用(也实现遍历进程链表的功能),并编写一个用户程序测试,体会利用模块从而带来的不用重新编译内核,测试方便等好处。
7.4 解决问题
7.4.1部分A
查看系统已加载模块。写一个小的模块打印“hello world”
使用命令lsmod查看已加载模块,如图7.3所示

图7.3 查看系统当前已加载模块
写一个小的模块打印“hello world!”,创建一个course_devise文件夹,并进入该文件夹,如图7.4所示

图2.4 创建文件夹并进入此文件夹
创建一个C语言文件,命名为hello.c文件,文件代码如下:
#include <linux/module.h>
#include <linux/kernel.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("20223626");
MODULE_DESCRIPTION("这是一个小的hello world!模块");
static int __init hello_init(void) {printk(KERN_INFO "Hello, World!\n");return 0;
}static void __exit hello_exit(void) {printk(KERN_INFO "Goodbye, World!\n");
}
module_init(hello_init);
module_exit(hello_exit);
创建好了之后,如图7.5所示

图7.5 创建hello.c文件
接下来就是创建Makefile文件,注意是使用TAB制表符,不是空格,文件代码如下:
obj-m += hello.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
创建好了如图7.6所示

图7.6 创建Makefile文件
使用make命令开始编译模块,编译完成之后,如图7.7所示

图7.7 编译模块成功
再使用sudo insmod hello.ko加载模块
加载完模块之后,使用dmesg | tail查看内核日志,如图7.8所示:

图7.8 加载模块成功
使用sudo rmmod hello卸载模块
卸载完模块之后,使用dmesg | tail查看内核日志,如图7.9所示:

图7.9 卸载模块成功
7.4.2部分B
设计模块遍历进程描述符链表,打印出系统的进程数目、当前进程,并尽量多地打印每个进程的信息。例如:进程PID,进程状态等。
- 编写一个C语言文件,命名为process_info.c,代码如下所示:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>MODULE_LICENSE("GPL");
MODULE_AUTHOR("20223626");
MODULE_DESCRIPTION("这是一个打印进程信息的模块");static int __init process_info_init(void) {struct task_struct *task;int process_count = 0;//初始化进程数量为0// 遍历进程链表for_each_process(task) {process_count++;printk(KERN_INFO "进程ID: %d, 状态: %ld, 名字: %s\n",task->pid, task->__state, task->comm);}// 打印当前进程信息printk(KERN_INFO "当前进程ID: %d, 状态: %ld, 名字: %s\n",current->pid, current->__state, current->comm);// 打印进程总数printk(KERN_INFO "进程总数: %d\n", process_count);return 0;
}static void __exit process_info_exit(void) {printk(KERN_INFO "退出进程信息模块!\n");
}module_init(process_info_init);
module_exit(process_info_exit);
- 编写完代码如图7.10所示:

图7.10 process_info.c的代码内容
- 在同一目录下创建一个名为 Makefile 的文件,内容如下:
obj-m += process_info.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
- 编写完Makefile之后,如图7.11所示:

图7.11 Makefile的代码内容
- 使用make开始编译模块,编译完之后,如图7.12所示,使用sudo insmod process_info.ko加载模块

图7.12 模块编译成功
- 加载完模块之后,使用dmesg查看信息如图7.13所示:

图7.13 查看进程信息
- 卸载模块,使用命令rmmod process_info.ko,如图7.14所示:

图7.14 卸载process_info模块
- 输入dmesg | tail命令查看信息,如图7.15所示:

图7.15 Makefile的代码内容
7.4.3部分C(可选)
使用模块实现一个系统调用(也实现遍历进程链表的功能),并编写一个用户程序测试,体会利用模块从而带来的不用重新编译内核,调试方便等好处。
步骤如下:
- 写一个mymod.c,代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>// 该宏用于声明内核模块的基本信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A Simple Linux Kernel Module for Traversing Process List");// 函数声明
static int process_list_show(struct seq_file *m, void *v);
static int process_list_open(struct inode *inode, struct file *file);// 定义文件操作结构体
static const struct proc_ops process_list_fops = {.proc_open = process_list_open,.proc_read = seq_read,.proc_lseek = seq_lseek,.proc_release = single_release,
};// 在/proc目录下创建一个虚拟文件
static int __init process_list_init(void)
{proc_create("process_list", 0, NULL, &process_list_fops);pr_info("Process list module loaded\n");return 0;
}static void __exit process_list_exit(void)
{remove_proc_entry("process_list", NULL);pr_info("Process list module unloaded\n");
}// 打开文件时的回调函数
static int process_list_open(struct inode *inode, struct file *file)
{return single_open(file, process_list_show, NULL);
}// 遍历进程链表并显示信息
static int process_list_show(struct seq_file *m, void *v)
{struct task_struct *task;// 遍历所有进程for_each_process(task) {seq_printf(m, "PID: %d, Name: %s\n", task->pid, task->comm);}return 0;
}module_init(process_list_init);
module_exit(process_list_exit);
写好代码如图7.16所示:

图7.16 mymod.c的内容
- 在同一目录下创建Makefile文件,注意,Makefile文件里的make前面的空一定要使用TAB,而不是空格,代码如下所示:
KVERS = $(shell uname -r)# Kernel modulesobj-m += mymod.obuild: kernel_moduleskernel_modules:make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modulesclean:make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
编写完Makefile文件如图7.17所示:

图7.17 Makefile的代码内容
- 使用make指令开始编译模块,编译完之后,如图7.18所示

图7.18 编译完之后的结果
- 写一个用户程序test_proc_list.c测试,代码如下:
#include <stdio.h>
#include <stdlib.h>int main() {FILE *fp;char buffer[256];// 打开/proc/process_list文件fp = fopen("/proc/process_list", "r");if (fp == NULL) {perror("Failed to open /proc/process_list");return 1;}// 读取并输出每一行while (fgets(buffer, sizeof(buffer), fp) != NULL) {printf("%s", buffer);}fclose(fp);return 0;}
- 开始加载模块,使用命令insmod mymod.ko,加载好了之后,使用gcc -o test_proc_list test_proc_list.c开始编译用户文件,编译好了之后,使用./test_proc_list执行用户文件,执行完之后如7.19所示:

图7.19 执行完test_proc_list之后的结果
7.5 实验总结
1.模块加载与卸载: 本次实验首先学习了如何编写和加载一个简单的 Linux 内核模块,通过打印“hello world”来验证模块的基本功能。这一过程涉及到使用 module_init 和 module_exit 宏,确保模块能够正确加载和卸载。在加载模块后,通过 dmesg 命令查看内核日志,确认输出信息。
2.遍历进程描述符链表: 在实验的第二部分,设计了一个模块来遍历进程描述符链表。使用 for_each_process 宏,可以遍历系统中所有的进程,并打印出每个进程的详细信息,包括进程 ID、状态、名称等。通过这种方式,能够直观地了解系统中正在运行的进程及其状态。
3.进程信息的输出: 为了确保输出的信息尽量丰富,模块中实现了对每个进程的详细打印。除了基本的 PID 和状态,还可以扩展打印其他信息,如优先级、父进程 ID 等。这种信息的收集和展示有助于理解系统资源的使用情况和进程管理的机制。
4.实验收获与反思: 通过本次实验,深入理解了 Linux 内核模块的基本结构和功能,掌握了进程管理的基本概念。实践中发现,内核模块的开发需要对内核数据结构有一定的了解,同时也要注意内核与用户空间的区别。未来可以进一步探索更多的内核功能,如信号处理、内存管理等,以加深对 Linux 内核的理解。
相关文章:
【操作系统】实验七:显示进程列表
实验7 显示进程列表 练习目的:编写一个模块,将它作为Linux内核空间的扩展来执行,并报告模块加载时内核的当前进程信息,进一步了解用户空间和内核空间的概念。 7.1 进程 进程是任何多道程序设计的操作系统中的基本概念。为了管理…...
day10 电商系统后台API——接口测试(使用postman)
【没有所谓的运气🍬,只有绝对的努力✊】 目录 实战项目简介: 1、用户管理(8个) 1.1 登录 1.2 获取用户数据列表 1.3 创建用户 1.4 修改用户状态 1.5 根据id查询用户 1.6 修改用户信息 1.7 删除单个用户 1.8 …...
JavaScript ES6+ 语法速通
一、ES6 基础语法 1. let 和 const 声明变量 let:块级作用域,可以重新赋值。const:块级作用域,声明常量,不能重新赋值。 let name Li Hua; name Li Ming; // 可修改const age 21; // age 22; // 报错࿰…...
移动端h5自适应rem适配最佳方案
网页开发中,我们常用的单位有如下几个: px:像素固定,无法适配各分辨率的移动设备em: 该单位受父容器影响,大小为父元素的倍数rem: 因为html根元素大小为16px,所以默认 1rem 16px,rem只受根元素…...
2024年使用 Cython 加速 Python 的一些简单步骤
文章结尾有最新热度的文章,感兴趣的可以去看看。 本文是经过严格查阅相关权威文献和资料,形成的专业的可靠的内容。全文数据都有据可依,可回溯。特别申明:数据和资料已获得授权。本文内容,不涉及任何偏颇观点,用中立态度客观事实描述事情本身 文章有点长,期望您能坚持看…...
EasyExcel设置表头上面的那种大标题(前端传递来的大标题)
1、首先得先引用easyExcel的版本依赖,我那 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version> </dependency> 2、然后得弄直接的实体类,&…...
【Linux网络编程】第十弹---打造初级网络计算器:从协议设计到服务实现
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、Protocol.hpp 1.1、Request类 1.1.1、基本结构 1.1.2、构造析构函数 1.1.3、序列化函数 1.1.4、反…...
无限弹窗?无限重启?
Windows开机自启目录: "%USERPROFILE%\AppData\Roaming\Microsoft\windows\StartMenu\Programs\Startup" 基于这个和 start 命令, shutdown 命令, 编写 bat 病毒程序。 无限弹窗 echo start cmd > hack.txt echo %0 >>…...
深入详解人工智能机器学习常见算法中的K-means聚类
目录 引言 1. K-means聚类的基本概念 1.1 K-means聚类的定义 1.2 K-means聚类的核心思想 1.3 K-means聚类的目标函数 2. K-means聚类的核心原理 2.1 初始化 2.2 分配 2.3 更新 2.4 迭代 3. K-means聚类的具体实现 3.1 K-means聚类的算法流程 3.2 K-means聚类的Pyt…...
lc146LRU缓存——模仿LinkedHashMap
146. LRU 缓存 - 力扣(LeetCode) 法1: 调用java现有的LinkedHashMap的方法,但不太理解反正都不需要扩容,super(capacity, 1F, true);不行吗,干嘛还弄个装载因子0.75还中途扩容一次浪费时间。 class LRUC…...
全面深入解析:C语言动态库
引言 动态库(Dynamic Library)是现代软件开发中不可或缺的一部分,它们不仅提高了代码的重用性和维护性,还显著提升了系统的性能和资源利用率。本文将全面探讨C语言中的动态库,从基础概念到高级应用,通过丰…...
运用 SSM 实现垃圾分类系统智能化升级
目 录 摘 要 1 前 言 3 第1章 概述 4 1.1 研究背景 4 1.2 研究目的 4 1.3 研究内容 4 第二章 开发技术介绍 5 2.1Java技术 6 2.2 Mysql数据库 6 2.3 B/S结构 7 2.4 SSM框架 8 第三章 系统分析 9 3.1 可行性分析 9 3.1.1 技术可行性 9 3.1.2 经济可行性 10 3.1.3 操作可行性 10 …...
LeNet-5:深度学习与卷积神经网络的里程碑
目录 编辑 引言 LeNet-5的结构与原理 输入层 C1层:卷积层 S2层:池化层 C3层:卷积层 S4层:池化层 C5层:卷积层 F6层:全连接层 输出层 LeNet-5的算法基础 LeNet-5的优点 LeNet-5的现代应用 …...
从资产流动分析WIF市场潜力X.game深究其他未知因素
近日,两则关于WIF最新消息引起了投资者们的注意。据报道,11月28日Vintermute在过去13小时内累计从Binance交易所提取了价值533万美元的WIF,此举不仅彰显了其强大的资金实力,更在某种程度上推动了WIF币价的反弹;另一方面…...
深入解析Vue3响应式系统:从Proxy实现到依赖收集的核心原理
深入解析Vue3响应式系统:从Proxy实现到依赖收集的核心原理 响应式系统的基本原理 作为一个热门的JavaScript框架,Vue在3.x版本中引入了基于Proxy的响应式系统。这个系统的核心思想是利用Proxy对象拦截对数据的访问和修改,从而实现数据的自动更…...
FPGA实现GTP光口数据回环传输,基于Aurora 8b/10b编解码架构,提供2套工程源码和技术支持
目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案 3、工程详细设计方案工程设计原理框图用户数据发送模块基于GTP高速接口的数据回环传输架构GTP IP 简介GTP 基本结构GTP 发送和接收…...
Linux网络 UDP socket
背景知识 我们知道, IP 地址用来标识互联网中唯一的一台主机, port 用来标识该主机上唯一的一个网络进程,IPPort 就能表示互联网中唯一的一个进程。所以通信的时候,本质是两个互联网进程代表人来进行通信,{srcIp&…...
如何持续优化呼叫中心大模型呼入机器人的性能?
如何持续优化呼叫中心大模型呼入机器人的性能? 原作者:开源呼叫中心FreeIPCC,其Github:https://github.com/lihaiya/freeipcc 持续优化呼叫中心大模型呼入机器人的性能是一个复杂而细致的过程,它涉及到数据、模型结构…...
鸿蒙项目云捐助第四讲鸿蒙App应用的登陆注册页实现
根据app的操作流程可以知道,当启动页启动后,点击启动页中的页面就进入到了登录页。本讲就是针对于登录注册页的实现,实现的界面参考下图。 这里根据这个素材的参考实现鸿蒙Next云捐助的登录页。 一、鸿蒙Next云捐助登录页的实现 在项目中继…...
Windows本地搭建Redis集群(集群模式)
手打不易,如果转摘,请注明出处! 注明原文:https://blog.csdn.net/q258523454/article/details/144477957 前言 Redis版本:redis 5.0.14.1 Windows版本:Windows10 本文只讲集群模式 1. 安装Redis 1.1 …...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
Android写一个捕获全局异常的工具类
项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都…...
6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...
【51单片机】4. 模块化编程与LCD1602Debug
1. 什么是模块化编程 传统编程会将所有函数放在main.c中,如果使用的模块多,一个文件内会有很多代码,不利于组织和管理 模块化编程则是将各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数声明,其他.c文…...
