您的位置:首页 > 运维架构 > Linux

linux驱动学习(二)——内核模块

2012-05-22 19:34 411 查看
因为linux内核的整体结构非常庞大,包含的组件非常多,如何使用需要的组件呢?有一种方法是把所有的组件都编译进内核文件(zImage或bzImage),但是这样会产生两个问题:一是生成的内核文件过大;二是如果要添加或删除某一个组件,需要重新删除编译整个内核。于是我们需找另外一种机制让内核文件本身不包含某组件,而是在该组件需要的时候,动态地添加到正在运行的内核中,这在linux中就叫做“内核模块”的机制。由此可看到,内核模块有2个特点:①模块本身并不编译进内核;②可以根据需求,在内核运行期间动态地安装与卸载。

内核模块与应用程序又有什么区别呢?一、大多数应用程序是从头到尾执行单个任务,而模块却只是预先注册自己以便服务于将来的某个请求,然后它的初始化函数就立即结束。换句话说,模块初始化函数的任务就是为以后调用模块函数预先做准备;二、应用程序在退出时,可以不用管资源的释放或者其他的清除工作,但模块的退出函数却必须仔细撤销初始化函数所做的一切,否则,在系统重新引导之前某些东西就会残留在系统中;三、应用程序可以调用它并未定义的函数,这是因为连接过程能够解析外部引用从而使用适当的函数库,然而,模块仅仅被链接到内核,因此它能调用的函数仅仅是由内核导出的那些函数,而不存在任何可链接的函数库。四、应用程序一般运行在用户空间,而内核模块运行在内核空间,在Unix中使用了处理器的最高级别和最低级别,当处理器处于最高级别时称其运行在内核空间,反之则称其运行在用户空间,并且每个模式都有自己的内存映射,也即自己的地址空间,一般用户空间为0~3G,内核空间为3~4G。

下面就介绍下如何为自己的模块创建makefile,首先先来看下一个例子

ifneq ($(KERNELRELEASE),)
obj-m := hello_world.o
else
KDIR := /FriendlyARM/linux-2.6.38
MOD := ./hello_world.ko
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
endif

在一个典型的构造过程中,makefile将被读取两次。在第一次调用的时候,因为KERNELRELEASE尚未设置,所以只是定位了内核树的目录,并且返回当前目录;在第二次调用的时候,设置了要构建的模块(obj-m),并且利用内核树的makefile来构建模块。

在上面的makefile中,obj-m为hello_world.o,其对应的C文件为hello_world.c,现在就来看下该文件的内容

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>

static char *name = "abcd";

static int __init hello_module_init(void)
{
printk("Hello, I am %s\n",name);
return 0;
}

static void __exit hello_module_cleanup(void)
{
printk("Good-bye, %s\n",name);
}
module_init(mini6410_hello_module_init);
module_exit(mini6410_hello_module_cleanup);
module_param(name,charp,S_IRUGO);
MODULE_LICENSE("GPL");

我们先来看下该模块的初始化函数和卸载函数

初始化函数为hello_module_init,负责注册模块所提供的任何设施,它被声明为static,因为这种函数在特定文件之外没有其他意义,且一个模块函数如果要对内核其他部分可见,则必须被显式导出。另外, 还被标记为__init,它对内核来讲是一种暗示,表明该函数仅在初始化期间使用。在模块被装载之后,模块装载器就会将初始化函数扔掉,这样可将该函数占用的内存释放出来,类似的还有__initdata。再来看下module_init,它的使用是强制性的,其会在模块的目标代码中增加一个特殊的段,用于说明内核初始化函数所在的位置,如果没有这个定义,则初始化函数永远不会被调用。

卸载函数为hello_module_cleanup,其作用是在模块被移除钱注销接口并向系统中返回所有资源。__exit修饰词标记该代码仅用于模块卸载。如果模块被直接内嵌到内核中,或者内核的配置不允许卸载模块,则被标记为__exit的函数将被简单地丢弃。module_exit的作用与module_init类似,也是模块必不可少的。

接下来再看module_param宏,该宏声明了模块参数。内核允许对驱动程序指定参数,而这些参数可在装载驱动程序模块时改变。参数的值可在运行insmod或modprobe命令装载模块时赋值。参数的类型一般有一下几种:bool、invbool、charp、int、long、short、uint、ulong、ushort。module_param宏的第一个成员是参数的名字,第二个成员是参数的类型,第三个成员是访问许可值,如果参数使用了S_IRUGO,则任何人均可读取该参数,但不能修改。在hello_world的例子中,如果要给参数赋值,具体方法如下:insmod
hello_world.ko name=xxxxxx

MODULE_LICENSE("GPL")指定了模块的许可证为GPL,可在模块中包含的其他描述性定义包括MODULE_AUTHOR、MODULR_DESCRIPTION、MODULE_VERSION、MODULE_ALIAS以及MODULE_DEVICE_TABLE等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: