⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 (ldd) ch02-编写和运行模块(转载).txt

📁 献给ARM初学者
💻 TXT
📖 第 1 页 / 共 4 页
字号:

      er_symtab都会替换相应模块的符号表。
       
      如果你的模块不需要开放任何符号,而且你也不想把所有的东西都声明成static的,在i
      nit_module里加上下面一行语句就可以了。这次对register_symtab的调用通过注册一个
      空表覆盖了模块默认的符号表:
       
      (代码)
       
      如果源文件不想给堆叠在其上的模块提供什么接口,用上面那行语句隐藏所有的符号总
      是不错的。
       
      当模块从内 诵对厥 ,它所声明的所有公共符号也就自动?主符号表中注销了。不过是
      全局符号还是显式符号表,这一点都适用。
       
      初始化和终止
      正如前面已述,init_module向内核注册模块所能提供的所有设施。这里我使用了“设施
      ”,我的意思是指新功能,是一整个设备驱动程序或新软件抽象,是一个可以由应用程
      序使用的新功能。
       
      通过调用内核函数完成新设施的注册。传递的参数通常为一个指向描述这个新设施的数
      据结构和要注册的设施名称。这个数据结构通常会包含一些指向模块函数的指针,这就
      是模块体内的函数是被调用的机制。
       

       
      除了用来标别模块类别(如字符和块设备驱动程序)的“主”设施之外,模块还可以注
      册如下项目:
       
      其他设备
       
             由于这类设施仅仅用于总线型鼠标,这些设备曾一度称为鼠标设备。它们都是些
      不完整的设备,通常要比那些功能健全的设备简单。
       
      串行端口
       
             可以在运行时向系统里加入串口设备驱动程序;这也是支持PCMCIA调治解调器的
      机制。
       
      行律
       
             行律是处理终端数据流的软件层。模块可以注册新行律,以非标准方式处理终端
      事务。例如,模块kmouse就使用行律从串口鼠标中偷取数据。
       
      终端设备驱动程序
       
             终端设备驱动程序一组实现终端底层数据处理的函数。控制台和串口设备驱动程
      序为了创建终端设备,它们都要注册自己的驱动程序。而多端口串口则有自己的驱动程

      序为了创建终端设备,它们都要注册自己的驱动程序。而多端口串口则有自己的驱动程
      序。
       
      /proc文件
       
             /proc包含了用来访问内核信息的文件。由于它们也可以用来调试,第4章的“使
      用/proc文件系统”将讲解/proc文件。
       
      二进制文件格式
       
             对于每个可执行文件,内核扫描“二进制文件格式”列表并按相应的格式执行它
      。模块可以实现新的格式,Java模块就是这样做的。
       
      Exec域
       
             为了提供与其他流行Unix系统的兼容,必须修改内核的某些内部表格。一个“执
      行域”就是一组从其他操作系统约定到Linux系统的映射。例如,模块可以定义执行SCO
      二进制文件的执行域。
       
      符号表
       
             这个已在前面的“注册符号表”小节中介绍了。
       

       
      上面这些项目都不是前一章所考虑的设备类型,而且都支持那些通常集成到驱动程序功
      能中的设施,如/proc文件和行律。之所以鼠标和其他设备驱动程序都没有象“完整”字
      符设备那样管理,这主要是为了方便。过一会儿,当你读到第3章“字符设备”的“主从
      设备号”小节时,原因就明了了。
       
      还可以将模块注册为某些驱动程序的附件,但这样做就太特殊了,这里就不作讨论了;
      它们都使用了“注册符号表”中讲到的堆叠技术。如果你想做更深一步的探究,你可以
      在内核源码中查查register_symtab,并且找找不同驱动程序的入口点。大部分注册函数
      都是以register_开始的,这样你就可以用“register_”在/proc/ksyms找找它们了。
       
      init_module中的错误处理
      如果你注册时发生什么错误,你必须取消失败前所有已完成的注册。例如,如果系统没
      有足够内存分配新数据结构时,可能会发生错误。尽管这不太可能,但确实会发生,好
      的程序代码必须为处理这类事件做好准备。
       
      Linux不为每个模块保留它都注册了那些设施,因此当init_module在某处失败时,模块
      必须统统收回。如果你在注销你已经注册的设施时失败了,内核就进入一种不稳定状态
      :卸载模块后,由于它们看起来仍然是“忙”的,你再也不能注册那些设施了,而且你
      也无法注销它们了,因为你必须使用你注册时的那个指针,而你不太可能得到那个指针
      了。恢复这种情况非常复杂,通常,重新启动是最好的解决方法。
       
      我建议你用goto语句处理错误恢复。我讨厌使用goto,但以我个人来看,这是一个它有

      我建议你用goto语句处理错误恢复。我讨厌使用goto,但以我个人来看,这是一个它有
      所做为的地方(而且,是唯一的地方)。在内核里,通常都会象这里处理错误那样使用g
      oto。
       
      下面这段样例在成功和失败时都能正确执行:
       
      (代码)
       
      返回值(err)是一个错误编码。在Linux内核里,错误编码是一个负值,在<linux/errn
      o.h>中定义。如果你不使用其他函数返回的错误编码而要生成自己的,你应该包含<linu
      x/errno.h>,这样就可以使用诸如-ENODEV,-ENOMEM之类的符号值。总是返回相应的错
      误编码是种非常好的习惯,因为这样一来用户程序就利用perror或相似的方法把它们转
      换成有意义的字符串了。
       
      很明显,cleanup_module要取消所有init_module中完成的注册。
       
      (代码)
       
      使用计数
      为了确定模块是否可以安全地卸载,系统为每个模块保留了一个使用计数。由于模块忙
      的时候是不能卸载模块的,系统需要这些信息:当文件系统还被安装在系统上时就不能
      删除这个文件系统类型,而且你也不能在还有程序使用某个字符设备时就去掉它。
       

      如果忘了更新使用计数,你就不能再卸载模块了。在开发期间这种情况很可能发生,所
      以你一定要牢记。例如,如果进程因你的驱动程序引用了NULL指针而终止,驱动程序就
      不可能区关闭设备,使用计数也就无法回复到0。一种可能的解决方法就是在调试期间完
      全不使用使用计数,将MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT重新定义为空操作。另
      一个解决方法就是利用其他方法将计数强制复位为0(在第5章的“使用ioctl参数”小节
      中介绍)。在编写成品模块时,决不能投机取巧。然而在调试时期,有时候忽略一些问
      题可以节省时间,是可以接受的。
       
      使用计数的当前值可以在/proc/modules中每一项的第3个域中找到。这个文件显式系统
      中当前共加载了那些模块,每一项对应一个模块。其中的域包括,模块名,模块使用的
      页面数和当前使用计数。这是一个/proc/modules样例:
       
      (代码)
       
      (autoclean)标志表明模块由kerneld管理(见第11章)。较新的内核中又加入了一些新
      的标志,除了一件事外,/proc/modules的基本结构完全相同:在内核2.1.18和更新的版
      本中,长度用字节计而不是页面计。
       
      卸载
      要卸载一个模块就要使用rmmod命令。由于无需连编,它的任务远比加载简单。这个命令
      调用系统调用delete_module,如果使用计数为0它又调用模块的cleanup_module。
       
      cleanup_module实现负责注销所有由模块已经注册了的项目。只有符号表是自动删除的

      cleanup_module实现负责注销所有由模块已经注册了的项目。只有符号表是自动删除的
       
      使用资源
      模块不使用资源是无法完成自己的任务的,这些资源包括内存,I/O端口和中断,如果你
      要用DMA控制器的话,还得有DMA通道。
       
      做为一个程序员,你一定已经习惯了内存分配管理,在这方面编写内核代码没什么区别
      。你的程序使用kmalloc分配内存,使用kfree释放内存。除了kmalloc多一个参数,优先
      级,外,它们和malloc,free很相似。很多情况下,用优先级GFP_KERNEL就可以了。缩
      写GFP代表“Get Free Page(获取空闲页面)。”
       
      与此不同,获取I/O端口和中断乍听起来怪怪的,因为程序员一般同用显式的指令访问它
      们,不必让操作系统了解这些。“分配”端口和中断与分配内存不同,因为内存是从一
      个资源池中分配,并且每个地址的行为是一样的;I/O端口都各有自己的作用,而且驱动
      程序需要在特定的端口上工作,而不能随便使用某个端口。
       
      端口
      对于大多数驱动程序而言,它们的典型工作就是读写端口。不管是初始化还是正常工作
      的时候,它们都是这样的。为了避免其他驱动程序的干扰,必须保证设备驱动程序以独
      占方式访问端口――如果一个模块探测因自己的硬件而写某个端口,而恰巧这个端口又
      是属于另一个设备的,这之后一定会发生点怪事。
       

       
      为了防止不同设备间的干扰,Linux的开发者决定实现端口的请求/释放机制。然而,未
      授权的对端口的访问并不会产生类似于“段失效”那样的错误――硬件无法支持端口注
      册。
       
      从文件/proc/ioports可以以文本方式获得已注册的端口信息,就象下面的样子:
       

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -