24.4 额外(单一)核心模块编译

我们现在知道核心所支持的功能当中,有直接编译到核心内部的,也有使用外挂模块的,外挂模块可以简单的想成就是驱动程序 啦!那么也知道这些核心模块依据不同的版本,被分别放置到 /lib/modules/$(uname -r)/kernel/ 目录中,各个硬件的驱动程序则是放置到 /lib/modules/$(uname -r)/kernel/drivers/ 当中!换个角度再来思考一下,如果刚刚我自己编译的数据中,有些驱动程序忘记编译成为模块了,那是否需要重新进行上述的所有动作? 又如果我想要使用硬件厂商释出的新驱动程序,那该如何是好?

24.4.1 编译前注意事项

由于我们的核心原本就有提供很多的核心工具给硬件开发商来使用, 而硬件开发商也需要针对核心所提供的功能来设计他们的驱动程序模块,因此, 我们如果想要自行使用硬件开发商所提供的模块来进行编译时,就需要使用到核心所提供的原始文件当中, 所谓的头文件案 (header include file) 来取得驱动模块所需要的一些函数库或标头的定义啦! 也因此我们常常会发现到,如果想要自行编译核心模块时,就得要拥有核心源代码嘛!

那核心源代码我们知道他是可能放置在 /usr/src/ 下面,早期的核心源代码被要求一定要放置到 /usr/src/linux/ 目录下,不过,如果你有多个核心在一个 Linux 系统当中,而且使用的源代码并不相同时, 呵呵~问题可就大了!所以,在 2.6 版以后,核心使用比较有趣的方法来设计他的源代码放置目录, 那就是以 /lib/modules/$(uname -r)/build 及 /lib/modules/$(uname -r)/source 这两个链接文件来指向正确的核心源代码放置目录。如果以我们刚刚由 kernel 3.10.89vbird 创建的核心模块来说, 那么他的核心模块目录下面有什么咚咚?

[[email protected] ~]# ll -h /lib/modules/3.10.89vbird/
lrwxrwxrwx.  1 root root   30 Oct 20 14:27 build -> /usr/src/kernels/linux-3.10.89
drwxr-xr-x. 11 root root 4.0K Oct 20 14:29 kernel
-rw-r--r--.  1 root root 668K Oct 20 14:29 modules.alias
-rw-r--r--.  1 root root 649K Oct 20 14:29 modules.alias.bin
-rw-r--r--.  1 root root 5.8K Oct 20 14:27 modules.builtin
-rw-r--r--.  1 root root 7.5K Oct 20 14:29 modules.builtin.bin
-rw-r--r--.  1 root root 208K Oct 20 14:29 modules.dep
-rw-r--r--.  1 root root 301K Oct 20 14:29 modules.dep.bin
-rw-r--r--.  1 root root  316 Oct 20 14:29 modules.devname
-rw-r--r--.  1 root root  81K Oct 20 14:27 modules.order
-rw-r--r--.  1 root root  131 Oct 20 14:29 modules.softdep
-rw-r--r--.  1 root root 269K Oct 20 14:29 modules.symbols
-rw-r--r--.  1 root root 339K Oct 20 14:29 modules.symbols.bin
lrwxrwxrwx.  1 root root   30 Oct 20 14:27 source -> /usr/src/kernels/linux-3.10.89

比较有趣的除了那两个链接文件之外,还有那个 modules.dep 文件也挺有趣的, 那个文件是记录了核心模块的相依属性的地方,依据该文件,我们可以简单的使用 modprobe 这个指令来载入模块呢!至于核心源代码提供的头文件,在上面的案例当中, 则是放置到 /usr/src/kernels/linux-3.10.89/include/ 目录中,当然就是借由 build/source 这两个链接文件来取得目录所在的啦!^_^

由于核心模块的编译其实与核心原本的源代码有点关系的,因此如果你需要重新编译模块时, 那除了 make, gcc 等主要的编译软件工具外,你还需要的就是 kernel-devel 这个软件!记得一定要安装喔!而如果你想要在默认的核心下面新增模块的话,那么就得要找到 kernel 的 SRPM 文件了! 将该文件给他安装,并且取得 source code 后,才能够顺利的编译喔!

24.4.2 单一模块编译

想像两个情况:

  • 如果我的默认核心忘记加入某个功能,而且该功能可以编译成为模块,不过, 默认核心却也没有将该项功能编译成为模块,害我不能使用时,该如何是好?
  • 如果 Linux 核心源代码并没有某个硬件的驱动程序 (module) ,但是开发该硬件的厂商有提供给 Linux 使用的驱动程序源代码,那么我又该如何将该项功能编进核心模块呢?

很有趣对吧!不过,在这样的情况下其实没有什么好说的,反正就是 “去取得源代码后,重新编译成为系统可以载入的模块”啊!很简单,对吧!^_^! 但是,上面那两种情况的模块编译行为是不太一样的,不过,都是需要 make, gcc 以及核心所提供的 include 头文件与函数库等等。

  • 硬件开发商提供的额外模块

很多时候,可能由于核心默认的核心驱动模块所提供的功能你不满意,或者是硬件开发商所提供的核心模块具有更强大的功能, 又或者该硬件是新的,所以默认的核心并没有该硬件的驱动模块时,那你只好自行由硬件开发商处取得驱动模块,然后自行编译啰!

如果你的硬件开发商有提供驱动程序的话,那么真的很好解决,直接下载该源代码,重新编译, 将他放置到核心模块该放置的地方后就能够使用了!举个例子来说,鸟哥在 2014 年底帮厂商制作一个服务器的环境时, 发现对方喜欢使用的磁盘阵列卡 (RAID) 当时并没有被 Linux 核心所支持,所以就得要帮厂商针对该磁盘阵列卡来编译成为模块啰! 处理的方式,当然就是使用磁盘阵列卡官网提供的驱动程序来编译啰!

虽然你可以选择“RHEL/CentOS 7 x86_64”这个已编译的版本来处理,不过因为我们的核心已经做成自订的版本, 变成 3.10.89vbird 这样,忘记加上 x86_64 的版本名,会导致该版本的自动安装脚本失败!所以,算了!我们自己来重新编译吧! 因此,请下载“Open Source Driver”的版本喔!同时,鸟哥假设你将下载的文件放置到 /root/raidcard 目录内喔!

# 1\. 将文件解压缩并且开始编译:
[[email protected] ~]# cd /root/raidcard
[[email protected] raidcard]# ll
-rw-r--r--. 1 root root 501477 Apr 23 07:42 RR64xl_Linux_Src_v1.3.9_15_03_07.tar.gz
[[email protected] raidcard]# tar -zxvf RR64xl_Linux_Src_v1.3.9_15_03_07.tar.gz
[[email protected] raidcard]# cd rr64xl-linux-src-v1.3.9/product/rr64xl/linux/
[[email protected] linux]# ll
-rw-r--r--. 1 dmtsai dmtsai 1043 Mar  7  2015 config.c
-rwxr-xr-x. 1 dmtsai dmtsai  395 Dec 27  2013 Makefile      # 要有这家伙存在才行!
[[email protected] linux]# make
make[1]: Entering directory `/usr/src/kernels/linux-3.10.89'
  CC [M]  /root/raidcard/rr64xl-linux-src-v1.3.9/product/rr64xl/linux/.build/os_linux.o
  CC [M]  /root/raidcard/rr64xl-linux-src-v1.3.9/product/rr64xl/linux/.build/osm_linux.o
.....(中间省略).....
  LD [M]  /root/raidcard/rr64xl-linux-src-v1.3.9/product/rr64xl/linux/.build/rr640l.ko
make[1]: Leaving directory `/usr/src/kernels/linux-3.10.89'
[[email protected] linux]# ll
-rw-r--r--. 1 dmtsai dmtsai    1043 Mar  7  2015 config.c
-rwxr-xr-x. 1 dmtsai dmtsai     395 Dec 27  2013 Makefile
-rw-r--r--. 1 root   root   1399896 Oct 21 00:59 rr640l.ko  # 就是产生这家伙!
# 2\. 将模块放置到正确的位置去!
[[email protected] linux]# cp rr640l.ko /lib/modules/3.10.89vbird/kernel/drivers/scsi/
[[email protected] linux]# depmod -a   # 产生模块相依性文件!
[[email protected] linux]# grep rr640 /lib/modules/3.10.89vbird/modules.dep
kernel/drivers/scsi/rr640l.ko:  # 确定模块有在相依性的配置文件中!
[[email protected] linux]# modprobe rr640l
modprobe: ERROR: could not insert 'rr640l': No such device
# 要测试载入一下才行,不过,我们实际上虚拟机没有这张 RAID card,所以出现错误是正常的啦!
# 3\. 若开机过程中就得要载入此模块,则需要将模块放入 initramfs 才行喔!
[[email protected] linux]# dracut --force -v --add-drivers rr640l \
> /boot/initramfs-3.10.89vbird.img 3.10.89vbird
[[email protected] linux]# lsinitrd /boot/initramfs-3.10.89vbird.img | grep rr640

通过这样的动作,我们就可以轻易的将模块编译起来,并且还可以将他直接放置到核心模块目录中, 同时以 depmod 将模块创建相关性,未来就能够利用 modprobe 来直接取用啦! 但是需要提醒你的是,当自行编译模块时, 若你的核心有更新 (例如利用自动更新机制进行线上更新) 时,则你必须要重新编译该模块一次, 重复上面的步骤才行!因为这个模块仅针对目前的核心来编译的啊!对吧!

  • 利用旧有的核心源代码进行编译

如果你后来发现忘记加入某个模块功能了,那该如何是好?其实如果仅是重新编译模块的话, 那么整个过程就会变的非常简单!我们先到目前的核心源代码所在目录下达 make menuconfig , 然后将 NTFS 的选项设置成为模块,之后直接下达:make fs/ntfs/

那么 ntfs 的模块 (ntfs.ko) 就会自动的被编译出来了! 然后将该模块复制到 /lib/modules/3.10.89vbird/kernel/fs/ntsf/ 目录下, 再执行 depmod -a ,呵呵~就可以在原来的核心下面新增某个想要加入的模块功能啰~ ^_^

24.4.3 核心模块管理

核心与核心模块是分不开的,至于驱动程序模块在编译的时候,更与核心的源代码功能分不开~ 因此,你必须要先了解到:核心、核心模块、驱动程序模块、核心源代码与头文件案的相关性, 然后才有办法了解到为何编译驱动程序的时候老是需要找到核心的源代码才能够顺利编译! 然后也才会知道,为何当核心更新之后,自己之前所编译的核心模块会失效~

此外,与核心模块有相关的,还有那个很常被使用的 modprobe 指令, 以及开机的时候会读取到的模块定义数据文件 /etc/modprobe.conf , 这些数据你也必须要了解才行~相关的指令说明我们已经在 第十九章、开机流程、模块管理与 Loader 内谈过了, 你应该要自行前往了解喔! ^_^

下一节:如果你跟鸟哥一样,曾经为了某些缘故需要最新的 4.x.y 的核心版本来实作某些特定的功能时,那该如何是好?没办法,只好使用最新的核心版本来编译啊! 你可以依照上面的程序来一个一个处理,没有问题~不过,你也可以根据 ELRepo 网站提供的 SRPM 来重新编译打包喔! 当然你可以直接使用 ELRepo 提供的 CentOS 7.x 专属的核心来直接安装。