4.5.2 将硬盘控制器驱动配置为模块
与前面配置硬盘控制器驱动类似,只不过这里我们将"AHCI SATA support"和"Intel ESB,ICH,PIIX3,PIIX4 PATA/SATA support"配置为模块,如图4-15所示。
图 4-15 配置SATA控制器驱动为模块
接下来重新编译内核和模块。内核和模块可以使用单独的命令分开编译,也可以使用一条make命令同时编译内核和模块。编译完成后,将模块暂时安装在"/vita/sysroot/lib/modules"目录下。
最终安装的硬盘控制器驱动模块包括:
我们将其复制到initramfs中。
为了加载内核模块,我们需要安装加载、卸载等管理模块的工具,这些工具在包kmod中:
检查kmod的依赖:
根据输出可见,kmod及库libkmod只依赖libc库,而libc已经安装到initramfs,所以复制kmod及库libkmod到initramfs即可,具体如下:
kmod是module-init-tools的替代者,但是kmod是向后兼容module-init-tools的,虽然kmod只提供一个工具kmod,但是通过符号链接的形式支持module-init-tools中的各个命令,而且目前来看,也只能使用这种方式来使用各种模块管理命令。因此,需要为各个模块管理命令建立符号链接,并将这些符号链接也复制到initramfs中:
其中,insmod、rmmod、modprobe用于加载/卸载模块;modinfo用于查看模块信息;lsmod用于查看已经加载的模块;depmod用于创建模块间的依赖关系。注意,这里一定要将modprobe等命令放在/sbin目录下,因为后面的udevd将会到使用"/sbin/modprobe"的形式调用modprobe命令。最新的合并到systemd中的udev不再直接调用modprobe等工具,而是使用libkmod提供的库提供的API加载模块,但并无本质区别。
bash的默认搜索命令的路径为/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.。我们当然可以使用全路径运行命令,比如/sbin/insmod,但是为了方便,我们还是在init脚本中对搜索命令的路径进行一些调整。bash中,保存搜索命令的环境变量为PATH:
压缩initramfs,并将其和不包含硬盘驱动的bzImage复制到虚拟机,然后重启系统。进入系统后,因为内核中已经没有硬盘控制器的驱动,所以在/dev目录下不会有类似/dev/sdaX的设备节点。为了识别硬盘,我们需要加载硬盘控制器的驱动。
前面提到,Intel的SATA控制器可以运行在Compatibility模式和AHCI模式。笔者的机器的SATA控制器工作在AHCI模式,因此使用ahci驱动。但是在试图加载ahci模块时,ahci模块在报告了若干找不到的符号后,加载以失败告终,如图4-16所示。
图 4-16 加载ahci模块失败
显然,这些找不到的符号应该定义在其他某个(些)未加载的模块中,我们需要使用命令modinfo查看一下模块ahci依赖了哪些模块,我们使用参数"-F depends"告诉modinfo仅显示ahci的依赖信息:
根据modinfo的输出,我们可以看到,ahci模块依赖libahci模块。因此,我们首先需要加载libahci模块,然后再加载ahci模块,如图4-17所示。
图 4-17 加载ahci模块成功
加载ahci模块后,该模块正确识别出了硬盘控制器,并且内核在devtmpfs中也建立了对应硬盘的设备节点。
虽然使用insmod可以完成加载模块的功能,但是我们发现必须要对模块的依赖关系非常清楚。幸运的是ahci只依赖libahci,而且libahci不依赖其他模块。但是如果一个模块依赖多个模块,并且依赖的模块又依赖其他的模块,如此下去,可想而知,加载这样一个模块将是多么复杂。好在kmod中提供了另外一个加载/卸载模块的工具modprobe,与insmod和rmmod相比,modprobe可以自动加载/卸载模块依赖的其他模块,而模块间的依赖关系存储在modules目录下的modules.dep中。以硬盘驱动这几个模块为例,其在modules.dep中的内容如下:
该片段表示模块ahci.ko依赖libahci.ko,而模块libahci.ko和ata_piix.ko不依赖其他模块。
如果模块间依赖关系简单也罢,但是如果比较复杂,那么手动去创建modules.dep是不现实的,幸运的是,模块管理工具中也提供了相应的工具创建modules.dep文件,这个工具就是depmod。在安装内核模块时,安装脚本将自动调用这个工具,创建modules.dep等文件。
为了加快搜索过程,modules.dep通常使用更有效率的Trie树来组织,并命名为modules.dep.bin。module-init-tools中实现的modprobe上述两种格式都支持,当然首选使用modules.dep.bin。但是kmod仅支持使用Trie树形式存储的modules.dep.bin。
接下来我们就体验一下使用modprobe加载驱动模块。首先需要创建模块依赖关系文件。一般而言,对于通用系统,通常在安装系统时使用depmod创建依赖关系文件,然后如果模块有变动,可以使用depmod命令更新这些文件。
在这里,在安装内核模块时,安装脚本已经调用depmod创建了modules.dep和使用Trie树组织的modules.dep.bin,注意需要将使用Trie树组织的modules.dep.bin复制到initramfs。当然,如果使用了module-init-tools中的模块管理工具,那么这里完全可以体验一下手写modules.dep文件。
为了验证modprobe是否正确加载了模块,可以使用命令lsmod查看内核加载的模块。但是lsmod是通过proc和sysfs获取内核信息的,因此,为了使用lsmod,首先需要挂载proc和sysfs文件系统。为此,我们需要在initramfs的根目录下创建proc和sys目录作为挂载点:
同时修改init脚本,添加挂载proc和sysfs文件系统的脚本:
重新压缩initramfs,并将其复制到虚拟机,重新启动系统,使用命令modprobe安装ahci模块,并使用命令lsmod查看内核安装的模块,如图4-18所示。
图 4-18 使用modprobe加载ahci模块
根据lsmod的输出可见,虽然我们并没有明确的指示modprobe加载模块libahci,但是modprobe根据modules.dep.bin中记录的依赖关系,自动加载了ahci依赖的模块libahci。