模块编写(转载)

来源:互联网 发布:全员网络闯关赛 题库 编辑:程序博客网 时间:2024/06/05 20:47
1.内核模块的概念
  Linux内核的整体结构非常庞大,其包含的组件也非常多,如何使用需要的组件呢:
  方法一:把所有的组件都编译进内核文件,即:zImage或bzImage,但这样会导致两个问题:一是生成的内核文件过大;二是如果要添加或删除某个组件时,需要重新编译这  个内核。
  方法二:组件在需要时被使用,动态的添加到正在运行的内核中,内核文件zImage或bzImage本身并不包含该组件,在linux中叫做"内核模块"。文件以.o或.ko的文件形式存  在。
2.内核模快的特点
  (1)模块本身并不编译进内核文件(zImage或bzImage)
  (2)可以根据需要,在内核运行期间动态的安装或卸载。
代码范例:(hello.c)
范例程序分析:
  安装模块时被系统自动调用的函数,通过module_init宏来指定,卸载模块时被系统自动调用的函数,通过module_exit宏指定。模块加载函数和卸载函数是内核模块程序所必  须的,模块加载函数是内核模块的入口函数,相当于应用程序的main函数。


模块可选信息
(1)许可证声明
宏MODULE_LICENSE用来告知内核,该模块带有一个许可证,没有这样的说明,加载模块时内核会抱怨。有效的许可证有"GPL"、"v2"、"GPL and additional rights"、"Dual BSD/GPL"、"Dual MPL/GPL"和"Proprietary"。
(2)作者声明(可选)
MODULE_AUTHOR()
(3)模块描述(可选)
MODULE_DESCRIPTION(Hello World Module )
(4)模块版本(可选)
MODULE_VERSION(V1.0)
(5)模块别名(可选)
MODULE_ALIAS(a simple module)
(6)模块参数
通过宏module_param指定模块参数,模块参数用于在加载模块时传递参数给模块。
module_param(name,type,perm)
name是模块参数的名称,type是这个参数的类型,perm是模块参数的访问权限。
type的常见值:
bool(布尔型),int(整型),charp(字符串型)
perm常见值:
S_IRUGO(任何用户都对/sys/module中出现的该参数具有读权限)
S_IWUSR(允许root用户修改/sys/module中出现的参数)
代码范例:(param.c)


下图中的命令insmod param.ko age=12,中的age=12就是传递的模块参数。
 
在linux2.6下编译内核模块通常使用的是makefile
makefile范例:
ifneq ($(KERNELRELEASE),)    #假如变量$(KERNELRELEASE)不等于空,执行下面的语句,否则执行else下面的语句。
obj-m :=hello.o      #根据需要变化
else
KDIR:= /lib/modules/2.6.18-53.el5/build     #内核源代码的路径,build这个其实是个连接文件,会连接到源代码目录(需要变)
all:
 make -C $(KDIR) M=$(PWD) modules    #进入到$(KDIR)目录下使用它自己的makefile,M=$(PWD)表示内核模块在当前目录下,modules表示编译的是内核拨快
clean:
 rm -f *.ko *.o *.mod.o *.mod.c .symvers
endif


3.模块的安装与卸载


加载 insmod (insmod hello.ko)
卸载 rmmod (rmmod hello)
查看 lsmod
加载 modprobe (modprobe hello)
modprobe如同insmod,也是加载一个模块到内核。它的不同之处在于它会根据文件/lib/modules/<$version>/modules.dep查看要加载的模块,看它是否还依赖于其他模块,如果是,modprobe会首先找到这些模块,把它们加载到内核。


4.内核符号的导出


/proc/kallsyms记录了内核中所有的导出符号的名字和地址。


[root@localhost 4-1-4]# cat /proc/kallsyms  |grep add_integar
ee577000 t add_integar  [calculate]


没导出来
[root@localhost 4-1-4]# cat /proc/kallsyms  |grep sub_integar
ee57700c r __ksymtab_sub_integar        [calculate]
ee577018 r __kstrtab_sub_integar        [calculate]
ee577014 r __kcrctab_sub_integar        [calculate]
ee577004 T sub_integar  [calculate]
13db98c9 a __crc_sub_integar    [calculate]
已经导出
出现关键字__ksymtab才表明符号已经导出
模块1需要依赖于模块2中的符号(如函数名)时,模块2就要导出相应的符号供模块1使用,否则模块1没法加载。
内核符号的导出使用:


EXPORT_SYMBOL(符号名)
EXPORT_SYSBOL_GPL(符号名)
其中EXPORT_SYSBOL_GPL只能用于包含GPL许可证的模块。
代码范例:(calculate.c)
5. 常见的问题
版本不匹配的问题:
   内核模块的版本由其所依赖的内核代码版本所决定(即内核模块是用的哪个内核版本编译的),在加载内核模块时,insmod程序会将内核模块版本与当前正在运行的内核版本比较,如果不一致将会出现一些错误。

解决方法:
(1)使用modprobe  --force  -modversion 强行插入。
(2)确保编译内核模块时,使用的内核代码版本和之前正在运行的内核代码一致。

6.总结与对比

 (1)与应用程序对比。内核模块有以下不同:
    应用程序从头(main)到尾执行任务,执行结束后从内存中消失。内核模块则是先在内核中注册自己以便于服务将来的某个请求,然后它的初始化函数结束,此时模块仍然存在于内核中,直到卸载函数被调用,模块才从内核中消失


(2)内核打印
printk是内核中出现最频繁的函数之一,printk与printf的相同点是都是用来打印信息的,不同的是printk在内核中使用,printf在应用程序中使用。printk允许根据严重程度,通过附加不同的优先级来对消息分类。在<linux/kernel.h>中定义了8中记录级别。按照优先级递减顺序分别是

KERN_EMERG 用于紧急消息,常常是那些崩溃前的消息  <0>
KERN_ALERT 需要立刻行动的消息                    <1>
KERN_CRIT 严重情况                               <2>
KERN_ERR错误情况                                 <3>
KERN_WARNING 有问题的警告                        <4>
KERN_NOTICE 正常情况但是仍然值得注意             <5>
KERN_INFO信息型消息                              <6>
KERN_DEBUG用于调试消息                           <7>

  在没有指定优先级的printk默认使用
DEFAULT_MESSAGE_LOGLEVEL优先级,它在kernel/printk.c中定义:
#define DEFAULT_MESSAGE_LOGLEVEL 4  /*KERN_WARNING*/

 (3)控制台的优先级配置

/proc/sys/kernel/printk
6  4  1  7

6:Console_loglevel
4:Default_message_loglevel
1:Minimum_console_level
7:Default_console_loglevel
0 0
原创粉丝点击