建立和运行模块
从hello world开始,一个完整的内核模块helloword.c如下:
#include<linux/init.h> //module_init 和 module_exit 的头文件
#include<linux/module.h> //这个头文件包含了许多符号与函数的定义,这些符号与函数多与加载模块有关
MODULE_LICENSE("Dual BSD/GPL"); //和开源软件带的license文件差不多,是个使用许可证,在module.h里有可定义的license。
static int hello_init(void) //尽量使用static,防止重名产生的编译错误
{
printk(KERN_ALERT"hello world !");//KERN_ALERT这个玩意是最高优先级,暂时当它是log里面的
//info级,避免某些地方打印不出来。
return 0;
}
static int hello_exit(void)
{
printk(KERN_ALERT"goodbye world !");//printk内核的打印函数,在 insmod 加载了它之后,
//模块被连接到内核并且可存取内核的公用符号,所以可用
}
module_init(hello_init);//加载到内核时调用
module_exit(hello_exit);//去除内核时调用
编译该文件之后会生成内核模,.ko文件,这个就是载入内核时的代码段,hello.ko。
依据你的系统用来递交消息行的机制, 你的输出可能不同. 特别地, 前面的屏幕输出是来自一个字符控制台; 如果你从一个终端模拟器或者在窗口系统中运行 insmod 和 rmmod, 你不会在你的屏幕上看到任何东西. 消息进入了其中一个系统日志文件中, 例如 /var/log/messages
使用insmod hello.ko可以加载该模块到内核,rmmod卸载该模块,卸载时会执行module_exit,相当于告诉内核我下班了,表给我打电话。
内核模块运行在内核空间,而不是应用程序一样运行在用户空间,意味着内核模块有着更高的特权级别,也意味着他们有着各自的地址空间。应用程序存在虚拟内存中,有一个很大的堆栈区。内核的堆栈很小,可能只有一个4096字节的页,而且还要与这个内核空间调用链共享这个堆栈。
模块的角色是扩展内核功能,模块化的代码在内核空间运行,驱动会进行两种任务,模块的里的一部分来作为系统调用,一部分用来负责中断处理。当一个应用冲虚发出一个系统调用或者硬件中断挂起时,执行系统调用的内核代码对进程来说是异步的,不和任何特别的进程有关。
内核编程要注意并发问题,因为在同一个时间不止一个程序能够调用同一个驱动,大部分设备能够终端处理器;中断处理异步运行,并且可能在你的驱动试图做其他事情的同一时间被调用,也可能字多个cpu上并发运行。所以内核代码,包括驱动代码,必须是可以重入的。这里必须要注意的是,内核模块在退出之前必须小心的释放所有的资源,否则会一直运行直到系统重启。
当你查看内核 API 时, 你会遇到以双下划线(__)开始的函数名. 这样标志的函数名通常是一个低层的接口组件, 应当小心使用. 本质上讲, 双下划线告诉程序员:" 如果你调用这个函数, 确信你知道你在做什么."
内核代码不支持浮点运算。