当前位置:软件学习 > 其它软件 >>

内核模块入门之深入浅出

出学内核模块,略做总结。希望对广大菜鸟有所帮助。
为了不浪费大牛们的时间,在开头先列出文章中将要讲到的几个知识点,都了解的可以飘过哈!
 
一、内核模块代码的特点
二、内核模块的Makefile 的编写
三、内核模块的安装、卸载
四、模块的可选信息
五、内核模块导出
六、内核加载常见的问题
 
 
 
一、内核模块代码的特点
 
   何谓内核模块?为什么需要内核模块?
   你当然可以不需要内核模块,只需要将代码编入内核中即可。但这又导致了内核将越来越庞大。使用内核模块还有一个特点,那就是“即插即用”,可以在系统运行是加载和卸载,大大方便了模块的使用和开发。
 
那么,内核模块在代码编写上有啥特点呢?
 
  View Code
(1)必须有一下函数:

//一个规范,并非一定要如下格式定义函数名:
int hello_init()    //入口
void hello_exit()   //出口


(2)模块尾部必须如下声明:

moudule_init(hello_init) //申明模块入口函数
moudule_exit(hello_exit) //模块出口

(3)没有main函数。


(4)使用printk打印,而不是printf

 
一个简单而又经典的hello world例子:
  View Code
#include <linux/moudle.h>
#include <linux/init.h>

static int __init hello_init()
{
    printk("Hello World!\n");
    return 0;
}

static void __exit hello_exit()
{
    printk("<7>hello <0>exit\n");
}

moudule_init(hello_init); 
moudule_exit(hello_exit);

怎么样?看完代码你是不是会大喊一声:尼玛,怎么会这么简单?尼玛,他就是那么简单啊。
 
二、内核模块的Makefile 的编写
 
了解了内核模块的基本格式后,开始来编写makefile。
以下是一个最简单的内核模块的makefile格式:
  View Code
#ifneq:不等于空  (第一次为空,走else,第二次才走下面)
ifneq ($(KERNELRELEASE),)

#内核模块名字
obj-m := hello.o
hello-objs := main.o add.o

else

#内核源码的路径
KDIR := /lib/modules/2.6.18-53.el5/build

all:

    #
    # -C : 表示进入$(KDIR)目录后使用目录中的makefile进行编译
    # M=$(PWD): 表示当前目录,modules:目标模块
    make -C $(KDIR) M=$(PWD) modules
   
clean:
    rm -f *.ko *.o *.mod.o *.mod.c *.symvers
   
   
endif

 
按照这个模块,我们只要对“内核模块名”和“内核源码的路径”两项根据你的实际情况进行修改即可。
尼玛,怎么又是那么简单?不是忽悠广大网友吧?
这里LZ以RP保证,正的就是这么简单。想学点难的?别急,好戏还在后头呢。
 
三、内核模块的安装、卸载
编译完模块后,我们当然需要来安装、卸载,使用如下命令:
  View Code
加载:insmod (insmod hello.ko)
卸载:rmmod  (rmmod hello)
查看:lsmod
加载:modprobe (modprobe hello)

 
modprobe 与 insmod 都是加载一个模块到内核中,区别在于:
modprobe会根据文件modules.dep查看要加载的模块是否依赖于其他模块,如果是,modprobe会首先安装依赖的模块加载到内核中。
 
四、模块的可选信息
1.你或许会在内核模块中看到以下代码:
MOUDLE_LICENSE("GPL");                //指定这个模块遵守的协议
MOUDLE_AUTHOR("Pearry Zhou");         //模块作者
MOUDLE_DESCRIPTION("XX Module");      //模块描述
MOUDLE_VERSION("V1.0");               //模块版本
MOUDLE_ALIAS("a 易做图 module");      //模块别名

 
2.模块参数
我们知道int main(int argc, char** argv), argc表示命令行输入的参数个数,argv中保存你输入的参数。
那么内核模块中可以通过命令行输入参数么?当然可以。
 
通过宏moudle_param指定模块参数,模块参数用于在加载模块时传递参数给模块。
moudle_param(name,type,perm)
这样你就定义了一个 变量名为name,类型为type,访问权限为perm的变量。
前两个就不用解释了吧?
第三个,perm常见的值有:
S_IRUGO : 任何用户都对/sys/module中出现的该参数有读权限
S_IWUSR : 允许root用户修改/sys/module中出现的该参数www.zzzyk.com
 
例如:
  View Code
int a = 3;
char *st;
module_param(a,int,S_IRUGO);
module_param(st,charp,S_IRUGO);

(注:charp为字符串指针)

模块参数的使用:
insmod xx.ko a=4    //例子中的参数a被修改为4.你也可以在函数中把a打印出来查看结果
 
是不是很神奇?赶紧动手尝试下吧。邓爷爷教导我们:实践是检验真理的唯一标准。这句话易做图就是说给我们程序员听的啊,有没有发现,很多时候写完代码发现很多低级错误都是因为自己“想当然”的这么认为而没有去实践过导致的?(比如 int *p; ... p +=2; p真的是p的地址+2么?赶紧膜拜邓爷爷吧!没有邓爷爷)
 
五、内核模块导出
 
1.为什么要有“内核模块导出”这个概念?
当你的模块中调用了其他模块的接口时,即使你已经加载了所依赖的模块,
在加载你的模块的时候还是会出现加载失败(提示:insmod:-1 Unknow symbl in module),
这个时候,就必须用到内核模块导出。

2.如何导出?
假设一个模块中存在函数:
int add_int(int a,int b);
int sub_int(int a,int b);
则使用如下宏导出:
EXPORT_SYMBOL(add_int);
EXPORT_SYMBOL(sub_int);
导出之后,其他模块即可使用extern引用(当然所依赖模块必须已经加载)。
 
类似的导出宏还有:EXPORT_SYMBOL_GPL //只能用于GPL许可证的模块。
 
 
六、内核加载常见的问题
1.版本不匹配
(1)出现如下错误表示版本不匹配
insmod hello.ko
disagrees about version ofsymbol struct_module
insmod:error inserting 'hello.ko':-1 Invalid module format

(2)解决方法
I.使用modprobe --force-modversion命令强行插入
II.查看内核版本命令:uname -r
确保内核模块版本与当前内核版本相同,更换内核版本等手段
 
因为在编译内核模块的时候需要在makefile中指定内核源代码,
所以只要指定的内核源码与使用的当前内核版本相同即可,可在代码的makefile最前面看到版本号:
VERSION = 3
PATCHLEVEL = 2
SUBLEVEL = 6
即内核版本为3.2.6
总之:代码中makefile中的版本号,要与uname -r显示出来的版本号相同。

 

摘自  pearry汇编小站
 
补充:软件开发 , 其他 ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,