1. 内核模块简介
Linux 内核的 整 体结构 非 常 庞 大 ,其 包 含 的 组 件也 非 常多 , 如 何使用 需 要 的 组 件 呢?
方 法 一 : 把 所有的 组 件都 编译 进 内核 文 件, 即 :zImage 或bzImage , 但 这样会 导 致 两 个 问 题 : 一是 生 成的 内核 文 件 过大 ;二 是 如果 要 添 加 或 删 除某 个 组 件, 需 要重 新 编译 整 个内核
方法二:使用内核模块, 内核 文 件(zImage 或bzImage) 本 身 并不 包 含 某 组 件 , 而 是 在该 组 件 需要 被 使用的 时 候 ,以模块的方式动 态地 添 加 到 正 在 运行 的内核中
2. 内核 模块 具 有 如下特 点 :
? 模块 本 身 并 不 被编译 进 内核 文 件( zImage 或者bzImage )
? 可以 根 据 需 求 , 在内核 运行 期 间 动 态 的安 装 或 卸 载
3. 范 例(hello world)
#include <linux/init.h> #include <linux/module.h> static int hello_init(void) { printk(KERN_WARNING"Hello, world !\n"); return 0; } static void hello_exit(void) { printk(KERN_INFO "Goodbye, world\n"); } module_init(hello_init); module_exit(hello_exit);
模块加载函数(必需):安 装 模块 时 被系统 自 动 调 用的 函 数 , 通 过module_init 宏 来指定,以上例子,指定的加载的函数就是hello_init
模块卸载函数(必须):卸 载 模块 时 被系统 自 动 调 用的 函 数 , 通 过module_exit 宏 来 指定 ,以上例子,指定的加载的函数就是hello_exit
5.pritk函数说明
printk函数和printf的函数实现的功能是一样的,只不过一个运行在内核态,一个运行在用户态。
用printk函数打印时候,内核会根据日志级别,可能把消息打印到当前的控制台上, 这个控制台通常是一个字符模式的终端、一个串口打印机或是一个并口打印机。这些消息正常输出的前提是:日志输出级别小于控制台日志级别(在内核中数字越小优先级越高)。如果没有指定打印级别,默认的基本是<4>,即是KERN_WARNING级别,其定制可以再/kernelprintk.c中找到。
日志级别一共有8个级别,printk的日志级别定义如下(在include/linux/kernel.h中):
#define KERN_EMERG 0
通过读写/proc/sys/kernel/printk文件可读取和修改控制台的日志级别
#cat /proc/sys/kernel/printk
4 4 1 7
上面显示的4个数据分别对应控制台日志级别、默认的消息日志级别、最低的控制台日志级别和默认的控制台日志级别
可以修改上面的数据修改控制台的打印级别
备注:ubuntu 图形化界面是无法打印到控制台的, printk打印的是控制台,也就是/dev/console,而图形界面中的终端,其实是把stdin,stdout,stderr三个文件重定向了一下。所以printk是无法再图形界面中的终端中显示的,当然可以再/var/log/syslog或者用dmesg查看。
6. 模块makefile编写,makefile名字必须命名为Makefile,否则编译通过不了,下面这个makefile会被编译两次,第一次KERNELRELEASE为空,去运行
KDIR下面的makefile,接着会重新调用自己的Makefile,编译成功,产生hello.o,hello.ko两个文件
ifneq ($(KERNELRELEASE),) obj-m := hello.o//根据自己的模块修改 else KDIR := /lib/modules/2.6.32-21-generic/build//根据自己lib/modules/下面自己内核版本库填写 all: make -C $(KDIR) M=$(PWD) modules clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers endif
insmod hello.ko
可以通过lsmod | grep hello查看自己刚刚加载的内核模块
8. 卸载模块rmmod
rmmod hello
原文地址:http://blog.csdn.net/nanfenglei23/article/details/42294183