Linux内核模块
1 内核模块
Linux内核的整体结构非常庞大,其包含的组件也非常多,如果把所有的组件都编译进内核文件,即:zImage或bzImage,但这样会导致一个问题:占用内存过多。
所以就需要动态的添加某些组件,这些组件就是内核模块。特点:模块本身并不被编译到内核文件(zImage或bzImage);可以根据需求,在内核运行期间动态的安装或卸载。
安装:insmod
insmod /home/dongry/dnw_usb.ko
卸载:rmmod
rmmod dnw_usb
查看:lsmod
lsmod
2 内核模块的设计与编写
/************************************************* *file_name:helloworld.c *************************************************/ #include <linux/init.h> #include <linux/module.h> static int hello_init(void) { printk(KERN_WARING"hello world\n"); return 0; } static void hello_exit(void) { printk(KERN_INFO"goodbye world\n"); } module_init(hello_init); module_exit(hello_init);
printk的用法:https://blog.csdn.net/eydwyz/article/details/53044863
必要的头文件:<linux/init.h> <linux/module.h>
module_init()内核模块的入口 即加载函数
module_exit()内核模块的出口 即卸载函数
当insmod安装helloworld.ko的时候module_init(hello_init)就会被调用
当rmmod卸载helloworld.ko的时候module_exit(hello_exit)就会被调用
2.1 内核模块必要元素框图
内核中打印用printk,应用程序用printf
/*********************************** *filename:Makefile ************************************/ obj-m := helloworld.o KDIR := /home/dongry/mini2440/mini2440 /*开发板内核代码路径*/ all: make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm clean: rm -f *.o *.ko *.order *.symvers
/*********************************** *filename:Makefile *function:can in x86 kernel run ************************************/ obj-m := helloworld.o KDIR := /home/dongry/x86/linux-4.19.30 /*开发板内核代码路径*/ all: make -C $(KDIR) M=$(PWD) modules clean: rm -f *.o *.ko *.order *.symvers
make含有helloworld.c和Makefile的文件得到helloworld.ko文件;如果是经过编译的x86内核后可以直接在Terminal窗口运行insmod、lsmod、rmmod;如果是经过编译的arm内核需在secure crt上链接开发板才能运行。
3 模块声明
a)MODULE_LICENSE("遵守的协议"):声明该模块遵守的许可证协议,如"GPL"、"GPL v2"
b)MODULE_AUTHOR("作者"):声明模块的作者
c)MODULE_VERSION("版本"):声明模块的版本信息
d)MODULE_DESCRIPTION("模块功能描述"):声明模块的功能
/************************************************* *file_name:helloworld.c *************************************************/ #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("GPL"); //模块声明 static int hello_init(void) { printk(KERN_WARING"hello world\n"); return 0; } static void hello_exit(void) { printk(KERN_INFO"goodbye world\n"); } module_init(hello_init); module_exit(hello_init);
4 模块参数
我们可以在加载内核模块的时候向其传递参数,以让同一代码达到不同的效果。通过宏module_param指定保存模块参数的变量。模块参数用于在加载模块时传递参数给模块。
格式:module_param(name,type,perm)
a)name:变量的名称
b)type:变量的类型,数据类型内核支持模块参数类型有:bool、invbool(bool的发转,true变为false,false变为true)、charp(char类型指针值)、int、long、short、uint、ulong、ushort、
c)perm:访问权限,常见的访问许可值通常为S_IRUGO(读权限)和S_IWUSR(写权限)。通常情况下将他们按位或
同时我们也可以用下面的宏声明数组:
Module_param_array(name,type,num,perm)
/************************************************* *file_name:helloworld.c *************************************************/ #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("GPL"); //模块声明 int a=3; charp *p; module_param(a,int,S_IRUGO | S_IWUSR); //加载一个整型参数 module_param(P,charp,S_IRUGO | S_IWUSR); //加载一个字符型参数 static int hello_init(void) { printk(KERN_WARING"hello world\n"); printk("a=%d\n",a); printf("p is %s\n",p); return 0; } static void hello_exit(void) { printk(KERN_INFO"goodbye world\n"); } module_init(hello_init); module_exit(hello_init);
/*结果显示*/ /*********************************************************/ #insmod helloworld.ko a=10 p=abcd hello,world! a=10 p is abcd
5 模块符号导出
当一个模块要使用另一个模块的函数(变量)的时候,要使用EXPORT_SYMBOL(符号名)或者EXPORT_SYMBOL_GPL(符号名)来申明。
注:EXPORT_SYMBOL_GPL()只适用于遵循GPL协议的模块
/********************************************** *filename:add.c *********************************************/ #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("GPL"); int add(int a,int b) { return a+b; } static int add_init(void) { return 0; } static void add_exit(void) { }
EXPORT_SYMBOL(add); //可以将add导出给其他函数 module_init(add_init); module_exit(add_exit);
/************************************************* *file_name:helloworld.c *function:calling add.c *************************************************/ #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("GPL"); //模块声明 extern add(int a,int b); int a=3; charp *p; module_param(a,int,S_IRUGO | S_IWUSR); //加载一个整型参数 module_param(P,charp,S_IRUGO | S_IWUSR); //加载一个字符型参数 static int hello_init(void) { printk(KERN_WARING"hello world\n"); printk("a=%d\n",a); printf("p is %s\n",p); return 0; } static void hello_exit(void) { add(1,4); printk(KERN_INFO"goodbye world\n"); } module_init(hello_init); module_exit(hello_init);
/*********************************** *filename:Makefile ************************************/ obj-m := helloworld.o add.o KDIR := /home/dongry/mini2440/mini2440 /*开发板内核代码路径*/ all: make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm clean: rm -f *.o *.ko *.order *.symvers
/*结果显示*/ /*********************************************************/ /***************先加载add.ko,后加载helloworld.ko***************/ #insmod add.ko #insmod helloworld.ko a=10 p=abcd hello,world! a=10 p is abcd