【操作系统】实验七:显示进程列表
实验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 …...

使用FastGPT制做一个AI网站日志分析器
越来越的多网站面临每天上千次的扫描和各类攻击,及时发现攻击IP,并有效的屏蔽不良访问成为网站安全的重要保障,这里我们使用AI来完成对网站日志的日常分析。 我们来使用FastGPT来制做一个AI网站日志析器,下面就开始: …...

探索 Echarts 绘图:数据可视化的奇妙之旅
目录 一、Echarts 初印象 二、搭建 Echarts 绘图环境 三、绘制第一个图表:柱状图的诞生 四、图表的美化与定制:让数据更具吸引力 1. 主题切换:一键变换风格 2. 颜色调整:色彩搭配的艺术 3. 标签与提示框:丰富信…...

网络基础(IP和端口)
网络连接的核心-TCP/IP体系结构(IP和端口) 什么是IP地址 1.IP地址是电子设备(计算机)在互联网上的唯一标识 2.用来在互联网中寻找电脑 IP 地址就像是你家的地址一样,不过它是在网络世界里用来找到一台电脑或者其他网…...

UE4与WEB-UI通信
前端HTML代码 <!DOCTYPE html><html><head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title>test web ui</title><script src"https://cdn.b…...

前缀和与差分算法详解
定义 前缀和是一种数据预处理技术,它指的是从数组的第一个元素开始,到当前元素为止的所有元素的和。这种技术可以快速计算任意区间内元素的和,而不需要每次都从头开始累加。 差分则是前缀和的逆运算,它主要用于处理对数组某个区…...

《深入探究:C++ 在多方面对 C 语言实现的优化》
目录 一、C 在 C 上进行的优化二、C 关键字(C 98)三、C 的输入输出1. cin 和 cout 的使用2. cin、cout 和 scanf()、printf() 的区别 三、命名空间1. 命名空间的使用2. 嵌套命名空间3. 在多个头文件中使用相同的命名空间 四、函数缺省值1. 缺省值的使用2…...

React 第十六节 useCallback 使用详解注意事项
useCallback 概述 1、useCallback 是在React 中多次渲染缓存函数的 Hook,返回一个函数的 memoized的值; 2、如果多次传入的依赖项不变,那么多次定义的时候,返回的值是相同的,防止频繁触发更新; 3、多应用在 父组件为函…...

使用C#和OPenCV实现圆形检测
文章目录 霍夫变换使用 OpenCV 和 C# 实现圆形检测 霍夫变换 在计算机视觉中,圆形检测是一个常见且有用的任务,特别是在物体识别、图像分析和图形处理等领域。OpenCV 是一个强大的开源计算机视觉库,它提供了许多工具来实现不同的图像处理功能…...

评估一套呼叫中心大模型呼入机器人的投入回报比?
评估一套呼叫中心大模型呼入机器人的投入回报比? 原作者:开源呼叫中心FreeIPCC,其Github:https://github.com/lihaiya/freeipcc 评估一套呼叫中心大模型呼入机器人的投入回报比(ROI),是一个多…...

十八、Label 和 Selector
Label 是键值对,用来标识 Kubernetes 资源(如 Pod、Node、Service 等)的属性。它们并不直接影响资源的行为,但可以帮助用户快速组织、查询和操作这些资源。标签可以用于选择、过滤和分组。 Label: 标签对 k8s 中各种资源进行分类、分组,如Pod和节点进行分组。通过添加kev…...