1: 虚拟机:virtualbox + ubuntu10.04 宿主机:windows 7 + x86
kernel version:2.6.32-37
2 : 在编译内核模块时,需要内核源代码树,当然这里的源代码树不一定是完整的代码,只是包含内核的头文件的树和makefile文件。如果系统里面没有,那么就先安装 kernel-headers 和kernel-devel。
安装方法:
# apt-cache search kernel-headers
出现如下结果:
comedi-source - Comedi kernel module source
kernel-package - A utility for building Linux kernel related Debian packages.
linux-libc-dev - Linux Kernel Headers for development
安装:
# apt-get install linux-libc-dev
OK了,这样就可以去编译模块了。
3:hello world
这个“臭名昭著”的例子,不论在C,C++,java......,甚至是shell,android......的书上,第一个见到的就是她了。当然这里我们也没有理由拒绝她的疑惑,那么就以hello world 开启我们linux driver 学习的大门吧。
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- MODULE_LICENSE("Dual BSD/GPL");
- MODULE_AUTHOR("Keter");
- static int __init hello_init(void)
- {
- printk(KERN_ALERT "Hello World\n");
- return 0 ;
- }
- static void __exit hello_cleanup(void)
- {
- printk(KERN_ALERT "Goodbye world\n");
- }
- module_init(hello_init);
- module_exit(hello_cleanup);
说明: #include
#include
#include
头文件实际包含代码需要的函数和宏,实际上指向:
/usr/src/linux-headers-2.6.32-37-generic/
尽管不是严格要求,但模块应该指定代码所使用的许可证。内核能够识别的许可证有:“GPL”、“GPL v2”、“GPL and additional rights"、“Dual BSD/GPL"、“GPL MPL/GPL”、“Proprietary”。在这里我们用“Dual BSD/GPL”
MODULE_LICENSE("Dual BSD/GPL");
#define MODULE_LICENSE(_license) MODULE_INFO(license, _license)
MODULE_LICENSE 宏告诉内核模块采用许可证类型。
模块定义了两个函数:static int hello_init(void) static void hello_cleanup(void)。模块被装载时调用hello_init,模块被移除时调用
hello_cleanup 。
这里用到了一个内核函数:prinfk,和C库差不多的用法,打印内核调试信息,分为8个等级。
在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 */
数字越小,优先级越大,如果小于控制台默认优先级,就不会输出log信息。 调整优先级:
#cat /proc/sys/kernel/printk:
4 4 1 7
可以修改这个文件控制输出情况。
__init ,__exit :告诉内存子啊装载和卸载模块后释放代码在内存中的空间。
4:Makefile 和 装载
- hello-objs := hello.o
- obj-m := hello.o
- #KERNELDIR ?= /lib/modules/$(shell uname -r)/build
- KERNELDIR := /lib/modules/$(shell uname -r)/build
- PWD := $(shell pwd)
- all:
- $(MAKE) -C $(KERNELDIR) M=$(PWD)
.PHONY: clean
- clean:
- rm -rf *.o .*.cmd *.ko *.mod.c .tmp_versions
$(MAKE) -C $(KERNELDIR) M=$(PWD) -C
首选改变目录到-C选项指定的位置:$KERNELDIR ,其中包含了内核顶层的makefile文件。 M=$(PWD)让
改makefile在构造模块前返回到当前模块的源代码目录。然后modules指向obj-m指定的模块。
hello-objs := hello.o:生成模块需要的.o文件。
执行make 输出如下:
- make -C /lib/modules/2.6.32-37-generic/build M=/home/share/samba/driver
- make[1]: Entering directory `/usr/src/linux-headers-2.6.32-37-generic'
- CC [M] /home/share/samba/driver/hello.o
- Building modules, stage 2.
- MODPOST 1 modules
- LD [M] /home/share/samba/driver/hello.ko
- make[1]: Leaving directory `/usr/src/linux-headers-2.6.32-37-generic'
可以清晰看出源代码树目录/usr/src/linux-headers-2.6.32-37-generic
/lib/modules/$(shell uname -r)/build 目录只是指向/usr/src/linux-headers-2.6.32-37-generic目录的一个软链接。
5:至此,可以装载模块了
# insmode hello.ko
# rmmode hello
# cat syslog
//dmesg | tail -2 也可以查看LOG
syslog中就看到如下2行log
Aug 1 22:06:10 keter kernel: [10574.941883] Goodbye world
Aug 1 22:06:20 keter kernel: [10584.619073] Hello World
至此hello world也完成。
阅读(1876) | 评论(0) | 转发(0) |