我在去年折騰過一陣 FreeBSD ,當時是安裝在家中的舊電腦上當 NAS 用的。最近 另一台電腦的硬盤壞掉,就把裝着 FreeBSD 的硬盤拆下來湊合用了;然後經過一 番研究,我將 FreeBSD 安裝到了閒置的 U 盤上。

這本來應該是很簡單的事情,但是我只有一隻 U 盤,沒有光驅,所以這隻 U 盤既 是安裝目標,又是安裝介質……

FreeBSD 的版本和安裝映像的格式

FreeBSD 在十月份剛剛發布了新版本 11.0 ,而我的安裝介質是 U 盤,所以用的 是 memstick 映像 (不同安裝映像的區別見 FreeBSD Handbook 2.3.1 )。

這個 memstick 映像是帶有分區表的完整磁盤映像,使用 file 命令可以看到 分區表是 GPT 格式(以下命令都是在 Linux 下執行的,「❯」和「#」字符是 shell 命令行提示符):

1
2
❯ file FreeBSD-11.0-RELEASE-amd64-memstick.img
FreeBSD-11.0-RELEASE-amd64-memstick.img: GPT partition table, version 1.0, GUID: fd366718-85e7-11e6-a2f6-002590ec6166, disk size: 1433746 sectors of 512 bytes

所以我們可以用 gdisk 命令看看這個映像上都有哪些分區:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
❯ gdisk FreeBSD-11.0-RELEASE-amd64-memstick.img
GPT fdisk (gdisk) version 1.0.1

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): p
Disk FreeBSD-11.0-RELEASE-amd64-memstick.img: 1433746 sectors, 700.1 MiB
Logical sector size: 512 bytes
Disk identifier (GUID): FD366718-85E7-11E6-A2F6-002590EC6166
Partition table holds up to 4 entries
First usable sector is 3, last usable sector is 1433743
Partitions will be aligned on 1-sector boundaries
Total free space is 0 sectors (0 bytes)

Number  Start (sector)    End (sector)  Size       Code  Name
   1               3            1602   800.0 KiB   EF00
   2            1603            1727   62.5 KiB    A501
   3            1728         1431695   698.2 MiB   A503
   4         1431696         1433743   1024.0 KiB  A502

Command (? for help):

在第一個 Command 提示符處我們輸入 p 命令打印分區表,可以看到一共 四個分區:

  1. EFI 系統分區;
  2. FreeBSD 啓動分區;
  3. FreeBSD 系統分區(「/」目錄);
  4. FreeBSD 交換(Swap)分區。

直接用 dd 命令將這個映像寫到 U 盤上,我們就可以使用 U 盤啓動電腦了(請務 必將 sdb 替換成正確的設備名稱):

1
# dd if=FreeBSD-11.0-RELEASE-amd64-memstick.img of=/dev/sdb bs=1M conv=sync

Live 系統的限制

經由這個 U 盤啓動的電腦會進入一個完整的 FreeBSD 系統(姑且稱之爲 Live 系 統)。我們的安裝目標就是這隻 U 盤,是不是直接用這個 Live 系統就可以了呢?

這取決於我們的需求。這個 Live 系統和許多嵌入式系統類似,根目錄 / 是只 讀的,而且根目錄所在的分區只有不到 700M, 將來更新系統或者安裝新軟件的時候 會非常麻煩。

當然,我們也沒辦法通過執行安裝程序將 FreeBSD 安裝到同一個 U 盤上,所以只 能儘量調整 U 盤上的分區以符合我們的要求。大概有兩個方法:

  1. 在 U 盤上新建一個可讀寫的分區,日常使用中新增的數據放到這個分區裡;
  2. 調整分區表,將根目錄所在分區擴大,並掛載爲讀寫模式。

爲了最大限度地利用 U 盤,除了 FreeBSD 用到的分區外,我還希望創建一個 NTFS 分區,這樣「理論上」這個 U 盤也可以在其他 Windows/Linux 機器上使用。

既然如此,我決定先嘗試第一種方法,因爲看起來比較簡單。

GPT 分區表的坑

我的 U 盤有 32G 空間,但是上面寫入的分區表只使用了約 700M, 我們來試試創 建一個 4G 大的分區:

1
2
3
4
5
6
# gdisk /dev/sdb
...(各種信息)...
Command (? for help): n
No table partition entries left

Command (? for help):

這裡的 n 命令就是「新建分區」的意思,但是 gdisk 給我們扔來一條 錯誤信息,說是「沒有分區表條目了」。誒? GPT 照理說應該能支持四個以上分 區的,這是怎麼回事?

Google 一番之後我在 維基百科 上看到了 GPT 的具體結構,原來 GPT 的 頭部(header)和分區表是分開存儲的。 Header 的大小是固定的,位於第二個 扇區(sector),而分區表由第三個扇區開始,長度是可變的,具體長度由 header 中的一個字段指定。

回顧前面我們用 gdisk 看到的映像分區信息,第一個分區是從第四個扇區 (sector 3)開始的,所以我們只有一個扇區的空間分配給了分區表。每個分區 表條目是 128 字節,所以一個 512 字節的扇區只能保存四個條目。

在這個情況下,要增加分區表條目,就只能將分區表後面的各個分區依次向後移 動,以騰出空間。

既然都要這樣大動干戈了,我還是採用第二種方法,順勢將 FreeBSD 的根目錄 擴大好了。

調整分區

前面已經說過了,我們要進行兩個操作:

  1. 把所有分區向後移動,騰出分區表的空間;
  2. 擴大 FreeBSD 根目錄所在分區。

我的系統現在是 Linux, 而貌似沒有哪個 Linux 下的磁盤工具提供了「移動 分區以及其中的數據」這樣的功能,所以我們只能親自上陣了。

GPT 分區表的默認條目數是 128, 所以我決定騰出 128 * 128 / 512 = 32 個 扇區的空間。減去已經存在的一個扇區,我們需要將所有分區向後移動 31 個 扇區(我的 U 盤可以採用 512 字節的扇區大小,但是請注意並不是所有存儲 設備都可以,進行計算前請務必確認你的扇區大小)。

在 Linux/Unix 下,「所有硬件設備都是文件」,所以我們可以用讀寫普通文 件的方式去移動分區數據:

1
2
# dd if=/dev/sdb of=./partition_data skip=3 bs=512 count=$((1433743 + 1 - 3)) status=progress
# dd if=./partition_data of=/dev/sdb seek=34 bs=512 status=progress conv=notrunc

第一個命令將 U 盤中所有分區的數據讀出,暫存在 partition_data 文件 內;第二個命令將暫存的數據重新寫入 U 盤,不過與讀出時的位置相比向後偏 移了 31 個扇區。

移動數據之後,分區表也要做相應的修改,才能對應上新的分區位置。我的做 法是用 gdisk 將所有分區刪除,然後再重建,不過起始和結束位置都 +31 (向後移動 31 個扇區)。

現在我們可以修改 GPT header 裡的分區表大小了:

1
2
3
4
5
6
7
8
9
# gdisk /dev/sdb
...(各種信息)...
Command (? for help): x

Expert command (? for help): s
Current partition table size is 4.
Enter new size (4 up, default 128): 128

Expert command (? for help): w

最後不要忘記用 w 命令寫入更改後的數據。現在我們可以創建最多 128 個分區了。但是在創建任何新分區之前,我們要先擴大 FreeBSD 根目錄所在 的第三個分區,而這個操作需要使用 FreeBSD 系統,因爲用到 FreeBSD 下的 growfs 命令。

我們用調整過的 U 盤啓動電腦,進入 U 盤上的 FreeBSD live 系統(用戶名 是 root, 密碼爲空),先確認一下分區情況(以下命令都在 FreeBSD 下 執行;請注意設備名稱,你的 U 盤不一定是 da0 ):

1
# gpart show /dev/da0

可以看到 freebsd-ufs 分區之後緊跟着一個 freebsd-swap 分區。 Swap 分 區保存的是臨時數據,完全可以刪掉,不過還是讓我們先確認一下該分區有沒 有被掛載:

1
# swapinfo

如果這個 swap 分區沒有在 swapinfo 裡列出,我們就可以把它刪除了:

1
# gpart delete -i 4 /dev/da0

然後將根目錄所在的分區擴大到 4G (當然你也可以選擇其他大小,如果準 備安裝桌面環境的話需要 2G 以上的空間):

1
# gpart resize -i 3 -s 4G /dev/da0

分區上的文件系統也需要作出調整,才能使用整個擴大後的分區:

1
# growfs /dev/da0p3

至此我們已經完成了 U 盤分區的調整。爲了保險起見我們還可以添加一個更 大的 swap 分區:

1
# gpart add -t freebsd-swap -s 512M /dev/da0

將根目錄掛載爲可讀寫模式

要將根目錄掛載爲讀寫模式很簡單,進入系統後用 mount 命令重新掛載 一次就可以了:

1
# mount -rw /

要在每次啓動時都進入讀寫模式,則需要更改 /etc/fstab 文件:

1
/dev/ufs/FreeBSD_Install / ufs ro,noatime 1 1

將這一行選項部分的 ro 改爲 rw 即可:

1
/dev/ufs/FreeBSD_Install / ufs rw,noatime 1 1

另外這裡還有一個坑:由於根目錄是只讀的,所以 /var/tmp 目錄被掛載爲 md 文件系統(相當於 ram disk, 見 man mdconfig ), 大小分別爲 32M 和 20M, 這在很多情況下都不夠用,但是我找了很久都沒發 現這兩個目錄是在哪裡被掛載的, /etc/fstab 根本沒有對應的條目, 直到在 /etc/defaults/rc.conf 裡見到這幾個選項:

1
2
3
4
5
6
tmpmfs="AUTO"
tmpsize="20m"
tmpmfs_flags="-S"
varmfs="AUTO"
varsize="32m"
varmfs_flags="-S"

看來 FreeBSD 還真是在嵌入式領域有着廣泛應用啊,竟然有兩個 rc 腳本 ( /etc/rc.d/var/etc/rc.d/tmp )專門處理根目錄爲只讀的 情況。由於我們的根目錄是可讀寫的,而且內核支持更方便的 tmpfs, 我在 /etc/rc.conf 裡將這兩個腳本關掉:

1
2
tmpmfs="NO"
varmfs="NO"

當然 /etc/fstab 也要作出相應調整,將 tmpfs 掛載上去:

1
2
tmpfs /tmp tmpfs rw,nosuid,noexec,mode=01777 0 0
tmpfs /var/tmp tmpfs rw,nosuid,noexec,mode=01777 0 0

這裡的掛載選項是爲了加強安全性,有興趣的同學可以自行 google. 重啓電 腦並選擇用 U 盤引導,我們就有一個與普通硬盤安裝相差無幾的 FreeBSD 系統了。

網絡設置

接下來你可能想要開始用 pkg 或者 ports 安裝軟件了,但是我們 還沒有網絡呢。普通網卡比較簡單,一般只要插上網線然後執行 dhclient 或者用 ifconfig 手動配置 IP 地址就足夠了,這裡主要討論無線網絡的 配置。

我個人不想在 Live 系統的配置文件裡保存特定的網絡信息,因爲這樣 U 盤 插到別的網絡環境中時不夠靈活,所以採用全手動的配置方式。如果你覺得無 所謂,可以按照 官方文檔 進行自動配置。

我們 U 盤上的 FreeBSD 系統在啓動時是默認不啓用無線網絡的,甚至連網卡 驅動都沒有加載,我們首先要確認當前機器上的無線網卡型號:

1
# pciconf -lv | grep -A5 -B5 -i wireless

對於最常見的 Atheros 和 Intel 的無線網卡,驅動已經編譯到 FreeBSD 11.0 的默認內核裡了,所以我們不需要做額外操作;對於其他型號的網卡,就可能 需要找一下對應的驅動模塊,然後用 kldload 命令載入。

確認驅動被正確載入之後,我們新建一個名爲 wlan0 的無線設備:

1
# ifconfig wlan0 create wlandev ath0

這裡的 ath0 是 Atheros 網卡的默認名稱,可以通過查閱對應驅動的文檔 獲得。此命令成功後再次執行 ifconfig 命令,會列出 wlan0 設備。

然後寫一個關於無線網絡的配置文件:

1
2
3
4
5
6
# cat << EOF > ./wpa_supplicant.conf
network={
    ssid="<網絡名稱>"
    psk="<WiFi密碼>"
}
EOF

執行 wpa_supplicant 命令進行連接:

1
# wpa_supplicant -i wlan0 -c ./wpa_supplicant.conf -B

最後,如果你的網絡使用 DHCP 分配地址,執行 dhclient 獲得一個 IP:

1
# dhclient -b wlan0

這時我們的電腦就上線了,可以執行 freebsd-updatepkg 之類的在 線工具更新或者安裝軟件。