专题5-内核模块开发
第1课-内核模块基础
什么是内核模块?
Linux内核的整体结构非常庞大,其中包含的组件也非常多,如何使用这些组件呢,方法1:把所有的组件都编译到内核文件中,即:zImage或者bzImage,但是这样会导致一个问题:内存占用过多。
有没有一种机制能让内核文件本身并不包含某组件,而是在该组件需要被使用的时候,动态地添加到正在运行的内核呢?
内核模块本身并不会被编译到内核文件(zImage或者bzImage);可以根据需求,在内核运行期间动态的安装护着卸载。
如何使用内核模块
安装:insmod 例如:insmod /home/dnw_usb.ko(usb下载线驱动程序)
卸载:rmmod 例如:rmmod dnw_usb
查看:lsmod 例如:lsmod
第2课-内核模块设计
- 范例代码分析
#include <linux/init.h>
#include <linux/module.h>
static int hello_init()
{
printk(KERN_WARNING"Hello world! ");
return 0;
}
static void hello_exit()
printk(KERN_WARNING"hello exit! ");
}
module_init(hello_init);
module_exit(hello_exit);
范例代码中没有main函数,在我们正常的应用程序中,main函数起到的是一个入口的作用。我们内核函数中没有mian函数,但是也是同样有入口的。这里用module_init这个宏定义来表示入口,module_init这里指向hello_init,当我们输入insmod时就会调用这一个函数了。module_exit这个宏定义表示出口,或者卸载函数(也称为出口函数)。这是内核函数的两个要素。第三个要素就是我们用的两个头文件,任何模块都是要用的。
头文件:linux/init.h; linux/module.h
加载函数:module_init
卸载函数:module_exit
操作注意:
当我们把模块导入开发板后,使用命令:insmod hellowordl.ko 进行模块安装,这是会显示hello world!,说明安装正确。但是在卸载时却会出问题,这是因为在/lib/modules下没有对应的文件夹,我们使用命令:mkdir –p /lib/modules/$(uname -r)进行建立即可。这样使用命令:rmmod helloworld就会显示hello exit!,说明模块正常卸载。
知识点插入:网关是什么意思?
大家都知道,从有个房间走到另一个房间,必然经过一扇门。同样,从一个网络向另一个网络发送信息,也必须经过一道“关口”,这道关口就是网关。顾名思义,网关(Gateway)就是一个网络连接到另一个网络的“关口”。
按照不同的分类标准,网关有很多种。TCP/IP协议里的网关是最常用的,在这里我们所讲的“网关”均指TCP/IP协议下的网关。
那么网关到底是什么呢?网关实质上是一个网络通向其他网络的IP地址。比如有网络A和网络B,网络A的IP地址范围:“192.168.1.1~192.168.1.154”,子网掩码为:“255.255.255.0”。网络B的IP地址范围是:“192.168.2.1~192.168.2.254”,子网掩码为:“255.255.255.0”。在没有路由器的情况下,两个网络之间是不能进行TCP/IP通讯的,即使是两个网络连接在同一台交换机(或者集线器)上,TCP/IP协议也会根据子网掩码(255.255.255.0)判定两个网络中的主机处在不同的网络里。而要实现两个网络之间的通信,就必须通过网关。如果网络A中的主机发现数据包额目的的主机不在本地网络中,就必须把数据转发给他自己的网关,再有网关转发给网络B的网关,网络B的网关在转发给网络B的某个主机。这就是网络之间的通讯。
所以说,只有设置好网关的IP地址,TCP/IP协议才能实现不同网络之间的通信。那么这个IP地址是哪台机器的IP地址呢?网关的IP地址是具有路由器功能的设备的IP地址,具有路由器功能的设备有路由器、启动了路由协议的服务器(实质上相当于一台路由器)、代理服务器(也相当于一台路由器)。
- 编译内核模块
编写makefile,编译内核模块
obj-m := helloworld.o
%hello-objs :=file1.0 file2.0 file3.o(在有多个文件时使用这条指令,hello对应这个模块的名字)%
KDIR := /home/free/part2/linux %来发版上运行这个代码的位置,特指电脑的根文件系统的存储代码的位置%
all:
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
rm -f *.o *.ko *.order *.aymvers
第3课-内核模块可选项
1. 模块申明
(1)MODULE_LICENSE(“遵守的协议”)
申明该模块遵守的许可证协议,如:”GPL”、”GPL v2”等
(2)MODULE_AUTHOR(“作者”)
申明模块的作者
(3)MODULE_DESCRIPTION(“模块的功能描述")
申明模块的功能
(4)MODULE_VERSION("V1.0")
申明模块的版本
例子:
在我们之前编译的helloworld.ko内核模块中,我们当运行它的时候会显示一些提示信息,主要指的是协议不合法等,我们在头文件下面加上:
MODULE_LICENSE(“GPL”);
再编译运行后,就不会显示之前的提示信息,这样的模块就是合法的了。
2. 模块参数
在应用程序中
int main(int argc, char** argv)
argc表示命令行输入的参数个数,argv中保存输入的参数
当然在内核模块中也可以通过命令来传递参数,而且也有保存的位置。通过宏module_param指定保存模块参数的变量。模块参数用于在加载模块时传递参数给模块。
module_param(name,type,perm)
name:变量的名称
type:变量类型,bool:布尔型int:整型charp:字符串型
perm是访问权限。S_IRUGO:读权限S_IWUSR:写权限
例子:
int a = 3;
char *st;
module_param(a,int, S_IRUGO);
module_param(st,charp, S_IRUGO);
例子:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE(“GPL”);
int a = 3;
module_param(a,int, S_IRUGO| S_IRUGO); //两个参数一起使用
/* int *p;
module_param(p,charp, S_IRUGO| S_IRUGO);
static int hello_init()
{
printk(KERN_WARNING"Hello world! ");
printk("a=%d ",a);
// printk("p=%s ",p);
return 0;
}
static void hello_exit()
{
printk(KERN_WARNING"hello exit! ");
}
module_init(hello_init);
module_exit(hello_exit);
在运行新的模块的时候,可以直接运行:insmod helloworld.ko a=10,这样在下面就会多显示a=10。
- 符号输出
内核符号的导出使用宏
EXPORT_SYMBOL(符号名)
EXPORT_SYMBOL_GPL(符号名
其中EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块。
例子- 导出内核模块
我们再创建一个模块add.ko做简单的加法运算:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
Int add(int a,int b)
{
return a+b;
}
static int add_init()
{
return 0;
}
static void add_exit()
{
}
EXPORT_SYMBOL(add); //必须有这不,这才能显示它导出供其他模块使用。
module_init(hello_init);
module_exit(hello_exit);
对应的helloworld.ko改为:
#include <linux/init.h>
#include <linux/module.h>
static int hello_init()
{
printk(KERN_WARNING"Hello world! ");
return 0;
}
static void hello_exit()
{
add(1,5);
printk(KERN_WARNING"hello exit! ");
}
module_init(hello_init);
module_exit(hello_exit);
makefile文件变为:
obj-m := helloworld.o add.o
%hello-objs :=file1.0 file2.0 file3.o(在有多个文件时使用这条指令,hello对应这个模块的名字)%
KDIR := /home/free/part2/linux %来发版上运行这个代码的位置,特指电脑的根文件系统的存储代码的位置%
all:
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
rm -f *.o *.ko *.order *.aymvers
我们将编译好的两个模块都导入到开发板中,先安装add.ko,再安装helloworld.ko,我们会看到正常运行,否则后面的模块是安装不上去的。