内核模块----
linux内核的整体结构庞大,其中包含的组件也很多,如何使用需要的组件?
1--把所有的组件都编译进内核文件,即zImage,bzImage,但这样会导致两个问题:一是生产的内核文件过大,二是,如果要添加或删除某个组件要重新编译整个内核。
内核模块机制---提供一种机制能让内核文件(zImage,bzImage)本身并不包含某组件,而是在该组件需要被使用的时候,动态的添加到正在运行的内核中。
内核模块特点--
模块本身并不被编译进内核文件,可在内核运行期间动态的安装或卸载。
#include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("DAVID"); MODULE_DESCRIPTION("Hello world module"); static int __init hello_init(void) { printk(KERN_ERR "hello world!\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_EMERG "hello exit!\n"); } module_init(hello_init); module_exit(hello_exit); #include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("DAVID"); MODULE_DESCRIPTION("Hello world module"); static int __init hello_init(void) { printk(KERN_ERR "hello world!\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_EMERG "hello exit!\n"); } module_init(hello_init); module_exit(hello_exit);
1--模块加载函数
安装模块时被系统自动调用的函数,通过module_init宏来指定。
2--模块卸载函数
卸载模块时被系统自动调用的函数。通过module_exit宏来指定。
模块的编译---
在linux2.6编译内核模块,通常使用makefile
1--内核模块由一个源文件构成,编写makefile
ifneq ($(KERNELRELEASE),)若变量和空不相等,第一次执行时变量等于空故执行else
obj-m :=hello.o编译出来的内核模块的名字。若想不一样,需要修改。
else
KDIR:= /lib/modules/2.6.18-53.el5/build编译内核模块需要依赖内核源代码,需要给出其路径
all:
make -C $(KDIR) M=$(PWD) modules -C表示进入内核源代码目录,$(PWD)表示,模块在当前目录下,总体来说,是使用目录下的内核源代码来编译当前目录下的模块,当第二次编译使用makefile时,KERNELRELEASE就有值了,是内核版本号。
clean:
rm -f *.ko *.o *.mod.o *.mod.c .symvers
endif
2--内核模块由多个源文件构成,编写makefile
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
hello-objs :=main.o add.o
else
KDIR := /lib/modules/2.6.18-53.el5/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
加载---insmod
卸载---rmmod
查看---lsmod
加载----modprobe
modprobe会根据文件/lib/modules/<$version>/modules.dep
来查看要加载的模块,看它是否还依赖于其它模块,若是,会先加载它们到内核。
模块可选信息---
1--许可证申明
宏MODULE_LICENSE用来告知内核,该模块带有一个许可证(GPL,GPLv2等)。
2---MODULE_AUTHER()作者
3--MODULE_DESCRIPTION()描述
4--MODULE_VERSION()版本
5--MODULE_ALIAS()别名
6--模块参数
module_param(name,type,perm)
name-参数名字
type-参数类型
perm--模块参数的访问权限。常见值:
S_IRUGO:任何用户都对/sys/module中出现该参数具有读权限。
S_IWUSR::允许root用户修改/sys/module中出现的该参数。
如:int a=3; char *st;
module_param(a,int,S_IRUGO);
module_param(st,charp,S_IRUGO);
通过宏module_param指定模块参数,模块参数用于在加载模块时传递参数给模块。
内核符号导出--
/proc/kallsyms记录了内核中所有导出符号的名字和地址。
EXPORT_SYMBOL(sum);当在一个模块中使用另外一个模块中的符号,比如函数,需要在被调用函数里导出函数名,调用函数才能使用。
EXPORT_SYMBOL(符号名)
EXPORT_SYMBOL_GPL(符号名)只能用于包含GPL许可证的模块。
内核打印---
在<linux/kernel.h>中定义了8种记录级别。按照优先级递减的顺序分别是:
KERN_EMERG 0
KERN_ALERT 1
KERN_CRIT 2
KERN_ERR 3
KERN_EMERG 4
KERN_WARNING 5
KERN_NOTICE 6
KERN_INFO 7
KERN_DEBUG 8
没有指定优先级的printk默认使用
DEFAULT_MESSAGE_LOGLEVEL它是一个在kernel/printk.c中定义的整数。
在2.6.29内核中
#define DEFAULT_MESSAGE_LOGLEVEL 5
控制台优先级配置---纯字符界面
/proc/sys/kernel/printk
6 4 1 7
Console_loglevel
Default_message_loglevel
Minimum_console_level
Default_console_loglevel