内核与内核模块

在整个开机的过程当中,是否能够成功驱动主机的硬件设备, 是内核 (kernel) 的工作,而内核一般都是压缩文件,因此在使用内核之前,需要将它解压缩后,才能加载到内存当中。

为了应对不断更新迭代的硬件,目前的内核都是具有 可读取模块化驱动程序 的功能, 也就是所说的 modules (模块化) 的功能。

所谓的模块化可以将它看作是一个插件, 该插件可能由硬件开发厂商提供,也有可能是内核本来就支持。较新的硬件, 通常都需要硬件开发商提供驱动程序模块。

内核与内核模块的存放位置:

  • 内核:/boot/vmlinuz/boot/vmlinuz-version

  • 内核解压缩所需 RAM Disk: /boot/initramfs (/boot/initramfs-version);

  • 内核模块: /lib/modules/version/kernel 或 /lib/modules/$(uname -r)/kernel

  • 内核源代码 /usr/src/linux 或 /usr/src/kernels/ (要安装才会有,预设不安装)

如果内核被顺利地加载到系统当中,就会有几个信息记录下来:

  • 内核版本: /proc/version

  • 系统内核功能:/proc/sys/kernel/

如果有个的硬件,而操作系统不支持,这时候应该重新编译核心,并加入最新的硬件驱动程序源码;将该硬件的驱动程序编译成为模块,在开机时加载该模块。

内核模块与依赖关系

基本上,内核模块的存放位置是在 /lib/modules/$(uname -r)/kernel 当中,里面主要还分成几个目录:

  • arch:与硬件平台有关的项目,例如 CPU 的等级等等;
  • crypto:核心所支持的加密的技术 ,例如 md5 或者是 des 等等
  • drivers:一些硬件的驱动程序,例如显示适配器、网络卡、PCI 相关硬件等等;
  • fs:核心所支持的 filesystems ,例如 vfat, reiserfs, nfs 等等;
  • lib:一些函式库;
  • net:与网络有关的各项协议数 据,还有防火墙模块 (net/ipv4/netfilter/*) 等等;
  • sound:与音效有关的各项模块;

如果要一个一个的去检查这些模块的主要信息,然后定义出他们的依赖关系,这显然是不人性化的。 Linux 提供了一些模块相依性的解决方案,在 /lib/modules/$(uname -r)/modules.dep 文件中记录了内核支持的模块的各项依赖关系。并且使用 depmod 命令可以实现建立该文件的需求:

1
[root@bogon ~]# depmod [-Ane]

选项参数:

  • -A:不加任何参数时, depmod 会主动的去分析目前内核的模块,并且重新写入 /lib/modules/$(uname -r)/modules.dep 当中。若加入 -A 参数时,则 depmod 会去搜寻比 modules.dep 内还要新的模块,如果真找到新模块,才会更新。

  • -n:不写入 modules.dep ,而是将结果输出到屏幕上(standard out);

  • -e:显示出目前已加载的不可执行的模块名称。

例如,做好了一个名为 a.ko 的网卡驱动程序,该如何更新模块的依恋关系呢?

1
2
[root@bogon ~]# cp a.ko /lib/modules/$(uname -r)/kernel/drivers/net
[root@bogon ~]# depmod

内核模块扩展名一般是 .ko.ko.xz 结尾的, 当使用 depmod 之后, 该程序会跑到模块标准存放目录 /lib/modules/$(uname -r)/kernel 并依据相关目录的定义将全部的模块拿出来分析,最终才将分析的结果写入 modules.dep 文件中。 这个文件至关重要,因为它会影响到 modprobe 命令的使用。

内核模块的查看

使用 lsmod 命令可以很方便地看到当前的内核中加载了多少模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@bogon ~]# lsmod 
Module Size Used by
nf_conntrack_ftp 18638 0
nf_conntrack_ipv4 15053 0
nf_defrag_ipv4 12729 1 nf_conntrack_ipv4
nf_conntrack 133095 2 nf_conntrack_ftp,nf_conntrack_ipv4
# ......省略部分内容......
nf_tables 147456 184
sg 40960 0
joydev 24576 0
vmw_vmci 86016 2 vmw_balloon,vmw_vsock_vmci_transport
i2c_piix4 24576 0
# ......省略部分内容......

其中,显示的内容包括有:

  • 模块名称(Module);

  • 模块的大小(size);

  • 此模块是否被其他模块所使用 (Used by)。

从上面的输出结果可以看出,在 nf_conntrack 模块先被加载后,nf_conntrack_ftp 这个模块才能够进一步的加载到系统中,这两者间是有依赖关系的。

要想了解某个内核模块的相关信息,可以使用 modinfo 命令:

1
modinfo [-adln] [module_name|filename]

选项与参数:

  • -a, --author:仅列出作者名称;

  • -d, --description:仅列出该 modules 的说明 (description) ;

  • -l, --license:仅列出授权 (license);

  • -n, --filename:仅列出该模块的详细路径;

  • -F, --field=FIELD:仅列出指定字段对应的信息;

使用 modinfo 除了可以查看 已经加载到内核中的模块 之外,还可以查看 某个模块文件:

1
modinfo /lib/modules/4.18.0-80.el8.x86_64/kernel/net/ipv4/ip_gre.ko.xz

内核模块的加载和移除

如果想要手动加载模块,最简单而且最建议的,是使用 modprobe 命令, 这是因为 modprobe 会主动去搜寻modules.dep 的内容,先解决了模块的依赖关系后, 才决定需要加载的模块有哪些。

而如果使用 insmod 命令,则完全由使用者自行加载一个完整文件路径的模块, 而且不会主动的分析模块的依赖关系。

1
[root@bogon ~]# insmod [/full/path/module_name] [parameters]

例如,手动载入 fat.ko.xz 这个文件系统相关的内核模块:

1
2
3
4
[root@bogon ~]# insmod /lib/modules/$(uname -r)/kernel/fs/fat/fat.ko.xz
[root@bogon ~]# lsmod | grep fat
fat 81920 0
[root@bogon ~]#

使用 insmod 命令会立即将给定的模块加载,但是给定的模块必须是完整路径。想要移除给定的模块则需要使用 rmmod 命令:

1
[root@bogon ~]# rmmod [-f] module_name

选项与参数:

  • -f:强制将该模块移除掉,不论是否正被使用;

例如,删除刚刚载入的 fat.ko.xz 内核模块:

1
[root@bogon ~]# rmmod fat

再比如,手动加载 vfat.ko.xz 这个文件系统相关的内核模块:

1
2
3
[root@bogon ~]# insmod /lib/modules/$(uname -r)/kernel/fs/vfat/vfat.ko.xz            
insmod: ERROR: could not load module /lib/modules/4.18.0-80.el8.x86_64/kernel/fs/vfat/vfat.ko.xz: No such file or directory
[root@bogon ~]#

使用 insmod 与 rmmod 命令的问题就是,必须要事先自己找到模块的位置才行。所以更建议直接使用 modprobe 命令来处理模块加载的问题:

1
[root@study ~]# modprobe [-cfr] module_name

选项与参数:

  • -c, --showconfig:列出目前系统所有的模块,会输出更详细的代号对应表。

  • -f, --force:强制加载该模块;

  • -r, --remove:类似于 rmmod 命令 ,用来移除某个模块。

例如,加载和移除 vfat.ko.xz 这个文件系统相关的内核模块:

1
2
3
4
5
6
7
[root@bogon ~]# modprobe vfat 
[root@bogon ~]# lsmod | grep vfat
vfat 24576 0
fat 81920 1 vfat
[root@bogon ~]# modprobe -r vfat
[root@bogon ~]# lsmod | grep vfat
[root@bogon ~]#

核心模块的额外参数设定

如果有某些特殊的需求导致必须要让内核模块加上某些参数时,就要对自定义模块进行相关的配置了。

开机流程当中,在 sysinit.target 系统初始化的阶段会加载用户自定义模块,有两个地方可以处理模块加载的问题,包括:

  • /etc/modprobe.d/*.conf :单纯要让内核从哪些位置加载模块
  • /etc/modules-load.d/*.conf:对内核模块做参数设置

基本上 systemd 已经将开机会用到的驱动程序全部加载了,因此,在一般情况下,这个部份应该无需变动。不过, 如果有某些特定的参数要处理时,就可以在这里做配置。比如,我们要做网络链接追踪,就需要在开机启动流程中加载相对应的内核模块:

1
2
3
4
5
[root@localhost ~]# vim /etc/modules-load.d/netfilter.conf 
# Load nf_conntrack.ko at boot
nf_conntrack
nf_conntrack_ipv4
nf_defrag_ipv4

再比如自定义了 vsftpd 服务的监听端口为 555,这时就需要修改防火墙设定,其中一个针对 FTP 很重要的防火墙模块为 nf_conntrack_ftp, 因此可以将这个模块写入到系统开机流程中:

1
2
3
[root@localhost ~]# vim /etc/modules-load.d/vbird.conf
nf_conntrack_ftp
[root@localhost ~]#

按照配置标准,应该一个模块 (驱动程序) 写一行。如果要对这些内核模块做参数设定,应该在 /etc/modules-load.d/ 目录下做配置。比如要对链接追踪模块的哈希表做参数设置:

1
2
[root@localhost ~]# vim /etc/modprobe.d/netfilter.conf 
options nf_conntrack hashsize=65536

当然,如果只是让内核加载模块,没有指定自己定义的参数,就会以默认值加载并运行。所以对于修改了服务端口的 vsftpd 来说,如果不设置参数,模块就默认处理 21 号端口,这时候我们需要将其调整到自定义的 555 号端口才能正常工作:

1
2
[root@localhost ~]# vim /etc/modprobe.d/vbird.conf
options nf_conntrack_ftp ports=555

之后重新启动就能够顺利的载入并且处理好些模块了。当然,如果不想重启,而直接进行加载,可以使用下面的方式来处理:

1
2
3
4
5
6
7
8
9
[root@localhost ~]# lsmod | grep nf_conntrack_ftp
[root@localhost ~]# # 因为没有加载这个模块,所以不会有任何信息输出
[root@localhost ~]#
[root@localhost ~]# systemctl restart systemd-modules-load.service
[root@localhost ~]#
[root@localhost ~]# lsmod | grep nf_conntrack_ftp
nf_conntrack_ftp 18478 0
nf_conntrack 139224 7 nf_nat,nf_nat_ipv4,nf_nat_ipv6,xt_conntrack,nf_conntrack_ftp,nf_conntrack_ipv4,nf_conntrack_ipv6
[root@localhost ~]#

通过上述的方式,就可以在开机的时候将所需要的驱动程序加载或者是调整这些模块的外加参数了。

有钱任性,请我吃包辣条
0%