开发环境:
主机:fedora 14
虚拟机:vmware workstation 10
交叉编译工具:arm-linux-gcc 4.3.2
开发板:mini2440(2m nor ,64m sdram,256m nand)
内核版本:linux2.6.32.2
上一篇帖子之后,开发环境基本已经搭建起来了,u-boot下载,命令,内核起yaffs2和nfs文件系统都已经可用,网卡,串口这些初期必须的驱动也搞好了,下面就开始做驱动部分的笔记。
一、什么是内核模块?
在执行# make menuconfig的时候,会出现很多配置选项,你每选一个,内核就将这个模块编译进去。为什么不直接把所有的选项都选上呢?那样的话我们编译出来的内核就非常非常的大,而且也违背嵌入式系统可裁剪的内涵。有时候,可能每个模块只使用很短的时间就不需要了,所以我们就不将其编译进内核,而是在使用的时候再进行动态地加载到运行中的内核,并且不需要的时候动态地卸载。
加载,卸载和查看内核模块的三个命令,以helloworld.ko为例:
# insmod helloworld.ko 加载
# rmmod helloworld 卸载,注意这里没有.ko了哦
# lsmod查看加载了哪些模块
二、内核模块和应用程序的区别
应用程序的入口是main函数,而内核模块没有main函数,它的入口是在module_init()宏里,我们看一个内核模块,都是从module_init()开始看的,因为执行insmod之后,会执行module_init()里面的函数,分析也就是从这里开始的。对应的卸载的时候,会调用module_exit()中的出口函数,回收资源,卸载模块。
内核模块所必须的两个头文件:
#include<linux/module.h>
#include<linux/init.h>
三、helloworld.c分析
#include <linux/init.h>
#include <linux/module.h>
static int hello_init()
{
printk(KERN_WARNING"Hello world!\n");
return 0;
}
static void hello_exit()
{
printk(KERN_WARNING"hello exit!\n");
}
module_init(hello_init);
module_exit(hello_exit);
首先头文件两个添加进来,然后在末尾写上module_init()和module_exit(),一个加载一个卸载,然后添加加载函数static inthello_init(),在里面就打印了一句话:hello world!,添加卸载函数static void hello_exit(),卸载时打印:hello exit!,比较简单,不多做赘述。
四、Makefile分析
obj-m := helloworld.o
将helloworld编译成内核模块
KDIR := /home/S5-driver/lesson7/linux-tq2440
指定编译内核模块需要加载的内核的路径,也就是开发板上的内核,不要这里指定一个内核,编译下载到开发板又是另外一个内核,这样在insmod的时候会出现错误。
all:
make-C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
make生成内核模块,-C指定刚才的KDIR路径,M=$(PWM)意思是要编译的文件在当前路径下,后面制定了交叉编译工具链和架构。
clean:
rm-f *.o *.ko *.order *.symvers
清除一些文件。
五、内核模块里的printk和static
1.printk
printk是在内核使用的输出方式,具有日志级别,使用方法是
printk(“<KERN_XXX>helleworld!/n”);
一共分为八种不同的日志级别:
#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 */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
这些定义在linux/kernel.h中,等级最高是0,最低是7,如果不指定日志级别,那就默认是KERN_WARNING.
2.static
在内核模块里使用static是因为我们的内核很大,里面有成千上万的个函数,有太多的全局函数和全局变量,这样比如A写了一个驱动叫a_function,B写了一个驱动也叫a_function,那在调用的时候就会出错误,所以我们要限制源码中函数和变量的作用空间,将其作用范围缩小到仅仅在当前文件。事实上,我们的注册和卸载函数使用完了也没它什么事情了,它已经完成了它的工作,所以内核模块里面很多函数前面都会加static。
六、编写测试helloworld模块
helloworld.c和Makefile内容如上,编写好之后,执行:
#make
#chmod 777 helloworld.ko /opt/rootfs
然后打开开发板,起nfs文件系统,进入控制台。
/ # insmodhelloworld.ko
helloworld: module license 'unspecified'taints kernel.
Disabling lock debugging due to kerneltaint
hello world!
/ # lsmod
Tainted: P
helloworld 648 0 - Live 0xbf000000 (P)
/ # rmmodhelloworld
hello exit!
忽略警告信息,看到已经成功了,结束。如有不正确的地方还请指出,大家共同进步。