Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1281967
  • 博文数量: 168
  • 博客积分: 3483
  • 博客等级: 中校
  • 技术积分: 1696
  • 用 户 组: 普通用户
  • 注册时间: 2006-02-06 13:17
文章分类

全部博文(168)

文章存档

2015年(6)

2014年(9)

2013年(47)

2012年(11)

2011年(13)

2010年(18)

2009年(11)

2008年(42)

2007年(11)

分类: LINUX

2007-11-11 23:03:22

模块的链接和取消

>   用户可以通过执行insmod外部程序把一个模块链接到正在运行的内核中。该程序执行以下操作:

1   从命令行中读取要链接模块名。

2   确定模块对象代码所在的文件在系统目录树中的位置。对应的文件通常都是在/lib/modules的某个子目录中。

3   从磁盘读入存有模块目标代码的文件。

4   调用init_module()系统调用,传入参数:存有模块目标代码的用户态缓冲区地址、目标代码长度和存有insmod程序所需参数的用户态内存区。

5   结束。

 

sys_init_module()服务例程是实际执行者,主要步骤如下:

1   检查是否允许用户链接模块(当前进程必须具有CAP_SYS_MODULE权能)。只要给内核增加功能,而它可以访问系统中的所有数据和进程,安全就是至关重要的。

2   为模块目标代码分配一个临时内存区,然后拷入作为系统调用第一个参数的用户态缓冲区数据。

3   验证内存区中的数据是否有效表示模块的ELF对象,如果不能,则返回错误码。

4   为传给insmod程序的参数分配一个内存区,并存入用户态缓冲区的数据,该缓冲区地址是系统调用传入的第三个参数。

5   查找modules链表,以验证模块未被链接。通过比较模块名(module对象的name字段)进行这一检查。

6   为模块核心可执行代码分配一个内存区,并存入模块相应节的内容。

7   为模块初始化代码分配一个内存区,并存入模块相应节的内容。

8   为新模块确定模块对象地址,对象映像保存在模块ELF文件的正文段gnu.linkonce.this_module一节,而模块对象保存在第6步中的内存区。

9   将第67步中分配的内存区地址存入模块对象的module_codemodule_init字段。

10  初始化模块对象的modules_which_use_me链表。当前执行CPU的计数器设为1,而其余所有的模块引用计数器设为0

11  根据模块对象许可证类型设定模块对象的license_gplok标志。

12  使用内核符号表与模块符号表,重置模块目标码。这意味着用相应的逻辑地址偏移量替换所有外部与全局符号的实例值。

13  初始化模块对象的symsgpl_syms字段,使其指向模块导出的内存中符号表。

14  模块异常表(参见第十章“异常表”一节)保存在模块ELF文件的__ex_table一节,因此它在第6步中已拷入内存区,将其地址存入模块对象的extable字段。

15  解析insmod程序的参数,并相应地设定模块变量的值。

16  注册模块对象mkobj字段中的kobject对象,这样在sysfs特殊文件系统的module目录中有一个新的子目录(参见第十三章“kobject”一节)。

17  释放第2步中分配的临时内存区。

18  将模块对象追加到modules链表。

19  将模块状态设为MODULE_STATE_COMING

20  如果模块对象的init方法以定义,则执行它。

21  将模块状态设为MODULE_STATE_LIVE

22  结束并返回0(成功)。

 

为了取消模块的链接,用户需要调用rmmod外部程序,该程序执行以下操作:

1   从命令行中读取要取消的模块的名字。

2   打开/proc/modules文件,其中列出了所有链接到内核的模块,检查待取消模块是否有效链接。

3   调用delete_module()系统调用,向其传递要卸载的模块名。

4   结束。

 

相应的sys_delete_module()服务例程执行以下操作:

1   检查是否允许用户取消模块链接(当前进程必须具有CAP_SYS_MODULE权能)。

2   将模块名存入内核缓冲区。

3   modules链表查找模块的module对象。

4   检查模块的modules_which_use_me依赖链表,如果非空就返回一个错误码。

5   检查模块状态,如果不是MODULE_STATE_LIVE,就返回错误码。

6   如果模块有自定义init方法,函数就要检查是否有自定义exit方法。如果没有自定义exit方法,模块就不能卸载,那么返回一个退出码。

7   为了避免竞争条件,除了运行sys_delete_module()服务例程的CPU外,暂停系统中所有CPU的运行。

8   把模块状态设为MODULE_STATE_GOING

9   如果所有模块引用计数器的累加值大于0,就返回错误码。

10  如果已定义模块的exit方法,则执行它。

11  modules链表删除模块对象,并且从sysfs特殊文件系统注销该模块。

12  从刚才使用的模块依赖链表中删除模块对象。

13  释放相应内存区,其中存有模块可执行代码、module对象以及有关符号和异常表。

14  返回0(成功)。

 

根据需要链接模块

>   模块可以在系统需要其所提供的功能时自动进行链接,之后可以自动删除。

 

>   例如,假设MS-DOS文件系统既没有被静态链接,也没有被动态链接。如果用户试图装载MS-DOS文件系统,那么mount()系统调用通常就会失败,返回一个错误码,因为MS-DOS没有被包含在已注册文件系统的file_system链表中。然而,如果内核已配置为支持模块的动态链接,那么Linux就试图链接MS-DOS模块,然后再扫描已经注册过的文件系统的列表。如果该模块成功的被链接,那么mount()系统调用就可以继续执行,就好像MS-DOS文件系统从一开始就存在一样。

 

modprobe程序

>   为了自动链接模块,内核要创建一个内核线程执行modprobe外部程序(注2),该程序要考虑由于模块依赖所引起的所有可能因素。模块依赖在前面介绍过:一个模块可能需要一个或多个其他模块,这些模块又可能需要其他模块。例如MS-DOS模块需要另外一个名为fat的模块,该模块包含基于文件分配表(File Allocation TableFAT)的所有文件系统所通用的一些代码。因此fat模块还不在系统中,那么在系统请求MS-DOS模块时,fat模块也必须被动态链接到内核中。对模块依赖进行解析以及对模块进行查找的操作最好都在用户态中实现,因为这需要查找和访问文件系统中的模块对象文件。

2:这是内核依赖于外部程序的少数例子之一

>   modprobe外部程序和insmod类似,因为它链接在命令行中指定的一个模块。然而,modprobe还可以递归地链接命令行中模块所使用的所有模块。例如,如果用户调用modprobe来链接MS-DOS模块,那么在需要的时候,modprobe就会在MS-DOS模块之后链接fat模块。实际上,modprode只是检查模块依赖关系,每个模块的实际的链接工作是通过创建一个进程并执行insmod命令来实现的。

>   modprobe又是如何知道模块间的依赖关系的呢?另外一个称为depmod的外部命令在系统启动时被执行。该程序查找为正在运行的内核而编译的所有模块,这些模块通常存放在/lib/modules目录下。然后他就把所有的模块间依赖关系写入一个名为modules.dep的文件。这样,modprobe就可以对该文件中存放的信息和/proc/modules文件产生的链接模块链表进行比较。

 

request_module()函数

>   在某些情况下,内核可以调用request_module()函数来试图自动链接一个模块。

>   在次考虑用户试图装载MS-DOS文件系统的情况。如果get_fs_type()函数发现这个文件系统还没有注册,就调用request_module()函数,希望MS-DOS已经被编译为一个模块。

>   如果request_module()函数成功地链接所请求的模块,get_fs_type()就可以继续执行,仿佛这个模块一直都存在一样。当然,并非所有的情况都是如此;在我们的例子中,MS-DOS模块可能根本就没有被编译。在这种情况下,get_fs_type()返回一个错误码。

>   request_module()函数接收要连接的模块名作为参数。该函数调用kernel_thead()来创建一个新的内核线程并等待,直到这个内核线程结束为止。

>   而此内核线程又接收待链接模块名作为参数,并调用execve()系统调用以执行modprobe外部程序(注3),向其传递模块名。然后,modprobe程序真正地链接所请求的模块以及这个模块所依赖的任何模块。

3:由exec_modprobe()执行的程序和路径名可以通过向/proc/sys/kernel/modprobe文件写入而自定义。

阅读(1947) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~