从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响(二)

简介:

2.4 模块使用计数

模块在被使用时,是不允许被卸载的。2.4内核中,模块自身通过MOD_INC_USE_COUNTMOD_DEC_USE_COUNT宏来管理自己被使用的计数。2.6内核提供了更健壮、灵活的模块计数管理接口try_module_get(&module)module_put(&module)取代2.4中的模块使用计数管理宏;模块的使用计数不必由自身管理,而且在管理模块使用计数时考虑到SMPPREEMPT机制的影响。
int try_module_get(struct module *module) :用于增加模块使用计数;若返回为0,表示调用失败,希望使用的模块没有被加载或正在被卸载中。
void module_put(struct module *module) :减少模块使用计数。
try_module_get  module_put的引入与使用与2.6内核下的设备模型密切相关。模块是用来管理硬件设备的,2.6 内核为不同类型的设备定义了struct module *owner 域,用来指向管理此设备的模块。如字符设备的定义:
 
struct cdev {
        struct kobject kobj;
        struct module *owner;
        struct file_operations *ops;
        struct list_head list;
        dev_t dev;
        unsigned int count;
};
 
从设备使用的角度出发,当需要打开、开始使用某个设备时,使用try_module_get(dev->owner)去增加管理此设备的owner模块的使用计数;当关闭、不再使用此设备时,使用module_put(dev->owner)减少对管理此设备的owner模块的使用计数。这样,当设备在使用时,管理此设备的模块就不能被卸载;只有设备不再使用时模块才能被卸载。
2.6 内核下,对于为具体设备写驱动的开发人员而言,基本无需使用try_module_getmodule_put,因为此时开发人员所写的驱动通常为支持某具体设备的owner模块,对此设备owner模块的计数管理由内核里更底层的代码如总线驱动或是此类设备共用的核心模块来实现,从而简化了设备驱动开发。
 

 
2.5  模块输出的内核符号
2.4  内核下,缺省情况时模块中的非静态全局变量及函数在模块加载后会输出到内核空间。
2.6  内核下,缺省情况时模块中的非静态全局变量及函数在模块加载后不会输出到内核空间,需要显式调用宏EXPORT_SYMBOL才能输出。所以在2.6 内核的模块下,EXPORT_NO_SYMBOLS宏的调用没有意义,是空操作。在同时支持2.42.6内核的设备驱动中,可以通过以下代码段来输出模块的内核符号
#include <linux/module.h>
#ifndef LINUX26
EXPORT_NO_SYMBOLS;
#endif 
EXPORT_SYMBOL(var);
EXPORT_SYMBOL(func);
 
需要注意的是如需在2.4内核下使用 EXPORT_SYMBOL,必须在 CFLAGS中定义 EXPORT_SYMTAB,否则编译将会失败。
从良好的代码风格角度出发,模块中不需要输出到内核空间且不需为模块中其它文件所用的全局变量及函数最好显式申明为static类型,需要输出的内核符号以模块名为前缀。
模块加载后,2.4内核下可通过 /proc/ksyms 2.6 内核下可通过/proc/kallsyms查看模块输出的内核符号
 

 
2.6  模块的命令行输入参数
在装载内核模块时,用户可以向模块传递一些参数,如`modprobe modname var=value`,否则,var将使用模块内定义的缺省值。
2.4 内核下,linux/module.h中定义有宏MODULE_PARM(var,type) 用于向模块传递命令行参数。var为接受参数值的变量名,type为采取如下格式的字符串[min[-max]]{b,h,i,l,s}minmax用于表示当参数为数组类型时,允许输入的数组元素的个数范围;bbytehshortiintllongsstring
2.6 内核下,宏MODULE_PARM(var,type)不再被支持。在头文件linux/moduleparam.h里定义了如下宏:
 
module_param(name, type, perm)
module_param_array(name, type, nump, perm)
 
type  类型可以是byteshort,ushort int uintlongulongcharp, bool or invbool, 不再采用2.4内核中的字符串形式,而且在模块编译时会将此处申明的type与变量定义的类型进行比较,判断是否一致。
perm 表示此参数在sysfs文件系统中所对应的文件节点的属性。2.6内核使用sysfs文件系统,这是一个建立在内存中比proc更强大的文件系统。sysfs文件系统可以动态、实时,有组织层次地反应当前系统中的硬件、驱动等状态。当perm0时,表示此参数不存在sysfs文件系统下对应的文件节点。模块被加载后,在/sys/module/ 目录下将出现以此模块名命名的目录。如果此模块存在perm不为0的命令行参数,在此模块的目录下将出现parameters目录,包含一系列以参数名命名的文件节点,这些文件的权限值等于perm,文件的内容为参数的值。
nump  为保存输入的数组元素个数的变量的指针。当不需保存实际输入的数组元素个数时,可以设为NULL。从2.6.02.6.10 版本,须将变量名赋给nump;从2.6.10 版本开始,须将变量的引用赋给nump,这更易为开发人员理解。加载模块时,使用逗号分隔输入的数组元素。
#include <linux/module.h>
#ifdef LINUX26
#include <linux/moduleparam.h>
#endif
int debug = 0;
char *mode = "800x600";
int tuner[4] = {1, 1, 1, 1};
#ifdef LINUX26
int tuner_c = 1; 
#endif
#ifdef LINUX26
MODULE_PARM(debug, "i");
MODULE_PARM(mode, "s");
MODULE_PARM(tuner,"1-4i");
#else
module_param(debug, int, 0644);
module_param(mode, charp, 0644);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10)
module_param_array(tuner, int, &tuner_c, 0644);
#else
module_param_array(tuner, int, tuner_c, 0644);
#endif
#endif
 
模块编译生成后,加载模块时可以输入:`modprobe my_module mode=1024x768 debug=1 tuner=22,33`
linux/moduleparam.h还定义有:
 
module_param_array_named(name, array, type, nump, perm)             
module_param_call(name, set, get, arg, perm) 
module_param_named(name, value, type, perm)
 
读者可以参阅linux/moduleparam.h查看这些宏的详细描述,有一点需注意,在2.6内核里,module_param这一系列宏使用的都是小写名字。
2.7  模块的许可证声明
2.4.10版本内核开始,模块必须通过MODULE_LICENSE宏声明此模块的许可证,否则在加载此模块时,会收到内核被污染"kernel tainted" 的警告。从linux/module.h文件中可以看到,被内核接受的有意义的许可证有 "GPL""GPL v2""GPL and additional rights""Dual BSD/GPL""Dual MPL/GPL""Proprietary"
在同时支持2.42.6内核的设备驱动中,模块可按如下方式声明自己的许可证。
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
MODULE_LICENSE("GPL");
#endif
 
2.8  小结
此外,2.6内核里还有一些模块机制的改变,不常为驱动开发人员用到。如加载内核模块的接口request_module2.4 下为request_module(const char * module_name);在2.6内核下为request_module(const char *fmt, ...)。在2.6 内核下,驱动开发人员可以通过调用
 
request_module("msp3400");
request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev))
 
这种更灵活的方式加载其它内核模块。
2.6 内核在linux/module.h中还提供了MODULE_ALIAS(alias)宏,模块可以通过调用此宏为自己定义一或若干个别称。而在2.4内核下,用户只能在/etc/modules.conf中为模块定义别称。
通过以上比较可以看到,从2.42.6内核,可装载模块管理机制的改变使设备驱动的开发变得更加简洁、灵活、健壮。


本文转自 曾永刚 51CTO博客,原文链接:http://blog.51cto.com/zyg0227/270376
相关文章
|
14天前
|
存储 编译器 Linux
动态链接的魔法:Linux下动态链接库机制探讨
本文将深入探讨Linux系统中的动态链接库机制,这其中包括但不限于全局符号介入、延迟绑定以及地址无关代码等内容。
189 19
|
21天前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
26天前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
1月前
|
算法 Linux 开发者
Linux内核中的锁机制:保障并发控制的艺术####
本文深入探讨了Linux操作系统内核中实现的多种锁机制,包括自旋锁、互斥锁、读写锁等,旨在揭示这些同步原语如何高效地解决资源竞争问题,保证系统的稳定性和性能。通过分析不同锁机制的工作原理及应用场景,本文为开发者提供了在高并发环境下进行有效并发控制的实用指南。 ####
|
1月前
|
安全 Linux 数据安全/隐私保护
深入探索Linux操作系统的多用户管理机制
【10月更文挑战第21天】 本文将详细解析Linux操作系统中的多用户管理机制,包括用户账户的创建与管理、权限控制以及用户组的概念和应用。通过具体实例和命令操作,帮助读者理解并掌握Linux在多用户环境下如何实现有效的资源分配和安全管理。
|
1月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
108 8
|
1月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
382 6
|
1月前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
88 3
|
1月前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
80 2
|
20天前
|
Linux Shell
Linux 10 个“who”命令示例
Linux 10 个“who”命令示例
50 14
Linux 10 个“who”命令示例