扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
Linux硬件驱动可以通过两种方式集成到内核中:一是将其直接编译进行内核从而一劳永逸;二是将其编写成一种目标格式,在需要添加某种硬件时,内核可以将其调入。当用户对Linux内核进行设置时,每个内核设置编译器都可显示各个可用内核设置变量的描述信息,从而使用户决定哪个变量要被消除,哪个需要写入内核,还有哪个可以编写成一种可加载内核模块。
直接将硬件驱动程序写入内核优点在于,用户可以随时对它进行调用而无需安装。但是这样大大增加内核占用的空间。将硬件驱动程序编写成一种可加载的内核模块,虽然会因为寻找驱动模块而增加系统资源的占用和运行时间,但是与庞大的内核所消耗的资源相比显得微不足道。将硬件驱动程序编写成一种可加载的内核模块,还可为软件开发提供许多便利。当用户需要对某一硬件驱动程序进行开发或纠错时,用户可以动态地卸载旧的版本并加载新的版本,但是如果用户的驱动程序已写入内核,那么必须对内核进行重新编写,并且每次对修改后的程序进行测试时,都必须重新启动系统。另外,将硬件驱动程序视为可加载的内核模块进行开发和配置,这样用户就可以将硬件驱动程序作为一种独立的系统进行升级,而不必对内核进行改动了。
用户要做的只是编译并安装可加载内核模块,其它的工作由模块自已来完成。当系统首次访问某一硬件设备时,只要存在使用“depmod”命令建立的模块从属关系树,与之对应的模块就可以自动加载。可加载内核模块通常情况下安装在系统/lib/modules目录的一个子目录下。该子目录的名称由建立内核的Makefile中的VERSION、PATCHLEVEL、SUBLEVEL和EXTRAVERSION等变量的值决定。
Linux 2.6内核为硬件驱动程序带来一个新的、统一的框架。用户对原本运行于旧版本内核下的硬件驱动程序进行定制。新驱动程序框架通过定义各种接口,为硬件的即插即用和电源管理提供全面支持。子系统可以通过这些接口与各个驱动程序进行通信。新驱动程序框架更加明确了总线和驱动程序之间的责任界限。Linux 2.6内核还引入了sysfs文件系统为每个系统的硬件树进行分级处理。Linux 2.6内核还对可加载内核模块规定了新的命名方法,使用的是.ko扩展名,而不是旧版本标准的.o (object)扩展名。
这里将重点阐述2.6内核下的硬件驱动程序与以往内核下的硬件驱动程序在主体结构上的不同之处。
升级硬件驱动程序的基本结构
Linux 2.4内核下的硬件驱动标准模板如下:
旧版本内核下的硬件驱动程序有一个普遍的问题,就是对初始化模块和清除功能的名称进行假设。当开发人员编写旧版本内核下的硬件驱动程序时,如果使用缺省的名称init_module()和cleanup_module(),那么就不需要对初始化模块和清除功能的名称进行记录。这种方法经常会出现错误,已逐渐被淘汰。在2.6内核下,用户必须使用module_init()宏和module_exit()宏对初始化和退出规程的名称进行记录。
另外,在2.6内核下,用户无论是在源代码中还是在Makefile文件中都不再需要对#define MODULE进行描述。内核搭建系统会自动对此类符号进行定义并校验。当用户为2.6内核编写硬件驱动程序时,必然会用到此类搭建系统。
要想对已有的模块进行编译,并使之加载到2.6内核,必须首先完成一些基本的结构变化。然而,当用户利用此类结构加载模块时,会注意到在标准输出设备和系统日志上会显示一个坏模块的出错信息。为了消除这条信息,用户需要为MODULE_LICENSE()宏增加一个示例,例如MODULE_LICENSE("GPL")。这种2.4内核以后的版本才引入的宏,可以将模块定义为获得GPL Version 2或更新版本许可的模块。其它有效的值还有"GPL v2"、"GPL and additional rights"、"Dual BSD/GPL"(选择BSD或GPL许可)、"Dual MPL/GPL"(选择Mozilla 或GPL许可)和"Proprietary"。
2.6内核下硬件驱动程序最简单的类属模板如下:
除了硬件驱动自身所需要的变化外,在Linux 2.6内核下,与之相应的最重要的变化是在内核搭建过程中完成的。
模块搭建过程中的变化
对于所有开发可加载硬件驱动程序的人来说,对他们影响较大的一个基本变化不是源于内核源代码,而是将外部模块编译过程整合为标准的内核搭建机制。如果用户使用的不是集成开发环境(例如TimeSys公司的TimeStorm,它可以检测内核版本并自动建立Makefile),那么用户需要手工为硬件驱动程序建立Makefile。
在2.4和更旧版本的内核下,模块的开发和编译位置不受限制,只要将适当的编译标记移到命令行或模块的Makefile中就可以了。这些标记包括两个编译模块时必须的符号定义和一个指针。该指针指向包含有内核所含文件的目录。以下面的语句为例,用户可以建立一个名为testmod.o的可加载内核模块:
#gcc -D__KERNEL__ -DMODULE -I/usr/src/linux-2.4.21/include -O2 -c testmod.c
为2.6内核搭建模块的过程比较简单,但是要想满足所有成功编译所需要的条件就不那么容易。用户既不需要手工指定以模块为导向的说明(例如MODULE, __KERNEL__等),也不必指定新的符号(如KBUILD_BASENAME和KBUILD_MODNAME等),只要对外部模块植入标准内核搭建系统的过程进行整合就可以了。用户也不必指定诸如-O2之类的选项,因为用户编译的模块与其它可加载内核模块一样,进程会自动调用所有的强制性标志。至于Makefile的编写就简单得多了,例如为testmod.ko模块编写的可与2.6内核兼容的Makefile如下所示:
obj-m := testmod.o。
然而,为了建立外部模块,用户必须先完成内核源代码树接口的编写。这样可以建立一些临时目录以供编译时使用。下面是一个为2.6内核构建模块的命令行。它可以从包含模块源代码目录下执行:
# make -C /usr/src/linux-2.6.1 SUBDIRS=$PWD modules
此示例命令假设用户的模块源代码和Makefile所在的目录与用户正在运行的命令中的相同。如果用户不使用POSIX命令(例如BASH),那么可以通过“SUBDIRS=`pwd`”命令,用$PWD变量代替SUBDIRS参数。这样用户就可以使用“pwd”命令识别工作目录。建立出口的命令如下所示:
"make"命令的成功完成将产生testmod.ko模块。对该模块的命名使用的是新的内核模块命名规则。如果用户已经对系统的启动程序进行了修改,以便通过名称清楚地加载模块,那么用户需要确定在升级到2.6内核后,这些模块的命名是否遵循了新的命名规则。
适应2.6内核的内部变化
Linux 2.6内核还带来了许多内部变化,用户需要改变已有的驱动程序以适应这种变化。这些变化包括内核的异步I/O机理、DMA支持层、存储器与页分配机理、数据块硬件驱动程序和新的类属硬盘接口等。例如,用来分配并管理存储器与页的功能就发生了新的变化。在2.6内核下,系统使用了一种名叫mempool的标准接口。对模块参考计数的使用和管理也发生了变化。模块参考计数主要用于决定一个模块是否正在使用,并对没有被使用的模块进行安全卸载。在2.6内核下,命令序列已被工作序列所代替,其中,对大量不同驱动程序产生影响的一个重要变化是参数模块的新接口。MODULE_PARM()宏已由详细的参数说明所代替。这种说明来源于新的module_param()宏。
Linux 2.6内核的优先能力和对SMP的识别能力,为驱动程序编写人员带来一些新问题。在单处理机系统中,在无优先能力的Linux内核下,一些驱动程序可以假设在两个处理器间不必再提供重入接口,因为它们无法同时运行驱动程序。驱动程序可以使用“spinlock”或“mutex”命令来保护那些可从多进程访问的数据。这些问题的考虑对于为嵌入式环境(如TimeSys Linux)编写高性能和实时硬件驱动程序的人来说尤为重要。
其它考虑因素
如果用户较为依赖Linux 2.6内核的工作,那么还需要对驱动程序做一些其它的改动。例如,尽管自从2.3内核诞生后,devfs文件系统已经被写入内核,并且在2.6内核设置中被标注为舍弃指令,但是它却经常在一些特殊领域中使用。例如在嵌入式计算中, devfs可提供较强的灵活性和一个紧缩的/dev文件名。devfs文件系统是介于hardcoded硬件节点间的中间步骤。此类节点主要用于早期的Linux和Unix系统中。同时,它还是udev、hotplug和sysfs文件系统的综合。对udev的支持技术目前正在被写入Linux 2.6内核。TimeSys公司已经开发出了拥有此类技术的商业Linux系统。如果用户正在使用其它的Linux发行版,那么用户也许会发现devfs支持和集成技术对于驱动程序来说十分重要。
如果用户想使用devfs文件系统,那么必须首先在搭建内核时激活对它的支持。这一步可以在内核设置编辑程序的File systems→Pseudo filesystems中完成。使用devfs还需要改变硬件驱动程序对硬件节点的识别方法。当用户使用传统的/dev目录作为Linux硬件描述符文件的放置位置时,硬件驱动程序通过启动register_blkdev() 或register_chrdev()函数来注册新硬件。具体使用哪一个,要看驱动程序注册的是一个数据块硬件还是字符硬件,而且必须事先知道硬件的主号码和次号码。另外,因为udev是一个可热插拔程序,它可以自动建立并删除/dev目录下的登录项,所以这一方法同样也适用于新的udev硬件机理。
使用devfs硬件文件系统时,硬件驱动程序必须使用devfs_register()系统呼叫来注册它们的硬件。驱动程序可以继续使用此前指定的主次编码,也可通过为devfs_register()呼叫指定DEVFS_FL_AUTO_DEVNUM 标志,由devfs自动指定编码。
小结
用户常常由于提高系统性能、增加系统功能、实现系统单一化和标准化等原因对内核进行修改。每一个新版本Linux内核都会带来许多新的变化,这些变化在不同层次上对开发人员有很大的影响。本文概括了在2.6内核下硬件驱动程序的变化及模块搭建过程的变化。诸如TimeStorm之类的工具可以为用户提供升级驱动程序的模板,并可自动为可加载内核模块建立并管理Makefile。然而,如果用户正在手工维护现有的硬件驱动程序或开发新的硬件驱动程序,那么用户将需要认真的考虑2.6内核的变化,做出正确的选择。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者