Linux内核驱动学习---编写最简单Linux内核模块HelloWorld

以下内容来源于
https://blog.csdn.net/lihaoweiV/article/details/6602261
https://blog.csdn.net/sh21_/article/details/60878812
https://blog.csdn.net/u010632165/article/details/86541941
https://blog.csdn.net/FallingU/article/details/76166194
推荐
https://blog.csdn.net/zhengnianli/article/details/120838057

什么是内核模块
模块是可以根据实际需要可以动态加载和卸载到内核中的代码。它们扩展了内核的功能,而无需重启系统,就可以进行模块加载,并工作。例如,一种类型的模块是设备驱动程序,它允许内核访问连接到系统的硬件。没有模块,我们必须构建整个内核并将新功能直接添加到内核映像中。除了拥有更大的内核之外,这还有一个缺点,就是每次我们想要新功能时都需要我们重新编译内核并烧录到设备。
Linux内核模块基本原理
Linux 内核模块(LKM)是一些在启动的操作系统内核需要时可以载入内核执行的代码块,不需要时由操作系统卸载。它们扩展了操作系统内核功能却不需要重新编译内核、启动系统。如果没有内核模块,就不得不反复编译生成操作系统的内核镜像来加入新功能,当附加的功能很多时,还会使内核变得臃肿。一个Linux 内核模块主要由以下几个部分组成:
(1) 模块加载函数(必须):当通过insmod 或modprobe 命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块相关初始化工作
(2) 模块卸载函数(必须):当通过rmmod 命令卸载模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能
(3) 模块许可证声明(必须):模块许可证(LICENCE)声明描述内核模块的许可权限,如果不声明LICENCE,模块被加载时将收到内核被污染的警告。大多数情况下,内核模块应遵循GPL 兼容许可权。Linux2.6 内核模块最常见的是以MODULE_LICENSE(“Dual BSD/GPL”)语句声明模块采用BSD/GPL 双LICENSE
(4) 模块参数(可选):模块参数是模块被加载的时候可以被传递给他的值,它本身对应模块内部的全局变量。
(5) 模块导出符号(可选):内核模块可以导出符号(symbol,对应于函数或变量),这样其他模块可以使用本模块中的变量或函数。
(6) 模块作者等信息声明(可选)。
一个内核模块至少包含两个函数,模块被加载时执行的初始化函数init_module()和模块被卸载时执行的结束函数cleanup_module()。在最新内核稳定版本2.6 中,两个函数可以起任意的名字,通过宏module_init()和module_exit()注册调用要编译内核模块,把代码嵌进内核空间,首先要获取内核源代码,且版本必需与当前正在运行的版本一致
hello.c

/*包含了对模块的结构定义以及模块的版本控制,
* 任何模块程序的编写都要包含这个头文件*/
#include <linux/module.h>    
#include <linux/kernel.h>    //包含了常用的内核函数
/*宏__init告诉编译程序相关的函数和变量仅用于初始化,
* 编译程序将标有__init的所有代码存储到特殊的内存段中,
* 初始化结束后就释放这段内存*/
#include <linux/init.h>      //包含了宏__init和宏__exit

static int __init hello_init(void)//lkp_init()是模块初始化函数
{
    /*printk()函数,该函数是由内核定义的,功能与C库中的
      printf()类似,它把要打印的信息输出到终端或者系统日志*/
    printk(KERN_INFO "module init success\n");
    return 0;
}
static void __exit hello_exit(void) //lkp_cleanup()是模块的退出和清理函数
{
    printk(KERN_INFO "module exit success\n");
    //printk("Hello World! End of hello world module!\n");
}
/*是模块编程中最基本也是必需的两个函数*/
module_init(hello_init);  //向内核注册模块提供新功能
module_exit(hello_exit); //注销由模块提供所有的功能
MODULE_LICENSE("GPL"); //告诉内核该模块具有GNU公共许可证
MODULE_AUTHOR("作者");
MODULE_DESCRIPTION("功能描述");

这是一个简单内核模块程序,可以动态加载和卸载。虽然没有实际的功能。
模块加载的时候系统会打印module init success\n
模块卸载的时候系统会打印module exit success\n
模块编译
Makefile文件

obj-m :=hello.o                  #产生hello模块的目标文件
all:
	make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) modules    #编译模块
clean:
	make -C /lib/modules/$(shell uname -r)/build SUBDIRS=$(PWD) clean      #清理

或者

obj-m:=hello.o
PWD:= $(shell pwd)
KERNELDIR:= /lib/modules/$(shell uname -r)/build
EXTRA_CFLAGS= -O0

all:
	make -C $(KERNELDIR)  M=$(PWD) modules
clean:
	make -C $(KERNELDIR) M=$(PWD) clean

上面的Makefile中使用了obj-m := 这个赋值语句,其含义说明要使用目标文件hello.o建立一个模块,最后生成的模块名字是hello.ko

hello.cMakefile放在同一路径下进行编译,编译成功,会在当前路径下生成hello.ko,这就是我们将要加载到内核的模块。

make

执行了make之后,当前目录下会生成加载内核模块所需要的文件
在这里插入图片描述
因为是要插入内核,要在内核空间运行,所以普通用户是不能直接把内核模块插入到内核中的,必须要获得权限才能进行内核模块的插入操作

su

测试结果
模块加载
加载hello.ko模块到内核中

insmod hello.ko

在这里插入图片描述
如果模块加载成功的话,可以查看模块

lsmod | grep hello

成功加载会显示以下结果

hello                  16384  0 

也可以直接用lsmod查看我们编写的模块是否插入成功

查看内核打印的消息

dmesg | grep "init success"
[ 4160.003247] module init success

也可以直接用dmesg查看查看内核打印的消息

模块卸载

rmmod hello.ko

成功卸载hello模块后,可以查看内核是否正常打印出我们预设在程序的打印信息。

dmesg | grep "exit success"

可以看到终端上显示module exit success,说明通过rmmod成功卸载hello.ko

[ 7160.003247] module exit success

这时候,如果再通过lsmod去查看当前的内核模块,就会发现hello.ko已经消失不见了。

相关指令

namefunction
lsmod查看已经加载到内核中的模块
insmod加载模块到内核中
rmmod从内核卸载模块
depmod生成模块所需要的依赖
modprobe很强大的指令(-h)

编写模块时的声明(含MODULE_LICENSE等)
编写模块必须先声明下面两句:

#include <linux/module.h>//这个头文件包含了许多符号与函数的定义,这些符号与函数多与加载模块有关

#include <linux/init.h>//这个头文件包含了你的模块初始化与清除的函数

另外,如果你的模块需要用到参数传递,那么你可能就要声明moduleparam.h这个头文件了。
再者,模块里常包含一些描述性声明,如:

MODULE_LICENSE("GPL");          // "GPL" 是指明了 这是GNU General Public License的任意版本                                                          // “GPL v2” 是指明 这仅声明为GPL的第二版本                                                          // "GPL and addtional"                                                         // "Dual BSD/GPL"                                                           // "Dual MPL/GPL"                                                           // "Proprietary"  私有的                                                            // 除非你的模块显式地声明一个开源版本,否则内核会默认你这是一个私有的模块(Proprietary)。
MODULE_AUTHOR              // 声明作者
MODULE_DESCRIPTION   //对这个模块作一个简单的描述,这个描述是"human-readable"的
MODULE_VERSION            // 这个模块的版本
MODULE_ALIAS              // 这个模块的别名
MODULE_DEVICE_TABLE // 告诉用户空间这个模块支持什么样的设备

MODULE_声明可以写在模块的任何地方(但必须在函数外面),但是惯例是写在模块最后。

  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值