Linux 内核模块

一、加载卸载内核模块

1.什么是:Linux?

Linux,全称GNU/Linux,是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX的多用户、多任务、支持多线程和多CPU的操作系统。

2.实验要求

创建内核模块并将其加载到 Linux 内核,完成该项目 Linux 操作系统。要求使用终端应用程序来编译程序,并且还要在命令行中输入命令来管理内核模块。

3.需要设备

ubuntu 20

内核文件simple.c 和Makefile

4.Makefile

Linux内核中Makefile的作用是根据配置的情况,构造出需要编译的源文件列表,然后分别编译,并把目标代码链接到一起,最终形成 linux 内核二进制文件

5.基本命令

which gcc 查看是否安装gcc编译器

mkdir src: 创建一个src目录

cd src 进入src目录

6.加载内核模块步骤

(1)准备工作

将simple.c和Makefile文件复制进虚拟机文件内并打开终端。

查看是否有gcc和make:

 

ls 查看目录:

 

报错:还未解压,是一个.zip文件

解压文件

 make 编译内核模块:

 simple.ko表示已编译的内核模块,接下来将此模块插入 Linux 内核,

输入密码后界面没显示?输入不了密码?这是因为系统的保护,直接输密码后按回车就行。

 

“Invalid module format" 格式错误?

原因:驱动文件编译的内核版本与当前运行系统的内核版本不一致导致。

  1. 通过modinfo 查看simple.ko的内核版本vermagic

  1. 通过uname -r查看系统的内核版本

是差不多的版本啊,百度发现: 在系统的/lib/modules下面有系统自带的内核module,也会有用户自己安装的内核; 我本系统ubuntu内核是3.11.0-15-generic, 安装的是: 3.2.62; 在编译hello.c时,建议使用ubuntu系统自带的内核来编译; 使用下面的命令: make -C /lib/modules/3.11.0-15-generic/build M=pwd modules 就能顺利编译通过; 后续使用insmod hello.ko时就不会因为版本的问题报错。

 

dmesg查看内核日志缓冲区信息,看到了loading Module就加载成功了!

 

7.卸载内核:

删除内核模块需要调用 rmmod 命令(请注意,.ko 后缀是不必要的);

 

二、内核数据结构

1.要求:

修改内核模块,以便使用内核的链表数据结构。

在模块入口点中,创建一个链表以包含五个 struct birthday 元素。遍历该链表并将其内 容输出到内核日志缓冲区。调用 dmesg 命令以确保在模块加载时,该列表构建正确。

在模块退出点,从链表中删除元素,然后将空闲内存返回到内核。另外,调用 dmesg 命 令以检查在模块卸载时,该列表已被删除

2.编写代码: vim(vi improved),是Linux系统中提供的编辑器,它是vi的增强版本,与vi向上兼容。通常,在LInux中用到的vi实际上是vim,即使输入命令时输入的是vi,但是使用的仍然是vim。

终端输入vim进入vim

vim: 两种模式:命令模式,编辑模式

需要输入 vim simple.c 进入simple文件,进入后看到simple.c的内容代码。现在需要输入 i 进入输入模式。

将下列代码打入simple文件,完成后 按Esc键进入命令模式,输入 “:wq"回车,就会保存并退出vim。

 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/slab.h>
 #include <linux/types.h>//----->list-head
 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)
 {
         struct birthday *person1,*person2,*person3,*person4,*person5;
         struct birthday *ptr;
 ​
         person1=kmalloc(sizeof(*person1),GFP_KERNEL);
         person1->day=2;
         person1->month=8;
         person1->year=2003;
         INIT_LIST_HEAD(&person1->list);
 ​
         list_add_tail(&person1->list,&birthday_list);
 ​
         person2=kmalloc(sizeof(*person2),GFP_KERNEL);
         person2->day=3;
         person2->month=9;
         person2->year=2004;
         INIT_LIST_HEAD(&person2->list);
 ​
         list_add_tail(&person2->list,&birthday_list);
 ​
         person3=kmalloc(sizeof(*person3),GFP_KERNEL);
         person3->day=4;
         person3->month=10;
         person3->year=2005;
         INIT_LIST_HEAD(&person3->list);
 ​
         list_add_tail(&person3->list,&birthday_list);
 ​
         person4=kmalloc(sizeof(*person4),GFP_KERNEL);
         person4->day=5;
         person4->month=11;
         person4->year=2006;
         INIT_LIST_HEAD(&person4->list);
 ​
         list_add_tail(&person4->list,&birthday_list);
 ​
         person5=kmalloc(sizeof(*person5),GFP_KERNEL);
         person5->day=6;
         person5->month=12;
         person5->year=2007;
         INIT_LIST_HEAD(&person5->list);
 ​
         list_add_tail(&person5->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;//模块入口点函数必须返回一个整数值,0 表示成功,其他值表示失败。
 }
 ​
 /* This function is called when the module is removed. */
 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");
 }
 ​
 /* 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");

代码解释: printk(): 是等价于 printf() 的内核函数,但是它的输出被发送到内核日志缓冲区,其内容可以由 dmesg 命令读取。printf ()和 printk()之间的一个区别是 printk()允许指定一个优先级标志,其具体值在 include 文件中给出。在本例中,优先级是 KERN_INFO,表示这是一个信息性消息。

注意成员 struct list_head list结构 list_head 的定义位于头文件<中。意思是将所形成的链表嵌入节点。结构 list_head 非常简单,它仅拥有两个成员,next 和 prev, 它们指向列表中的下一个和前一个节点。通过在结构内的嵌入链表成员,linux 可以使用一 组宏函数来管理这个数据结构。

头文件:

这些是Linux内核开发中常用的头文件。它们包含了许多Linux内核中的数据结构、宏定义和函数原型等,可以在Linux内核开发中使用。

<linux/init.h>:包含了Linux内核初始化和清理相关的宏定义和函数原型。

<linux/module.h>:包含了Linux内核模块相关的宏定义和函数原型。

<linux/kernel.h>:包含了Linux内核中常用的宏定义和函数原型,如打印调试信息的printk函数等。 <linux/list.h>:包含了Linux内核中双向链表的数据结构和相关函数。

<linux/slab.h>:包含了Linux内核中的内存分配和释放相关的函数原型,如kmalloc和kfree等。 <linux/types.h>:包含了Linux内核中的基本数据类型的定义,如u8、u16、u32等。 这些头文件都是Linux内核开发中必不可少的,通过它们,可以访问到Linux内核中的许多功能和数据结构。

3.加载卸载内核

打开vim:终端输入vim——进入输入模式:i——退出编辑模式 esc——保存输入:w仅仅保存。 输入:q退出。输入:wq保存并且退出。 :wq 文件名.格式 (退出并且保存)注意要打冒号!

再次编译:

 查看日志缓冲区信息:

 卸载内核模块

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值