随着计算机的周边外设越来越丰富,设备管理已经成为现代操作系统的一项重要任务,这对于Linux来说也是同样的情况。每次Linux内核新版本的发布,都会伴随着一批设备驱动进入内核。在Linux内核里,驱动程序的代码量占有了相当大的比重。下图是我在网络上搜索到的一幅Linux内核代码量的统计图,对应的内核版本是2.6.29。
我们可以很明显的看到,在Linux内核中驱动程序的比例已经非常高了。
Linux 2.6内核最初为了应付电源管理的需要,提出了一个设备模型来管理所有的设备。在物理上,外设之间是有一种层次关系的,比如把一个U盘插到笔记本上,实际上这个U盘是接在一个USB Hub上,USB Hub又是接在USB 2.0 Host Controller (EHCI)上,最终EHCI又是一个挂在PCI Bus上的设备。这里的一个层次关系是:PCI->EHCI->USB Hub->USB Disk。如果操作系统要进入休眠状态,首先要逐层通知所有的外设进入休眠模式,然后整个系统才可以休眠。因此,需要有一个树状的结构可以把所有的外设组织起来。这就是最初建立Linux设备模型的目的。
当然,Linux设备模型给我们带来的便利远不止如此。既然已经建立了一个组织所有设备和驱动的树状结构,用户就可以通过这棵树去遍历所有的设备,建立设备和驱动程序之间的联系,根据类型不同也可以对设备进行归类,这样就可以更清晰的去“看”这颗枝繁叶茂的大树。另外,Linux驱动模型把很多设备共有的一些操作抽象出来,大大减少了重复造轮子的可能。同时Linux设备模型提供了一些辅助的机制,比如引用计数,让开发者可以安全高效的开发驱动程序。达成了以上这些好处之后,我们还得到了一个非常方便的副产品,这就是sysfs----一个虚拟的文件系统。sysfs给用户提供了一个从用户空间去访问内核设备的方法,它在Linux里的路径是/sys。这个目录并不是存储在硬盘上的真实的文件系统,只有在系统启动之后才会建起来。
下面这个命令可以用来显示sysfs的大致结构:
tree /sys
这个命令的信息量非常大,我就不贴出来了,如果有兴趣的话可以看看这里,或者自己动手实验一下。
我们来看看第一层目录结构:
/sys
|-- block
|-- bus
|-- class
|-- dev
|-- devices
|-- firmware
|-- fs
|-- kernel
|-- module
`-- power
这里有10个子目录,但并不是说这10个目录代表了10种完全不同的设备类型,实际上这些目录只是给我们提供了如何去看整个设备模型的不同的视角。其实从不同的目录出发都有可能找到同一个设备的。那真正的设备信息到底放在哪里呢?看看目录的名称就应该能猜到,对,就是devices子目录,Linux的所有设备都可以在这个目录里找到。这里是一个大杂烩,虽然五脏俱全但我们却无从下手。这里还是以U盘为例,插上U盘之后,在devices目录里如何找到这支U盘呢?真得很难办到。但是如果知道这个U盘在系统里的设备文件名(暂且假设为sdb),那就可以从block目录着手。
透过block目录,我们很容易就可以找到这个U盘设备,符号链接device正是指向devices目录下的位置。
到这里,我们总结一下/sys目录下各个子目录的作用。block目录是从块设备的角度来组织设备;bus目录是从系统总线这个角度来组织设备,这里的总线可以是具有实际意义的总线比如PCI总线,也可以是完全逻辑上的总线比如USB总线;class目录把看问题的视角提高到了类别的高度,比如PCI设备或者USB设备等;dev目录的视角是设备节点;devices目录在前面提到了,这里是所有设备的大本营;firmware目录包含了一些比较低阶的子系统,比如ACPI、EFI等;fs目录里看到的是系统支持的所有文件系统;kernel目录下包含的是一些内核的配置选项;modules目录下包含的是所有内核模块的信息,内核模块实际上和设备之间是有对应关系的,通过这个目录顺藤摸瓜找到devices或者反过来都是可以做到的;power目录存放的是系统电源管理的数据,用户可以通过它来查询目前的电源状态,甚至可以直接“命令”系统进入休眠等省电模式。
sysfs正是用户和内核设备模型之间的一座桥梁,通过这个桥梁我们可以从内核中读取信息,也可以向内核里写入信息。
在Linux里也可以找到一些图形化的工具来查询设备信息。比如GNOME下基于HAL的Device Manager:
或者KDE下基于Solid的KInfoCenter:
这些图形化的工具提供了更加直观的方式来访问设备,但是它们的提供的信息还不够全面,而且没有向内核设备写数据的功能。
如果具体到某一类型的设备,Linux下还有一些专用的工具可以使用。比如面向PCI设备的pciutils,面向USB设备的usbutils,以及面向SCSI设备的lsscsi等。对于Linux开发者来说,有时使用这些专用的工具更加方便。
我们如果要写程序来访问sysfs,可以像读写普通文件一样来操作/sys目录下的文件,或者,也可以使用libsysfs。不过需要注意的是,Linux内核社区并不推荐用libsysfs,因为这个API的更新不够快,赶不上内核的变化。libsysfs已经逐渐背离最初创建它的目标,这个lib带来的问题似乎比它解决的还要多。当然,如果只是要访问设备,一般很少会直接操作sysfs,它太细节太底层了,大部分情况下可以使用更加方便的DeviceKit或者libudev。
总结一下,本文主要简单介绍了Linux设备模型的基本概念和虚拟文件系统sysfs。接下来的篇章里将和大家继续探讨设备模型在内核空间的一些细节。