Linux内核模块编程详解

  1. 转载

  2. L什么是内核模块

    (1)内核模块是具有独立功能的程序,它可以被单独的编译,但是不能独立的运行,他必须连接到内核作为内核的一部分在内核空间中运行。
    (2)模块编程与内核版本密切相关,由于不同的内核版本中某些函数的函数名会有变化。因此模块编程也可称作内核的编程。
    (3)特点:模块本身不被编译进内核映像,从而控制了内核的大小;模块一旦被加载就和内核中的其他部分完全一样。

  3. 用户层编程与内核模块编程的区别:
    在这里插入图片描述

  4. 编写内核模块
    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也可以使对应的函数在运行完成后自动的回收内存。

  1. 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命令来查看输出信息。

  2. 加载模块与卸载模块
    modules_init(hello_init)告诉内核你编写的模块程序从哪里开始执行。
    modules_exit(hello_exit)告诉内核你编写的模块程序从哪里离开。
    参数就是卸载函数与注册函数的函数名

  3. 许可权限,
    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”);//别名

  4. 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(去除中间生成的文件)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值