linux内核驱动模块编程框架---(hello world模块)

驱动模块框架     

        hello world是软件中的经典!!

        和平常写一般代码一样,vim 新建一个hello.c文件:

#include<linux/init.h> //模块加载卸载需要的头文件
#include<linux/module.h> //需要内核的一些认证和许可  
// 上面两个头文件对一个内核模块程序来说是必不可少的

//这是模块初始化函数,在模块加载时触发被调用
static int __init hello_init(void)
{
   printk("hello world module init!\n");
   return 0;
}

//这是模块收尾处理函数,在模块卸载时触发被调用
static void __exit hello_exit(void)
{
   printk("hello world module exit!\n");
}

//下面这两个相当于是注册函数,用来加载模块和卸载模块时触发的
module_init(hello_init);
module_exit(hello_exit);

//模块加载时一个许可认证,如果没有编译的时候会报警告
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("YU ZHI HUI"); //额外的信息,模块作者信息

        对于这种模块框架,只要理解然后照搬来用就可以,这是在内核中操作,稍有不慎就会让整个系统崩溃的,要多小心点。

        重点来说下注册的回调函数,static int __init hello_init(void)和static void __exit hello_exit(void)函数;
        一、模块初始化函数,这个函数是有返回值得,而模块卸载所触发的收尾处理函数是没有返回值得。

        二、这两个函数和我们平时写得函数不一样,多了个__init和__exit(前面是两个_),这个可以加也可以不加。为什么会有这个呢?因为这两个函数都是模块加载与卸载时触发调用,但一个模块加载和卸载都只有一次而已。那么加载完后这段代码就没有什么作用了,所以用__init表示这段代码只作用一次,当作用完以后内核会自动释放掉代码占用的空间。而__exit也是类似作用,表明这段代码只作用一次。

        三、就是参数问题,如果没有参数一定要写void,不然编译会报警告,这是个习惯问题,也是代码规范问题。

        四、复杂模块编写流程,上面仅仅是个模块框架。如果要添加复杂的代码,要从hello_init()中开始添加。hello_init()是模块初始化函数,也是模块工作入口函数。hello_exit()函数是收尾清楚工作,主要调用些函数来释放申请的内存和关闭打开的文件等等操作。

Makefile文件

#指定生成可加载模块的名称
obj-m:=hello.o

#实际是指向你自己创建的内核代码树的路径,build是个软连接
KERDIR=/lib/modules/$(shell uname -r)/build

#编译模块的当前路径
CURDIR=$(shell pwd)

#make后默认自动跳转到这里执行,make功能比较多,有兴趣的可以网上搜下
all:
        make -C $(KERDIR) M=$(CURDIR) modules

#这是模块清理工作,一般会清理掉所有生成的文件
clean:
        make -C $(KERDIR) M=$(CURDIR) clean
        简单的编译流程(直接make), make -C $(KERDIR) M=$(CURDIR) modules,会进入指定的KERDIR(也就是内核树目录的路径)对这个模块文件(hello.c)进行编译,因为这个是要加载到内核中去的,所以要借助内核代码树的环境进行编译(-C是指定路径参数)。当编译完后,还要退回到当前目录下来。请看实际的编译流程:

        

        模块清理make clean:make -C $(KERDIR) M=$(CURDIR) clean;执行流程和make类似,默认会删除所有make生成的文件。如下图: 

        linux内核驱动模块编程框架---(hello world模块)_第1张图片

        make编译模块后会生成一大堆文件,但是只有一个文件是我们所需要的,那就是以.ko为后缀的文件,这里是hello.ko。其他文件都是编译时生成的中间文件,没多大用(至少我现在很少用到过,除了些库文件,其他没怎么用到过)。

模块操作常用命令

        加载模块命令:insmod hello.ko

        

        加载模块时,如果输完命令后没什么反应,那么多半要恭喜你,说明你的模块没问题,内核接受了(linux的一惯作风:成功什么都不显示,出错显示一大堆)。加载完模块没问题后,当然得去瞧瞧自己的模块了。

        查看模块命令:lsmod

        linux内核驱动模块编程框架---(hello world模块)_第2张图片

        最后嘚瑟之后,就要把这个模块卸载掉去了。注意上面模块的名称为hello(并不是hello.ko),所以卸载时可以用hello来指定模块。

        卸载模块命令:rmmod hello

        

        和加载模块时一样,如果没有什么显示就表明一切ok(大部分的时候是这样)。其实到这里一个模块的加载和卸载及查看已经完成了,但是还有一点遗憾就是我上面的hello.c文件中明明有两句打印语句,怎么看不见了?那么这就是模块加载进去后输出的信息在哪里查看的问题了。

        先看下上面的hello.c文件,模块加载时,初始化函数会打印一句:printk("hello world module init!\n");模块卸载时,收尾函数会打印一句:printk("hello world module exit!\n");这个打印的消息和一般的printf打印的不一样,printk是专门为内核打印信息的(反正有带有k的,大部分都是在内核使用的,比如:内核中的内存申请kmalloc),所以不会把消息输出到一般的控制台上。那么怎么查看打印信息呢?有两种方法:第一监听日志;第二查看消息;

        监听日志命令:tail -f /var/log/messages

               这个命令是实时监听用户的所有动作(就是一有什么动作,就会马上显示出来,看上图最后两行),从开机开始记录。所以这个命令一般还可以用来查看你操作上的一些错误。如果觉得这个方法不太直观,那么还有个就是专门来查看模块消息的命令。

        模块消息查看命令:dmesg

        这个命令就是专门显示模块输出信息的了,一般可以先清除下,因为开机后要加载很多模块,不仅仅是你自己手动加载的那个模块。所以先清理消息,然后加载打印就可以很方便的看出自己那个模块的输出信息了。清理消息命令为:dmesg -c(建议每次加载时都先清理下消息)

        linux内核驱动模块编程框架---(hello world模块)_第3张图片

        至此,linux驱动模块框架和一些基本操作命令也分析完了。这只是驱动模块中的皮毛,如果要做驱动模块编程那么还要继续加油。

       =================================================================================================

        交叉编译驱动:

       我的开发板是mini2440的,只修改下Makefile就可以了;

obj-m:=hello.o

CC=arm-linux-gcc

#KERDIR=/lib/modules/$(shell uname -r)/build
KERDIR=/home/kernel/linux-2.6.32.2  #开发板内核源代码所在目录,编译的内核树所在的源码位置

CURDIR=$(shell pwd)

all:
        make -C $(KERDIR) M=$(CURDIR) modules

clean:
        make -C $(KERDIR) M=$(CURDIR) clean
        编译好模块后就把.ko文件利用nfs服务器放到开发板上运行就可以了。


        转载请注明作者和原文出处,原文地址: http://blog.csdn.net/yuzhihui_no1/article/details/40210897
        若有不正确之处,望大家指正,共同学习!谢谢!!!


你可能感兴趣的:(makefile,linux内核,驱动框架,驱动模块编程,模块常用操作命令)