linux下模块与模块间,模块与内核间共享变量,初探Linux内核编程,参数传递以及模块间函数调用...
一.前言我们一起从3个小例子来体验一下linux内核编程。如下:1.内核编程之hello world2.模块参数传递3.模块间函数调用二.准备工作首先,在你的linux系统上面安装linux头文件,debian系列:1 $:sudo apt-get install linux-headers-`uname -r`安装后,在你的/lib/modules/目录下有你刚刚安装的头文件版本号对应的目录。头
一.前言
我们一起从3个小例子来体验一下linux内核编程。如下:
1.内核编程之hello world
2.模块参数传递
3.模块间函数调用
二.准备工作
首先,在你的linux系统上面安装linux头文件,debian系列:
1 $:sudo apt-get install linux-headers-`uname -r`
安装后,在你的/lib/modules/目录下有你刚刚安装的头文件版本号对应的目录。头文件夹下面还有个build文件夹,里面的Makefile文件是等会要编译内核模块用的。如图,这是我机器上面的:

注意:安装的头文件版本一定要和你的系统版本一样,不然你自己编写的模块不能插入到本机的内核。如果你apt-get安装头文件时,没有对应的头文件,或者你的源里面放不稳定版本的源后,依然没有对应的头文件,你可以到这里搜索需要的deb包来安装。再或者下载跟本机对应的内核源码来构建环境。
三.内核编程之hello world
我们先来了解下内核模块文件的入口和出口。它由2个宏来注册入口和出口,分别是:
1 module_init(x);2 module_exit(x);
这2个宏在头文件目录的include/linux/module.h。宏里面的x代表注册到内核的入口和出口函数。通俗一点讲就是模块初始化和模块卸载时调用的函数。
初始化函数的形式:int my_init(void);
退出函数的形式:void my_exit(void);
另外还有一些宏:
MODULE_LICENSE(_license):模块的许可证。
MODULE_AUTHOR(_author):模块的作者。
MODULE_VERSION(_version):模块版本
MODULE_DESCRIPTION(_description):模块的描述。
还有一些就不一一举例了。
现在,我来看最简单的hello world例子:
文件名:kernel_hello.c
1 #include
2 #include
3 #include
4
5 /*以下4个宏分别是许可证,作者,模块描述,模块版本*/
6 MODULE_LICENSE("Dual BSD/GPL");7 MODULE_AUTHOR("yuuyuu");8 MODULE_DESCRIPTION("kernel module hello");9 MODULE_VERSION("1.0");10
11 /*入口函数*/
12 static int hello_init(void)13 {14 printk(KERN_ALERT "hello_init() start\n");15
16 return 0;17 }18
19 /*退出函数*/
20 static void hello_exit(void)21 {22 printk(KERN_ALERT "hello_exit() start\n");23 }24
25 /*注册到内核*/
26 module_init(hello_init);27 module_exit(hello_exit);
上面的printk()函数时内核自己实现的输出函数,KERN_ALERT时输出信息的级别!
然后再写一个很简单的Makefile文件:
1 KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build2 PWD := $(shell pwd)3
4 obj-m :=kernel_hello.o5
6 default:7 $(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules
KERNAL_DIR:你刚刚安装头文件的目录
PWD:代表当前你编写模块文件的目录。
obj-m:模块的依赖目标文件。另外,内核的Makefile文件:obj-m代表将目标文件编译成模块,obj-y代表编译到内核,ojb-n代表不编译。
-C:执行make的时候,把工作目录切换到-C后面指定的参数目录,这里即头文件目录下的build目录。
M:这个M时内核Makefile里面的一个变量。作用时回到当前目录继续读取Makefile,这里的就是读完build目录下的Makefile之后再回到我们的这个目录,读取我们刚刚编写的那个Makefile。
最后的modules是代表编译模块。
现在我们来编译下,在我们编写Makefile的目录下执行make,会看到生成了模块文件kernel_hello.ko

查看模块信息:sudo modinfo kernel_hello.ko

可以看到刚刚那几个宏插入的模块信息。
现在我们一口气执行4个动作:插入模块,查看内核已插入的模块,卸载模块,查看dmesg信息:

可以看到,模块在初始化和退出时都打印了函数里面的信息。
四.模块参数传递
模块的参数传递也是一个宏,在头文件目录的include/linux/moduleparam.h:
1 module_param(name, type, perm)
name:模块中的变量名,也是用户可指定参数名。
type:byte,short,ushot,int,uint,long,ulong,charp,bool这些
perm:模块的权限控制。跟linux文件权限控制一样的。
文件名:kernel_hello_param.c
1 #include
2 #include
3 #include
4
5 /*以下4个宏分别是许可证,作者,模块描述,模块版本*/
6 MODULE_LICENSE("Dual BSD/GPL");7 MODULE_AUTHOR("yuuyuu");8 MODULE_DESCRIPTION("kernel module hello");9 MODULE_VERSION("1.0");10
11 static char *msg;12 module_param(msg, charp, 0644);13
14 /*入口函数*/
15 static int hello_init(void)16 {17 printk(KERN_ALERT "hello_init() start\n");18 printk(KERN_ALERT "%s\n", msg);19
20 return 0;21 }22
23 /*退出函数*/
24 static void hello_exit(void)25 {26 printk(KERN_ALERT "hello_exit() start\n");27 }28
29 /*注册到内核*/
30 module_init(hello_init);31 module_exit(hello_exit);
比上一个文件,就增加了11,12,18行。注意第12行的charp,是内核的字符指针。
编译后,传参插入,dmesg查看信息:

插入的参数msg跟在模块后面即可。
五.模块间函数调用
模块的函数导出到符号表才可以供其他函数使用,需要用到宏:
1 EXPORT_SYMBOL(sym)
该宏在include/linux/export.h里面。
既然模块间函数调用,我们要编写2个模块。
文件一:kernel_fun.h
1 #ifndef KERNEL_FUN_H2 #define KERNEL_FUN_H
3
4 void fun(void);5
6 #endif

文件二,要导出的模块文件:kernel_fun.c

1 #include
2 #include
3 #include
4 #include
5
6 #include "kernel_fun.h"
7
8 /*以下4个宏分别是许可证,作者,模块描述,模块版本*/
9 MODULE_LICENSE("Dual BSD/GPL");10 MODULE_AUTHOR("yuuyuu");11 MODULE_DESCRIPTION("kernel module hello");12 MODULE_VERSION("1.0");13
14 /*入口函数*/
15 static int fun_init(void)16 {17 printk(KERN_ALERT "fun_init() start\n");18
19 return 0;20 }21
22 voidfun()23 {24 printk(KERN_ALERT "fun() is called\n");25 }26
27 /*退出函数*/
28 static void fun_exit(void)29 {30 printk(KERN_ALERT "fun_exit() start\n");31 }32
33 /*注册到内核*/
34 module_init(fun_init);35 module_exit(fun_exit);36
37 /*导出符号表*/
38 EXPORT_SYMBOL(fun);
最后一行就是导出到符号表。
文件三,要调用模块文件二的函数:kernel_mod.c
1 #include
2 #include
3 #include
4 #include
5
6 #include "kernel_fun.h"
7
8 /*以下4个宏分别是许可证,作者,模块描述,模块版本*/
9 MODULE_LICENSE("Dual BSD/GPL");10 MODULE_AUTHOR("yuuyuu");11 MODULE_DESCRIPTION("kernel module hello");12 MODULE_VERSION("1.0");13
14 /*入口函数*/
15 static int mod_init(void)16 {17 printk(KERN_ALERT "mod_init() start\n");18
19 /*调用fun*/
20 fun();21 return 0;22 }23
24 /*退出函数*/
25 static void mod_exit(void)26 {27 printk(KERN_ALERT "mod_exit() start\n");28 }29
30 /*注册到内核*/
31 module_init(mod_init);32 module_exit(mod_exit);
第20行即是调用其他模块的函数。
这里要编译2个模块,对应的Makefile文件:
1 KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build2 PWD := $(shell pwd)3
4 obj-m :=kernel_mod.o kernel_fun.o5
6 default:7 $(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules
编译好这2个模块后,我们现在来验证。注意,因为kernel_mod依赖kernel_fun,所以我要先插入kernel_fun模块。
卸载模块的时候,我们要先卸载kernel_mod,原因同上。
依次插入kernel_fun,查看它的符号表,然后插入kernel_mod,查看dmesg:

可以看到kernel_fun的fun()被kernle_mod调用了。

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐



所有评论(0)