第二章 构造和运行模块
Hello World模块
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL")
static int hello_init(void)
{
printk(KERN_ALERT "Hello,World\n");
return 0;
}
static void hello_exit(void)
{
printk(KENR_ALERT "Goodbye,cruel world\n");
}
module_init(hello_init);
module_eixt(hello_exit);
1.内核模块与应用程序的不同:大多数小规模及中规模应用程序是从头到尾执行单个任务,而模块只是预先注册自己以便服务于将来的某个请求,然后它的初始化函数就立即结束。应用程序在退出时,可以不管资源的释放或者其他清除工作,但模块的退出函数却必须仔细撤销初始化函数所做的一切,否则,在系统重新引导之前某些东西就会残留在系统中。
2.用户空间和内核空间:模块运行在所谓的内核空间里,而应用程序运行在所谓的用户空间中。当处理器存在多个级别时,Unix使用最高级别和最低级别。在Unix当中,内核运行在最高级别(超级用户态),在这种级别中可以进行所有的操作。而应用程序运行在最低级别(用户态),在这个级别中,处理器控制着对硬件的直接访问以及对内存的非授权访问。每当应用程序执行系统调用或者硬件中断挂起时,Unix将执行模式从用户空间切换到内核空间。一个驱动程序要执行两类任务:模块中某些函数作为系统调用的一部分而执行,而其他函数则负责中断处理。
3.内核中的并发:编写内核模块时要铭记:同一时刻,可能会有许多事情正在发生。
4.应用程序在虚拟内存中布局,并具有一块很大的栈空间。当然,栈是用来保持函数调用历史以及当前活动函数的自动变量的。而相反的是,内核具有非常小的栈,它可能只和4096字节大小的页那样小。我们自己的函数必须和整个内核空间调用链一同共享这个栈。因此,声明大的自动变量不是一个好主意,如果我们需要大的结构,则应该在调用时动态分配该结构。