对于内核模块(kernel module)编程,我们需要了解并注意下面的一些事项:
-
- 中小型的应用程序包含一个完整的task,而在kernel模块中只是注册以便为未来请求提供服务,他是为以后的触发进行准备。模块是事件驱动方式。换言之,他的初始话部分程序很快就结束,不应该出现等待的状态。
- 一般的应用程序,在结束的时候,不需要仔细地去是否资源,因为进程终结,系统会自动进行。但是在kernel模块中,exit需要仔细地去删除所有init中建立或分配的资源,否则直到系统结束(例如reboot)这些资源才会释放。
- 在kernel模块设计中,我们要注意并行执行的思想。在普通应用程序中,除了多线程,都是顺序执行,不需要考虑环境的变化,但是kernel module中,我们应注意同时并行,同时出发的情况。例如驱动模块。即便是一个很简单的模块,并行思想也需要在设计时考虑到。
- 内核stack空间是有限的,因此尽量使用动态分配的方式分配较大的数据空间,并及时回收。
- 对于low-level的接口,在函数的前面加上"__",表示这是个很底层的函数需要谨慎使用。
- kenel code不支持浮点运算。
- 如果我们需要一个模块同时能在多个版本中工作,维护源代码的一致性,需要加 上#ifdef之类的定义,可以使用linux/utsrelease.h下面的UTS_RELESE,liux/version.h下面的 LINUX_VERSION_CODE或者KERNEL_VERSION。后两者将他们值转换为0x的16进制格式 ,比较清晰。我们说的只是源代码的一致性,仍需要分别编译。对于这种情况,根据我们的编程思想,我们将low-level的代码,需要通过宏来指定的部分,和high-level的代码,相对和OS版本独立,或者能够通过底层代码屏蔽差异的部分分开,这样程序变得易读,容易维护。
- 在初始化过程中,一旦注册,就可能慢上被调用(call),即使初始化函数没有执行完,因此要在注册前将必要的初始化完成。初始化失败,但是已经注册也有 可能被调用,这些我们都必须考虑。简单的处理可以设置tag,当初始化成功后,tag置位,触发函数检查tag,以判断是否真正初始化结束。
相关链接:
我的与kernel module有关的文章