root@vickytong:/home/project/modules# uname -a
Linux vickytong 3.2.0-23-generic-pae #36-Ubuntu SMP Tue Apr 10 22:19:09 UTC 2012 i686 i686 i386 GNU/Linux
root@vickytong:/home/project/modules# ls /usr/src/linux-headers-3.2.0-23-generic-pae/
arch crypto drivers fs init Kbuild kernel Makefile Module.symvers samples security source ubuntu virt
block Documentation firmware include ipc Kconfig lib mm net scripts sound tools usr
可以看到,目录/usr/src/linux-headers-3.2.0-23-generic-pae/ 就是在本地编译内核模块需要的头文件
编写自己的内核模块文件myModule.c,内容如下:
#include
#include
#include
static int __init myModule_init(void)
{
printk(KERN_INFO"myModule init.\r\n");
return 0;
}
static void __exit myModule_exit(void)
{
printk(KERN_INFO"myModule exit.\r\n");
}
module_init(myModule_init);
module_exit(myModule_exit);
MODULE_LICENSE("GPL");
#kernel代码的路径
KERN_DIR = /usr/src/linux-headers-3.2.0-23-generic-pae/
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
obj-m += myModule.o
root@vickytong:/home/project/modules# make
make -C /usr/src/linux-headers-3.2.0-23-generic-pae/ M=`pwd` modules
make[1]: Entering directory `/usr/src/linux-headers-3.2.0-23-generic-pae\’
CC [M] /home/project/modules/myModule.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/project/modules/myModule.mod.o
LD [M] /home/project/modules/myModule.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-23-generic-pae’
root@vickytong:/home/project/modules# ls
Makefile modules.order Module.symvers myModule.c myModule.ko myModule.mod.c myModule.mod.o myModule.o readme.txt
编译成功,产物myModule.ko
root@vickytong:/home/project/modules# insmod myModule.ko
root@vickytong:/home/project/modules# cat /proc/modules
myModule 12396 0 - Live 0xf853d000 (O)
rfcomm 38139 0 - Live 0xf845f000
bnep 17830 2 - Live 0xf8529000
bluetooth 158438 10 rfcomm,bnep, Live 0xf969e000
ppdev 12849 0 - Live 0xf845a000
snd_hda_codec_via 46138 1 - Live 0xf8d97000
snd_hda_intel 32765 3 - Live 0xf8d7f000
snd_hda_codec 109562 2 snd_hda_codec_via,snd_hda_intel, Live 0xf8de1000
snd_hwdep 13276 1 snd_hda_codec, Live 0xf8524000
snd_pcm 80845 2 snd_hda_intel,snd_hda_codec, Live 0xf8852000
psmouse 72846 0 - Live 0xf87e8000
snd_seq_midi 13132 0 - Live 0xf886c000
snd_rawmidi 25424 1 snd_seq_midi, Live 0xf8d77000
snd_seq_midi_event 14475 1 snd_seq_midi, Live 0xf87fb000
snd_seq 51567 2 snd_seq_midi,snd_seq_midi_event, Live 0xf8d89000
snd_timer 28931 2 snd_pcm,snd_seq, Live 0xf8876000
snd_seq_device 14172 3 snd_seq_midi,snd_rawmidi,snd_seq, Live 0xf8574000
serio_raw 13027 0 - Live 0xf850e000
snd 62064 15 snd_hda_codec_via,snd_hda_intel,snd_hda_codec,snd_hwdep,snd_pcm,snd_rawmidi,snd_seq,snd_timer,snd_seq_device, Live 0xf8841000
parport_pc 32114 1 - Live 0xf851b000
i915 414603 3 - Live 0xf8781000
mac_hid 13077 0 - Live 0xf8475000
soundcore 14635 1 snd, Live 0xf846b000
snd_page_alloc 14108 2 snd_hda_intel,snd_pcm, Live 0xf8470000
drm_kms_helper 45466 1 i915, Live 0xf8501000
drm 197692 4 i915,drm_kms_helper, Live 0xf8542000
i2c_algo_bit 13199 1 i915, Live 0xf8448000
video 19068 1 i915, Live 0xf8450000
lp 17455 0 - Live 0xf8427000
parport 40930 3 ppdev,parport_pc,lp, Live 0xf843d000
atl1c 36718 0 - Live 0xf842f000
安装驱动应该会调用myModule_init函数,那么应该有打印输出,但是实际上没有;
root@vickytong:/home/project/modules# rmmod myModule.ko
步骤5在安装myModule.ko的时候终端上并没有打印出信息,但是实际上打印的信息写入文件/var/log/kern.log,可以通过tail命令查看该文件最后的条目
root@vickytong:/home/project/modules# tail -f /var/log/kern.log
May 16 15:55:52 vickytong kernel: [6581379.212426] myModule init.
May 16 16:05:35 vickytong kernel: [6581961.311745] myModule exit.
May 16 16:07:07 vickytong kernel: [6582053.519025] myModule init.
May 16 16:13:56 vickytong kernel: [6582463.150886] myModule exit.
May 16 16:13:58 vickytong kernel: [6582464.618507] myModule init.
May 16 16:13:59 vickytong kernel: [6582465.680317] myModule exit.
Linux 内核中通过printk输出信息,信息的类型在文件/usr/src/linux-headers-3.2.0-23-generic-pae/include/linux/printk.h中(有些源码在文件include\linux\kernel.h中)定义,
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
如果不指定,默认是 DEFAULT_MESSAGE_LOGLEVE,和KERN_WARNING一样。
root@vickytong:/usr/src/linux-headers-3.2.0-23-generic-pae# find . -name “*.h” | xargs grep “DEFAULT_MESSAGE_LOGLEVE”
./include/generated/autoconf.h:#define CONFIG_DEFAULT_MESSAGE_LOGLEVEL 4
有时候我们需要将模块移植到其他芯片方案的主机(或开发板)上运行,那么需要交叉编译。交叉编译需要修改Makefile,主要是修改kernel代码的路径和编译工具。kernel代码的路径要修改为目的主机系统的源代码路径,编译工具gcc要修改为目的主机芯片架构对应的编译工具。
交叉编译工具CC=mipsel-buildroot-linux-uclibc-gcc;
kernel代码 -C ./linux/kernels/linux-2.6.36.x
通过tftp将编译好的myModule.ko下载到目标主机上,安装
/tmp # tftp -g 192.168.0.104 -r myModule.ko
myModule.ko 100% |*********************| 2558 0:00:00 ETA
/tmp # insmod myModule.ko
myModule init.
/tmp # lsmod
Module Size Used by Not tainted
myModule 526 0
…
有时候需要从用户态传参数给内核模块,这时候在内核模块中需要通过宏module_param声明参数。本例增加两个参数,一个是字符串参数cString,一个是int型参数iInt,修改后代码如下:
#include
#include
#include
static char* cString = NULL;
static int iInt = 0;
module_param(cString, charp, 0644);
module_param(iInt, int, 0644);
static int __init myModule_init(void)
{
printk(KERN_INFO"myModule init, cString = %s, iInt = %d\r\n", cString, iInt);
return 0;
}
static void __exit myModule_exit(void)
{
printk(KERN_INFO"myModule exit.\r\n");
}
module_init(myModule_init);
module_exit(myModule_exit);
MODULE_LICENSE("GPL");
交叉编译后,下载,安装命令如下:
/tmp # insmod myModule.ko cString=helloworld iInt=100
myModule init, cString = helloworld, iInt = 100