第二章 最简单的设备驱动模块 hello world

 上一篇主要是介绍了设备驱动的定义,作用,以及编写设备驱动的原则等等,总之都是一些文字叙述。现在终于要开始和代码打交道了,很是激动啊。以下开始编写一个最最简单的设备驱动模块,经典的hello world。下面所叙述的步骤都经过我的亲身体验。

    首先,自然是需要一个linux的源码包了,可以去http://www.kernel.org/下载,注意选择源码包的版本。我使用的是2.6.39.3。最好把源码包放到用户主目录下去操作。关于编译源码需要的工具,环境什么的,这里就不赘述了,有很多资料可以参考。

    在源码的drivers/char目录下建立一个文件夹examples,这个目录用于存放我们的设备驱动模块的源码。如果是只要编译这个模块的话,drivers/char这个目录其实并不是必须的,不过为了能够把模块编译进入内核,就这样放置了。当然,可不是仅仅把代码放这里就完了,还要修改drivers/char目录下的makefileKConfig,这一部分以后再说吧。examples目录下要放两个文件,源程序hello.cmakefile

hello.c 代码如下:

 1 #include <linux/init.h>
2 #include <linux/module.h>
3 MODULE_LICENSE("Dual BSD/GPL");
4 static int hello_init(void)
5 {
6 printk(KERN_ALERT "Hello, world\n");
7 return 0;
8 }
9 static void hello_exit(void)
10 {
11 printk(KERN_ALERT "Goodbye, world\n");
12 }
13 module_init(hello_init);
14 module_exit(hello_exit);

  

  稍微解释以下吧。头两行是众所周知的导入头文件了,所以这个代码一定要基于linux的源代码进行编译。MODULE_LICENSE("Dual BSD/GPL")用于说明证书,跟设备驱动的功能没有关系。关键在于下面的 init函数和 exit函数了。光看代码的话,就是简单的输出两句话了。这里的 printk函数其实就跟c语言的标准库里头的printf函数功能类似。linux用 printk代替了printfKERN_ALERT代表一个优先级,这个我们先不管它了。init有返回值, exit无返回值。这两个函数是设备驱动程序必备的函数。即便在这样一个最最简单的设备驱动模块中,这两个函数也是不可缺少的。module_init(hello_init)module_exit(hello_exit)这两个宏直接反映了上面两个函数的功能。 hello_init在驱动被加载的使用调用, hello_exit在设备被卸载的时候调用。

Makefile代码如下:

# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
# Otherwise we were called directly from the command
# line; invoke the kernel build system.
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

如果只是为了完成编译obj-m := hello.o一行代码就已经足矣了。但是为了让编译出来的ko能够被加载到当前运行的内核,就一定要这么写。$(MAKE) -C $(KERNELDIR) M=$(PWD) modules的大致意思就是说,用当前运行的内核为基础来编译这个设备驱动模块。当然,如果你能保证你的源码和当前运行的内核的版本是一致的,这个步骤就多余了。如果版本不一致的话,虽然能通过编译,但是在加载的时候会出现1 Invalid module format 错误。

      完成这两个文件之后就可以进入目录直接make,顺利的话,目录下会多出几个文件:

     其中,hello.ko就是我们要的设备驱动模块的二进制对象文件了。把它单独取出来吧。

     然后就是insmod把这个模块加载到内存了。Insmodmodprobe的区别在于modprobe要考虑模块之间的依赖关系。像我,到了这个时候,就发现终端既没有报错也没有输出我想要的hello world。实在是意见很让人费解的事情。lsmod之后发现模块已经加载进去了,再次insmod的话终端会报1 File exists的错误,意思是说模块已经存在了,不能重复加载。一切似乎都那么的自然,只是我们要的字符串没有输出而已,呵呵。

    上网搜索了一下,找到原因了。原来,ubuntu的终端是模拟出来的,printk的信息并没有直接打印在终端上,如果是ubuntu系统,字符串应该是被输出到了/var/log/syslog这个日志文件上,进入这个文件搜索,果然找到了hello world。又或者,你可以切换到纯文本行模式,这样子就肯定可以看到输出了,用CTRL+ART+F1进入纯文本行模式,CTRL+ART+F7回到ubuntu的图形界面,刚开始的时候我就是因为不知到这个返回图形界面的快捷键,硬是重启了两次.........在纯文本模式行模式下,我测试是可以显示hello world的,rmmod卸载驱动则显示goodbye world,就跟我们代码一样。纯文本模式行模式下不能截图,这里就不发截图了。

posted @ 2011-07-27 19:23  自由泳的青蛙  阅读(663)  评论(0编辑  收藏  举报