ubuntu12.04下实现第一个设备驱动模块HelloWorld

前段时间编译了android源码,内核源码以及第一个android程序到system.img镜像,接下来想自己写一个设备驱动模块添加到android源码内核中,但是这方面一点都不了解,于是乎,打算先看看ldd3(linux devices driver 3rd)了解一下基本知识,然后再去给android内核写设备驱动模块,看了之后,打算根据书上面说的例子来写一个helloworld的驱动程序,过程中还真是遇到了不少的问题,这个主要是因为环境的不同,毕竟现在这个时候,linux kernel还是已经发布了很多版本了,于是,打算参考书本上面的理论知识,在我现有的环境上来实现,中间有区别是肯定的,下面就记录一下我整个的实现过程。

操作的过程中,应该保证在root权限下!

---------------------------------------------------------------------------------

LDD3环境

kernel:2.6


我的环境:

Ubuntu 12.04

kernel:3.2.0-23-generic


查看ubuntu内核版本的命令:cat /proc/version,内核一般在/lib/modules/文件夹下面

-------------------------------------------------------------------------------

我再桌面上面建立我文件夹,路径为:/home/gavin-guo/Desktop/workdir/driver/hellodriver

这个里面有两个文件:hellodriver.cMakefile

他们的内容为:

hellodriver.c:

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");

static int hellodriver_init(void)
{
      printk(KERN_ALERT “hellodriver.driver's world”);
      return 0;
}

static void hellodriver_exit(void)
{
      printk(KERN_ALERT “Goodbye,hellodriver”);
}

module_init(hellodriver_init);
module_exit(hellodriver_exit);

1.两个include是头文件。

2.MODULE_LICENSE是一个内核的宏,用于表明该模块带有自由许可证。

3.printk在内核中定义并对模块可用,它与标准c库函数printf类似,但是一般在ubuntu的terminal页面是看不到这个输出的,至于怎么样才能看到,我们后面再说。

4.KERN_ALERT是表示输出信息的级别,值为“<1>”,这个具体大家可以百度。

5.module_init和module_exit两个函数使用了两个特别的内核宏来告诉内核它们的角色,init在模块加载到内核中被调用,exit在卸载的时候被调用。

MakeFile:

obj-m := hellodriver.o
KERNELDIR := $(shell uname -r)
PWD := $(shell pwd)
modules:
这个是一个tab符号$(MAKE) -C /lib/modules/$(KERNELDIR)/build M=$(PWD) modules
modules_install:
这个是一个tab符号$(MAKE) -C /lib/modules/$(KERNELDIR)/build M=$(PWD) modules_install
1.obj-m应该是指生成的目标文件吧(不太懂)

2.KERNELDIR其实,这个卸载这里已经不准确了,它仅仅是代表了内核的版本,而没有明确指出来内核的路径,这个在后面能看出来的。

3.PWD是Makefile所在的整个路径。

---------------------------------------------------------------------------

文件编译好了之后,我们在项目的根目录进行编译:make

中间出了一些小问题,比如路径没有配置对,好着语法错误之类的,这些都是常见问题,大家根据提示信息修改是没有问题的,这里就不列举出来了。

最后会在目录下面生成几个文件,生成之后的目录清单为:

hellodriver.c  hellodriver.ko  hellodriver.mod.c  hellodriver.mod.o  helloriver.o  Makefile  module.symvers  modules.order

这样,我们就生成成功了。

--------------------------------------------------------------------------

对于动态加载模块和卸载,我们使用insmod和rmmod工具来进行测试。

先加载:insmod hellodriver.ko

这个时候,并没有在terminal中出现我们想要的log数据。这个原因和解释嘛有很多,我现在也没太明白,这个大家自己研究吧,我就不误人子弟了。这里我只说,我们可以怎么看到它们和判断是否模块已经被加载了。

看到log:

1.有人说,生成的log在/var/log/syslog文件里面,这个大家可以去找找,反正我的是没有。

2.有人说,在/var/log/messages文件里面,可是我的文件系统里面压根就找不到这个文件,我们可以通过下面的操作来进行找回:

我么可以通过修改 /etc/rsyslog.d/50-default.conf 配置文件实现
去掉注释或添加:
*.=info;*.=notice;*.=warn;\
auth,authpriv.none;\
cron,daemon.none;\
mail,news.none -/var/log/message

然后重启rsyslog服务
sudo service rsyslog restart

这样,我们就可以具有messages记录日志的正常功能。

但是,messages还是看不到,输出的日志,具体为什么吗?我还不知道,毕竟我对linux系统也就知道那么一点点。

3.使用dmesg -c命令来查看,这个是没有问题的,是可查看到日志的。

查看模块是否加载:

cat /proc/modules可以看到当前内核中加载到的所有的模块。

------------------------------------------------------------------------------------

加载了之后,就是卸载了:rmmod hellodriver

然后,悲剧的事情就来了,尽然没有办法卸载:ERROR: Removing ‘hello‘: Device or resource busy

这么一个错误,问题也可能有很多,下面说一些解决办法,一般的就不说了,搜得到,就说一下不常见的:

1.gcc版本和内核的gcc版本不一直。

于是看了一下:gcc -v和cat /proc/version

发现gcc的版本是4.4.7的,而内核里面使用的gcc是4.6的,这才想起来,我再编译android源码的时候,因为一个问题,把4.6的gcc降低为4.4的了,具体这个版本怎么修改,大家参考一下这个博客吧:http://blog.csdn.net/vrix/article/details/8330135

2.现在的内核模块在插入卸载时都会要转到技术分享b/modules/内核版本号/这个目录里。所以只要建立这个目录并且把要使用的模块.ko文件复制到这个目录就行了。 于是,我再我的/lib/modules/3.2.0-23-generic/文件夹下面建立了hellodriver文件夹,然后把原来项目里面生成的hellodriver.ko文件mv到这个里面来,就可以卸载了。

3,当然了,log还是看不到的,依旧使用dmesg -c命令和cat /proc/modules来查看log输出以及模块是否被删除。

-----------------------------------------------------------------------------------

ok,经过上面的折腾,总算是勉强成功了,虽然过程很打击人(一个最简单的hellowrold,不是说helloworld都是用来找自信的吗?~!~),但是完成了之后,对于linux系统以及内核编写的基本认识总算是加深了一些,对于一些奇怪的问题,就不去深究了,一方面本身在linux方面就没有什么知识储备,另一方面因为版本的更迭,中间产生一些变化是很正常的,当我们深入了解了之后,在回头看这些问题都可以迎刃而解,所以现在不用那么纠结。



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。