向下之旅(二十三):模块

  Linux内核是模块化组成的,它允许内核在运行时动态的向其中插入或从中删除代码。其中包括相关的子例程、数据、函数入口和函数出口被一并组合在一个单独的二进制镜像中,即可装载内核模块中,或被简称为模块。

  构建模块

  在2.6内核中,采用了新的"kbulid"构建系统,现在构建模块相比从前更加容易。构建过程中的第一步是决定在哪里管理模块代码。可以把模块源码加入到内核源代码树中——或者是作为一个补丁或者是最终把你的代码合并到正式的内核代码树中;另一种可行的方式是内核源代码树之外维护和创建你的模块源码。

  1.放在内核源代码树中

  模块放入Linux内核中,会被存放在内核源代码树中。假设你想建立自己代码的子目录,你的驱动程序是一个钓鱼杆和计算机的接口,名为Fish Master XL 2000 Titanium,那么你应该在/drivers/char/目录下创建一个名为fishing的子目录。

  现在你需要向drivers/char/下的Makefile文件中添加一行。编辑drivers/char/Makefile加入:

  obj-m += fishing/

  这行编译指令告诉模块构建系统在编译模块时需要进入fishing/子目录中。更可能发生的是,你的驱动程序的编译取决于一个特殊配置选项,比如,可能CONFIG_FISHING_POLE。如果这样,你需要用下面的指令代替刚才的那条指令:

  obj-$(CONFIG_FISHING_POLE) += fishing/

  最后,在drivers/char/fishing/下,需要添加一个新的Makefile文件,其中需要有下面这行:

  obj-m += fishing.o

  此刻创建系统运行就将会进入fishing/目录下,并且将fishing.c编译为fishing.ko模块。

  2.放在内核代码外

  在你自己的源代码树建立一个Makefile文件,它只需一行指令: obj-m : =fishing.o 就可以把fishing.c编译成fishing.ko。如果你有多个源文件,那么用两行代码足够了:

  obj-m := fishing.o

  fishing-objs := fishing-main.o fishing-line.o

  这样, fishing-main.c和fishing-line.c 就一起被编译和链接到fishing.ko模块内。

  上述两种最大的区别在于构建过程。当你的模块在内核源代码树外围时,你必须告诉make如何找到内核源代码文件和基础Makefile文件。

  安装模块

  编译后的模块被装入到目录/lib/modules/version/kernel/下。比如,如果使用2.6.10内核,而且你将你的模块源代码直接放在divers/char下,那么编译后的钓鱼杆驱动程序的存放路径将是:/lib/modules/2.6.10/kernel/drivers/char/fishing.ko

  下面的构建命令用来安装编译的模块到合适的目录下:

  make modules_install

  通常需要以root权限运行

  产生模块依赖性

  Linux模块之间存在依赖性,也就是说钓鱼模块依赖于鱼饵模块,那么当带入钓鱼模块时,鱼饵模块会被自动载入。这里需要的依赖信息必须事先生成。多数Linux发布版都能自动产生这些依赖关系信息,而且在每次启动时更新。

  载入模块

  载入模块最简单的方法是通过insmod命令,它的作用就是请求内核载入你指定的模块。insmod程序不执行依赖性分析或进一步的错误检查。

  另一种更先进的工具是modprobe,它提供了模块依赖性分析,错误智能检查,错误报告以及许多其他功能和选项。不但会加载指定的模块,而且会自动加载任何它所依赖的有关模块。

  管理配置选项

  从上面看到只要设置了CONFIG_FISHING_POLE配置选项,钓鱼杆模块就将自动编译。由于2.6版本以后,新引入的"kbulid"系统所赐,加入一个新配置选项现在很简单,就是向Kconfig文件中添加一项——用以对应内核源码树。对驱动程序而言,Kconfig通常和源代码处于同一目录。如果钓鱼杆驱动程序在目录drivers/char/下,那么你便会发现drivers/char/Kconfig同时存在。

  如果你建立了一个新子目录,而且也希望Kconfig文件存在于该目录中的话,那么必须在一个已存在的Konfig文件中将它引入,使用下面命令:

  source “drivers/char/fishing/Konfig”

  这里所谓存在的Konfig文件可能是drivers/char/Kconfig.

  可以很方便的在Kconfig文件中加入一个配置选型,钓鱼杆模块的选项如下:

  配置选项第一行定义了该选项所代表的配置目标。CONFIG_前缀并不需要写上。

  第二行声明选项类型为tristate,即可以被编译进内核(Y),也可作为模块编译(M),或者干脆不编译(N)。

  第三行指定了该选项的默认选择,这里默认操作不是编译它。

  Helo指令目的是为该选项提供帮助文档。

  除了上述选项外,还存在其他选项。比如depends指令规定了在该选项被设置前,首先要设置的选项。假如依赖性不满足,那么该选项就被禁止。

  select指令和depends类似,不同之处在于,只要是select指定了谁,它会就强行将被指定的选项打开。此外还有If指定等。

  模块参数

  Linux提供了一个简单的框架——它可允许驱动程序声明参数,从而用户可以在系统启动或者模块装载时在指定参数值。这些参数对于驱动程序属于全局变量。值得一提的是模块参数同时也将出现在sysfs系统文件中。

  定义一个模块参数可通过宏module_param()完成。

  module_param(name, type, perm)

  参数name既是用户可见的参数名,也是模块中存在模块参数的变量名。参数type存放了参数的类型,例如byte,short,unshort,int,uint,long,ulong,charp,bool或invbool。最后一个参数perm指定了模块在sysfs文件系统下对应的文件权限。

  上面的宏其实并没有定义变量,因此需要在宏前定义变量,如下所示:

  static int allow_live_bait = 1;

  module_param(allow_live_bait, bool ,0644)

  导出符号表

  模块被导入后,就会动态连接到内核。它与用户空间的动态连接库类似,只有当被显示导出后的外部函数,才可以被动态库调用。在内核中,到处内核函数需要使用特殊的指令:EXPORT_SYMBOL()和EXPORT_SYMBOL_GPL()。

  导出的内核函数可以被模块调用,而未导出的函数模块则无法被调用。而核心代码在内核中可以调用任意非静态接口,因为所有的核心源代码文件被链接成了同一个镜像。

  导出的内核符号表被看做是到处的内核接口,甚至称为内核API。

  导出符号相当简单,在声明函数后,紧跟上EXPORT_SYMBOL()指定就搞定了,比如:

  假定get_pirate_beard_color()同时也定义在一个可访问的头文件中,那么任何模块现在都可以访问它。

  如果你的代码被配置为模块,那么就必须确保它被编译为模块时所用的全部接口都已被导出,否则就会产生连接错误(而且模块不能成功编译)。

  

  参考自:《Linux Kernel Development》.

时间: 2024-10-13 16:19:52

向下之旅(二十三):模块的相关文章

向下之旅(十三):内核同步方法

原子操作 原子操作可以保证指令以原子的方式执行——执行过程不会被打断. 内核提供了两组原子操作的接口——一组针对整数进行操作,另一组针对单独的位进行操作.在Linux支持的所有体系结构中都实现了这两组接口. 原子整数操作 针对整数的原子操作只能对atomic_t类型的数据进行处理.在这里之所以引入了一个特殊数据类型,而没有直接使用C语言的int类型,主要是出于两个原因:首先,确保原子操作只与这种特殊类型数据一起使用,同时,也保证了该类型的数据不会被传递给其他任何非原子函数.此外,使用atomic

JAVA之旅(二十三)——System,RunTime,Date,Calendar,Math的数学运算

JAVA之旅(二十三)--System,RunTime,Date,Calendar,Math的数学运算 map实在是太难写了,整理得我都晕都转向了,以后看来需要开一个专题来讲这个了,现在我们来时来学习一些新的小东西吧 一.System 翻译过来系统的意思,系统类,里面的方法都是静态的,可以直接调用 我们来演示一下吧,先从获取系统信息开始: package com.lgl.hellojava; import java.util.Properties; public class HelloJJAVA

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十三:DS1302模块

实验二十三:DS1302模块 DS1302这只硬件虽然曾在<建模篇>介绍过,所以重复的内容请怒笔者懒惰唠叨了,笔者尽可以一笑带过,废话少说让我们进入正题吧.DS1302是执行事实时钟(Real Time Clock)的硬件,采用SPI传输. 表示23.1 访问(地址)字节. [7] [6] [5] [4] [3] [2] [1] [0] 1 A5 A4 A3 A2 A1 A0 R/W DS1302作为从机任由主机蹂躏 ... 啊,是任由主机访问才对.对此,访问便有方向之分.如表23.1所示,访

滴滴Booster移动APP质量优化框架 学习之旅 二

推荐阅读: 滴滴Booster移动App质量优化框架-学习之旅 一 Android 模块Api化演练 不一样视角的Glide剖析(一) 续写滴滴Booster移动APP质量优化框架学习之旅,上篇文章分析内置的transform:booster-transform-shrink booster-transform-shared-preferences,今天分析booster-task-compression以及定制task对资源索引文件resource.asrc进行优化(重复资源优化.无用资源优化

winform学习日志(二十三)---------------socket(TCP)发送文件

一:由于在上一个随笔的基础之上拓展的所以直接上代码,客户端: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net.Sockets; using Sys

算法系列之二十三:离散傅立叶变换之音频播放与频谱显示

算法系列之二十三:离散傅立叶变换之音频播放与频谱显示 算法系列之二十三离散傅立叶变换之音频播放与频谱显示 导语 什么是频谱 1 频谱的原理 2 频谱的选择 3 频谱的计算 显示动态频谱 1 实现方法 2 杂项说明 结果展示 导语 频谱和均衡器,几乎是媒体播放程序的必备物件,没有这两个功能的媒体播放程序会被认为不够专业,现在主流的播放器都具备这两个功能,foobar 2000的十八段均衡器就曾经让很多人着迷.在上一篇对离散傅立叶变换介绍的基础上,本篇就进一步介绍一下频谱是怎么回事儿,下一篇继续介绍

从零开始学android&lt;RatingBar评分组件.二十三.&gt;

如果现在用户要对某个应用程序打分往往会使用图所示的组件,通过选择的"五角星"的个数来决定最终的打分成绩 这样的功能在Android之中,可以使用RatingBar组件实现,使用此组件可以方便用户的输入,而且很直观,RatingBar类的定义结构如下: java.lang.Object ? android.view.View ? android.widget.ProgressBar ? android.widget.AbsSeekBar ? android.widget.RatingBa

全栈JavaScript之路( 二十三 )DOM2、DOM3, 涉及XML命名空间的扩展(一)

<!DOCTYPE html> <xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml"> <head> <title>Example XHTML page</title> </head> <body> <s:svg xmlns:s="http://www.w3.org/2000/svg" version="1.1&

【管理心得之二十三】道是道,非常道。名可名,非常名。

场景再现 ====================== {一次PMP社团活动} 主持人   :在坐的各位,有谁自己动手煮过饺子或面条?请举手示意. 主持人   :真不少呀!那么把饺子或面条下锅之后,水沸腾.向外溢出时,你会怎么做? 80%回答道:加冷水 主持人   :为什么加冷水? 80%回答道:冷水 可以防止水沸腾,防止溢出. 主持人   :这是唯一方法吗? 20%回答道:还可以把火拧小,控制火温. 主持人   :这个方法似乎更方便,为什么刚开始的作答不是后者呢? ==============

云计算设计模式(二十三)——Throttling节流模式

云计算设计模式(二十三)——Throttling节流模式 控制由应用程序使用,一个单独的租户或整个服务的一个实例的资源的消耗.这种模式可以允许系统继续运行并满足服务水平协议,即使当增加需求的资源放置一个极端载荷. 背景和问题 在云应用负载通常上变化的基础上的活动用户的数量或他们正在执行的活动类型的时间.例如,多个用户可能会在工作时间被激活,否则系统可能被要求在每月结束时执行计算昂贵的分析.也有可能是突然和意外的突发活动.如果系统的处理要求超过了可用的资源的能力,其将遭受性能不佳,甚至会失败.该系