【1】查询Linux内核 #uname -r 2.6.31-14-generic #ls /usr/src/ linux-headers-2.6.31-14 linux-headers-2.6.31-14-generic 由此可见内核版本和内核头文件版本是一致的,都是2.6.31-14。(如果不一致的话在insmod一步必定出错: Error inserting './hello.ko': -1 Invalid module format 【2】编写hello.c
新建hello.c文件,加入以下内容:
#include Linux驱动程序的Makefile与一般的应用程序Makefile有所不同,驱动Makfile要指定内核源代码位置,先看一个简单的驱动Makefile: #如果已定义KERNELRELEASE,则说明是从内核构造系统调用的,因此可利用其内建语句。 ifneq ($(KERNELRELEASE),) obj-m := hello.o #否则,是直接从命令行调用的,这时要调用内核构造系统 else endif KERNELDIR = /usr/src/linux-headers-2.6.31-14-generic/ PWD := $(shell pwd) $(MAKE) -C $(KERNELDIR) M=$(PWD) modules rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c default: clean: 下面逐一分析一下各个语句: obj-m := hello.o 这句意为有一个模块需要从目标文件hello.o中构造,构造的模块名称为hello.ko。 KERNELDIR = /usr/src/linux-headers-2.6.31-14-generic/ 这里是定义一个变量KERNELDIR,并且赋值为/usr/src/linux-headers-2.6.31-14-generic。即当前内核的源代码目录。 PWD := $(shell pwd) 获取当前目录了。总之在Makefile里,$(shell xxx)就是相当于在终端中执行xxx命令。 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile时,KERNELRELEASE没有被定义,所以make将读取执行else之后的内容。 当make的目标为all时,-C $(KERNELDIR)指明跳转到内核源码目录下读取那里的Makefile;M=$(PWD)表明然后返回到当前目录继续读入、执行当前的Makefile。 当从内核源码目录返回时,KERNELRELEASE已被被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。else之前的内容为kbuild语法的语句,指明模块源码中各文件的依赖关系,以及要生成的目标模块名。 (这就是编译模块了:首先改变目录到-C选项指定的位置(即内核源代码目录),其中保存有内核的顶层makefile;M=选项让该makefile在构造modules目标之前返回到模块源代码目录;然后,modueles目标指向obj-m变量中设定的模块;在上面的例子中,我们将该变量设置成了hello.o。) 【4】开始make #make 输出类似以下消息: make -C /usr/src/linux-headers-2.6.31-14-generic/ M=/home/book/hello modules make[1]: Entering directory `/usr/src/linux-headers-2.6.31-14-generic' CC [M] /home/book/hello/hello.o Building modules, stage 2. MODPOST 1 modules CC /home/book/hello/hello.mod.o LD [M] /home/book/hello/hello.ko make[1]: Leaving directory `/usr/src/linux-headers-2.6.31-14-generic' 则编译成功,用ls查看应该可以看到生成一堆文件,如: hello.c hello.ko hello.mod.c hello.mod.o hello.o Makefile Module.markers modules.order Module.symvers 【5】安装模块: #insmod hello.ko 用lsmod命令也能查看到hello模块。 Module hello Size Used by 1148 0 1 1 nfsd 2 binfmt_misc 8356 nfsd exportfs snd_ens1371 4412 22016 241100 9 lsmod命令实际上读取并分析/proc/modules文件,与上述lsmod命令结果对应的。 /proc/modules文件如下: #cat /proc/modules hello 1148 0 - Live 0xe086b000 binfmt_misc 8356 1 - Live 0xe0855000 nfsd 241100 9 - Live 0xe0c85000 exportfs 4412 1 nfsd, Live 0xe08d9000 snd_ens1371 22016 2 - Live 0xe08e7000 使用modinfo <模块名>命令可以获得模块的信息,包括模块的作者、模块的说明、模块所支持的参数以及vermagic,如下所示: #modinfo hello.ko filename: hello.ko version: V1.0 description: A simple hello Module author: name license: Dual BSD/GPL srcversion: 16BA31B053E9BEDA151A9DF depends: vermagic: 2.6.31-14-generic SMP mod_unload modversions 586 【6】卸载模块 #rmmod hello.ko 再用 #lsmod 发现hello模块已经不存在了。 【7】关于printk 现在谈谈这个问题:insmod的时候,printk问什么没有在终端中显示Hello World呢?因为hello.c中明明是这么写的: static int hello_init(void) { printk(\"Hello World!\\n\"); return 0; } 内核模块中用于输出的函数式内核空间的printk()而非用户空间的printf(),printk()的用法 和printf()相似,但前者可定义输出级别。Printk()可作为一种最基本的内核调试手段。 printk有8个loglevel,定义在 #dmesg | tail -8 这个命令格式类似于lsmod(只显示最后8行)。 [ 95.586960] Good bye, ubuntu [ 96.483964] Hello World! [ 97.031787] Good bye, ubuntu [ 97.594151] Hello World! [ 98.109896] Good bye, ubuntu [ 98.615569] Hello World! [ 99.098943] Good bye, ubuntu [ 1893.976170] Hello World! 以上的信息显示:在95.586960做了rmmod hello.ko,在96.483964做了insmod hello.ko…… 这里[]中的数字表示执行模块安装/卸载聚开机的时间,如96.483964 S(约开机一个半小时以后)。 【9】那么这些信息都存在哪儿呢? 都说默认是在/val/log/messages目录下;可是有时候你看了,没有; 那么请看看另一个目录: #cd /etc #ls 你会找到包含syslog的文件,如: syslog.conf 那么就是它了。 #cat syslog.conf | head -20 同理,只显示前20行的东东;看到类似以下的东东: # /etc/syslog.conf Configuration file for syslogd. # # For more information see syslog.conf(5) # manpage. # # First some standard logfiles. Log by facility. # auth,authpriv.* /var/log/auth.log *.*;auth,authpriv.none -/var/log/syslog #cron.* /var/log/cron.log daemon.* -/var/log/daemon.log kern.* -/var/log/kern.log lpr.* -/var/log/lpr.log mail.* -/var/log/mail.log user.* -/var/log/user.log # # Logging for the mail system. Split it up so that ... 看到kern.*了吧~看到/var/log/kern.log了吧! 于是, # cat /var/log/kern.log | tail -10 显示: Aug 4 23:40:53 ubuntu kernel: [ 38.848634] eth0: no IPv6 routers present Aug 4 23:41:47 ubuntu kernel: [ 92.893834] Hello World! Aug 4 23:41:50 ubuntu kernel: [ 95.586960] Good bye, ubuntu Aug 4 23:41:51 ubuntu kernel: [ 96.483964] Hello World! Aug 4 23:41:51 ubuntu kernel: [ 97.031787] Good bye, ubuntu Aug 4 23:41:52 ubuntu kernel: [ 97.594151] Hello World! Aug 4 23:41:52 ubuntu kernel: [ 98.109896] Good bye, ubuntu Aug 4 23:41:53 ubuntu kernel: [ 98.615569] Hello World! Aug 4 23:41:53 ubuntu kernel: [ 99.098943] Good bye, ubuntu Aug 5 00:11:48 ubuntu kernel: [ 1893.976170] Hello World! 跟先前的dmesg显示的内容相似而更完整了! 因篇幅问题不能全部显示,请点此查看更多更全内容