> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/articles/freebsd-he-gu-tai-ying-pan.md).

# FreeBSD 和固态硬盘

* 原文：[FreeBSD and Solid State Devices](https://docs.freebsd.org/en/articles/solid-state/)

## 摘要

本文讨论了在 FreeBSD 上使用固态磁盘设备来创建嵌入式系统。

嵌入式系统由于缺少固有的活动部件（如硬盘），因此具有更高的稳定性。然而，需要考虑的是，系统中的磁盘空间通常较小，以及存储介质的耐用性。

本文将涵盖以下具体内容：适合在 FreeBSD 中使用的固态存储介质的类型和属性，相关内核选项，自动初始化这些系统的 `rc.initdiskless` 机制，文件系统只读需求，以及从零开始构建文件系统。最后，文章将总结一些适用于小型和只读 FreeBSD 环境的通用策略。

## 1. 固态磁盘设备

本文的范围将仅限于基于闪存的固态磁盘设备。闪存是一种固态存储介质（没有活动部件），并且具有非易失性（即使断电，数据仍能保持）。闪存可以承受巨大的物理冲击，且读写速度相对较快（本文讨论的闪存解决方案在写操作上略慢于 EIDE 硬盘，但在读取操作上要快得多）。闪存的一个非常重要的特性（其影响将在本文后面讨论），是每个扇区具有有限的重写次数。每个扇区只能进行有限次数的写入、擦除和重写，超过这个次数后，扇区将永久无法使用。尽管许多闪存产品会自动映射坏块，并且一些产品甚至会将写操作均匀分布到整个设备上，但无法忽视设备的写入次数限制。竞争性产品的规格通常为每个扇区支持 1,000,000 到 10,000,000 次写入，这个数字会受到环境温度的影响。

本文特别讨论的对象是 ATA 兼容的紧凑型闪存单元，这些设备广泛用于数字相机存储。其特别之处在于它们直接与 IDE 总线连接，并且兼容 ATA 命令集。因此，使用非常简单且低成本的适配器，这些设备可以直接连接到计算机的 IDE 总线。这样连接后，FreeBSD 等操作系统会将该设备视作一个正常的硬盘（虽然容量较小）。

其他类型的固态磁盘解决方案虽然存在，但由于它们的高昂价格、相对不常见以及使用难度，超出了本文的讨论范围。

## 2. 内核选项

对于那些创建嵌入式 FreeBSD 系统的人来说，有一些特定的内核选项值得关注。

所有使用闪存作为系统磁盘的嵌入式 FreeBSD 系统都应关注内存磁盘和内存文件系统。由于闪存的写入次数有限，磁盘和文件系统通常会被挂载为只读。在这种环境中，**/tmp** 和 **/var** 等文件系统通常会挂载为内存文件系统，以便系统能够创建日志、更新计数器和临时文件。内存文件系统是成功实现固态 FreeBSD 系统的关键组件。

在内核配置文件中，你应确保以下行存在：

```sh
options         MD_ROOT         # md 设备可作为潜在的根设备
```

## 3. `rc` 子系统与只读文件系统

嵌入式 FreeBSD 系统的启动后初始化是由 **/etc/rc.initdiskless** 控制的。

**/etc/rc.d/var** 会将 **/var** 挂载为内存文件系统，并使用 [mkdir(1)](https://man.freebsd.org/cgi/man.cgi?query=mkdir\&sektion=1\&format=html) 命令在 **/var** 中创建一个可配置的目录列表，并更改其中某些目录的权限。在执行 **/etc/rc.d/var** 时，另一个与之相关的 rc.conf 变量 `varsize` 会发挥作用。**/var** 分区会根据此变量的值在 rc.conf 中由 **/etc/rc.d/var** 创建：

```sh
varsize=8192
```

请记住，该值默认以扇区为单位。

**/var** 是一个可读写的文件系统，这一点非常重要，因为 **/** 分区（以及你可能在闪存介质上创建的其他分区）应该挂载为只读。请记住，在 [固态磁盘设备](https://docs.freebsd.org/en/articles/solid-state/#intro) 部分，我们详细介绍了闪存的限制，尤其是写入次数的限制。再怎么强调都不为过：不应该将闪存上的文件系统挂载为读写模式，同时应避免使用交换文件。因为在一个繁忙的系统中，交换文件可能会在不到一年内把闪存烧坏。大量的日志记录或临时文件的创建和销毁也会造成类似的影响。因此，除了从 **/etc/fstab** 中移除 `swap` 条目外，还应将每个文件系统的选项字段更改为 `ro`，如下所示：

```sh
# Device                Mountpoint      FStype  Options         Dump    Pass#
/dev/ad0s1a             /               ufs     ro              1       1
```

由于这个更改，系统中的一些应用程序将立即出现故障。例如，由于缺少 cron 表，`cron` 将无法正常运行，`syslog` 和 `dhcp` 等程序也会遇到问题，原因是只读文件系统以及 **/var** 中缺少 **/etc/rc.d/var** 创建的项。这些问题仅是暂时的，在 [小型和只读环境的系统策略](https://docs.freebsd.org/en/articles/solid-state/#strategies) 中，我们会介绍如何解决这些问题，并让其他常见软件包正常运行。

需要记住的是，使用 **/etc/fstab** 挂载的只读文件系统，可以随时通过以下命令切换为读写模式：

```sh
# /sbin/mount -uw partition
```

如果需要切换回只读模式，可以使用以下命令：

```sh
# /sbin/mount -ur partition
```

## 4. 从零开始构建文件系统

由于 ATA 兼容的紧凑型闪存卡被 FreeBSD 视为普通的 IDE 硬盘，因此理论上，你可以通过网络使用 kern 和 mfsroot 启动盘安装 FreeBSD，或使用 CD 安装。

然而，即使是通过常规安装程序进行的小型 FreeBSD 安装，也会生成超过 200MB 的系统大小。大多数人会使用较小的闪存设备（128MB 被认为相对较大，32MB 甚至 16MB 都是常见的），因此使用常规机制进行安装是不可行的——因为空间根本不足以容纳最小的传统安装。

克服这一空间限制的最简单方法是，通过常规方式将 FreeBSD 安装到普通硬盘上。在安装完成后，将操作系统精简至适合闪存媒体的大小，然后将整个文件系统打包成 tar 文件。以下步骤将指导你如何为 tar 打包的文件系统准备闪存。请记住，因为没有执行常规安装，所以需要手动进行分区、标签和文件系统创建等操作。除了 kern 和 mfsroot 启动盘外，你还需要使用 fixit 启动盘。

### 1. 为闪存设备分区

在使用 kern 和 mfsroot 启动盘启动后，从安装菜单中选择 `custom`。在自定义安装菜单中，选择 `partition`。在分区菜单中，使用 <kbd>d</kbd> 删除所有现有分区。删除所有现有分区后，使用 <kbd>c</kbd> 创建一个新分区，并接受分区大小的默认值。当询问分区类型时，请确保将其设置为 `165`。然后通过按 <kbd>w</kbd> 将分区表写入磁盘（这是该屏幕上的隐藏选项）。如果你使用的是 ATA 兼容的紧凑型闪存卡，请选择 FreeBSD 启动管理器。然后按 <kbd>q</kbd> 退出分区菜单。启动管理器菜单将再次出现——重复之前的选择。

### 2. 在闪存设备上创建文件系统

退出自定义安装菜单后，从主安装菜单选择 `fixit` 选项。进入 fixit 环境后，输入以下命令：

```sh
# disklabel -e /dev/ad0c
```

此时，你将借助 `disklabel` 命令进入 vi 编辑器。接下来，你需要在文件的末尾添加一个 `a:` 行。这个 `a:` 行应该类似于以下内容：

```sh
a:      123456  0       4.2BSD  0       0
```

其中，*123456* 是与现有 `c:` 项的大小相同的数字。基本上，你是将现有的 `c:` 行复制为 `a:` 行，确保文件系统类型为 `4.2BSD`。保存文件并退出。

```sh
# disklabel -B -r /dev/ad0c
# newfs /dev/ad0a
```

### 3. 将文件系统放到闪存上

挂载刚刚准备好的闪存设备：

```sh
# mount /dev/ad0a /flash
```

将这台机器接入网络，以便我们可以传输 tar 文件并将其解压到闪存文件系统中。以下是一个示例：

```sh
# ifconfig xl0 192.168.0.10 netmask 255.255.255.0
# route add default 192.168.0.1
```

现在机器已经接入网络，传输你的 tar 文件。在这个过程中，你可能会遇到一些问题——例如，如果你的闪存介质为 128MB，并且 tar 文件大于 64MB，你就不能同时将 tar 文件和解压后的内容放在闪存中——这时你会遇到空间不足的问题。如果你使用 FTP 进行传输，解决方案之一是，在传输过程中直接解压 tar 文件。如果你以这种方式进行传输，tar 文件和解压后的内容就不会同时存在于磁盘上：

```sh
ftp> get tarfile.tar "| tar xvf -"
```

如果你的 tar 文件是 gzipped 的，你可以这样做：

```sh
ftp> get tarfile.tar "| zcat | tar xvf -"
```

在 tar 文件的内容被解压到闪存文件系统后，你可以卸载闪存并重启：

```sh
# cd /
# umount /flash
# exit
```

假设你在正常硬盘上构建文件系统时正确配置了文件系统（包括将文件系统挂载为只读，并将必要的选项编译到内核中），你现在应该能够成功启动你的 FreeBSD 嵌入式系统。

## 5. 小型和只读环境的系统策略

在 [`rc` 子系统和只读文件系统](https://docs.freebsd.org/en/articles/solid-state/#ro-fs)中，指出了由 **/etc/rc.d/var** 构建的 **/var** 文件系统以及只读根文件系统的存在，会导致许多与 FreeBSD 一起使用的常见软件包出现问题。本文将提供一些成功运行 cron、syslog、Port 安装和 Apache web 服务器的建议。

### 5.1. Cron

在启动时，**/var** 会通过 **/etc/rc.d/var** 根据 **/etc/mtree/BSD.var.dist** 中的列表进行填充，因此 **cron**、**cron/tabs**、**at** 以及一些其他标准目录将被创建。

然而，这并不能解决跨重启维护 cron 表的问题。当系统重启时，内存中的 **/var** 文件系统将消失，你存放在其中的 cron 表也会消失。因此，一个解决方案是为需要的用户创建 cron 表，将 **/** 文件系统挂载为读写，并将这些 cron 表复制到一个安全的位置，例如 **/etc/tabs**，然后在 **/etc/rc.initdiskless** 的末尾添加一行，在系统初始化期间创建 **/var/cron/tabs** 目录后，将这些 cron 表复制进去。你可能还需要添加一行，使用 **/etc/rc.initdiskless** 来更改你创建的目录和复制的文件的模式和权限。

### 5.2. Syslog

**syslog.conf** 指定了存在于 **/var/log** 中的某些日志文件的位置。这些文件在系统初始化时不会由 **/etc/rc.d/var** 创建。因此，你需要在 **/etc/rc.d/var** 中的某个位置，在创建 **/var** 中的目录后，添加如下内容：

```sh
# touch /var/log/security /var/log/maillog /var/log/cron /var/log/messages
# chmod 0644 /var/log/*
```

### 5.3. 安装 Port

在讨论成功使用 Port 树所需的更改之前，必须提醒你注意闪存媒体上文件系统的只读性质。由于它们是只读的，你需要使用 [`rc` 子系统和只读文件系统](https://docs.freebsd.org/en/articles/solid-state/#ro-fs) 中显示的挂载语法暂时将其挂载为读写。在完成任何维护后，你应始终将这些文件系统重新挂载为只读——对闪存媒体的不必要写入可能会大大缩短其使用寿命。

为了能够进入 Port 目录并成功运行 `make install`，我们必须在一个非内存文件系统上创建一个包数据库目录，该目录将跨重启跟踪我们的包。由于安装包时需要将文件系统挂载为读写，因此可以合理地假设，闪存媒体上的某个区域也可以用来写入包信息。

首先，创建一个包数据库目录。通常这是在 **/var/db/pkg** 中，但由于它会在每次系统启动时消失，因此我们不能将其放在那里。

```sh
# mkdir /etc/pkg
```

接下来，在 **/etc/rc.d/var** 中添加一行，将 **/etc/pkg** 目录链接到 **/var/db/pkg**。示例如下：

```sh
# ln -s /etc/pkg /var/db/pkg
```

现在，每次你将文件系统挂载为读写并安装包时，`make install` 将正常工作，包信息将成功写入 **/etc/pkg**（因为在此时，文件系统将挂载为读写），并将始终以 **/var/db/pkg** 的形式可供操作系统访问。

### 5.4. Apache 网络服务器

> **注意**
>
> 本节中的步骤仅在 Apache 设置为将其 pid 或日志信息写入 **/var** 之外时需要。默认情况下，Apache 将其 pid 文件保存在 **/var/run/httpd.pid** 中，并将其日志文件保存在 **/var/log** 中。

现在假设 Apache 将其日志文件保存在 **apache\_log\_dir** 目录中，该目录位于 **/var** 之外。当此目录位于只读文件系统上时，Apache 将无法保存任何日志文件，并可能无法正常工作。如果是这样，就需要在 **/etc/rc.d/var** 中要创建的 **/var** 目录列表中添加一个新目录，并将 **apache\_log\_dir** 链接到 **/var/log/apache**。还需要对这个新目录设置权限和所有权。

首先，将 `log/apache` 目录添加到 **/etc/rc.d/var** 中要创建的目录列表中。

其次，在 **/etc/rc.d/var** 中的目录创建部分后添加以下命令：

```sh
# chmod 0774 /var/log/apache
# chown nobody:nobody /var/log/apache
```

最后，删除现有的 **apache\_log\_dir** 目录，并将其替换为链接：

```sh
# rm -rf apache_log_dir
# ln -s /var/log/apache apache_log_dir
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/articles/freebsd-he-gu-tai-ying-pan.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
