[2]Linux内核模块

[1]内核模块特点:

内核模块具有如下特点:

  • 模块本身并不被编译进内核文件(zImage或者bzImage);
  • 可以根据需求,在内核运行期间动态地安装或卸载。

[2]内核模块范例:

#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宏来指定。
  • 模块卸载函数(必需):卸载模块时被系统自动调用的函数,通过module_exit宏来指定。

内核模块Makefile:

ifneq ($(KERNELRELEASE),)

obj-m := hello.o
hello-objs := main.o add.o

else

KDIR := /lib/modules/2.6.35.6-45.fc14.i686/build
all:
         make-C $(KDIR) M=$(PWD) modules
clean:
         rm-f *.ko *.o *.mod.o *.mod.c *.symvers
endif

其中:

  • KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量。ifneq($(KERNELRELEASE),) 判断该变量是否为空。在第一次读取执行此Makefile时,KERNELRELEASE没有被定义,所以make将读取执行else之后的内容。
  • KDIR := /lib/modules/2.6.35.6-45.fc14.i686/build是给KDIR这个变量赋值,即Linux的内核源码。本机可以直接使用KDIR := /lib/modules/ $(shell uname -r) /build。
  • 当make的目标为all时,-C (KDIR)MakefileM= (PWD)表明然后返回到当前目录继续读入、执行当前的Makefile。当从内核源码目录返回时,KERNELRELEASE已被被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。

[3]内核模块安装:

  • 加载 insmod (insmod hello.ko)
  • 卸载 rmmod (rmmod hello)
  • 查看 lsmod
  • 加载 modprobe (modprobe hello):
    modprobe 如同 insmod, 也是加载一个模块到内核。它的不同之处在于它会根据文件件/lib/modules/

[4]模块可选信息:

  1. 许可证申明:
    宏MODULE_LICENSE用来告知内核, 该模块带有一个许可证,没有这样的说明,加载模块时内核会抱怨。
    有效的许可证有”GPL“、”GPLv2”、”GPL and additional rights”、”Dual BSD/GPL”、”Dual MPL/GPL”和
    “Proprietary”。
  2. 作者申明(可选):
    MODULE_AUTHOR(“Simon Li”);
  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中出现的该参数。
      内核参数相关例子详见:

[5]内核符号导出:

即一个内核模块使用另一个内核模块中定义的函数或者数据。/proc/kallsyms 记录了内核中所有导出的符号的名字与地址。
内核符号的导出使用:EXPORT_SYMBOL(符号名);EXPORT_SYMBOL_GPL(符号名);其中EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块。导出之后其他模块才能访问该符号。

[6]内核打印:

printk的用法:内核通过 printk() 输出的信息具有日志级别,日志级别是通过在 printk() 输出的字符串前加一个带尖括号的整数来控制的,如 printk(“<6>Hello, world!\n”);。内核中共提供了八种不同的日志级别,在 linux/kernel.h 中有相应的宏对应。

#define KERN_EMERG   "<0>"   /* system is unusable */
#define KERN_ALERT   "<1>"   /* action must be taken immediately */
#define KERN_CRIT     "<2>"   /* critical conditions */
#define KERN_ERR      "<3>"   /* error conditions */
#define KERN_WARNING "<4>"   /* warning conditions */
#define KERN_NOTICE   "<5>"   /* normal but significant */
#define KERN_INFO     "<6>"   /* informational */
#define KERN_DEBUG    "<7>"   /* debug-level messages */

所以 printk() 可以这样用:printk(KERN_INFO “Hello, world!\n”);。未指定日志级别的 printk() 采用的默认级别是 DEFAULT_MESSAGE_LOGLEVEL,这个宏在 kernel/printk.c 中被定义为整数 4,即对应KERN_WARNING。
在 /proc/sys/kernel/printk 会显示4个数值(可由 echo 修改),分别表示当前控制台日志级别、未明确指定日志级别的默认消息日志级别、最小(最高)允许设置的控制台日志级别、引导时默认的日志级别。当 printk() 中的消息日志级别小于当前控制台日志级别时,printk 的信息(要有\n符)就会在控制台上显示。但无论当前控制台日志级别是何值,通过 /proc/kmsg (或使用dmesg)总能查看。另外如果配置好并运行了 syslogd 或 klogd,没有在控制台上显示的 printk 的信息也会追加到 /var/log/messages.log 中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值