linux驱动学习(二)——内核模块
2012-05-22 19:34
411 查看
因为linux内核的整体结构非常庞大,包含的组件非常多,如何使用需要的组件呢?有一种方法是把所有的组件都编译进内核文件(zImage或bzImage),但是这样会产生两个问题:一是生成的内核文件过大;二是如果要添加或删除某一个组件,需要重新删除编译整个内核。于是我们需找另外一种机制让内核文件本身不包含某组件,而是在该组件需要的时候,动态地添加到正在运行的内核中,这在linux中就叫做“内核模块”的机制。由此可看到,内核模块有2个特点:①模块本身并不编译进内核;②可以根据需求,在内核运行期间动态地安装与卸载。
内核模块与应用程序又有什么区别呢?一、大多数应用程序是从头到尾执行单个任务,而模块却只是预先注册自己以便服务于将来的某个请求,然后它的初始化函数就立即结束。换句话说,模块初始化函数的任务就是为以后调用模块函数预先做准备;二、应用程序在退出时,可以不用管资源的释放或者其他的清除工作,但模块的退出函数却必须仔细撤销初始化函数所做的一切,否则,在系统重新引导之前某些东西就会残留在系统中;三、应用程序可以调用它并未定义的函数,这是因为连接过程能够解析外部引用从而使用适当的函数库,然而,模块仅仅被链接到内核,因此它能调用的函数仅仅是由内核导出的那些函数,而不存在任何可链接的函数库。四、应用程序一般运行在用户空间,而内核模块运行在内核空间,在Unix中使用了处理器的最高级别和最低级别,当处理器处于最高级别时称其运行在内核空间,反之则称其运行在用户空间,并且每个模式都有自己的内存映射,也即自己的地址空间,一般用户空间为0~3G,内核空间为3~4G。
下面就介绍下如何为自己的模块创建makefile,首先先来看下一个例子
在一个典型的构造过程中,makefile将被读取两次。在第一次调用的时候,因为KERNELRELEASE尚未设置,所以只是定位了内核树的目录,并且返回当前目录;在第二次调用的时候,设置了要构建的模块(obj-m),并且利用内核树的makefile来构建模块。
在上面的makefile中,obj-m为hello_world.o,其对应的C文件为hello_world.c,现在就来看下该文件的内容
我们先来看下该模块的初始化函数和卸载函数
初始化函数为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等等。
内核模块与应用程序又有什么区别呢?一、大多数应用程序是从头到尾执行单个任务,而模块却只是预先注册自己以便服务于将来的某个请求,然后它的初始化函数就立即结束。换句话说,模块初始化函数的任务就是为以后调用模块函数预先做准备;二、应用程序在退出时,可以不用管资源的释放或者其他的清除工作,但模块的退出函数却必须仔细撤销初始化函数所做的一切,否则,在系统重新引导之前某些东西就会残留在系统中;三、应用程序可以调用它并未定义的函数,这是因为连接过程能够解析外部引用从而使用适当的函数库,然而,模块仅仅被链接到内核,因此它能调用的函数仅仅是由内核导出的那些函数,而不存在任何可链接的函数库。四、应用程序一般运行在用户空间,而内核模块运行在内核空间,在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等等。
相关文章推荐
- linux驱动学习之一内核模块
- 学习Linux-4.12内核网路协议栈(1.8)——网络设备驱动模块的加载
- linux驱动学习--第五天:第四章 Linux 内核模块 之 Linux 内核模块简介
- .Linux设备驱动程序学习(0)——设备驱动介绍& Hello, world!模块 内核参数传递
- linux 设备驱动开发学习笔记(一):最简单的内核模块
- Linux内核驱动学习(三)----内核模块基础 | 设计 | 可选项
- linux驱动学习--第六天:第四章 Linux 内核模块 之 Linux 内核模块编译
- 分享Linux内核学习和驱动开发的经验
- linux 驱动学习笔记03--Linux 内核的引导
- Linux内核驱动学习(一)----内核简介 | 配置 | 编译| 安装(PC平台下)
- Linux强制卸载内核模块(由于驱动异常导致rmmod不能卸载)
- LINUX驱动学习:加载模块时出现Device or resource busy的解决方法
- 面对不断升级的内核,如何学习linux设备驱动
- 如果你在学嵌入式底层驱动,内核模块编程将是你的第一课!小白学Linux之内核模块编程详解
- AM335X 串口驱动学习(1)-基于linux3.8内核
- linux驱动学习之内核线程分析
- 从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响
- Linux 驱动开发-0、内核模块设计
- 【TINY4412】LINUX学习笔记:(2)内核模块编译、安装、加载、卸载
- Linux内核及驱动、 ARM体系结构 学习视频分享