做网站要买多大服务器,国内外网站开发技术,一个交易网站开发的成本是多少,烟台高端网站开发模块1 构建模块放在内核源代码树中放在内核代码外2 安装模块3 产生模块依赖性4 载入模块5 管理配置选项6 模块参数7 导出符号表Linux内核是模块化组成的#xff0c;它允许内核在运行时动态地向其中插入或从中删除代码。
与开发的内核核心子系统不同#xff0c;模块开发更接近…
模块1 构建模块放在内核源代码树中放在内核代码外2 安装模块3 产生模块依赖性4 载入模块5 管理配置选项6 模块参数7 导出符号表Linux内核是模块化组成的它允许内核在运行时动态地向其中插入或从中删除代码。
与开发的内核核心子系统不同模块开发更接近编写新的应用程序因为至少要在模块文件中具有入口点和出口点。
下面是hello_world内核模块
#include linux/init.h
#include linux/module.h
#include linux/kernel.hstatic int hello_init(void)
{printk(KERN_ALERT I bear a charmed life.\n);return 0;
}static void hello_exit(void)
{printk(KERN_ALERT Out,out,brief candle !\n);
}module_init(hello_init);
module_exit(hello_exit);MODULE_LICENSE(GPL);
MODULE_AUTHOR(Shakespeare);hello_init()函数是模块的入口点它通过module_init()注册到系统中在模块装载时被调用。调用module_init()实际上不是真正的函数调用而是一个宏调用它唯一的参数便是模块的初始化函数。模块的所有初始化函数必须符合下面的形式
int my_init(void);因为init函数通常不会被外部函数直接调用所以不必导出该函数故它可被标记为static类型。init函数会返回一个int型数据如果初始化顺利完成那么它的返回值为0失败的话返回一个非0值。
hello_exit()函数是模块的出口函数它由module_exit()注册到系统在模块从内存卸载时内核便会调用hello_exit()。在退出函数返回后模块就被卸载了。
退出函数必须符号以下形式
void my_exit(void);MODULE_LICENSE()宏用于指定模块的版权如果载入非GPL模块到系统内存则会在内核中设置被污染标志。MODULE_AUTHOR()宏指定代码作者完全是用作信息记录目的。
1 构建模块
在2.6内核中由于采用了新的kbuild构建系统现在构建模块相比从前更加容易构建过程中的第一步是决定在哪里管理模块代码你可以把模块源码加入到内核源代码树中也可以在内核源码树外维护构建模块源码。
放在内核源代码树中
最理想的情况莫过于模块正式成为Linux内核的一部分这样就会被存放在内核源代码树中。
首先你要清楚你的模块应该在内核源代码树中何处。设备驱动程序存放在内核源码树根目录drivers/的子目录下在drivers内部设备驱动文件被进一步细分。如字符设备存在于drivers/char/目录下而块设备存放在drivers/block/目录下USB设备则存放在drivers/usb/目录下。
假设你有一个字符设备而且希望放在drivers/char/目录下那么你要注意在该目录下同时会存在大量的C源代码文件和其他目录。所以对于仅仅只有一两个源文件的设备驱动程序可以直接存放在该目录下。如果驱动程序包含许多源文件和其他辅助文件那么可以创建一个新子目录。
假设你想创建自己代码的子目录你的驱动程序是一个钓鱼杆和计算机的接口名为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模块虽然拓展名是.o但是模块被编译后的拓展名是.ko。 要是你的钓鱼杆驱动程序编译时有编译选项那么你可能需要这么来做
obj-$(CONFIG_FISHING_POLE) fishing.o如果喜欢把源文件置于drivers/char/目录下并且不建立新目录。那么你要做的便是将前面提到的行也就是原来处于drivers/char/fishing/下你自己的Makefile中的都加入到drivers/char/Makefile中。
开始编译吧运行内核构建过程如果模块编译取决于配置选项比如有CONFIG_FISHING_POLE约束那么在编译前首先要确保选项被允许。
放在内核代码外
如果你喜欢脱离内核源代码树来维护和构建你的模块那么你要做的就是在你自己的源代码树目录建立一个Makefile文件它只需要一行指令
obj-m : fishing.o就可以把fishing.c编译成fishing.ko。
模块在内核内或内核外构建的最大区别 在于构建过程。当你的模块在内核源代码树外时你必须告诉make如何找到内核源代码文件和基础的Makefile文件。
make -C /kernel/sourece/location SUBDIRS$PWD modules/kernel/source/location是你以配置的内核源码树。
2 安装模块
编译后的模块将被装入到目录/lib/modules/version/kernel/下。比如如果使用的是2.6.10内核而且你将你的模块源代码直接放在drivers/char/下那么编译后的钓鱼杆驱动程序的存放路径是/lib/modules/2.6.10/kernel/drivers/char/fishing.ko
下面的构建命令用来安装编译的模块到合适的目录下
make modules_install3 产生模块依赖性
Linux模块之间存在依赖性也就是说钓鱼模块依赖鱼饵模块那么当载入钓鱼模块时鱼饵模块会被自动载入这里需要的依赖信息必须事先生成。若想产生内核依赖关系的信息root用户可运行命令
depmod为了执行更快的更新操作可以只为新模块生成依赖信息而不是生成所有的依赖关系这时root用户可运行命令
depmod -A模块依赖关系信息存放在/lib/modules/version/modules.dep文件中
4 载入模块
载入模块最简单的方法是通过insmod命令这是个功能很有限的命令它的作用就是请求内核载入你指定的模块。insmod程序不执行任何依赖性分析或进一步的错误检查它用法简单以root运行命令
insmod module需要载入的模块名称由参数module指定比如装载钓鱼杆模块那你就执行命令
insmod fishing卸载一个模块你可使用rmmod命令同样用root身份执行
rmmod module比如rmmod fishing将卸载钓鱼杆模块。
系统为我们提供了一个更先进的工具modprobe它提供了模块依赖性分析错误智能检查错误报告以及许多其他功能和选项推荐用这个命令
modprobe module [module parameters]module指定需要载入的模块名称后面的参数将在模块加载时传入内核。modprobe命令不但会加载指定的模块而且会自动加载任何它所依赖的有关模块。所以说它是加载模块的最佳技术。
modprobe命令也可用来从内核中卸载模块当然这也需要root身份运行。
modprobe -r modules参数modules指定一个或多个需要卸载的模块与rmmod命令不同modprobe也会卸载给定模块所依赖的相关模块前提是这些相关模块没有被使用。
5 管理配置选项
2.6内核中新引入了kbuid系统加入一个新配置选项是很容易的。你所需做的全部就是向Kconfig文件中添加一项用于对应内核源码树。对驱动程序而言Kconfig通常和源码处于同一目录。如果钓鱼杆驱动程序子drivers/char/下那么你便会发现drivers/char/Konfig同时存在。 如果你建立了一个新子目录而且也希望Kconfig文件存在于该目录中的话那么必须在一个已存在的Kconfig文件中将它引入
source drivers/char/fishing/Kconfig可以很方便地在Kconfig文件中加入一个配置选项请看钓鱼杆模块的选项如下所示 配置选项第一行定义了该选项所代表的配置文件注意CONFIG_前缀不需要写上。这个就是构建模块时在Makefile里加入的特殊配置选项CONFIG_FISHING_POLE。 第二行声明选项类型为tristate也就是说被编译进内核Y也可作为模块编译M或干脆不编译它N选YCONFIG_FISHING_POLE的值就是Y依次类推。如果编译选项代表的是一个系统功能而不是一个模块那么编译选项将用bool代替tristate这说明它不允许被编译成模块。处于指令之后的引号内文件为该选项指定了名称。 第三行指定了该选项的默认选择这里默认操作是不编译它。
help指令是为该选项提供帮助文档。
除了上述选项外还存在其他选项。
6 模块参数
Linux允许驱动程序声明参数从而用户可以在系统启动或者模块装载时再指定参数值这些参数对于你的驱动程序属于全局变量模块参数同时也将出现在sysfs文件系统中。 定义一个模块参数可通过宏module_param()完成
module_param(name,type,perm);参数name既是用户可见的参数名也是模块中存放模块参数的变量名。参数type则存放参数的类型最后一个参数perm指定了模块在sysfs文件系统下对应文件的权限该值可以是八进制格式比如0666或是S_Ifoo的定义形式比如S_IRUGO|S_IWUSR如果该值为0则表示禁止所有的sysfs项。
上面的宏并没有定义变量你必须在使用该宏前进行变量定义。通常使用类似下面的语句完成定义。
static int allow_live_bait 1;
module_param(allow_live_bait,bool,0666);有可能模块的外部参数名称不同于它对应的内部变量名称这是就该使用宏module_param_named()定义了
module_param_named(name,variable,type,perm);参数name是外部可见的参数名称参数variable是参数对应的内部全局变量名称。比如
static unsigned int max_text DEFAULT_MAX_LINE_TEST;
module_param_named(maximum_line_test,max_test,int ,0)其他宏
module_param_string(name,string,len,name); /* 拷贝字符串到指定的字符数组 */
module_param_array(name,type,nump,perm); /* 接受逗号分割的参数序列 */
module_param_array_named(name,array,type,nump,perm); /* 将内部参数数组命名区别于外部参数 */上述所有宏被定义在linux/moduleparam.h文件中。
7 导出符号表
模块被载入后就会动态连接到内核。注意它与用户空间中的动态连接库类型只有当被显式导出后的外部函数才可以被动态库调用。在内核中导出内核内核函数需要使用特殊的指令 EXPORT_SYMBOL()和EXPORT_SYMBOL_GPL()。
导出的内核函数可以被模块调用而未导出的函数模块则无法使用。导出的内核符号表被看做是导出的内核接口甚至称为内核API。
导出符号相当简单在声明函数后紧跟上EXPORT_SYMBOL()指令就搞定了比如
int get_priate_beard_color(void)
{return pirate-beard-color;
}
EXPORT_SYMBOL(get_priate_beard_color);假定get_priate_beard_color同时也定义在一个可访问的文件中那么任何模块现在都可以访问它。
有一些开发者希望自己的接口仅仅对GPL兼容的模块可见内核连接器使用MODULE_LICENSE()宏可满足这个要求如果你希望先前的函数仅仅对标记为GPL协议的模块可见那么你就需要用
EXPORT_SYMBOL_GPL(get_priate_beard_color);如果你的代码被设置为模块那么就必须确保它被编译为模块时所用的全部接口已被导出否则就会产生连接错误而且模块不能成功编译。