linux模块编程
一、摘要
Linux内核模块编程的资料有些纷繁复杂,有的过于简单,有的过于庞杂,我试图用笔记的形式想读者展示怎样来进程Linux模块编程,力图做到简明扼要,这篇文章也是作为本人备忘的资料,所以有些地方过于简略是难免的。本来这篇文章的目的就是让用户知其然,至于所以然还是请参考相应的资料,其实最好的资料莫过于Linux Kernel Source。
二、Linux模块简介
首先这个module不同于microkernel的module,microkernel的module是一个个的daemon进程,工作于用户空间,Linux的module只是一个内核的目标代码,内核通过执行运行时的连接,来把它整合到kernel中去,所以说Linux的module机制并没有改变Linux内核为monolithic OS本质,其module也是工作于内核模式,享有内核的所有特权。
至于为什么要引入Linux Kernle Module(一下简称LKM),我想至少有一下几点:
l 模块化编程的需要,降低开发和维护成本。
l 增强系统的灵活性,使得修改一些内核功能而不必重新编译内核和重启系统。
l 降低内核编程的复杂性,使入门门槛降低。
三、案例
3.1、源码HelloModule
现在我们介绍一个运行内核态的Hello,World程序,它其实是一个最简单的驱动程序模块。
我们将Hello Module的源代码放在/root/code目录
文件名字为hellomodule.c,源代码内容如下
#include <linux/kernel.h> #include <linux/module.h> static int _init sep0611_hello_module_init(void) { printk("Hello,sep0611 module is installed! \n"); return 0; } static void _exit sep0611_hello_module_cleanup(void) { printk("Good-bye,sep0611 module was removed!\n"); } MODULE_LICENSE("GPL"); MODULE_AUTHOR(DRIVER_AUTHOR); /* Who wrote this module? */ MODULE_DESCRIPTION(DRIVER_DESC); /* What does this module do */ module_init(sep0611_hello_module_init); module_exit(sep0611_hello_module_cleanup); |
3.2编译Hello Module源代码
由于这个模块是加到嵌入式Linux的内核中,所以它肯定会用到许多嵌入式Linux源码的头文件,我们的嵌入式linux的内核源码位置在/linux/下面,这中间的链接过程非常复杂,为了不让我们手动输入编译指令,一般编译2.6版本的驱动模块需要把驱动代码加入内核代码树,并做相应的配置,如下步骤
Step1:编辑文件Kconfig,加入驱动选项,使之在make menuconfig的时候出现,打开/Linux/DDR2-Demo/kernel/drivers/char/Kconfig文件,添加如图1-1所示:
zhangjie@WX-ASIC-S02-Android:~/Linux/DDR2-Demo/kernel/drivers/char$ vi Kconfig |
图1-1 Kconfig修改
这是我们添加的部分。
保存退出,这时在kernel目录位置运行一下make menuconfig就可以在Device Drivers/Character devices菜单中看到刚才添加的选项了,按下空格键将会选择为<M>,此意为要把该选项编译为模块方式;再按下空格键会变为<*>,意为要把该选项编译到内核中,再次我们选择<M>,如图1-2,1-3,1-4
zhangjie@WX-ASIC-S02-Android:~/Linux/DDR2-Demo/kernel$ make menuconfig |
图1-2 Device Drivers选项
图1-3 Character devices选项
图1-4 sep0611 hello module
Step2:通过上一步,我们虽然可以在配置内核的时候进行选择,但实际中此时执行编译内核还是不能把hellomodule.c编译进去,还需要在makefile中把内核配置选项和真正的源码联系起来,打开kernel/drivers/char/Makefile,如图1-5添加并保存退出:
zhangjie@WX-ASIC-S02-Android:~/Linux/DDR2-Demo/kernel/drivers/char$ vi Makefile |
图1-5 Makefile修改
此处为我们添加的:其中CONFIG_SEP0611_hellomodule为在Kconfig中使用“_”连接,同时config变为大写CONFIG,后面的hellomodule.o为对应hellomodule.c的要生成的。
Step3:这时回到kernel目录位置,执行make modules,就可以生成我们需要的内容模块文件hellomodule.ko了,如图1-6:
zhangjie@WX-ASIC-S02-Android:~/Linux/DDR2-Demo/kernel$ make modules |
图1-6编译的模块
上面的make modules是编译所有的ko文件,当你只需要编译自己的ko,而不需要编译其他的时候,我们可以使用: |
make CONFIG_SEP0611_hellomodule=m -C /home/zhangjie/Linux/DDR2-Demo/kernel M=/home/zhangjie/Linux/DDR2-Demo/kernel/drivers/char modules |
CONFIG_SEP0611_hellomodule=m:是你想要编译的模块 |
-C:后面的是kernel目录,即你编译的Makefile的目录 |
M=后面的路径为你编译生成的*.ko所在的路径 |
|
注:编译模块的时候首先要先编译uImage,当你make clean后,也要重新编译uImage。 |
3.3、把HelloModule下载到开发板并安装使用
最简单的方法莫过于把hellomodule.ko拷贝到网络文件系统中,这样你就可以直接在板子上运行程序了。假设我们已经把hellomodule.ko放到了板子的/目录下,现在执行:
#insmod hellomodule.ko
可以看到该模块已经被装载了;在执行,可以看到该模块被卸载
#rmmod hellomodule.ko
整个过程如图1-7:
图1-7模块加载和删除