Linux中添加一个带参数的模块

一、具体任务

设计一个带参数的模块,其参数为某个进程的 PID 号。模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID 号、进程状态。

二、模块构成

一个模块主要由2部分构成,即 .c文件 以及 Makefile 文件。
而在.c文件的构成中又包括文件头(包含的库函数),初始化函数(init),清除函数(exit),module_init module_exit函数和最后的模块许可声明。
在Makefile文件中包括一个指定生成.ko文件的语句,指向内核以及指向模块源目录的语句,default和clean语句。

三、代码解析

①创建一个module2.c文件(vim module2.c)

在这里插入图片描述
#include <linux/init.h>、#include <linux/module.h>是模块编程必需头文件;
<kernel.h>中包含了内核打印函数 printk函数等;
<moduleparam.h>允许用户传递参数;
<sched.h>包含了定义了进程运行可能处于的状态,提供task_struct
在这里插入图片描述
函数原型 module_param(name, type, perm);
参数name,type是自己定义的变量的类型,perm是权限

perm可取值:
#define S_IRUSR 00400 文件所有者可读
#define S_IWUSR 00200 文件所有者可写
#define S_IXUSR 00100 文件所有者可执行
#define S_IRGRP 00040 与文件所有者同组的用户可读
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IROTH 00004 与文件所有者不同组的用户可读
#define S_IWOTH 00002
#define S_IXOTH 00001

而针对网络上清一色的’0644’,解释是这样的:
0755->用户具有读/写/执行权限,组用户和其它用户具有读/写权限;
0644->用户具有读/写权限,组用户和其它用户具有只读权限;
一般赋予目录0755权限,而文件赋予0644权限。
在这里插入图片描述
task_strut 结构包含的是进程描述符内容(例如本题需要的:comm:进程名字、state:进程状态以及pid);
list_head 结构是创建双向循环链表的结构,包含指针next和prev,一般被嵌入到别的结构里面使用。
在这里插入图片描述
函数原型pid_task(find_vpid(pid), PIDTYPE_PID) 结构体类型,用于根据pid和其类型找到对应的task_struct,返回值为pid所对应的结构体类型,定义式是struct task_struct *pid_task(struct pid *pid, enum pid_type type);
printk printk是内核输出,在终端是看不见的。它实际上是为内核提供日志功能, 记录内核信息或用来给出警告。每个printk() 声明都会带一个优先级(即:KERN_ALERT等)使用像 KERN_ALERT这样的高优先级,来确保printk()将信息输出到控制台而不是只是添加到日志文件中。 后面语句中的KERN_ALERT本题可不加。
在这里插入图片描述
list_for_each //双向链表的遍历
原型:list_for_each(pos, head){
for (pos = (head)->next; pos != (head);pos = pos->next)
}
//遍历完以后最终回到head并返回head的地址
list_entry
原型:list_entry(ptr, type, member)
表示在找出ptr指向的链表节点所在的type类型的结构体首地址,member是type类型结构体成员,和ptr类型相同。返回值是ptr指向的节点的首地址,该节点是type类型。作用是用当前节点地址ptr值减去member离type结构体首地址的距离(以0作为首地址)

即:返回值=ptr-(member-type);

最后就得到了ptr节点指向的节点的type类型结构体的首地址。
在这里插入图片描述
从上至下依次是:
清除函数(module2_exit)(自定义,可不写printk等具体代码内容)
module_init(); //执行初始化函数注册
module_exit();//执行清除函数注册
模块许可声明–>(MODULE_LICENSE(“GPL”))
注意 这些都是模块必需,且模块必须通过MODULE_LICENSE 宏声明此模块的许可证,否则在加载此模块时,会收到内核被污染 “kernel tainted” 的警告,有意义的许可证除了 “GPL”,还有“GPL v2”,“GPL and additional rights”,“Dual BSD/GPL”,“Dual MPL/GPL”,“Proprietary”。

②Makefile文件(vim Makefile)

在这里插入图片描述
obj-m:= 后添加的是要生成的模块名称(如这里是module2.o,则生成的模块名称为module2.ko)。一般与.c文件名相同(为了好找)
KDIR:= 后面要指向的是自己已编译过的内核源码目录
PWD:= PWD 是当前目录,就是指向的是shell窗口当前所处位置,一般后面都是加“$(shell pwd)”不变
注意 default:和clean:之后回车不是8个空格,而是一个Tab键,虽然最后效果相同,但是运行是会报错的(一般":"完直接enter是会自动缩进的)
语句内 -C 指定内核源码目录, M 指定模块源码所在目录

四、完整代码

module2.c文件

#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/sched.h>

static pid_t pid=1;
module_param(pid,int,0644);

static int module2_init(void){
	struct task_struct *p;
	struct task_struct *psibling;
	struct task_struct *pchildren;
	struct list_head *L;
	
	//输出当前进程信息
	p=pid_task(find_vpid(pid),PIDTYPE_PID);
	printk(KERN_ALERT"归属\t程序名\t\tPID号\t进程状态\n");
	printk(KERN_ALERT"本身:    %8s%8d%8ld\n",p->comm,p->pid,p->state);
	//输出父进程信息
	if(p->parent==NULL)
		printk(KERN_ALERT"无父进程\n");
	else
                printk(KERN_ALERT"父进程:  %8s%8d%8ld\n",
                p->parent>comm,p->parent->pid,p->parent->state);
	//输出兄弟进程信息
	list_for_each(L,&p->parent->children){
                psibling=list_entry(L,struct task_struct,sibling);
                printk(KERN_ALERT"兄弟进程:%8s\t%d\t%8ld\n",
                psibling->comm,psibling->pid,psibling->state);
        }
	//输出子进程信息
        list_for_each(L,&p->children){
                pchildren=list_entry(L,struct task_struct,sibling);
                printk(KERN_ALERT"子进程:  %8s\t%8d\t%ld\n",
                pchildren->comm,pchildren->pid,pchildren->state);
        }

        return 0;
}


static void module2_exit(void){
        printk(KERN_ALERT"good bye\n");
}


module_init(module2_init);
module_exit(module2_exit);

Makefile文件

obj-m:=module2.o
KDIR:=/usr/src/linux-5.5.6
PWD:=$(shell pwd)

default:
        make -C $(KDIR) M=$(PWD) modules
clean:
        make -C $(KDIR) M=$(PWD) clean                                      

五、运行

注意: 要确保.c文件和Makefile文件在同一目录下
在这里插入图片描述
1、make
在这里插入图片描述
之后文件夹中会多出一堆乱七八糟的东西
在这里插入图片描述
2、(包括此处,之后的步骤需要获取权限“sudo su”)
insmod (.ko文件名)(参数名)=(要输入的参数)
在这里插入图片描述
注意 此处赋予pid的值需要有这个值对应的进程,否则会报错

3、lsmod查看是否安装成功
在这里插入图片描述
4、dmesg查看,成功
在这里插入图片描述

六、模块卸载以及可能报错

rmmod+.ko文件名
在这里插入图片描述
再次查看,卸载成功
在这里插入图片描述
在卸载模块时候可能遇到无法卸载,模块被莫名占用的情况,这是编写内容出错可能会报错(即:在insmod后无任何反应)
此时删除make产生的文件,在重新启动可以消除占用,而且既然会在这里卡着也就不是系统的问题是代码问题导致无端占用,就要重新检查再进行make安装。

其它情况暂时还没遇到。

  • 9
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
实验一 linux 内核编译及添加系统调用 设计目的 Linux 是开源操作系统,用户可以根据自身系统需要裁剪、修改内核,定制出功能更 合适、运行效率更高的系统,因此,编译 linux 内核是进行内核开发的必要基本功。 在系统根据需要添加新的系统调用是修改内核的一种常用手段,通过本次实验,读 者应理解 linux 系统处理系统调用的流程以及增系统调用的方法。 内容要求 (1) 添加一个系统调用,实现对指定进程的 nice 值的修改或读取功能,并返回进程最 新的 nice 值及优先级 prio。建议调用原型为: int mysetnice(pid_t pid, int flag, int nicevalue, void __user * prio, void __user * nice); 参数含义: pid:进程 ID。 flag:若值为 0,表示读取 nice 值;若值为 1,表示修改 nice 值。 Prio、nice:进程当前优先级及 nice 值。 返回值:系统调用成功时返回 0,失败时返回错误码 EFAULT。 (2) 写一个简单的应用程序测试(1)添加的系统调用。 (3) 若程序调用了 linux 的内核函数,要求深入阅读相关函数源码。 实验二 linux 内核模块编程 设计目的 Linux 提供的模块机制能动态扩充 linux 功能而无需重新编译内核,已经广泛应用在 linux 内核的许多功能的实现。在本实验将学习模块的基本概念、原理及实现技术,然后利 用内核模块编程访问进程的基本信息,从而深对进程概念的理解、对模块编程技术的掌 握。 内容要求 (1) 设计一个模块,要求列出系统所有内核线程的程序名、PID 号、进程状态及 进程优先级。 (2) 设计一个参数模块,其参数为某个进程的 PID 号,该模块的功能是列出该 进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID 号。 (3) 请根据自身情况,进一步阅读分析程序用到的相关内核函数的源码实现。 实验四 linux 进程管理 设计目的 (1) 熟悉 linux 的命令接口。 (2) 通过对 linux 进程控制的相关系统调用的编程应用,进一步深对进程概念的理解, 明确进程和程序的联系和区别,理解进程并发执行的具体含义。 (3) 通过 Linux 管道通信机制、消息队列通信机制、共享内存通信机制的使用,深 对不同类型的进程通信方式的理解。 (4) 通过对 linux 的 Posix 信号量的应用,深对信号量同步机制的理解。 (5)请根据自身情况,进一步阅读分析相关系统调用的内核源码实现。 设计内容 (1)熟悉 linux 常用命令:pwd,useradd,passwd, who, ps, pstree, kill, top, ls, cd, mkdir, rmdir, cp, rm, mv, cat, more, grep 等。 (2) 实现一个模拟的 shell: 编写三个不同的程序 cmd1.c,cmd2.c,cmd3.c,每个程序的功能自定,分别编译成可执 行文件 cmd1,cmd2,cmd3。然后再编写一个程序,模拟 shell 程序的功能,能根据用户输 入的字符串(表示相应的命令名),去为相应的命令创建子进程并让它去执行相应的程序,而父进程则等待子进程结束,然后再等待接收下一条命令。如果接收到的命令为 exit,则父 进程结束;如果接收到的命令是无效命令,则显示“Command not found”,继续等待。 (3) 实现一个管道通信程序: 由父进程创建一个管道,然后再创建 3 个子进程,并由这三个子进程利用管道与父进程 之间进行通信:子进程发送信息,父进程等三个子进程全部发完消息后再接收信息。通信的 具体内容可根据自己的需要随意设计,要求能试验阻塞型读写过程的各种情况,测试管道 的默认大小,并且要求利用 Posix 信号量机制实现进程间对管道的互斥访问。运行程序,观 察各种情况下,进程实际读写的字节数以及进程阻塞唤醒的情况。 (4) 利用 linux 的消息队列通信机制实现两个线程间的通信: 编写程序创建两个线程:sender 线程和 receive 线程,其 sender 线程运行函数 sender(), 它创建一个消息队列,然后,循环等待用户通过终端输入一串字符,将这串字符通过消息队 列发送给 receiver 线程,直到用户输入“exit”为止;最后,它向 receiver 线程发送消息“end”, 并且等待 receiver 的应答,等到应答消息后,将接收到的应答信息显示在终端屏幕上,删除 相关消息队列,结束程序的运行。Receiver 线程运行 rece

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值