# 21.3 Ubuntu/Debian 兼容层

> **注意**
>
> 由于 FreeBSD Ports 中的 [sysutils/debootstrap](https://www.freshports.org/sysutils/debootstrap/) 长期未进行版本更新，经过实际测试验证，该工具目前尚不支持 Ubuntu 24.04 和 Debian 13 发行版的基本系统引导。

视频教程：[06-FreeBSD-Ubuntu 兼容层脚本使用说明](https://www.bilibili.com/video/BV1iM4y1j7E9)

## Ubuntu 兼容层

本节将系统地介绍在 FreeBSD 操作系统上构建 Ubuntu Linux 兼容层的技术方法与实现步骤。

![Ubuntu 兼容层](https://338876981-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCJR3FQGH1PkdRtOljuxb%2Fuploads%2Fgit-blob-69340802c0ef7f32cb67ee5e2074f876f847ab50%2FUbuntuonBSD.png?alt=media)

以下教程在 FreeBSD 14.3-RELEASE 上测试通过。

采用类似的方法也可以构建 Debian 兼容层。其他系统的支持情况请参见 `/usr/local/share/debootstrap/scripts/` 目录。

### 开始构建 Ubuntu 兼容层（基于 Ubuntu 22.04 LTS）

在开始构建之前，需要先启动一些相关的守护进程和服务。

启用相关守护进程和服务：

```sh
# service linux enable   # 启用 Linux 兼容层服务，并设置为开机自启
# service linux start    # 启动 Linux 兼容层服务
# service dbus enable    # 启用 D-Bus 服务，并设置为开机自启；通常桌面已经配置
# service dbus start     # 启动 D-Bus 服务。通常桌面已经配置
```

构建 Ubuntu 22.04 LTS 基本系统：

```sh
# pkg install debootstrap                                   # 安装 debootstrap 工具
# debootstrap jammy /compat/ubuntu http://mirrors.ustc.edu.cn/ubuntu/   # 使用 debootstrap 安装 Ubuntu Jammy 到 /compat/ubuntu
```

### 挂载文件系统

构建完基本系统后，需要挂载一些必要的文件系统以确保 Ubuntu 兼容层能够正常工作。

将 `nullfs_load="YES"` 写入 `/boot/loader.conf`。

将以下行写入 `/etc/fstab`：

```ini
# Device        Mountpoint              FStype          Options                      Dump    Pass#
devfs           /compat/ubuntu/dev      devfs           rw,late                      0       0
tmpfs           /compat/ubuntu/dev/shm  tmpfs           rw,late,size=1g,mode=1777    0       0
fdescfs         /compat/ubuntu/dev/fd   fdescfs         rw,late,linrdlnk             0       0
linprocfs       /compat/ubuntu/proc     linprocfs       rw,late                      0       0
linsysfs        /compat/ubuntu/sys      linsysfs        rw,late                      0       0
/tmp            /compat/ubuntu/tmp      nullfs          rw,late                      0       0
#/home           /compat/ubuntu/home     nullfs          rw,late                      0       0
```

检查挂载有无报错。挂载所有在 `/etc/fstab` 中标记为自动挂载的文件系统：

```sh
# mount -al
```

在 Ubuntu 兼容环境中创建 home 目录：

```sh
# mkdir /compat/ubuntu/home
```

重启系统：

```sh
# reboot
```

### 设定 Linux 内核版本

为了确保 Ubuntu 兼容层中的软件能够正常运行，需要设定一个合适的 Linux 内核版本。

如果不进行这一步，在 chroot 时可能会提示 `FATAL: kernel too old`。

`6.16.2` 仅为示例，建议参考 [The Linux Kernel Archives](https://www.kernel.org/) 中公布的版本号进行设置。

```sh
# echo "compat.linux.osrelease=6.16.2" >> /etc/sysctl.conf   # 将 Linux 兼容层内核版本写入 sysctl 配置文件，使其永久生效
# sysctl compat.linux.osrelease=6.16.2                      # 立即设置 Linux 兼容层内核版本，便于当前会话继续使用
```

### 进入 Ubuntu 兼容层

完成文件系统挂载和内核版本设定后，就可以进入 Ubuntu 兼容层进行后续配置了。

首先 chroot 进入 Ubuntu 兼容环境，移除会导致报错的软件：

```sh
# chroot /compat/ubuntu /bin/bash	# 进入 /compat/ubuntu 目录对应的 Linux 兼容环境，并启动 Bash Shell
# apt remove rsyslog # 此时已经位于 Ubuntu 兼容层了，卸载 rsyslog 软件包
```

### Ubuntu 换源

为了提高软件下载速度，需要更换 Ubuntu 兼容层的软件源。

在卸载 rsyslog 之后需要更换软件源；由于 SSL 证书尚未更新，因此暂时无法使用 HTTPS 源。

使用 ee 编辑 Ubuntu 兼容环境中的 APT 软件源配置文件：

```sh
# ee /compat/ubuntu/etc/apt/sources.list # 此时处于 FreeBSD 系统！因为 Ubuntu 兼容层还没有文本编辑器。
```

写入软件源：

```ini
deb http://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
```

进入 Ubuntu 兼容层，开始更新系统，安装常用软件：

```sh
# LANG=C # 设定字符集，防止出现错误
# apt update && apt upgrade && apt install nano wget fonts-wqy-microhei  fonts-wqy-zenhei language-pack-zh-hans # 此时已经位于 Ubuntu 兼容层了。
# update-locale LC_ALL=zh_CN.UTF-8 LANG=zh_CN.UTF-8 # 设置中文字符集
```

## 附录：Ubuntu 兼容层脚本（基于 Ubuntu 22.04 LTS，FreeBSD 14.3-RELEASE 测试通过）

脚本内容如下：

```sh
#!/bin/sh                                             # 使用 /bin/sh 作为脚本解释器

rootdir=/compat/ubuntu                                # 定义 Ubuntu 根目录路径
baseurl="https://mirrors.ustc.edu.cn/ubuntu/"         # 定义 Ubuntu 软件源基础地址
codename=jammy                                        # 定义 Ubuntu 发行版代号（22.04）

echo "Starting Ubuntu jammy (22.04 LTS) installation..."                    # 输出开始安装提示
echo "check modules ..."                              # 输出模块检查提示

# check linux module                                  # 注释：检查 Linux 兼容层模块
if [ "$(sysrc -n linux_enable)" = "NO" ]; then        # 判断 linux 兼容层是否未启用
        echo "linux module should be loaded. Continue?(N|y)"  # 提示是否继续
        read answer                                  # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
                [Nn][Oo]|[Nn])                        # 用户选择否
                        echo "linux module not loaded" # 输出提示信息
                        exit 1                       # 退出脚本并返回错误码 1
                        ;;
                *)                                   # 其他情况，视为同意
                        sysrc linux_enable=YES       # 启用 linux 兼容层并写入 rc.conf
                        ;;
        esac                                         # case 语句结束
fi                                                    # if 判断结束
echo "start linux"                                   # 输出启动 linux 服务提示
service linux start                                  # 启动 linux 兼容层服务

# check dbus                                         # 注释：检查 dbus 服务
if ! /usr/bin/which -s dbus-daemon;then               # 判断 dbus-daemon 是否存在
        echo "dbus-daemon not found. install it [N|y]"# 提示是否安装 dbus
        read  answer                                 # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
            [Nn][Oo]|[Nn])                            # 用户选择否
                echo "dbus not installed"             # 输出提示信息
                exit 2                               # 退出脚本并返回错误码 2
                ;;
            *)                                       # 其他情况，视为同意
                pkg install -y dbus                  # 使用 pkg 安装 dbus
                ;;
        esac                                         # case 语句结束
    fi                                                # if 判断结束

if [ "$(sysrc -n dbus_enable)" != "YES" ]; then       # 判断 dbus 是否未设置为启用
        echo "dbus should be enabled. Continue?(N|y)"# 提示是否继续
        read answer                                  # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
            [Nn][Oo]|[Nn])                            # 用户选择否
                        echo "dbus not running"      # 输出提示信息
                        exit 2                       # 退出脚本并返回错误码 2
                        ;;
            *)                                       # 其他情况，视为同意
                        service dbus enable          # 启用 dbus 服务
                        ;;
        esac                                         # case 语句结束
fi                                                    # if 判断结束
echo "start dbus"                                    # 输出启动 dbus 提示
service dbus start                                   # 启动 dbus 服务

echo "NOW I should change 'compat.linux.osrelease'. continue? (Y|n)" # 提示修改内核版本
read answer                                          # 读取用户输入
case $answer in                                      # 根据用户输入进行分支判断
	[Nn][Oo]|[Nn])                                    # 用户选择否
		echo "close to success"                       # 输出提示信息
		exit 4                                       # 退出脚本并返回错误码 4
		;;
	[Yy][Ee][Ss]|[Yy]|"")                            # 用户选择是或直接回车
		echo "compat.linux.osrelease=6.16.2" >> /etc/sysctl.conf  # 写入 sysctl 配置文件
		sysctl compat.linux.osrelease=6.16.2          # 立即设置 linux 兼容层内核版本
                ;;
esac                                                 # case 语句结束

if ! /usr/bin/which -s debootstrap; then              # 判断 debootstrap 是否存在
        echo "debootstrap not found. install it? (N|y)" # 提示是否安装 debootstrap
        read  answer                                 # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
            [Nn][Oo]|[Nn])                            # 用户选择否
                echo "debootstrap not installed"     # 输出提示信息
                exit 3                               # 退出脚本并返回错误码 3
                ;;
            *)                                       # 其他情况，视为同意
                pkg install -y debootstrap           # 使用 pkg 安装 debootstrap
                ;;
        esac                                         # case 语句结束
    fi                                                # if 判断结束
echo "now we will bootstrap ${codename}. press any key." # 提示开始 bootstrap
read  answer                                         # 等待用户确认

debootstrap ${codename} ${rootdir} ${baseurl}         # 使用 debootstrap 安装 Ubuntu 系统

if [ ! "$(sysrc -f /boot/loader.conf -qn nullfs_load)" = "YES" ]; then # 检查 nullfs 是否配置加载
        echo "nullfs_load should load. continue? (N|y)" # 提示是否继续
        read answer                                  # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
            [Nn][Oo]|[Nn])                            # 用户选择否
                echo "nullfs is not loaded"               # 输出提示信息
				exit 4                               # 退出脚本并返回错误码 4
                ;;
            *)                                       # 其他情况，视为同意
                sysrc -f /boot/loader.conf nullfs_load=yes # 设置 nullfs 启动加载
                ;;
        esac                                         # case 语句结束
    fi                                                # if 判断结束

if ! kldstat -n nullfs >/dev/null 2>&1;then           # 判断 nullfs 模块是否已加载
        echo "load nullfs module"                     # 输出加载提示
        kldload -v nullfs                             # 加载 nullfs 内核模块
fi                                                    # if 判断结束

echo "mount some fs for linux"                        # 输出挂载文件系统提示
echo "devfs ${rootdir}/dev devfs rw,late 0 0" >> /etc/fstab      # 挂载 devfs
echo "tmpfs ${rootdir}/dev/shm tmpfs rw,late,size=1g,mode=1777 0 0" >> /etc/fstab # 挂载 tmpfs
echo "fdescfs ${rootdir}/dev/fd fdescfs rw,late,linrdlnk 0 0" >> /etc/fstab        # 挂载 fdescfs
echo "linprocfs ${rootdir}/proc linprocfs rw,late 0 0" >> /etc/fstab               # 挂载 linprocfs
echo "linsysfs ${rootdir}/sys linsysfs rw,late 0 0" >> /etc/fstab                  # 挂载 linsysfs
echo "/tmp ${rootdir}/tmp nullfs rw,late 0 0" >> /etc/fstab                        # 绑定 /tmp
echo "/home ${rootdir}/home nullfs rw,late 0 0" >> /etc/fstab                      # 绑定 /home
mount -al                                         # 挂载所有自动挂载的文件系统

echo "add ustc apt sources"                        # 输出添加 APT 源提示
echo "deb http://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse" > /compat/ubuntu/etc/apt/sources.list      # 写入主仓库源
echo "deb-src http://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse" >> /compat/ubuntu/etc/apt/sources.list # 写入源码仓库
echo "deb http://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse" >> /compat/ubuntu/etc/apt/sources.list # 写入安全更新源
echo "deb-src http://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse" >> /compat/ubuntu/etc/apt/sources.list # 写入安全源码源
echo "deb http://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse" >> /compat/ubuntu/etc/apt/sources.list # 写入更新源
echo "deb-src http://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse" >> /compat/ubuntu/etc/apt/sources.list # 写入更新源码源
echo "deb http://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse" >> /compat/ubuntu/etc/apt/sources.list # 写入回溯源
echo "deb-src http://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse" >> /compat/ubuntu/etc/apt/sources.list # 写入回溯源码源

echo "remove rsyslog and install nano fonts-wqy-microhei fonts-wqy-zenhei language-pack-zh-hans and wget" # 输出软件安装提示
chroot ${rootdir} /bin/bash -c "apt remove rsyslog && apt update && apt upgrade && apt install nano wget fonts-wqy-microhei fonts-wqy-zenhei language-pack-zh-hans" # 在 Ubuntu 环境中执行软件管理命令
chroot ${rootdir} /bin/bash -c "update-locale LC_ALL=zh_CN.UTF-8 LANG=zh_CN.UTF-8" # 设置中文 UTF-8 语言环境
echo "Now you can run '#chroot /compat/ubuntu/ /bin/bash' to enter Ubuntu 22.04 LTS"   # 输出完成提示
```

## 附录：Debian 12（bookworm）（FreeBSD 14.2-RELEASE 测试通过）

脚本内容如下：

```sh
#!/bin/sh                                             # 使用 /bin/sh 作为脚本解释器

rootdir=/compat/debian                                # 定义 Debian 根目录路径
baseurl="https://mirrors.ustc.edu.cn/debian/"        # 定义 Debian 软件源基础地址
codename=bookworm                                     # 定义 Debian 发行版代号（12）

echo "Starting Debian 12 AKA bookworm installation..."    # 输出开始安装提示
echo "check modules ..."                              # 输出模块检查提示

# check linux module                                  # 注释：检查 Linux 兼容层模块
if [ "$(sysrc -n linux_enable)" = "NO" ]; then        # 判断 linux 兼容层是否未启用
        echo "linux module should be loaded. Continue?(N|y)"  # 提示是否继续
        read answer                                  # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
                [Nn][Oo]|[Nn])                        # 用户选择否
                        echo "linux module not loaded" # 输出提示信息
                        exit 1                       # 退出脚本并返回错误码 1
                        ;;
                *)                                   # 其他情况，视为同意
                        sysrc linux_enable=YES       # 启用 linux 兼容层并写入 rc.conf
                        ;;
        esac                                         # case 语句结束
fi                                                    # if 判断结束
echo "start linux"                                   # 输出启动 linux 服务提示
service linux start                                  # 启动 linux 兼容层服务

# check dbus                                         # 注释：检查 dbus 服务
if ! /usr/bin/which -s dbus-daemon;then               # 判断 dbus-daemon 是否存在
        echo "dbus-daemon not found. install it [N|y]"# 提示是否安装 dbus
        read  answer                                 # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
            [Nn][Oo]|[Nn])                            # 用户选择否
                echo "dbus not installed"             # 输出提示信息
                exit 2                               # 退出脚本并返回错误码 2
                ;;
            *)                                       # 其他情况，视为同意
                pkg install -y dbus                  # 使用 pkg 安装 dbus
                ;;
        esac                                         # case 语句结束
    fi                                                # if 判断结束

if [ "$(sysrc -n dbus_enable)" != "YES" ]; then       # 判断 dbus 是否未设置为启用
        echo "dbus should be enabled. Continue?(N|y)"# 提示是否继续
        read answer                                  # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
            [Nn][Oo]|[Nn])                            # 用户选择否
                        echo "dbus not running"      # 输出提示信息
                        exit 2                       # 退出脚本并返回错误码 2
                        ;;
            *)                                       # 其他情况，视为同意
                        service dbus enable          # 启用 dbus 服务
                        ;;
        esac                                         # case 语句结束
fi                                                    # if 判断结束
echo "start dbus"                                    # 输出启动 dbus 提示
service dbus start                                   # 启动 dbus 服务

if ! /usr/bin/which -s debootstrap; then              # 判断 debootstrap 是否存在
        echo "debootstrap not found. install it? (N|y)" # 提示是否安装 debootstrap
        read  answer                                 # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
            [Nn][Oo]|[Nn])                            # 用户选择否
                echo "debootstrap not installed"      # 输出提示信息（原文拼写保留）
                exit 3                               # 退出脚本并返回错误码 3
                ;;
            *)                                       # 其他情况，视为同意
                pkg install -y debootstrap           # 使用 pkg 安装 debootstrap
                ;;
        esac                                         # case 语句结束
    fi                                                # if 判断结束
echo "now we will bootstrap ${codename}. press any key." # 提示开始 bootstrap
read  answer                                         # 等待用户确认

debootstrap ${codename} ${rootdir} ${baseurl}        # 使用 debootstrap 安装 Debian 系统

if [ ! "$(sysrc -f /boot/loader.conf -qn nullfs_load)" = "YES" ]; then # 检查 nullfs 是否配置加载
        echo "nullfs_load should load. continue? (N|y)" # 提示是否继续
        read answer                                  # 读取用户输入
        case $answer in                              # 根据用户输入进行分支判断
            [Nn][Oo]|[Nn])                            # 用户选择否
                echo "nullfs is not loaded"               # 输出提示信息
				exit 4                               # 退出脚本并返回错误码 4
                ;;
            *)                                       # 其他情况，视为同意
                sysrc -f /boot/loader.conf nullfs_load=yes # 设置 nullfs 启动加载
                ;;
        esac                                         # case 语句结束
    fi                                                # if 判断结束

if ! kldstat -n nullfs >/dev/null 2>&1;then           # 判断 nullfs 模块是否已加载
        echo "load nullfs module"                     # 输出加载提示
        kldload -v nullfs                             # 加载 nullfs 内核模块
fi                                                    # if 判断结束

echo "mount some fs for linux"                        # 输出挂载文件系统提示
echo "devfs ${rootdir}/dev devfs rw,late 0 0" >> /etc/fstab      # 挂载 devfs
echo "tmpfs ${rootdir}/dev/shm tmpfs rw,late,size=1g,mode=1777 0 0" >> /etc/fstab # 挂载 tmpfs
echo "fdescfs ${rootdir}/dev/fd fdescfs rw,late,linrdlnk 0 0" >> /etc/fstab        # 挂载 fdescfs
echo "linprocfs ${rootdir}/proc linprocfs rw,late 0 0" >> /etc/fstab               # 挂载 linprocfs
echo "linsysfs ${rootdir}/sys linsysfs rw,late 0 0" >> /etc/fstab                  # 挂载 linsysfs
echo "/tmp ${rootdir}/tmp nullfs rw,late 0 0" >> /etc/fstab                        # 绑定 /tmp
#echo "/home ${rootdir}/home nullfs rw,late 0 0" >> /etc/fstab                     # 绑定 /home（注释掉）
mount -al                                         # 挂载所有自动挂载的文件系统

echo "NOW I should change 'compat.linux.osrelease'. continue? (Y|n)" # 提示修改内核版本
read answer                                          # 读取用户输入
case $answer in                                      # 根据用户输入进行分支判断
	[Nn][Oo]|[Nn])                                    # 用户选择否
		echo "close to success"                       # 输出提示信息
		exit 4                                       # 退出脚本并返回错误码 4
		;;
	[Yy][Ee][Ss]|[Yy]|"")                            # 用户选择是或直接回车
		echo "compat.linux.osrelease=6.16.2" >> /etc/sysctl.conf  # 写入 sysctl 配置文件
		sysctl compat.linux.osrelease=6.16.2          # 立即设置 linux 兼容层内核版本
                ;;
esac                                                 # case 语句结束

echo "add ustc apt sources"                        # 输出添加 APT 源提示
echo "deb http://mirrors.ustc.edu.cn/debian stable main contrib non-free non-free-firmware" > /compat/debian/etc/apt/sources.list      # 写入主仓库源
echo "# deb-src http://mirrors.ustc.edu.cn/debian stable main contrib non-free non-free-firmware" >> /compat/debian/etc/apt/sources.list # 写入源码仓库注释
echo "deb http://mirrors.ustc.edu.cn/debian stable-updates main contrib non-free non-free-firmware" >> /compat/debian/etc/apt/sources.list # 写入更新源
echo "# deb-src http://mirrors.ustc.edu.cn/debian stable-updates main contrib non-free non-free-firmware" >> /compat/debian/etc/apt/sources.list # 写入更新源码注释
echo "# deb http://mirrors.ustc.edu.cn/debian stable-proposed-updates main contrib non-free non-free-firmware" >> /compat/debian/etc/apt/sources.list # 写入预发布源注释
echo "# deb-src http://mirrors.ustc.edu.cn/debian stable-proposed-updates main contrib non-free non-free-firmware" >> /compat/debian/etc/apt/sources.list # 写入预发布源码注释
echo "deb http://mirrors.ustc.edu.cn/debian-security/ stable-security main non-free contrib" >> /compat/debian/etc/apt/sources.list # 写入安全更新源
echo "# deb-src http://mirrors.ustc.edu.cn/debian-security/ stable-security main non-free contrib" >> /compat/debian/etc/apt/sources.list # 写入安全源码注释

echo "install nano fonts-wqy-microhei fonts-wqy-zenhei and wget" # 输出软件安装提示
chroot ${rootdir} /bin/bash -c " apt update && apt --fix-broken install -y && apt upgrade && apt install nano wget fonts-wqy-microhei  fonts-wqy-zenhei -y" # 在 Debian 环境中执行软件管理命令
chroot ${rootdir} /bin/bash -c "update-locale LC_ALL=zh_CN.UTF-8 LANG=zh_CN.UTF-8" # 设置中文 UTF-8 语言环境
echo "Now you can run '#chroot /compat/debian/ /bin/bash' to enter Debian 12 bookworm"   # 输出完成提示
```

## 附录：安装 Windows 11 字体（自制包）

同时兼容 debian 和低版本 Ubuntu。安装方法：

```sh
# apt install git                          # 安装 Git
# git clone https://github.com/ykla/ttf-mswin11-zh-deb   # 克隆字体包仓库
# cd ttf-mswin11-zh-deb                    # 进入字体包目录
# dpkg -i ttf-ms-win11-*.deb               # 安装 Windows 11 中文字体包
```

## 附录：运行 X11 软件

允许所有本地用户访问当前 X Server 实例：

```sh
# xhost +local:	# 此时处于 FreeBSD 系统
```

## 故障排除与未竟事宜

### 不知道程序的命令行启动命令是什么？

请按以下方法依次查找 (以 `gedit` 为例)：

* 直接执行软件包名 `# gedit`；
* `whereis 软件包名`，定位后执行。`whereis gedit`；
* 通过 `find` 命令全局查找 `# find / -name 软件包名`——`# find / -name gedit`。
* 通过软件图标定位，进入 `/usr/share/applications` 目录，根据软件包名找到对应文件，并使用文本编辑器（如 `ee`、`nano`）打开。（软件图标本质上是文本文件，而非软链接或图片），找到其中的程序启动命令并复制到终端运行即可。

> 如何查找软件？
>
> ```sh
> # apt search --names-only XXX	# 按名称搜索包含 XXX 的软件包
> ```
>
> 将 XXX 换成想要搜索的软件名即可。

### 缺少 .so 文件

* 首先看看缺少哪些 .so 文件，一般不会只缺失一个。

```sh
# ldd /usr/bin/qq	# 查看可执行文件 qq 所依赖的动态链接库
	linux_vdso.so.1 (0x00007ffffffff000)
	libffmpeg.so => not found
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x0000000801061000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x0000000801066000)
	…………………………以下省略……………………………………
```

可以看到 `libffmpeg.so => not found`，缺“libffmpeg.so”。

* 安装工具

```sh
# apt install apt-file   # 安装 apt-file 工具，用于查询文件所属的软件包
# apt-file update        # 更新 apt-file 的软件包索引数据库
```

* 查看 `libffmpeg.so` 属于哪个包：

```sh
# apt-file search libffmpeg.so	# 查询包含 libffmpeg.so 文件的软件包
qmmp: /usr/lib/qmmp/plugins/Input/libffmpeg.so
webcamoid-plugins: /usr/lib/x86_64-linux-gnu/avkys/submodules/MultiSink/libffmpeg.so
webcamoid-plugins: /usr/lib/x86_64-linux-gnu/avkys/submodules/MultiSrc/libffmpeg.so
webcamoid-plugins: /usr/lib/x86_64-linux-gnu/avkys/submodules/VideoCapture/libffmpeg.so
```

可以看到多个包都提供了这个 so 文件，随便安装一个：

```sh
# apt install webcamoid-plugins	# 安装 Webcamoid 的插件组件
```

* 按照上述路径复制文件，并刷新 ldd 缓存：

```sh
# cp /usr/lib/x86_64-linux-gnu/avkys/submodules/MultiSink/libffmpeg.so /usr/lib   # 将 libffmpeg.so 复制到系统库目录
# ldconfig                                                                     # 重新生成动态链接库缓存
```

* 再次查看可执行文件 `/usr/bin/qq` 所依赖的动态链接库：

```sh
# ldd /usr/bin/qq
	linux_vdso.so.1 (0x00007ffffffff000)
	libffmpeg.so => /lib/libffmpeg.so (0x0000000801063000)
	…………………………以下省略……………………………………
```

## systemd 与 wine

systemd 不可用，但可以使用 `service xxx start` 命令替代。

笔者试图导入过 <https://github.com/zq1997/deepin-wine> 源以安装 deepin-qq、deepin-wechat 等软件，但都提示“段错误”。所有 Wine 程序均无法正常运行。如果读者能解决这个问题，请提出 issue 或者 PR。

## 课后习题

1. 修改 Ubuntu 兼容层脚本，使其同时支持 Ubuntu 24.04 和 Debian 13，并分析为什么当前 debootstrap 不支持这些版本，贡献上游。
2. 在 Ubuntu 兼容层中移除 `/etc/fstab` 中的 `/home` 挂载，或启用它，对比两种情况下用户对文件系统的访问权限差异，分析 nullfs 绑定挂载机制。
3. 尝试复现文中提到的 Wine 段错误问题，使用 `compat.linux.debug` 系统参数开启调试日志，分析错误发生的位置，并尝试解决。
