hello world是软件中的经典!!
和平常写一般代码一样,vim 新建一个hello.c文件:
#include<linux/init.h> //模块加载卸载需要的头文件 #include<linux/module.h> //需要内核的一些认证和许可 // 上面两个头文件对一个内核模块程序来说是必不可少的 //这是模块初始化函数,在模块加载时触发被调用 static int __init hello_init(void) { printk("hello world module init!\n"); return 0; } //这是模块收尾处理函数,在模块卸载时触发被调用 static void __exit hello_exit(void) { printk("hello world module exit!\n"); } //下面这两个相当于是注册函数,用来加载模块和卸载模块时触发的 module_init(hello_init); module_exit(hello_exit); //模块加载时一个许可认证,如果没有编译的时候会报警告 MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("YU ZHI HUI"); //额外的信息,模块作者信息
对于这种模块框架,只要理解然后照搬来用就可以,这是在内核中操作,稍有不慎就会让整个系统崩溃的,要多小心点。
重点来说下注册的回调函数,static int __init hello_init(void)和static void __exit hello_exit(void)函数;
一、模块初始化函数,这个函数是有返回值得,而模块卸载所触发的收尾处理函数是没有返回值得。
二、这两个函数和我们平时写得函数不一样,多了个__init和__exit(前面是两个_),这个可以加也可以不加。为什么会有这个呢?因为这两个函数都是模块加载与卸载时触发调用,但一个模块加载和卸载都只有一次而已。那么加载完后这段代码就没有什么作用了,所以用__init表示这段代码只作用一次,当作用完以后内核会自动释放掉代码占用的空间。而__exit也是类似作用,表明这段代码只作用一次。
三、就是参数问题,如果没有参数一定要写void,不然编译会报警告,这是个习惯问题,也是代码规范问题。
四、复杂模块编写流程,上面仅仅是个模块框架。如果要添加复杂的代码,要从hello_init()中开始添加。hello_init()是模块初始化函数,也是模块工作入口函数。hello_exit()函数是收尾清楚工作,主要调用些函数来释放申请的内存和关闭打开的文件等等操作。
#指定生成可加载模块的名称 obj-m:=hello.o #实际是指向你自己创建的内核代码树的路径,build是个软连接 KERDIR=/lib/modules/$(shell uname -r)/build #编译模块的当前路径 CURDIR=$(shell pwd) #make后默认自动跳转到这里执行,make功能比较多,有兴趣的可以网上搜下 all: make -C $(KERDIR) M=$(CURDIR) modules #这是模块清理工作,一般会清理掉所有生成的文件 clean: make -C $(KERDIR) M=$(CURDIR) clean简单的编译流程(直接make), make -C $(KERDIR) M=$(CURDIR) modules,会进入指定的KERDIR(也就是内核树目录的路径)对这个模块文件(hello.c)进行编译,因为这个是要加载到内核中去的,所以要借助内核代码树的环境进行编译(-C是指定路径参数)。当编译完后,还要退回到当前目录下来。请看实际的编译流程:
模块清理make clean:make -C $(KERDIR) M=$(CURDIR) clean;执行流程和make类似,默认会删除所有make生成的文件。如下图:
make编译模块后会生成一大堆文件,但是只有一个文件是我们所需要的,那就是以.ko为后缀的文件,这里是hello.ko。其他文件都是编译时生成的中间文件,没多大用(至少我现在很少用到过,除了些库文件,其他没怎么用到过)。
加载模块命令:insmod hello.ko
加载模块时,如果输完命令后没什么反应,那么多半要恭喜你,说明你的模块没问题,内核接受了(linux的一惯作风:成功什么都不显示,出错显示一大堆)。加载完模块没问题后,当然得去瞧瞧自己的模块了。
查看模块命令:lsmod
最后嘚瑟之后,就要把这个模块卸载掉去了。注意上面模块的名称为hello(并不是hello.ko),所以卸载时可以用hello来指定模块。
卸载模块命令:rmmod hello
和加载模块时一样,如果没有什么显示就表明一切ok(大部分的时候是这样)。其实到这里一个模块的加载和卸载及查看已经完成了,但是还有一点遗憾就是我上面的hello.c文件中明明有两句打印语句,怎么看不见了?那么这就是模块加载进去后输出的信息在哪里查看的问题了。
先看下上面的hello.c文件,模块加载时,初始化函数会打印一句:printk("hello world module init!\n");模块卸载时,收尾函数会打印一句:printk("hello world module exit!\n");这个打印的消息和一般的printf打印的不一样,printk是专门为内核打印信息的(反正有带有k的,大部分都是在内核使用的,比如:内核中的内存申请kmalloc),所以不会把消息输出到一般的控制台上。那么怎么查看打印信息呢?有两种方法:第一监听日志;第二查看消息;
监听日志命令:tail -f /var/log/messages
这个命令是实时监听用户的所有动作(就是一有什么动作,就会马上显示出来,看上图最后两行),从开机开始记录。所以这个命令一般还可以用来查看你操作上的一些错误。如果觉得这个方法不太直观,那么还有个就是专门来查看模块消息的命令。
模块消息查看命令:dmesg
这个命令就是专门显示模块输出信息的了,一般可以先清除下,因为开机后要加载很多模块,不仅仅是你自己手动加载的那个模块。所以先清理消息,然后加载打印就可以很方便的看出自己那个模块的输出信息了。清理消息命令为:dmesg -c(建议每次加载时都先清理下消息)
至此,linux驱动模块框架和一些基本操作命令也分析完了。这只是驱动模块中的皮毛,如果要做驱动模块编程那么还要继续加油。
=================================================================================================
交叉编译驱动:
我的开发板是mini2440的,只修改下Makefile就可以了;
obj-m:=hello.o CC=arm-linux-gcc #KERDIR=/lib/modules/$(shell uname -r)/build KERDIR=/home/kernel/linux-2.6.32.2 #开发板内核源代码所在目录,编译的内核树所在的源码位置 CURDIR=$(shell pwd) all: make -C $(KERDIR) M=$(CURDIR) modules clean: make -C $(KERDIR) M=$(CURDIR) clean编译好模块后就把.ko文件利用nfs服务器放到开发板上运行就可以了。