-
转载
-
L什么是内核模块
(1)内核模块是具有独立功能的程序,它可以被单独的编译,但是不能独立的运行,他必须连接到内核作为内核的一部分在内核空间中运行。
(2)模块编程与内核版本密切相关,由于不同的内核版本中某些函数的函数名会有变化。因此模块编程也可称作内核的编程。
(3)特点:模块本身不被编译进内核映像,从而控制了内核的大小;模块一旦被加载就和内核中的其他部分完全一样。 -
用户层编程与内核模块编程的区别:
-
编写内核模块
testm.c 文件:
#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("weipeng jing");
MODULE_DESCRIPTION("Hello World Module");
MODULE_ALIAS("a simplest module");
static int __init hello_init()
{
printk(KERN_EMERG"Hello World!\n");
return 0;
}
static void __exit hello_exit()
{
printk("<6>hello exit\n");
printk(KERN_EMERG"good bye!\n");
}
module_init(hello_init);
module_exit(hello_exit);
- 内核模块编程的具体实现
#include <linux/module.h>
#include <linux/init.h>
编写内模块程序的必须的头文件
由于内核编程与用户编程所用的库函数不同,所以头文件也不同。
内核头文件的位置:/usr/src/linux2.6.29/include/
用户头文件的位置:/usr/include/
加载函数static int hello_init(void);//不加 void 在调试时会出现报警
卸载函数static void hello_exit(void);// 不加 void 会出现报警 , 若改为 static int 也会报错 , 因为出口函数是不能返会值的
在模块编程中必须要有上面这两个函数;
注册函数和卸载函数还有一种写法两者的区别与联系:
1> 模块加载函数
static int _init init_fun(void)
{
printk(“hello world/n”);
return 0;
}
2> 卸载函数 无返回值
static void __exit exit(void){
printk(“bye bye!/n”);
通过比较我们可以发现第二个函数区别在于一个有_init
和_exit前缀,_init与_exit都是Linux内核的一个宏定义,使系统在初始化完成后释放该函数,并释放其所占的内存。因此它的优点是显而易见的,所以一般都建议使用第二种写法。
(1)在Linux内核中,所有标示_init的函数在连接时都放在.init.text这个区段内,此外。所有的_init函数在区段.initcall.init中还保存了一份函数的指针,在初始化时内核会通过这些指针调用这些_init函数,并在初始化完成后释放_init区段。
(2)和_init一样,_exit也可以使对应的函数在运行完成后自动的回收内存。
-
printk()函数
printk是内核态的信息
打印函数,功能和标准的printf类似,但是printk有信息打印级别。
int printk(const char*fmt,…)
消息的打印级别:
fmt–消息级别:
#define KERN_EMERG“<0>”/紧急事件消息,系统崩溃之前的提示,表示系统不可以用。
#define KERN_ALERT"<1>"/报告消息,表示必须立即采取措施。
#define KERN_CRIT"<2>"/临界条件,通常涉及严重的硬件或软件操作失败。
#define KERN_ERR"<3>"/错误条件,驱动程序常用KERN_ERR来报告错误。
#define KERN_WARNING"<4>"/警告条件,对可能出现的问题情况进行警告。
#define KERN_NOTICE"<5>"/正常但又重要的条件,用于提醒。常常用于安全相关的信息。
#define KERN_INFO"<6>"/提示信息,如驱动程序启动时,打印硬件信息。
#define KERN_DEBUG"<7>"/调试级别信息。
不同的级别用不同的字符串表示,数字越小级别越高。
在内核态使用printk()而在用户态使用printf()是因为,printk()函数是直接的使用了向终端写函数tty_write(),而printf()函数时调用write()系统调用函数向标准输出设备写,所以在用户态不能直接的使用printk()函数,而在内核态由于它已是特权级,所以无需系统调用来
改变特权级,因而能直接使用printk()函数。printk()是内核态的输出,在终端是看不见的,可以使用cat /var/log/messages,或者是dmesg命令来查看输出信息。 -
加载模块与卸载模块
modules_init(hello_init)告诉内核你编写的模块程序从哪里开始执行。
modules_exit(hello_exit)告诉内核你编写的模块程序从哪里离开。
参数就是卸载函数与注册函数的函数名 -
许可权限,
MOUDLES_LICENSE("DUAL BSD/GPL);
在linux2.6中可接受的LICENSE包括“GPL”,“GPL V2”,“GPL and additional rights”,“Dual MPL/GPL”,“Proprietary”。
可省略的模块声明与描述
MODULE_AUTHOR(“author”)//作者
MOUDLE_DESCIRIPTION(“description”);//描述
MOUDLE_VERSION(“version_string”);版本
MOUDEL_DEVICE_TABLE(“table_info”)//设备表
MOUDLE_ALIAS(“alternate_name”);//别名 -
Makefile文件
makefile是一种脚本文件,多用于文件的编译
make程序可以维护具有良好的依耐性的源文件,但是某些的文件发生改变时它能够自动识别出,并只对相应的文件进行编译。
,makefile文件由五部分组成:显示规则 含规则 变量的定义 makefile指示符和注释
一条Make的规则原型:
目标 …: 依赖 …
命令
…
makefile中可以使用Shell命令,例如pwd,uname
示例:
Makefile规则
ifneq ($(KERNELRELEASE),)
obj-m := hello.o//产生hello模块目标
else
KDIR := /lib/modules/2.6.18-53.el5xen/build//定义内核源文件目录
all:
make -C $(KDIR) M=$(PWD) modules//生成内核模块参数为内核源码目录以及模块所在目录
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers//清除生成的模块文件及其中间文件
endif
内核模块的操作过程:
1)在控制台输入make进行编译链接
2)正确后在控制台输入sudo insmod module.ko(加载模块)
3)在控制台输入dmesg查看结果
4)在控制台输入rmmod tiger(卸载模块)
5)输入dmesg 查看结果
6)make clean(去除中间生成的文件)