操作系统作业:Linux内核模块(内核数据结构)

327 阅读4分钟

Linux内核模块

前言

操作系统编程项目Linux内核模块

准备工作

1:准备好Linux环境,下载好make和gcc可参考以下教程

gcc安装

make安装

2:Linux下建立Makefile文件

obj-m += simple.o
all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

3:Linux建立内核模块命名为simple.c

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

/* This function is called when the module is loaded. */
int simple_init(void)
{
       printk(KERN_INFO "Loading Module\n");

       return 0;
}

/* This function is called when the module is removed. */
void simple_exit(void) {
	printk(KERN_INFO "Removing Module\n");
}

/* Macros for registering module entry and exit points. */
module_init( simple_init );
module_exit( simple_exit );

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple Module");
MODULE_AUTHOR("SGG");

4:将simple.c文件和makefile放同一文件中或同一桌面

第一部分:创建内核模块

​ 这个项目的第一部分包括以下一系列步骤,用于创建模块并将其插入到Linux内核。要列出当前加载的所有内核模块,可输入命令

*编译makefile文件*

1.输入make指令得到文件simple.ko

在这里插入图片描述

2.编译生成多个文件

在这里插入图片描述

  1. 内模块的加载生成了字符串Loading Module,它是在simple_init()中定义的。说明simple模块已经加载到内核中了。我们可以使用lsmod命令查看,lsmod命令的作用是告诉我们所有在内核中运行的模块的信息,包括模块的名称,占用空间的大小,使用计数以及当前状态和依赖性。

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

  1. 安装后移除内模块看到了打印在屏幕上的“Removing Module!”,它是在simple_exit()中定义的。由此说明,simple模块已经被删除。如果这时候我们再使用lsmod查看,会发现simple模块已经不在了。

在这里插入图片描述

  1. 清除缓冲区

在这里插入图片描述

第二部分:内核数据结构

​ 在模板入口点,创建链表以包含5个structura birthday元素。遍历链表并且输出内容到内核日志缓冲区。调用dmesg,以确保在模板加载时刻列表构造正确。

​ 在模板退出点,从链表中删除元素,并且将空闲内存返回到内核。另外,调用dmesg,以检查在模块卸载时该列表已经删除。

将文件中多余文件删除(仅留下Makefile和simple.c)

将simple.c的代码改成如下

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/types.h>
struct birthday{
    int day;
    int month;
    int year;
    struct list_head list;
};//创建对象
static LIST_HEAD(birthday_list);
/* This function is called when the module is loaded. */
int simple_init(void)
{
    static LIST_HEAD(birthday_list);//生成头节点
    struct birthday *person; //创建对象
    struct birthday *ptr;
    //初始化所有变量
    person = kmalloc(sizeof(*person), GFP_KERNEL);//分配空间
    person->day = 2;//赋值
    person->month= 8;
    person->year = 1995;
    INIT_LIST_HEAD(&person->list);
   
    //连接
    list_add_tail (&person->list, &birthday_list); 
  
    //遍历
     printk(KERN_INFO "Loading Module\n");
     list_for_each_entry(ptr, &birthday_list, list)
     {
         printk(KERN_INFO "Year: %d, Month: %d, Day: %d\n",ptr->year,ptr->month,ptr->day);
     }
     return 0;
}

void simple_exit(void) {
    struct birthday *ptr, *next;
    list_for_each_entry_safe(ptr, next, &birthday_list, list)
    { 
        printk(KERN_INFO "Removing Year: %d, Month: %d, Day: %d\n",ptr->year,ptr->month,ptr->day);
        list_del(&ptr->list); 
        kfree(ptr); 
    } 
    printk(KERN_INFO "Removing Module\n");
}

module_init( simple_init );
module_exit( simple_exit );

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple Module");
MODULE_AUTHOR("SGG");

在simple_init函数中添加我们将要建立的对象,以及用来遍历的指针person。

给每一个对对象分配空间,并初始化赋值。再使用 list_add_tail(&person1->list, &birthday_list);函数将对象串接起来。最后使用list_for_each_entry(ptr, &birthday_list,list)函数进行遍历。在遍历函数中打印信息。在退出函数中,list_for_each_entry_safe(ptr, next, &birthday_list, list)进行遍历在遍历函数中删除每一个尾部节点,并打印信息,释放分配的空间。

步骤跟第一部分类似

Makefile编译

1.输入make指令得到文件simple.ko

在这里插入图片描述

2.遍历链表并输出内容到内容到内核日志缓冲区

在这里插入图片描述

3.从链表上删除元素

在这里插入图片描述

总结:

1:内核模块的代码编写没有外部的函数库可以用,只能使用内核导出的函数;这点于应用程序是有区别的,应用程序习惯于使用外部的库函数,在编译的时候将程序与库函数链接在一起。比如说:内核模块中不能使用printf(),而只能使用printk()函数。

2:内核模块运行在内核空间,而应用程序在用户空间。应用程序的运行会形成新的进程,而内核模块一般不会。每当应用程序执行系统调用时,linux执行模式从用户空间切换到内核空间。

3.内核是硬件与软件之间的一个中间层。作用是将应用层序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。内核就像一个库,提供了一组面向系统的命令。系统调用对于应用程序来说,就像调用普通函数一样。

新人创作打卡挑战赛

发博客就能抽奖!定制产品红包拿不停!