Linux学习之内核模块编程

article/2025/9/24 6:44:46

前言

之前成功编译了内核,这次学习如何修改增加删除内核模块,为了保证内核的纯净,我特意重新编译安装了一个新的5.11.8的内核,其他内核同理。
本文原创,创作不易,转载请注明!!!
本文链接
个人博客:https://ronglin.fun/?p=169
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/115422459

通过内核模块显示进程控制块信息

环境

主机:联想Y7000P;64位windows10;CPU:i7-9750H;显卡:GTX 1660 Ti;内存:16G
虚拟机:Ubuntu 18.04 LTS;硬盘100G;内存4G;64位;4核心
Linux内核:5.11.8

实验简述

首先先看实验内容,实验内容来自指导书,如有侵权联系我删除:

实验说明

在内核中,所有进程控制块都被一个双向链表连接起来,该链表中的第一个进程控制块为init_task。编写一个内核模块,模块接收用户传递的一个参数num,num指定要打印的进程控制块的数量;若用户不指定num或者num<0,模块则打印所有进程控制块的信息。需要打印的进程控制块信息有:进程PID和进程的可执行文件名。

解决方案

(1)定义模块参数
该模块需要接受用户传递的参数,在使用该参数之前,需要在代码中预先定义好该参数,将该参数的类型设置为整型,并且在sysfs文件系统中的权限是只读的。定义的方法为:
static int num=-1;
module _param(num, int, S_IRUGO);
该参数的初始值被设置为-1。-1将作为打印所有进程控制块的标记,默认值为-1,意味着当用户不传入任何参数时,模块将打印所有的进程的信息。
(2)访问进程控制块链表
在内核中,进程控制块被组织成多个双向链表,其中有一个双向链表包含所有的进程
精袈
块,只需要访问该双向链表,就可以访问到所有进程控制块。Linux内核中几乎所有双向
都采用相同的数据结构来实现,内核中定义list_head通用数据结构,其定义如下:

struct list_head {struct list head *next,*prev;
};

list_head中,next指向链表中的下一个list_head 数据结构,prev指向链表中的前一个list_head 数据结构。之所以说该结构是用于实现一个通用的双向链表是因为:如果一个数据结构包含list_head结构,开发者就可以通过内核提供的一组宏创建并操作一个双向链表,而该链表中元素的类型为该数据结构,如图12-1所示:
在这里插入图片描述
在进程控制块task_struct中,包含一个名为tasks 的成员,该成员的类型为list_head,这意味着进程控制块能够通过该成员将进程控制块串成一个双向链表。Linux内核通过该成员将所有的进程都放入同一个双向链表,因为 list_head 结构中的next 和 prev指针并不是指向包含list_head 的数据结构,而是指向另一个list_head数据结构。为了访问包含list_head的数据结构,内核提供一个宏:

list_entry(ptr,type,member);

在该宏中, ptr是一个指向list_head的指针, type是包含list_head的数据结构类型,而member是list_head在该数据结构中的成员名。例如,若一个进程控制块中的tasks 的地址为p,为了访问该进程控制块,可以采用:

list_entry(p,struct task _struct,tasks);

该宏便会返回该进程控制块的地址。
知道如何使用双向链表后,就可以方便地访问内核中所有的进程控制块,因为它通过tasks成员串成一个双向链表,如果得到一个进程控制块的地址p,开发者可以通过:

list_entry(p->tasks.next, struct task _struct, tasks);

访问该双向链表中的下一个进程控制块。在该双向链表中,第一个进程控制块为init_task,如果开发者发现下一个进程控制块为init_task时,说明已经完整地遍历过所有进程控制块。
内核定义宏for_each process用于遍历所有的进程控制块,开发者通过该宏就能将所有的进程控制块访问一遍,该宏展开的形式为:

for (= &init_task ; (p= list_entry((p)->tasks.next, struct task_struct, tasks) !=&init_task ; )

(3)输出进程控制块信息
进程控制块中包含进程大部分信息,根据实验要求,模块需要打印进程的 pid和可执行文件名,在进程控制块的数据结构中,成员pid为进程的PD,而成员comm包含进程的可执行文件名。在内核中,模块可以通过 printk( )内核函数将这些信息打印到系统日志中。

程序框架

在这里插入图片描述
在这里插入图片描述

实操

实验说明太多了,看的有点头大,幸好它给了源码,直接上手实操,出了问题再看资料。

模块文件知识

先把程序框架中的代码敲出来,exp_exit()函数中就添加一句

printk("Good bye,Ronglin\n");

先大概的讲述一下c文件的内容:
头文件声明
头两行是模块头文件,头文件module.h和 init.h是必不可少的。module.h包含加载模块需要的函数和符号定义; init.h中包含模块初始化和清理函数的定义。如果在加载时允许用户传递参数,模块还应该包含moduleparam.h头文件
模块许可声明
从内核v2.4.10版本开始,模块必须通过MODULE_LICENSE宏声明此模块的许可证,否则在加载此模块时,内核会显示“kernel tainted”(内核被污染)的警告信息。从linux/module.h文件中可看到,被内核接受的许可证有GPL,GPL v2,GPL and additional rights,Dual BSD/GPL,Dual MPL/GPL,Dual MIT/GPL和Proprietary。
初始化和清理函数
声明内核模块必须调用宏module_init和 module_exit 去注册初始化与清理函数。在模块源代码的最后两行已声明该模块被加载时的初始化函数是exp_init(),模块被卸载时的清理函数是 exp_exit()。需要注意,初始化与清理函数必须在宏module_init和 module_exit使用前定义,否则会出现编译错误。这两个函数配对使用,例如,当module_init()申请一个资源,那么module_exit()中就应释放这个资源,使得模块不留下任何副作用。
一般来说有这上述3部分足以。

大概了解了c文件结构,再来看看makefile文件的结构

obj-m:=listprocess.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesobj-m:=listprocess.o

其实核心部分就是这一句,obj-m:=listprocess.o,生成的内核模块名为listprocess.ko。如果需要生成一个名为mymodule.ko 的内核模块,并且该内核模块的源代码来源于modulesrc1.c和 modulesrc2.c两个文件, makefile文件应该写成如下形式:

obj -m:-mymodule.o
module objs:-modulesrc1.o modulesrc2.o

如果用户采用这种makefile,在调用make命令时,需要将内核源代码所在目录作为一个参数传递给make命令。
例如,如果v2.6的内核源代码位于/usr/src/linux-2.6目录下,用户模块源代码所在目录应该使用的make命令为:

make -C/usrlsrc/linux-2.6 M='pwd'modules

makefile还提供另一种形式,用户可指定内核源代码所在的目录,而不用每次都把该目录作为参数传递给make命令。对于“hello world!”示例,在 makefile 中指定内核源代码的方式为上面的开头的代码。
在这个makefile文件中,KERNELDIR指定内核的源代码目录,该目录通过当前运行内核使用的模块目录中的build符号链接指定。
巴拉巴拉这么一大堆理论看着头疼马上开始实操。

安装模块

将两个文件放在Ubuntu下,并且命名为mykernel。本机文件结构如下图
在这里插入图片描述
mykernel文件夹下,直接make编译,如果没有make指令的话,可以输入sudo apt install make安装make。
输入

sudo make

直接make编译报错了,看输出信息
在这里插入图片描述

error: function declaration isn’t a prototype [-Werror=strict-prototypes]

查了资料,是函数参数表没指明,如果没有参数表应该写void而不能为空,用nano打开.c文件,改一下,输入

sudo nano listprocess.c

exp_initexp_exit的括号里加上void
在这里插入图片描述
然后Ctrl+O写入,回车确定,然后Ctrl+X退出,然后再输入sudo make编译
在这里插入图片描述
又报错了,提示

obj-m:=listprocess.o
/bin/sh: 1: obj-m:=listprocess.o: not found
Makefile:6: recipe for target 'default' failed
make: *** [default] Error 127

用nano打开makefile,然后然后删除最后一句,输入

sudo nano Makefile

在这里插入图片描述
删除最后一句的obj-m:=listprocess.o
然后Ctrl+O写入,回车确定,然后Ctrl+X退出,然后再输入sudo make编译
没有报错了
开始安装,输入

sudo insmod listprocess.ko

注意是.ko文件,然后输入dmesg查看效果
有了pid,成功了,卸载的话输入

sudo rmmod listprocess.ko

然后输入dmesg查看效果
在这里插入图片描述
卸载成功,模块测试成功,=w=

补充:
问题:for_each_process报错
原因:有些人编译的时候会有报错没找到 for_each_process 函数,可能是对于不同的内核版本,for_each_process 位置不同,可以去查看一下源码,找一下for_each_process 函数。以本人尝试结果为例,4.11以后,for_each_process 在 include/linux/sched/signal.h中。
解决方案:在.c文件的头中加入一行代码#include <linux/sched/signal.h>就行了


http://chatgpt.dhexx.cn/article/KlSij0nG.shtml

相关文章

MATLAB脚本调用simulink仿真文件及simulink模块参数修改-load_system函数-sim函数-set_param函数

文章目录 1. load_system2. sim3. set_param4. get_param5. 应用 1. load_system 加载系统&#xff0c;添加所需要加载的simulink仿真模型所在的路径和名称。 load_system(pathname\filename);必须加载系统之后才可以完成后续的运行simulink仿真模型和获取仿真模型参数和设置…

Matlab学习笔记3——str2num

Matlab学习笔记3——str2num Convert character array or string to numeric array 将字符数组或字符串转换为数字数组 语法 X str2num(chr) [X,tf] str2num(chr) 输出 X — 输出数组 数字矩阵 输出数组&#xff0c;以数字矩阵形式返回。 tf — 真或假 1 |0 真或假结果…

strlen,strcpy,strcat,strcmp函数

1.strlen函数 strlen函数的作用是计算给定字符串的长度&#xff0c;从内存的某个位置开始&#xff0c;遇到第一个\0结束。 使用样例&#xff1a; int main() {const char *ar "abcdef";printf("%d\n", strlen(ar)); } 返回字符串"abcdef"的…

MATLAB str char cell num格式互相转换

简介 关于如何str char cell num格式互相转换的例子很多&#xff0c;但是都很单一&#xff0c;有时候使用的时候需要查找很久才可以解决问题。这里就对这几种方法进行一个汇总。 之所以会涉及使用cell&#xff0c;是因为涉及字符串、数字在同一个“矩阵”中&#xff0c;这时候…

C++ std::string::substr()

substr()函数返回一个新建的 初始化为string对象的子串的拷贝string对象。 子串是&#xff0c;在字符位置_Off开始&#xff0c;跨越_Count个字符&#xff08;或直到字符串的结尾&#xff09;对象的部分 void main() {//std::string::substr(_Off 0, _Count 4294967295U)&am…

matlab中 str2num 函数与 str2double 函数的区别

str2num 函数与 str2double 函数的相同点与不同点 1. 相同点&#xff1a; 当str为一个含数字的字符串时&#xff0c; str2num 函数与 str2double 函数一样。 如&#xff1a; 2. 不同点&#xff1a;当str为多个字符串构成的数组时&#xff0c; str2num 函数与 str2double 函数有…

可逆计算:下一代软件构造理论

可逆计算&#xff1a;下一代软件构造理论 众所周知&#xff0c;计算机科学得以存在的基石是两个基本理论&#xff1a;图灵于1936年提出的图灵机理论和丘奇同年早期发表的Lambda演算理论。这两个理论奠定了所谓通用计算&#xff08;Universal Computation&#xff09;的概念基础…

android 微积分计算器,不到1M的良心之作 连微积分都能算的计算器APP

计算器可谓是被手机取代的一大电子产品了&#xff0c;不过手机上的APP是否真的有传统的计算器好用&#xff1f;也并不一定。 一来&#xff0c;手机上的计算器APP功能普遍偏弱&#xff0c;特别是手机ROM自带的计算器&#xff1b;二来&#xff0c;计算器APP也算得上是流氓软件的重…

matlab对信号积分,对信号求积分 - Simulink - MathWorks 中国

说明 Integrator 模块输出其输入信号相对于时间的积分值。 Simulink 将 Integrator 模块作为具有一种状态的动态系统进行处理。模块动态由以下方程指定: {x˙(t)=u(t)y(t)=x(t)x(t0)=x0 ,其中: u 是模块输入。 y 是模块输出。 x 是模块状态。 x0 是 x 的初始条件。 虽然这些…

清华大学计算机学复变函数吗,清华大学计算机系课程 - osc_vq6lx46c的个人空间 - OSCHINA - 中文开源技术交流社区...

这么NB的大学&#xff0c;这么NB的课程&#xff0c;我们还有何理由不努力&#xff0c;同是大学生&#xff0c;人家在学校学的本身就比我们多&#xff0c;还需要更加努力才能跟上步伐&#xff0c;加油。 补充&#xff1a; 清华大学计算机系的课程分为六类课程&#xff1a; (一)公…

Chapter2.4:复数和复变函数运算

该系列博客主要讲述Matlab软件在自动控制方面的应用&#xff0c;如无自动控制理论基础&#xff0c;请先学习自动控制系列博文&#xff0c;该系列博客不再详细讲解自动控制理论知识。 自动控制理论基础相关链接&#xff1a;https://blog.csdn.net/qq_39032096/category_10287468…

信号与系统分析中的复变函数

动态图片来自于&#xff1a; ShutterStock 网站 . 01 教程规划 1.1 背景介绍 针对于信号与系统分析 课程学习的同学&#xff0c;由于之前没有先修过课程复变函数 &#xff0c;则会在后面信号与系统理论学习中缺少复变函数相关理论支持。为了帮助这部分同学及时补充上复变函数相…

matlab复变函数应用,matlab在复变函数中的一些应用修改后的.doc

matlab在复变函数中的一些应用修改后的.doc MATLAB语言课程论文MATLAB在复变函数中的一些应用姓名刘乐学号12013241953专业通信工程班级2013级通信2班指导老师朱瑜红学院物理电气性息学院完成日期2013年11月9日MATLAB在复变函数中的一些应用刘乐120132419532013级通信2班【摘要…

复变函数与积分变换matlab,matlab在复变函数与积分变换的应用

matlab在复变函数与积分变换的应用 本科毕业论文题目&#xff1a; MATLAB在复变函数与积分变换的应用 学院&#xff1a; 数学与计算机科学学院 班级&#xff1a; 数学与应用数学2009级班 姓名&#xff1a; 指导教师&#xff1a; 职称&#xff1a; 副教授 完成日期&#xff1a;…

复变函数与积分变换

复变函数与积分变换 一、拉普拉斯变换1.拉氏变换的性质a.线性性质b.相似性质c.微分性质例子例子 拉式变换 象函数的微分性质例子例子 积分性质象函数的积分性质例子例子 延迟性质位移性质拉氏变换的应用 一、拉普拉斯变换 1.拉氏变换的性质 a.线性性质 b.相似性质 pygame ursi…

unigui美化界面源码框架

对于delphier来说&#xff0c;顺应互联网时代&#xff0c;用delphi开发web程序&#xff0c;一直是一个很头痛的问题&#xff0c;以往开发delphi程序往往不需要前端和美工参与。但在ui界面上要想漂亮&#xff0c;需要配合和学习css、JS和美工知识&#xff0c;所以很多人会放弃。…

【一起学UniGUI】--UniGUI的界面与程序架构(4)

1、【统一的界面】 uniGUI是统一的图形用户界面&#xff0c;简称统一GUI。之所以称为统一的是因为它在所有带有Web浏览器的设备中提供了相同的UI体验。无论设备、操作系统、CPU和显示器是什么&#xff0c;在所有具有兼容Web浏览器的设备上&#xff0c;用户体验都是相同…

【一起学UniGUI】--创建新的uniGUI应用程序(11)

打开Delphi 10.3.1&#xff0c;在Delphi IDE中可以通过uniGUI应用程序向导轻松创建一个新的uniGUI应用程序(必须通过此方式来创建一个新的uniGUI应用程序)&#xff0c;并按向导界面提示操作。&#xff08;一&#xff09;、使用uniGUI应用程序向导创建一个新的uniGUI应用程序&am…

(1)uniGUI for C++ builder网站开发之uniGUI控件安装和你好世界

uniGUI for CBuilder网站开发之uniGUI控件安装和你好世界 By runsky中行雷威 2018.2.5 &#xff08;同一个世界&#xff0c;同一个梦想&#xff0c;交流学习CBuilder XE10&#xff0c;传承cbuilder的魅力&#xff01;欢迎各地朋友加入我的QQ群299497712,860634510、484979943…

uniGUI之UniDBGrid

uniDBGrid的相关样式设置 1.自适应列宽 代码如下&#xff08;示例&#xff09;&#xff1a; function store.load(sender, records, successful, operation, eOpts) {sender.grid.columnManager.columns.forEach(function(col){col.autoSize()}) }2.显示page当前记录及总记录…