原创
Linux设备驱动程序学习(0)-Hello, world!模块
Linux设备驱动程序学习(0) -Hello, world!模块 当linux环境已经搭建好,就准备开始下一步的学习,linux设备驱动和内核。计划按照LINUX设备驱动开发详解一书的顺序,一章一章学习。
一个学习Linux设备驱动程序都会碰到的第一个例程:
# include < linux/ init. h> # include < linux/ module. h> MODULE_LICENSE( "Dual BSD/GPL" ) ; static int hello_init( void ) { printk( KERN_ALERT "Hello, world!\n" ) ; return 0; } static void hello_exit( void ) { printk( KERN_ALERT "Goodbye, world
\n
"
) ; } module_init( hello_init) ; module_exit( hello_exit) ;
MODULE_AUTHOR("hzh1024n")
MODULE_DESCRIP
我将其复制到我的工作目录,并编写了一个简单的Makefile文件:
KERNELDIR = /usr/src/kernels/2.6.21-1.3194.fc7-i686 # The current directory is passed to sub-makes as argument PWD := $(shell pwd) #CC = $(CROSS_COMPILE)gcc CC =gcc obj-m := helloworld.o
modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
make以后产生helloworld.ko驱动。
[root@localhost helloworld]# ls helloworld.c helloworld.ko helloworld.mod.o Makefile Module.symvers helloworld.c~ helloworld.mod.c helloworld.o Makefile~ [root@localhost helloworld]# [root@localhost helloworld]# insmod helloworld.ko
[root@localhost helloworld]# dmesg [<c04744e9>] sys_open+0x1c/0x1e [<c0404f70>] syscall_call+0x7/0xb ======================= Hello world Goodbye, world Hello world Goodbye, world Hello world [root@localhost helloworld]#
有些心得,网友总结的很好,搬过来了。
学习心得:
(1)驱动模块运行在内核空间,运行时不能依赖于任何函数库和模块连接,所以在写驱动时所调用的函数只能是作为内核一部分的函数。
(2)驱动模块和应用程序的一个重要不同是:应用程序退出时可不管资源释放或者其他的清除工作,但模块的退出函数必须仔细撤销初始化函数所作的一切,否则,在系统重新引导之前某些东西就会残留在系统中。
(3)处理器的多种工作模式(级别)其实就是为了操作系统的用户空间和内核空间设计的。在Unix类的操作系统中只用到了两个级别:最高和最低级别。 (4)要十分注意驱动程序的并发处理。 (5)内核API中具有双下划线(_ _)的函数,通常是接口的底层组件,应慎用。 (6) 内核代码不能实现浮点书运算。 (7)Makefile文件分析: obj- m : = hello. o 代表了我们要构造的模块名为hell.ko,make 会在该目录下自动找到hell.c文件进行编译。如果
hello.o 是由其他的源文件生成(比如file1.c和
file2.c
)的,则在下面加上(注意红色字体 的对应关系):
hello
-objs := file1.o file2.o ......
$( MAKE) - C $( KERNELDIR) M= $ ( PWD) modules 其中
- C $( KERNELDIR) 指定了内核源代码的位置,其中保存有内核的顶层makefile文件。
M= $ ( PWD)
指定了模块源代码的位置
modules目标指向obj-m变量中设定的模块。
(8)insmod使用公共内核符号表来解析模块中未定义的符号。公共内核符号表中包含了所有的全局内核项(即函数和变量 的地址 ),这是实现模块化驱动程序所必须的。 (9)Linux使用模块层叠技术,我们可以将模块划分为多个层,通过简化每个层可缩短开发周期。如果一个模块需要向其他模块到处符号,则使用下面的宏: EXPORT_SYMBOL( name) ; EXPORT_SYMBOL_GPL( name) ;
符号必须在模块文件的全局变量部分导出,因为这两个宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。 (10)所有模块代码中都包含一下两个头文件: # include < linux/ init. h> # include < linux/ module. h>
(11)所有模块代码都应该 指定所使用的许可证:MODULE_LICENSE( "Dual BSD/GPL" ) ;
此外还有可选的其他描述性定义:
MODULE_AUTHOR( "" ) ; MODULE_DESCRIPTION( "" ) ; MODULE_VERSION( "" ) ; MODULE_ALIAS( "" ) ; MODULE_DEVICE_TABLE( "" ) ;
上述MODULE_
声明习惯上放在文件最后。 (12)初始化和关闭 初始化的实际定义通常如下: static int _ _
init
initialization_function( void) {/*初始化代码*/ } module_init( initialization_function)
清除函数的实际定义通常如下: static int _ _exit cleanup_function( void ) { /*清除代码*/ } module_exit( cleanup_function)
(13) Linux内核模块的初始化出错处理一般使用“goto”语句。通常情况下很少使用“goto”,但在出错处理是(可能是唯一的情况),它却非常有用。在大二学习C语言时,老师就建议不要使用“goto”,并说很少会用到。在这里也是我碰到的第一个建议使用“goto”的地方。“在追求效率的代码中使用goto语句仍是最好的错误恢复机制。 ”-- 《Linux设备驱动程序(第3版)》 以下是 初始化出错处理的推荐代码示例: struct something * item1; struct somethingelse * item2; int stuff_ok;
void my_cleanup( void) { if ( item1)
release_thing( item1) ; if ( item2) release_thing2( item2) ; if ( stuff_ok) unregister_stuff( ) ; return; }int __init my_init( void) { int err = - ENOMEM; item1 = allocate_thing( arguments) ; item2 = allocate_thing2( arguments2) ; if ( ! item2 | | ! item2) goto fail; err = register_stuff( item1, item2) ; if ( ! err) stuff_ok = 1; else goto fail; return 0; /* success * /
fail: my_cleanup( ) ; return err; }
(14)模块参数:内核允许对驱动程序指定参数,而这些参数可在装载驱动程序模块时改变 。以下是我的实验程序: # include < linux/ init. h> # include < linux/ module. h> # include < linux/ moduleparam. h> MODULE_LICENSE( "Dual BSD/GPL" ) ; static char * whom = "Tekkaman Ninja" ; static int howmany = 1; static int TNparam[ ] = { 1, 2, 3, 4} ; static int TNparam_nr = 4; module_param( howmany, int , S_IRUGO) ; module_param( whom, charp, S_IRUGO) ; module_param_array( TNparam , int , & TNparam_nr , S_IRUGO) ; static int hello_init( void ) { int i; for ( i = 0; i < howmany; i+ + ) printk( KERN_ALERT "(%d) Hello, %s !\n" , i, whom) ; for ( i = 0; i < 8 ; i+ + ) printk( KERN_ALERT "TNparam[%d] : %d \n" , i, TNparam[ i] ) ; return 0; } static void hello_exit( void ) { printk( KERN_ALERT "Goodbye, Tekkaman Ninja !\n Love Linux !Love ARM ! Love KeKe !\n" ) ; } module_init( hello_init) ; module_exit( hello_exit) ;
实验结果是 :
[ Tekkaman2440@SBC2440V4] # cd / lib/ modules/ [ Tekkaman2440@SBC2440V4] # ls cs89x0. ko hello. ko prism2_usb. ko hello- param. ko p80211. ko[ Tekkaman2440@SBC2440V4] # insmod hello- param. ko howmany= 2 whom= "KeKe" TNparam= 4, 3, 2, 1( 0) Hello, KeKe !( 1) Hello, KeKe ! TNparam[ 0] : 4 TNparam[ 1] : 3 TNparam[ 2] : 2 TNparam[ 3] : 1 TNparam[ 4] : 1836543848 TNparam[ 5] : 7958113 TNparam[ 6] : 1836017783 TNparam[ 7] : 0[ Tekkaman2440@SBC2440V4]# insmod hello-param.ko howmany="2" whom="KeKe" TNparam="4",3,2,1,5,6,7,8 TNparam: can only take 4 arguments hello_param: `4' invalid for parameter `TNparam' insmod: cannot insert 'hello-param.ko': Invalid parameters (-1): Invalid argument [Tekkaman2440@SBC2440V4]#
我这个实验除了对参数的改变进行实验外,我的一个重要的目的是测试“ module_param_array( TNparam , int , &TNparam_nr , S_IRUGO) ;
”中&TNparam_nr
对输入参数数目的限制作用。经过我的实验,表明&TNparam_nr
并没有对输入参数的数目起到限制作用。真正起到限制作用的是“ static int TNparam[] = {1,2,3,4};
” 本身定义的大小,我将程序进行修改:static int TNparam[] = {1,2,3,4}; 改为 static int TNparam[] = {1,2,3,4,5,6,7,8}; 其他都不变。 编译后再进行实验,其结果是:[ Tekkaman2440@SBC2440V4] # insmod hello- param. ko howmany= 2 whom= "KeKe" TNparam= 4, 3, 2, 1, 5, 6, 7, 8( 0) Hello, KeKe !( 1) Hello, KeKe ! TNparam[ 0] : 4 TNparam[ 1] : 3 TNparam[ 2] : 2 TNparam[ 3] : 1 TNparam[ 4] : 5 TNparam[ 5] : 6 TNparam[ 6] : 7 TNparam[ 7] : 8[ Tekkaman2440@SBC2440V4] #
(15)“#include < linux/ sched. h
> ” 最重要的头文件之一。包含驱动程序使用的大部分内核API的定义,包括睡眠函数以及各种变量声明。 (16)“#include < linux/ version. h
> ” 包含所构造内核版本信息的头文件。 在学习过程中找到了几篇很好的参考文档:(1) 第一章 模块(Modules) URL:http://greenlinux.blogcn.com/diary,103232026.shtml (2)《 从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响》
URL:http://www.ibm.com/developerworks/cn/linux/l-module26/
(3)《Linux2.6内核驱动移植参考》
URL:http://blog.chinaunix.net/u1/40912/showart_377391.html
文章评论(0条评论)
登录后参与讨论