第一个最简单的内核模块什么事都不做,仅在内核加载时打印 “ hello,kernel world ” ,在模块卸载时打印 “ goodbye,kernel world” ,虽然什么事情也做不了,但这已经是一个可以加载如内核的简单模块了。
在新建hello文件夹下建立两个文件:
hello.c Makefile
hello.c 内容如下:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init hello_init(void) //模块的入口,当此模块被加载的时候调用
{
printk(KERN_INFO "hello , kernel world\n");
return 0;
}
static void __exit hello_exit(void) //模块的出口,当模块被卸载的时候调用
{
printk(KERN_INFO " goodbye,kernel world\n ");
}
module_init(hello_init);//指定模块入口函数
module_exit(hello_exit);//指定模块出口函数
//一些基本的模块信息,可以通过 "modinfo 模块名" 查看
MODULE_AUTHOR("xxxx");
MODULE_DESCRIPTION("A simple Hello World Module");
MODULE_ALIAS("a simplest module");
备注:
1. 引入一些思考,__init __exit 的作用是什么? 以下是完整的宏定义:
#define __init __attribute__((__section__(".init.text") ) )
#define __exit __attribute__((__section__(".exit.text")) )
另外,数据部分同样包含以下两个宏。
#define __initdata __attribute__((__section__(".init.data")))
#define __exitdata __attribute__((__section__(".ext.data")))
以上四个宏将指定了函数或者数据需要被编译器指定的section位置(代码段,数据段,==)。
init.text和.init.data这两个section段将会在模块被加载后释放掉。
.exit.text.和.exit.data这两个section.段将会在模块被卸载后释放掉。
所以程序编写过程中可以把某些合适的数据和函数指定到这四个seciton,以达到节约内存的目的。
如果有另一个问题,什么是section? ,这将涉及到一些ELF文件格式和编译方面的知识,读者可以google之。
2.printk函数
内核空间不能使用标准函数库,为了获得内核的打印输出,我们可以使用printk函数,该函数由以下几个级别定义:
#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(级别 "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 中。
Makefile 内容如下:
obj-m:=hello.o
备注:
如果内核模块由多个文件组成那应该怎么写这个Makefile呢? 答案是 : obj-m:=hello.o good.o xxx.o ...
$ make -C /usr/src/linux M=`pwd` modules
# insmod hello.ko
# rmmod hello
# dmesg | tail -10
[ 2840.707373] hello,kernel world
[ 3054.477649] goodbye,kernel world