Linux2.6.32内核笔记(1)内核模块helloworld

时间:2021-05-25 14:47:25

开发环境:

    主机: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!

    忽略警告信息,看到已经成功了,结束。如有不正确的地方还请指出,大家共同进步。