第六章:内核游戏

内核是计算机的机密部分之一,凡人不该涉足。

在开源类Unix世界的许多地方,干预内核是改变系统行为的一种非常可行和预期的方法。如果允许的话,这可能是调整其他操作系统的好方法。

我们将讨论内核的sysctl接口,以及如何使用它来更改正在运行的内核。

内核的某些部分,只有在系统处于启动的早期阶段时才能更改。引导加载程序允许你在主机找到其文件系统之前调整内核。

对于特定的很有针对性的环境,最好的方法是构建自己的内核。

FreeBSD有一个模块化内核,这意味着可以从操作系统加载或卸载整个内核块,根据需要打开或关闭整个子系统。这在可移动硬件(如PC卡和USB设备)的时代非常有用。可加载内核模块会影响性能、系统行为和硬件支持。

最后,我们将介绍内核的基本调试,包括它发出的一些看起来很可怕的消息,以及何时以及如何启动备用内核。

什么是内核?

the kernel is the interface between the hardware and the software 内核是硬件和软件之间的接口。

内核提供程序访问硬件资源所需的所有软件接口。

FreeBSD包含的内核和任何模块都是/boot/kernel目中的文件。第三方内核模块进入/boot/modules。系统中其他位置的文件不是内核的一部分。非内核文件通常为用户区,这意味着即使用户使用内核设施,它们也是面向用户的。

由于内核只是一组文件,因此您可以在特殊情况下使用其他内核。在构建了自己内核的系统上,您会发现/boot/kernel.old,这是一个包含在当前内核之前安装的内核的目录。我习惯性地将随系统安装的内核复制到/boot/kernel.install中。您还可以创建自己的特殊内核。FreeBSD团队使配置和安装内核变得尽可能简单。更改内核的最简单和最受支持的方法是通过sysctl接口。

内核状态: sysctl

sysctl(8)程序允许你查看内核使用的值,并在某些情况下对其进行设置。

-o标志显示不透明的sysctls。

文件sysctl.out现在包含数百个sysctl变量及其值,其中大多数看起来毫无意义。然而,你可以在不了解太多的情况下解释其中的一些:

 

sysctl MIBs

sysctls以树格式组织,称为管理信息库(management information base——MIB),有几个大类,如net(网络)、kern(内核)和vm(虚拟内存)。下表列出了运行GENERIC内核的系统上sysctl MIB树的根。

以上类别中的每一个都被进一步划分。例如net类又分为IP、ICMP、TCP和UDP等类别。

每个类别都是通过将父类别及其所有子类别串在一起来命名的,例如:

这里我们有五个从kern类别中间挑选出来的系统。前两个直接位于kern标签之下,除了与内核相关的事实外,没有与其他值进行合理的分组。其余三个都以kern.ipc开头;它们是内核系统IPC(进程间通信)部分的一部分。如果你继续阅读你保存的sysctl,你会发现一些sysctl变量有几个类别。

sysctl 值和定义

每个MIB都有一个值,表示内核使用的缓冲区、设置或特性。更改该值会改变内核的操作方式。例如,内核处理数据包的发送和接收,但默认情况下不会将数据包从一个接口发送到另一个接口。你可以更改sysctl以允许此转发,从而将你的主机变成路由器。

每个sysctl值要么是字符串、整数、二进制值,要么是不透明的(opaque)。

许多sysctl值没有很好的记录;没有一个文档列出所有可用的sysctl MIB机器功能。MIB的文档通常出现在相应函数的手册页中,有时仅出现在源代码中。例如MIB kern.securelevel是security(7)。尽管近年来sysctl文档有所扩展,但许多MIB依然没有文档。

幸运的是,一些MIB具有明显的含义。例如,正如我们在本章后面讨论的那样,如果您经常启动不同的内核,这是一个重要的MIB:

如果你正在调试一个问题,并且必须连续使用几个不同的内核重新启动,你很容易忘记你启动了哪个内核(实际上,我从来没有遇到过这种情况)。因此,提醒可能会有所帮助。

了解sysctl功能的一个简单方法是将-d开关与完整MIB一起使用。这将打印sysctl的简要描述:

这个简短的定义告诉您,这个sysctl精确地控制您可能认为它所做的事情。不幸的是,并非所有sysctls都提供带有-d的定义。虽然这个例子相当简单,但其他MIB可能更难猜测。

查看 sysctls

要查看MIB树的特定子树中可用的所有MIB,请使用sysctl命令和您要查看的树部分的名称。例如,要查看kern下的所有内容,请输入以下命令:

这份名单持续了相当长的一段时间。如果您刚刚熟悉sysctls,您可以使用它来查看可用的内容。要获取特定sysctl的确切值,请将完整的MIB名称作为参数:

MIB kern.securelevel的整数值为-1。我们将在第9章讨论这个sysctl的含义及其价值。

更改 sysctls

有些sysctls是只读的,比如,查看一下硬件的MIBs:

FreeBSD不允许改变此类值,试图改变它们不会造成任何伤害,但会收到警告。

另一方面,有些MIB是可以更改的,比如:

此MIB确定用户是否可以装载可移动介质,如CDROM和软盘驱动器,如第13章所述。更改此MIB不需要在内核中进行大量调整或修改硬件;这只是一个内核内权限设置。要更改此值,请使用sysctl(8)命令、sysctl MIB、等号和所需值:

sysctl(8)程序通过显示sysctl名称、旧值和新值进行响应。此sysctl现在已更改。可以像这样动态调整的sysctl称为运行时可调sysctl(runtime tunable sysctl)。

自动设置sysctls

一旦你根据自己的想法调整了内核的设置,你就会希望这些设置在重启后保持不变(也称固化设置)。为此,请使用/etc/sysctl.conf文件。在此文件中列出要设置的每个sysctl和所需的值。例如,要在启动时设置vfs.usermount sysctl,请在/etc/sysctl.conf中添加以下内容:

内核环境

内核是由引导加载程序启动的程序。引导加载程序可以将环境变量传递给内核,从而创建内核环境。内核环境也是一个MIB树,很像sysctl树。这些环境变量中的许多(但不是全部)后来被映射到只读sysctls上。

Viewing the Kernel Environment

使用kenv(8)查看内核环境。给它一个内核环境变量的名称,只查看该变量,或者在没有参数的情况下运行它,查看整个树。

这些变量看起来非常像加载器变量。因为它们是加载器变量。它们经常与初始硬件探测有关。

这些环境变量也称为启动时可调sysctls(boot-time tunable sysctls)或可调量(tunables),通常与低级硬件设置相关。例如,当内核首次探测硬件时,它必须决定是提供基于标识(ident-based)的标签,还是基于GPT ID(GPT ID-based)的标签。这个决定必须在内核中的任何东西访问硬盘之前就做出来,如果不重启机器,你就无法改变它们。

内核环境变量只能从加载器中设置。你可以在启动时手动进行更改,也可以在/boot/loader.conf中设置更改,以便在下一次启动时生效(参阅第四章)。

就像sysctl.conf一样,在loader.conf中设置可调值会让你真的把机器搞砸。好消息是,这些值很容易修复。

不要混淆只能在启动时设置的sysctl值、可以动态调整的sysctl数值和可以动态设置但已配置为在启动时自动调整的sysctls。请记住,启动时可调系统涉及低级内核函数,而运行时可调则涉及高级函数。让sysctl在启动时自我调整只是保存工作的一个例子——它不会改变sysctl所属的类别。

向设备驱动程序提供提示

您可以使用环境变量来告诉设备驱动程序所需的设置。您将通过阅读驱动程序手册页和其他文档来了解这些设置。此外,许多古老的硬件要求内核以非常特定的IRQ和内存值对其进行寻址。如果你年纪大了,还记得“即插即用”、“硬件配置”软盘和总线主卡的特殊插槽,你就知道我在说什么,即使在今天,这些系统中的一个也可能污染你的硬件柜。(如果你还太年轻,可以给我们中的一个人买一杯饮料,听听我们的恐怖故事。)你可以告诉FreeBSD在你指定的任何IRQ或内存地址探测此类硬件,这在你有一张已知配置的卡,但几年前可以改变该配置的软盘已经生物降解的情况下非常有用。

如果你真的很不幸,你可能有一台内置软盘驱动器的机器。在/boot/device.hints中查找配置此硬件的条目:

这些条目都是fdc(4)设备驱动程序➊的提示。该条目用于fdc设备编号0➋。如果启用此设备,启动内核将在内存地址(或端口➌)0x3F0➍、IRQ 6➎和DRQ 2➏处探测卡。如果它发现具有这些特征的设备,它将被分配fdc(3)驱动程序。如果那个设备不是软盘驱动器,你会遇到有趣的崩溃。

所有这些提示和引导时间可调系统都可以在引导加载程序中使用,并且可以在OK提示符下交互式设置,如第4章所述。您可以在不编辑loader.conf的情况下测试设置,找到有效的值,然后将更改永久保存在文件中。

启动时可调参数和sysctl允许您调整内核的行为方式,但内核模块允许您为正在运行的内核添加功能。

内核模块

内核模块是内核的一部分,可以在需要时启动或加载,在不使用时卸载。当您插入一块硬件并将其与该硬件一起移除时,可以加载内核模块。这大大扩展了系统的灵活性。此外,一个编译了所有可能功能的内核会相当大。使用模块,你可以拥有一个更小、更高效的内核,并且只在需要时加载很少使用的功能。

正如默认内核保存在/boot/kernel/kernel文件中一样,内核模块是/boot/kernel下的其他文件。在该目录中查看数百个内核模块文件。每个内核模块名称都以.ko结尾。一般来说,文件以模块中包含的功能命名。例如,/boot/kernel/wlan.ko文件处理wlan(4)无线层。FreeBSD需要此模块用于无线网络。

查看加载的模块

kldstat(8)命令展示内核中加载的模块:

此桌面加载了三个内核模块。第一个是内核本身➊;然后,支持ZFS➌和ZFS➌所需的OpenSolaris内核函数的模块如下。我在这个主机上试用Linux软件(见第17章),所以发现加载了linprocfs(5)模块➍并不奇怪。

每个模块都包含一个或多个子模块,您可以使用kldstat -v查看这些子模块,但内核本身有几百个子模块,因此请准备好大量输出。

加载和卸载模块

内核模块的加载和卸载是通过kldload(8)和kldunload(8)完成的。

例如,假设我正在测试主机上试验IPMI。这需要ipmi(4)内核模块。可使用以下方式加载:

也可以使用模块名称来加载(模块名称没有.ko):

做完试验后,可以卸载模块:

任何正在使用的模块都不允许卸载:

在启动时加载模块

使用/boot/loader.conf在启动时加载模块。默认的loader.conf包含许多加载内核模块的示例,但语法始终相同。取内核模块的名称,去掉尾随的.ko,并添加字符串_load=“YES”。例如,要在引导时自动加载/boot/kernel/procfs.ko模块,请将以下内容添加到loader.conf中:

当然,最困难的部分是知道要加载哪个模块。最简单的是设备驱动程序;如果安装了内核不支持的新网卡或SCSI卡,则可以加载驱动程序模块,而无需重新配置内核。在这种情况下,您需要找出支持您的卡的驱动程序;手册页和谷歌是你的朋友。在本书中,我将给出内核模块的具体指针,以解决特定的问题。

不过,等一下——如果FreeBSD在启动时几乎可以识别所有内容,为什么它会让你加载设备驱动程序来识别硬件?这个问题问得很好!答案是,您可能已经构建了自己的自定义内核,并删除了对不使用的硬件的支持。你不知道如何构建内核吗?好吧,让我们现在就解决这个问题。

构建你自己的内核

最终,你会发现,你不能只使用sysctl(8)和模块来调整你的内核,你唯一的解决方案就是构建一个定制的内核。这听起来比实际困难得多;我们在这里讨论的不是编写代码,只是编辑一个文本文件并运行几个命令。如果你遵循这个过程,它是完全安全的。如果你不遵循这个过程,好吧,这就像在错误的一边开车。(市中心。在高峰时段。)但从坏内核中恢复也没那么糟糕。

默认安装中附带的内核称为GENERIC。GENERIC被配置为在各种各样的硬件上运行,尽管不一定是最佳的。在过去15年左右的时间里,GENERIC在大多数硬件上运行良好,我经常在生产中使用它。自定义内核时,可以添加对特定硬件的支持,删除对不需要的硬件的支持或启用GENERIC中未包含的功能。

没事不要重建内核。只有在使用实验功能或专用硬件时才需要重建内核。

准备工作

构建内核需要内核源代码,即src。查看/usr/src目录,如果里面有内容,就表示已经安装了内核源代码。否则可以从FreeBSD镜像站下载源代码,或跳到第十八章查看使用svnlite(1)来安装源代码。

构建新内核之前,你必须知道系统有什么硬件。这可能很难确定,组件上的品牌名称不一定描述设备的身份或功能。许多公司使用重新命名的通用组件。

第四章讨论的/var/run/dmesg.boot文件中包含了系统上发现的所有硬件,这对开发新内核极具参考价值。

总线和附件

计算机中的每个设备都连接到其他设备。如果你仔细阅读dmesg.boot,你可以看到这些附件链。以下是一组经过编辑的启动消息来演示:

我们在这个系统上的第一个设备是acpi0➊。你可能不知道那是什么,但你总是可以通过阅读man acpi来了解。(或者,如果你必须,你可以阅读本章的其余部分。)acpi0设备上有一个电源按钮➋。CPU➌也连接到acpi0,计时设备➍也是如此。最终,我们将第一个PCI桥pcib0➎连接到acpi0设备。第一条PCI总线➏依次连接到PCI桥。

因此,您的通用PCI设备连接到总线层次结构,总线层次结构又连接到PCI桥,与计算机的其他部分通信。您可以读取dmesg.boot并绘制系统上所有设备的树;虽然这不是必需的,但了解附加在哪里的内容会使配置内核更有可能成功。

如果您有疑问,请使用pciconf(8)查看您的系统上实际有什么。pciconf-lv将列出连接到系统的每个PCI设备,无论当前内核是否找到了它的驱动程序。

备份你的工作内核

一个糟糕的内核会使你的系统无法启动,所以你绝对必须始终保持一个好的内核。内核安装过程会将之前的内核保留在/boot/kernel.old目录中以备备份。这很好,因为你能够退缩,但我建议你走得更远。有关启动备用内核的详细信息,请参阅第四章。

如果你不保持已知的良好备份,可能会发生以下情况。如果你构建了一个新的内核,发现你犯了一个小错误,必须再次重建它,系统生成的备份内核实际上是你做的第一个内核——那个有小错误的内核。您的工作内核已被删除。当你发现你的新自定义内核也有同样的问题,或者出现更严重的错误时,你会对失去可用的内核深感遗憾。

一个常见的保存已知良好内核的地方是/boot/kernel.good。像这样备份你工作的、可靠的内核:

如果你使用的是ZFS,引导环境可能比复制更有意义(见第12章)。

不要害怕手头有各种各样的内核。磁盘空间比时间便宜。我认识一些人,他们将内核保存在按日期命名的目录中,以便在必要时可以回退到早期版本。许多人还在/boot/kernel中保存了GENERIC内核的最新副本。用于测试和调试目的。拥有太多内核的唯一困扰就是它们会填满硬盘。

配置文件格式

FreeBSD的内核是通过文本文件配置的。内核配置没有图形实用程序或菜单驱动系统;它仍然与4.4 BSD中的基本相同。如果你对文本配置文件不熟悉,那么构建内核就不适合你。

每个内核配置条目都在一行中。您将看到一个标签,指示这是什么类型的条目,然后是条目的术语。许多条目也有用哈希标记分隔的注释,就像FreeBSD文件系统FFS的这个条目一样:

每个完整的内核配置文件都由五种类型的条目组成:cpu、ident、makeoptions、options和devices。这些条目的存在与否决定了内核如何支持相关功能或硬件:

这是配置文件的另一个片段——涵盖ATA控制器的部分:

这些设备中的每一个都是不同类型的ATA控制器。将这些条目与/var/run/dmesg.boot中的几个ATA条目进行比较:

内核配置有一个ATA总线,device ata。无论“遗留”(legacy)一词在今天意味着什么,它都是一种“遗留”ATA总线。这里的dmesg代码段从atapci设备开始,即ATA与PCI相遇的控制器。然后我们有两个ATA总线,ata0和ata1。磁盘ada0位于ata0上,而CD驱动器cd0位于ata1上。

如果内核配置中没有device ata,内核将无法识别ATA总线。即使系统发现系统有一个DVD驱动器,内核也不知道从中获取信息的路径。您的内核配置必须包括依赖它们的驱动程序的所有中间设备。另一方面,如果您的系统没有ATA RAID驱动器、软盘驱动器或磁带驱动器,您可以从内核中删除这些设备驱动程序。

如果此主机具有AHCI、MVS或SIIS控制器,则这些设备名称将显示在dmesg中,而不是ata中。

配置文件

幸运的是,您通常不会从头开始创建内核配置文件;相反,你建立在现有的基础上。从硬件架构的GENERIC内核开始。它可以在/sys/<arch>/conf中找到——例如,i386内核配置文件位于/sys/i386/conf中,amd64内核配置文件在/sys/amd64/conf中,以此类推。这个目录包含几个文件,其中最重要的是DEFAULTGENERICGENERIC.bintsMINIMALNOTES

许多架构也有特定于架构的配置,只需要该硬件。i386架构包括PAE内核配置,它允许您在32位系统上使用4GB以上的RAM。arm架构包括数十种配置,FreeBSD支持的许多不同平台各有一种。

有时,您会发现一个内核配置完全符合您的要求。我想要尽可能小的内核。MINIMAL内核看起来是一个很好的起点。让我们建造它。

构建内核

FreeBSD的基本安装,结合操作系统源代码,包括轻松构建内核所需的所有基础设施。您需要做的就是通过KERNCONF变量告诉系统要构建哪个内核配置。你可以在/etc/src.conf(老派的做法是在/etc/make.conf文件)中设置KERNCONF:

不过,如果你正在尝试构建和运行不同的内核,最好在构建内核时在命令行上设置配置文件。使用make buildkernel命令构建内核:

构建过程首先运行config(8)以查找语法配置错误。如果config(8)检测到问题,它会报告错误并停止。有些错误是显而易见的——例如,您可能意外删除了对Unix文件系统(UFS)的支持,但包括了对启动UFS的支持。一个需要另一个,config(8)会告诉你到底出了什么问题。其他信息既奇怪又晦涩;那些可能需要最长时间才能弄清楚的是这样的:

NET6是IPv6选项,不是吗?不,那是我INET6。显然,一些傻瓜在文本编辑器中检查了配置文件,不小心删除了一个字母。一旦你熟悉了所有支持的内核选项,这个错误就完全不言自明了。仔细阅读这些错误! 一旦config(8)验证了配置,在现代机器上,内核构建过程需要几分钟的时间。成功的构建以这样的消息结束:

构建内核后,安装它。运行make installkernel将当前内核移动到/boot/kernel.old,并在/boot/kernel中安装新内核。安装内核比构建内核快得多。

真正另系统管理员紧张的是运行 make kernel && reboot。

安装完成后,重新启动服务器并查看启动消息。如果一切正常,您将得到以下内容,准确显示正在运行的内核以及构建的时间:

问题是,MINIMAL内核并不能引导所有硬件。它不能启动大多数硬件。在MINIMAL可以启动的硬件中,它无法在该硬件上启动大多数FreeBSD安装。

MINIMAL将所有可以作为模块的东西留在模块中。GPT和MBR磁盘分区方法都可以是模块。您必须通过loader.conf加载geom_part_gpt.ko或geom_part_mbr.ko来启动MINIMAL。文件系统也是模块,所以你必须加载它们。简而言之,您必须加载硬件和安装决策所需的每个愚蠢模块。MINIMAL是所有内核需求的良好参考,也是开始设计自己的内核的好地方,但不足以用于生产

启动备用内核

那么,如果你的新内核不工作,或者工作不好,该怎么办?也许你忘记了设备驱动程序,或者意外地删除了INET选项,无法访问互联网。有时它会在启动过程的早期挂起,你唯一能做的就是重新启动主机。别慌!你确实保留了你的旧内核,对吧?以下是要做的事情。

首先记录错误消息。您需要研究该消息,以找出新内核是如何让您失败的。不过,要修复错误,您需要启动一个工作内核,以便构建一个改进的内核。

回到第4章,我们讨论了启动备用内核的机制。我们将在这里完成键入内容的过程,但要了解加载器管理的一些深入细节,您需要回到前面的部分。现在,我们将重点介绍引导备用内核的原因以及如何正确引导。

首先决定要启动哪个内核。您的旧内核应该位于/boot下的目录中;在本节中,我们将假设您想在/boot/kernel.good中启动内核。重新启动并中断启动以进入启动菜单。第五个选项允许您选择不同的内核。菜单显示loader.conf中kernels选项中列出的每个内核目录。虽然默认情况下它列出了kernel和kernel.old,但我会添加kernel.good。

不过,一旦你安装了另一个新内核,请记住:现有的/boot/kernel会被复制到/boot/kernel.old,这样你的新内核就可以放在/boot/kernet中。如果那个内核无法启动,而你的新内核也无法启动,那么你将失去一个可用的内核。这太糟糕了。确保你手头有一个已知的好内核。

定制内核配置

也许提供的内核配置都不适合您。你需要一些不同的东西。FreeBSD允许你创建任何你想要的东西。然而,修改现有配置是最简单的。您可以复制现有文件或使用包含选项。我们将从修改现有文件开始。请确保使用正确的体系结构目录,可能是/sys/amd64/conf或/sys/i386/conf。

不要直接编辑配置目录中的任何文件。相反,将GENERIC复制到以您的机器或内核函数命名的文件中,然后编辑副本。对于这个例子,我正在构建一个支持VirtualBox系统的最小内核。我将文件GENERIC复制到一个名为VBOX的文件中,并在我喜欢的文本编辑器中打开VBOX。

修剪内核

对我们大多数人来说,从内核中剥离不必要的驱动程序和功能来缩小它是浪费时间和精力,但我鼓励你这样做一次。它将教你如何构建内核,这样当你必须测试内核补丁或其他东西时,你就不需要学习内核构建,也不需要应对迫使重建的问题。当你开始在BeagleBone或Raspberry Pi等小型主机上尝试FreeBSD时,它也会有所帮助。

我想构建一个支持VirtualBox内核的内核。我在VirtualBox上启动了一个可运行的FreeBSD安装,这样我就可以访问dmesg.boot了。我将在dmesg和配置之间来回切换,注释掉不需要的条目。

CPU 类型

在大多数架构上,FreeBSD只支持一种或两种类型的CPU。amd64平台只支持HAMMER。i386平台支持三种,但其中两种——486和最初的奔腾——在嵌入式市场之外已经过时了。

你只需要包括你拥有的CPU。如果您不确定硬件中的CPU,请检查dmesg.boot。我有一台古老的笔记本电脑,上面显示:

这是一个686类CPU,这意味着我可以删除I486_CPU和I586_CPU语句,使我的内核更小。

核心选项

在CPU类型配置条目之后,我们有一个完整的FreeBSD基本服务选项列表,如TCP/IP和文件系统。一个普通的系统不需要所有这些,但拥有它们可以提供很大的灵活性。您还将遇到在您的环境中很少使用的选项,以及可以从自定义内核配置中删除的选项。我们不会讨论所有可能的内核选项,但会介绍不同选项类型的具体示例。我会特别提到那些可以从互联网服务器上删除的内容。LINT文件、手册页和您最喜欢的互联网搜索引擎可以为您提供其他选项。如果你对一个选项有疑问,保留它。或者禁用它,看看有什么问题。

考虑以下与网络相关的选项:

这些选项支持联网。INET是标准的老式TCP/IP,而INET6支持IPv6。许多类Unix软件都依赖于TCP/IP,所以你当然需要这两者。IPSEC和IPSEC_SUPPORT允许您使用IPSEC VPN协议。我当然不会在我的虚拟机上使用这些,所以我会把它们注释掉。

TCP_OFFLOAD选项允许网络堆栈将TCP/IP计算卸载到网卡。这听起来不错,只是虚拟机上的vnet(4)网络接口不执行该功能。砍下它的头!

TCP_HHOOK选项为您提供了一个方便阅读的手册页。我会使用这个选项吗?马艾贝。更重要的是,我不知道我运行的软件需要它。我会保留它。

SCTP传输协议很好,但对于我笔记本电脑上运行的虚拟机来说完全无用。再见。

FFS选项提供标准的FreeBSD文件系统UFS。即使是ZFS主机也需要UFS支持。保留它。其他选项都与FFS有关。我们在第11章中更详细地讨论了FFS及其选项,但就目前而言,请相信我并接受它。

即使系统错误关闭,软更新也能确保磁盘的完整性。正如acl(9)中所讨论的,UFS访问控制列表允许您对文件授予非常详细的权限,我在虚拟主机上不需要这些权限。重击!

UFS_DIRHASH支持目录哈希,使包含数千个文件的目录更高效。留着。我将使用软更新日记,而不是日记,这样UFS_GJOURNAL就可以消失了。

此选项以及所有其他_ROOT选项允许系统使用标准UFS或ZFS文件系统以外的东西作为根分区的磁盘设备。安装程序使用内存设备(MD)作为根分区。如果你使用的是无盘系统(见第23章),你需要一个NFS根分区。如果你在一个标准的计算机系统上运行FreeBSD,有硬盘、键盘等等,你的内核不需要这些功能。

这两个选项支持网络文件系统(见第13章)。这里的关键问题是,你需要NFS吗?如果是这样,你需要成为服务器还是客户端?我会包括这些。

这些选项支持间歇性使用的文件系统,如FAT、CD、进程文件系统和伪文件系统框架。我们在第13章中讨论了许多这些文件系统,但它们都可以作为内核模块使用。杀了他们。

这些兼容性选项允许您的系统运行为旧版本FreeBSD构建的软件,或者对内核做出假设的软件,这些假设对旧版本FreeBSD有效,但不再适用。如果你从头开始安装系统,你可能不需要与FreeBSD 4、5或6兼容,但数量惊人的软件需要与32位FreeBSD兼容。保留COMPAT_FREEBSD32选项,否则你的系统会崩溃。

SCSI_DELAY选项指定FreeBSD在找到SCSI控制器后等待的毫秒数,然后再探测它们,让它们有机会启动并向SCSI总线标识自己。如果没有SCSI硬件,可以删除此行。

这些选项启用System-V风格的共享内存和进程间通信。许多数据库程序都使用此功能。

多个处理器

以下条目在i386内核中启用了对称多处理(symmetric multiprocessing——SMP):

这些可能不会造成伤害,但如果你知道你在一个单核的板上运行,可能是一个非常旧的系统或使用嵌入式硬件,你可以删除它们。

设备驱动

在所有选项之后,您将找到设备驱动程序条目,这些条目以相当合理的方式进行分组。要缩小内核,您需要删除主机没有使用的所有内容,但主机到底没有使用什么?在dmesg.boot中搜索每个设备驱动程序。

第一个设备条目是总线,如设备pci和设备acpi。保留这些,除非你的系统中真的没有那种总线。

接下来,我们将探讨大多数人认为合适的设备驱动程序——软盘驱动器、SCSI控制器、RAID控制器等的条目。如果你的目标是减小内核的大小,这是一个进行大量精简的好地方;删除计算机没有的硬件的所有设备驱动程序。你还可以找到一部分设备驱动程序,用于键盘、视频卡、USB端口等日常设备。你几乎肯定不想删除这些。

网卡设备驱动程序部分很长,看起来很像SCSI和IDE部分。如果你不打算很快更换网卡,你可以删除任何不使用的网卡的驱动程序。

我们不会在这里列出所有的设备驱动程序,因为除了我写这一节时支持的FreeBSD硬件外,从这样的列表中可以学到的东西很少。查看您正在运行的FreeBSD版本的发行说明,了解它支持哪些硬件。

您还可以找到虚拟化的大部分驱动因素。最常用的虚拟接口基于VirtIO,但您也会看到Xen、Hyper-V和VMware的特定驱动程序。内核只需要运行它的虚拟化平台的驱动程序。真正硬件的内核不需要任何驱动程序,即使主机上运行着虚拟机。

伪设备

您将在GENERIC内核配置的底部附近找到一系列伪设备。顾名思义,这些完全是由软件创建的。以下是一些更常用的伪设备。

环回设备(lookback device)允许系统通过网络套接字和网络协议与自身通信。我们将在下一章中详细讨论网络连接。你可能会惊讶于有多少程序使用环回设备,所以不要删除它。

这些设备提供密码操作和游戏等关键任务应用所需的伪随机数。其中一些需要底层芯片组的支持。FreeBSD支持各种随机源,将它们透明地聚合到随机设备/dev/random和/dev/urandom中。

以太网具有许多类似设备的特性,FreeBSD将其视为设备是最简单的。离开这里,除非你正在寻找学习机会。

这些设备支持VLAN和不同类型的隧道等网络功能。

内存磁盘允许您将文件存储在内存中。这对于非常快速的临时数据存储非常有用,我们将在第13章中学习。对于大多数(但不是全部)互联网服务器来说,内存磁盘是对RAM的浪费。您还可以使用内存磁盘来挂载和访问磁盘映像。如果你没有使用内存磁盘,你可以从内核中删除它们。

可移动硬件

GENERIC内核支持几种不同类型的可移动硬件。如果你的笔记本电脑在一年内内置了两个连续的9或0,它可能有Cardbus甚至PCMCIA卡。否则,您的内核中就不需要这种支持。FreeBSD支持热插拔PCI卡,但如果你没有呢?把那些驱动赶出去。

包括配置文件

您的内核二进制文件可能与构建它的机器分离。我建议使用INCLUDE_CONFIG_FILE选项将内核配置复制到编译的内核中。您将丢失任何注释,但至少您将拥有此内核中的选项和设备,并且可以在需要时复制它。sysctl kern.conftxt包含内核。

一旦你有了修剪过的内核,试着构建它。你的第一个内核配置总是会出错。

内核构建故障排除

如果内核构建失败,第一步故障排除是查看输出的最后一行。其中一些错误相当隐晦,但其他错误则不言自明。重要的是要记住,说“在某个目录中停止”的错误是没有用的;有用的错误将在这些之前。我们在第11页的“寻求帮助”中讨论了如何解决这些问题:拿着错误消息蹒跚地走向搜索引擎。编译错误通常是由配置错误引起的。

幸运的是,FreeBSD坚持在安装任何东西之前编译一个完整的内核。崩溃的构建不会损坏您安装的系统。然而,它将为您提供一个机会,测试我们在第1章中讨论过的故障排除技能。

最常见的错误是make buildkernel阶段失败。它可能看起来像这样:

您将看到几页错误代码1消息,但实际错误出现在它们之前。 内核中的某一行需要函数vesa_unload_ioctl➊和vesa_load_ioctl➋,但提供该函数的设备或选项不在内核中。尝试在互联网上搜索错误。查看是否有这些功能的手册页。如果其他方法都失败了,请搜索源代码。

等等,GENERIC配置文件中没有引用“syscons”驱动程序吗?

我已经把sc(4)驱动删掉了。请重新添加并重试。 有更多“正确”的方法来确定哪些内核设备需要哪些设备。它们都归结为“阅读和理解源代码”。对我们大多数人来说,尝试、错误、研究和更多的尝试和错误会更快。

包含、排除和扩展内核

现在您可以构建内核了,让我们稍微想象一下,看看如何使用包含、各种no配置和NOTES文件。

NOTES

FreeBSD的内核包括GENERIC中没有的各种功能。这些特殊功能中的许多都是针对非常特定的系统或特殊网络的奇怪角落情况而设计的。您可以在每个平台的内核配置目录下的文件NOTES中找到硬件特定功能的完整列表,例如/sys/amd64/conf/NOTES。独立于硬件的内核特性——适用于FreeBSD支持的每个平台——可以在/sys/conf/NOTES中找到。如果您的硬件在GENERIC内核中似乎没有完全支持,请查看注释。其中一些功能是模糊的,但如果你有硬件,你会欣赏它们的。让我们来看看NOTES中的一个典型条目:

你在桌面上使用这些视频卡吗?也许你想要一个包含适当设备驱动程序的自定义内核。

如果NOTES文件列出了每个可能设备的所有功能,为什么不把它作为内核的基础呢?首先,这样的内核将比GENERIC内核消耗更多的内存。虽然即使是小型现代机器也有足够的内存来运行GENERIC而不会出现问题,但如果内核变大了十倍而功能没有相应增加,人们会感到恼火。此外,许多选择是相互排斥的。例如,您会发现一些选项,可以让您指定内核如何调度进程。内核一次只能使用一个调度器,每个调度器在整个内核中运行其卷须。将所有这些同时添加到内核中会增加代码复杂性并降低稳定性。

我特意在一两个版本中查看NOTES,只是为了寻找有趣的新功能。

包含和排除

FreeBSD的内核配置有两个有趣的功能,可以使维护内核更容易:no选项和include功能。

include功能允许您将单独的文件拉入内核配置中。例如,如果你有一个内核配置,可以描述为“GENERIC加上几个额外的花絮”,你可以用include语句包含GENERIC内核配置:

因此,如果你想构建一个具有GENERIC所有功能但也支持VIA 3d芯片DRM功能的内核,你可以创建一个完全由以下部分组成的有效内核配置:

你可能会认为,这实际上比将GENERIC复制到新文件并编辑它更费力,你是对的。那你为什么要费心呢?最大的原因是,当你升级FreeBSD时,GENERIC配置可能会发生变化。

FreeBSD 12.1中的GENERIC与12.0中的GENERAIC略有不同。您的新配置对这两个版本都有效,在这两种情况下都可以合理地描述为“GENERIC加上我的选项”。

这对于包含项目很有效,但对于从内核中删除项目不是很好。您可以使用include语句,但使用nodevice和nooptions关键字排除不需要的条目,而不是为每个新的FreeBSD版本手动重新创建内核。使用nodevice删除不需要的设备驱动程序,而nooptions禁用不需要的选项。

查看当前计算机上的GENERIC-NODEBUG内核配置。它与GENERIC配置相同,但禁用了所有调试功能。

我们首先包括GENERIC内核配置。不过,这个内核将自己标识为GENERIC-NODEBUG。以下七条nooptions语句关闭了FreeBSD current的标准调试选项。开发人员使用GENERIC-NODEBUG内核来查看内核调试器是否导致问题。如果一个有调试的内核死机,而一个没有调试的内核没有死机,那么调试代码突然看起来可疑。

跳过模块

跳过模块如果你已经费心构建了一个自定义内核,你可能确切地知道你的主机需要哪些内核模块。如果你永远不会使用这些内核模块,为什么还要构建它们呢?您可以使用modules_OVERRIDE选项关闭模块的构建。将MODULES_OVERRIDE设置为要构建和安装的模块列表。

也许你想构建大多数模块,但你有理由讨厌某个特定的模块。使用WITHOUT_MODULES将其从构建中排除。在这里,我将vmm排除在构建之外,因为我甚至不想在VirtualBox上运行bhyve(8)。运行十几层虚拟化并想知道为什么我的笔记本电脑很慢,这只是一小步。

有选择地构建模块,结合自定义内核,可以让你把自己锁定在非常小的盒子里。只有当你发现你缺少了一个你从未想过会需要的功能时,你才会明白这些盒子有多小。如果你必须构建一个内核,那就慷慨地保留它。

现在,您的本地机器已经按照您想要的方式进行了精确调整,让我们考虑一下互联网的其他部分。