2.1. 你好,世界(第一部分):最简单的模块

当第一个穴居人程序员在第一个洞穴计算机的墙壁上凿刻第一个程序时,那是一个用羚羊图片绘制字符串 “Hello, world” 的程序。罗马的编程教科书以 “Salut, Mundi” 程序开始。我不知道打破这个传统的人会怎么样,但我认为最好还是不要知道。我们将从一系列 “你好,世界” 程序开始,这些程序演示了编写内核模块基础知识的不同方面。

这是最简单的模块。先不要编译它;我们将在下一节介绍模块编译。

示例 2-1. hello-1.c

/*  
 *  hello-1.c - The simplest kernel module.
 */
#include <linux/module.h>	/* Needed by all modules */
#include <linux/kernel.h>	/* Needed for KERN_INFO */

int init_module(void)
{
	printk(KERN_INFO "Hello world 1.\n");

	/* 
	 * A non 0 return means init_module failed; module can't be loaded. 
	 */
	return 0;
}

void cleanup_module(void)
{
	printk(KERN_INFO "Goodbye world 1.\n");
}

内核模块必须至少有两个函数:一个 “start”(初始化)函数,名为init_module()它在模块被 insmoded 到内核时调用,以及一个 “end”(清理)函数,名为cleanup_module()它在模块被 rmmoded 之前调用。实际上,从内核 2.3.13 开始,情况发生了变化。现在,您可以为模块的 start 和 end 函数使用任何您喜欢的名称,您将在 2.3 节 中学习如何做到这一点。事实上,新方法是首选方法。但是,许多人仍然使用init_module()cleanup_module()作为它们的 start 和 end 函数。

通常,init_module()要么向内核注册一个处理程序,要么用自己的代码(通常是执行某些操作然后调用原始函数的代码)替换内核函数之一。cleanup_module()函数应该撤销init_module()所做的事情,以便可以安全地卸载模块。

最后,每个内核模块都需要包含linux/module.h。我们需要包含linux/kernel.h仅仅是为了printk()日志级别的宏展开,KERN_ALERT,您将在 2.1.1 节 中学习到。

2.1.1. 介绍printk()

尽管您可能认为,printk()并非旨在向用户传达信息,即使我们在 hello-1 中正是为此目的使用它!它碰巧是内核的日志记录机制,用于记录信息或发出警告。因此,每个printk()语句都带有一个优先级,即您看到的<1>KERN_ALERT。共有 8 个优先级,内核为它们提供了宏,因此您不必使用神秘的数字,您可以在linux/kernel.h中查看它们(及其含义)。如果您未指定优先级,则将使用默认优先级,DEFAULT_MESSAGE_LOGLEVEL

花时间阅读优先级宏。头文件还描述了每个优先级的含义。在实践中,不要使用数字,例如<4>始终使用宏,例如.

KERN_WARNING如果优先级低于int console_loglevel,消息将打印在您当前的终端上。如果 syslogdklogd 都在运行,则消息也将附加到/var/log/messagesKERN_ALERT,无论它是否打印到控制台。我们使用高优先级,例如printk(),以确保