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

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...

算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
背包问题双雄:01 背包与完全背包详解(Java 实现)
一、背包问题概述 背包问题是动态规划领域的经典问题,其核心在于如何在有限容量的背包中选择物品,使得总价值最大化。根据物品选择规则的不同,主要分为两类: 01 背包:每件物品最多选 1 次(选或不选&#…...