操作系统实验:Linux内核模块编程

实验内容

(1)设计一个模块,要求列出系统中所有内核线程的程序名、PID、进程状态、进程优先级、父进程的PID。
(2)设计一个带参数的模块,其参数为某个进程的PID号,模块的功能是列出该进程的家族信息,包括父进程、兄弟进程和子进程的程序名、PID号及进程状态。

实验步骤

实验内容一

1、编写模块代码(文件名module1.c,随便创建一个文件夹放在里边就好)

#include 
#include 
#include 
#include 
#include 

// 初始化函数
static int hello_init(void)
{
    struct task_struct *p;  //Linux内核的进程控制块是task_struct结构体,所有运行在系统中的进程都以task_struct链表的形式存在内核中
    printk(KERN_ALERT"            名称\t进程号\t状态  \t优先级\t父进程号\t");
    for_each_process(p)  //for_each_process是一个宏,在sched.h里面定义: 是从init_task开始遍历系统所有进程,init_task是进程结构链表头。
    {
        if(p->mm == NULL){ //对于内核线程,mm为NULL
            printk(KERN_ALERT"%16s\t%-6d\t%-6ld\t%-6d\t%-6d\n",p->comm,p->pid, p->state,p->normal_prio,p->parent->pid);
        }
    }
    return 0;
}
// 清理函数
static void hello_exit(void)
{
    printk(KERN_ALERT"goodbye!\n");
}

// 函数注册
module_init(hello_init);  
module_exit(hello_exit);  

// 模块许可申明
MODULE_LICENSE("GPL");  

2、编写Makefile(文件名Makefile,下面代码中,$(MAKE)前面的是Tab,不是单纯的空格)这个文件和module1.c写在一个文件夹内

obj-m := module1.o
KDIR:= /lib/modules/$(shell uname -r)/build
PWD:= $(shell pwd)

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

obj-m: 编译成可动态加载的module,通过insmod动态重定位装入到内核
obj-y: 静态编译链接进内核,在系统启动过程中进行初始化

操作系统实验:Linux内核模块编程_第1张图片

3、编译,添加模块,并输出日志

(1)编译

make

(2)添加模块

insmod module1.ko

(3)输出日志查看效果

dmesg

(4)若要修改module1.c代码,重新添加同名模块前需要先卸载模块,否则会报错。

rmmod module1.ko

操作系统实验:Linux内核模块编程_第2张图片

实验内容二

1、编写模块(文件名module2.c,创建一个新的文件夹,放在里边)

#include
#include
#include
#include 
#include 

static pid_t pid=1;
module_param(pid,int,0644);  //用来传递命令行参数

static int hello_init(void)
{
    struct task_struct *p;
    struct list_head *pp;
    struct task_struct *psibling;

    // 当前进程的 PID
    p = pid_task(find_vpid(pid), PIDTYPE_PID); //通过pid和其对应的类型PIDTYPE_PID找到对应的task_struct
    printk("当前进程: %d %s %ld\n", p->pid, p->comm, p->state);

    // 父进程
    if(p->parent == NULL) {
        printk("没有父进程!\n");
    }
    else {
        printk("父进程: %d %s %ld\n", p->parent->pid, p->parent->comm, p->parent->state);
    }

    // 兄弟进程
    list_for_each(pp, &p->parent->children) //for循环遍历链表
    {
        psibling = list_entry(pp, struct task_struct, sibling); //找出pp指向的链表节点所在的task_struct类型的结构体首地址, sibling是结构体成员
        printk("兄弟进程: %d %s %ld\n", psibling->pid, psibling->comm, psibling->state);
    }

    // 子进程
    list_for_each(pp, &p->children)
    {
        psibling = list_entry(pp, struct task_struct, sibling); //sibling用于把当前进程插入到兄弟链表中
        printk("子进程: %d %s %ld\n", psibling->pid, psibling->comm, psibling->state);
    }

    return 0;
}

static void hello_exit(void)
{
    printk(KERN_ALERT"goodbye!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

2、编写Makefile(文件名Makefie,依旧是放到同一个文件夹里)

obj-m:=module2.o
KDIR:= /lib/modules/$(shell uname -r)/build

PWD:= $(shell pwd)

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

3、编译,添加模块,并输出日志

(1)编译

make

(2)添加模块,PID号可以利用实验内容一读取出来的进程号,不然随便写一个数字很可能由于没有该进程从而报错

insmod module2.ko pid=168

(3)输出日志,从左到右依次是进程说明、进程号、进程的程序名、进程状态

dmesg

操作系统实验:Linux内核模块编程_第3张图片

你可能感兴趣的:(Linux,linux,操作系统)