Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1871663
  • 博文数量: 283
  • 博客积分: 10141
  • 博客等级: 上将
  • 技术积分: 2931
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-21 14:33
文章分类

全部博文(283)

文章存档

2013年(2)

2012年(2)

2011年(17)

2010年(36)

2009年(17)

2008年(18)

2007年(66)

2006年(105)

2005年(20)

分类: LINUX

2005-12-27 09:23:22

嗯,一份学习笔记...

 

Linux kernel-2.4下的可装载模块

1.一个Hello world模块

#define MODULE

#define __KERNEL__

#include
#include
MODULE_LICENSE("GPL");
//#define UTS_RELEASE "2.4.20-8"
//#define LINUX_VERSION_CODE 132116
//#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + c)
int init_module(void)
{
        printk(KERN_EMERG"Hello World! ");
        return 0;
}
void cleanup_module(void)
{
        printk(KERN_EMERG"Goodbye, my nice world! ");
}
解释:
1)printk是否打印消息到终端,与syslogd,klogd和printk的参数有关。
2)显然的问题,模块链入内核空间而不是用户空间,所以使用内核符号导出表,所以头文件和一些函数都有所不同。
注意
 1)编译链接时,linux/kernel.h module.h是当前跑的内核代码文件,而gcc默认是去/usr/include下面找的。对于一些发行版,比如RH9.0,这两个文件中对内核版本的定义是不同的。
2)可以重新编译内核让内核不对加载的模块进行版本检查,但那样容易带来更多的问题...

2. 核心模块与应用程序的几个区别

 0.操作系统理论的一个基础:
       模块运行在kernel space,而应用程序运行在user space中
1.应用程序执行某个任务,而模块是向内核预先注册自己以便将来服务于某个请求。
2.应用程序可调用它未定义的函数(链接过程能够解析外部引用),模块仅被链接到内核,因此它只能调用由内核导出的函数,
3.模块的源文件中不包含通常的头文件,但某些应用程序也可能包含内核的头文件(早期版本的libc的头文件树指向实际安装的
   内核源代码的头文件)
4.并发问题问题更复杂:系统支持多进程并发、硬件的复杂性(eg: SMP系统)等要求内核代码的可重入性,必须能够同时运行在多个上下文中。

 

3. 构建模块的几个简单注意事项

1.在包含任何头文件前定义__KERNEL__,没有这个符号,模块就不能使用内核头文件中针对内核的特殊内容。
2.除模块直接链入内核外,要在包含前定义MODULE。
3.如果为一个SMP系统编译模块,要在包含内核头文件前定义 __SMP__
4.为编译器指定-O选项。如果禁用优化选项,gcc就不会展开内联函数,而使用比-O2更高的优化级别时,编译器会将没有声明为内联函数的函数当作内联函数来编译,而内核中某一些函数被调用时需要一个标准的栈布局。
5.注意避免名字空间污染。
6.参考内核文档,使用合适的编译器与编译选项,建议使用-Wall
7.版本依赖,平台相关……
======================================================
1.Insmod的大致工作流程
a.打开待安装模块并读入到用户空间
b.通过系统调用sys_query_module()向内核询问模块内部无法落实的、要链接到内核的符号的地址,然后将那些符号在内核映象中的地址填入模块内相关的指令及数据结构中。这些符号包括内核本身的符号与已经安装的其他模块的符号。
c.通过系统调用sys_create_module()在内核中创建一个module数据结构,并且“预订”内核空间。
d.通过系统调用sys_init_module()把用户空间中完成了b步内容的模块映象装入内核空间,然后调用模块中的init_module()函数。模块的init_module()函数用于向内核“登记”本模块中的一些包含函数指针的数据结构。
2.rmmod的工作
a.调用sys_delete_module()将模块的module结构释放,同时释放模块映像所占的内核空间。
b.调用模块中的cleanup_module(),在这个函数中向内核撤销对本模块提供的数据结构的“登记”,告诉内核别再访问这些数据结构以及函数指针。
几个系统函数的主要调用关系
sys_query_module()
   ->lock_kernel()
   ->get_mod_name()
      从用户空间复制模块名
   ->find_module()
      在队列module_list中寻找相应的module结构,module_list是内核中的全局变量,指向所有已经安装的模块的module结构链接成的链。
   ->根据参数不同调用qm_modules(),qm_deps(),qm_refs(), qm_symbols(),qm_info
   ->unlock_kernel()

sys_create_module()
   ->capable(CAP_SYS_MODULE)
      检查当前进程是否有权在内核中创建模块
   ->lock_kernel()
   ->get_mod_name()
      从用户空间复制模块名
   ->find_module()
      在队列module_list中寻找相应的module结构
   ->module_map()
      在i386中其实就是vmalloc(),用于初始化模块的module结构,并将其链入module_list前端。
   ->unlock_kernel()
  
sys_init_module()
   ->capable(CAP_SYS_MODULE)
   ->lock_kernel()
   ->get_mod_name()
   ->find_module()
   …检查模块头大小   ->get_user()…
   …把内核中的module结构暂时保存在堆栈中作后备,然后从用户空间复制module结构。
   ->调用mod_bound()检查用户提供的指针所指的对象是否在模块的边界内
   ->宏操作mod_member_present()
      检查用户空间的module结构是否包括can_unload字段
   ->检查模块依赖关系,如果失败,则装载模块失效,应用程序(如insmod要调用delete_module()从module_list中删除相应的module结构)。
   ->将每个module_ref结构链接到所依赖模块的refs队列中,交将结构中的ref指针指向正在安装中的module结构。简单地说,就是建立模块依赖关系。
   ->put_mod_name()
    释放临时缓冲区
   ->执行模块的init_module函数,允许为空。
   (正常)->unlock_kernel()…
sys_delete_module()
   ->capable(CAP_SYS_MODULE)
   ->lock_kernel()
   ->get_mod_name()
   ->find_module()
   ->put_mod_name()
   ->判断有没有模块依赖于目标模块
   ->spin_lock()
   ->__MOD_IN_USE()操作
      判断目标模块是否在使用中,如果不在使用
   ->spin_unlock(),free_module()拆除模块
      如果正在使用,->spin_unlock();     ……

阅读(2243) | 评论(0) | 转发(0) |
0

上一篇:不是开篇的开篇

下一篇:今天考试

给主人留下些什么吧!~~