22.12 code-server 和 clangd 开发配置

本教程目前在 13.2-RELEASE 和 14.0-RELEASE 上测试正常,其他版本请慎重参考。

概述

code-server 是 Visual Studio Code 的开源服务器版本,可在远程服务器上运行并通过浏览器访问。通过在 FreeBSD 上配置 code-server,无需安装桌面环境即可在原生 FreeBSD 环境中获得集成开发环境,能够利用熟悉的 VSCode 界面和强大的 clangd 插件支持 FreeBSD 内核开发,从而降低代码贡献与二次开发的学习成本。

由于上游 FreeBSD 版 code-server 存在问题,历史版本中仅有一个修订版可用。此外,自 code-server 基于 Node.js 18 以来,对 glibc 的最低版本要求提高,CentOS 提供的运行时环境已无法满足其需求。因此通过 Linux 兼容层运行 code-server 是目前最省时省力的方案。

本教程使用 Arch Linux bootstrap 镜像构建 Linux 兼容层环境。选择 Arch Linux 的原因是其滚动更新特性能够提供较新版本的 glibc 和其他依赖库,满足 code-server 的运行要求。

Linux 兼容层路径映射机制

虽然为了运行 code-server 使用了 Linux 兼容层,但 clangd 以及其他所有开发工具仍然完全由 FreeBSD 提供。这一机制基于 FreeBSD 内核的路径劫持特性。

首先需要明确,Linux 二进制兼容模式不是 Linux 模拟器,也不是 Linux 虚拟机。运行 Linux 程序的主体仍然是 FreeBSD 内核本身。

由于主体仍是 FreeBSD 内核,这必然涉及到 Linux 程序和 FreeBSD 程序的混合运行,而混合运行又会带来动态链接库调用的问题。对于一个已经编译好的二进制程序来说,它所需的动态链接库路径是写死的,比如如果某个 Linux 二进制程序依赖 /lib/glibc.so,那么它一定会去 /lib/glibc.so 查找这个文件,而非其他地方。但在 FreeBSD 上,Linux 的运行时环境位于 /compat/linux 目录下。

为了解决这个问题,FreeBSD 在内核层面劫持路径:当一个 Linux 二进制程序尝试 open /lib/glibc.so 时,FreeBSD 内核会自动在路径前加上 /compat/linux,使其变为 /compat/linux/lib/glibc.so。这一过程对应用程序而言是透明的,应用程序自身并不知道它最终获得的是哪个文件,但从 FreeBSD 内核的视角来看,它实际访问的是 /compat/linux/lib/glibc.so

如果某个文件不在 /compat/linux 目录下,而是在 FreeBSD 本地系统目录中,FreeBSD 内核会自动回退到原始路径进行二次尝试。如果仍然找不到文件,open 系统调用才会真正失败。

回到 code-server,它作为一款 Linux 程序,在尝试访问文件或目录时,默认会先在 /compat/linux 下查找。假设想打开 /usr/src 目录以查看 FreeBSD 的源码树,如果 /compat/linux/usr/src 存在,那么实际被打开的是 /compat/linux/usr/src,而不是真正需要的 /usr/src。因此需要删除 /compat/linux/usr/src,确保 FreeBSD 内核能够 fallback 到正确的 /usr/src

同样的逻辑也适用于 clangd。由于 /compat/linux/bin/clangd 并不存在,当 code-server 试图启动 clangd 时,它最终会调用 FreeBSD 提供的 /usr/local/bin/clangd,其他开发工具也同理。

服务器启用 Linux 二进制兼容,并部署 archlinux-bootstrap 镜像

首先启用 Linux 兼容层服务并设置开机自启:

下载并解压 Arch Linux bootstrap 镜像:

服务器配置 pacman 源,并添加 archlinuxcn 仓库

编辑 /compat/linux/etc/pacman.conf,配置软件源:

服务器初始化 Arch Linux 运行时环境

在 Linux 兼容环境中初始化 pacman 密钥环:

服务器更新 Arch Linux 运行时环境,并安装 code-server

复制 FreeBSD 的 DNS 配置到 Linux 兼容环境:

在 Linux 兼容环境中更新软件包并安装 code-server:

服务器删除 Arch Linux 运行时环境中的无用目录

删除 Linux 兼容环境中可能干扰 FreeBSD 本地路径访问的目录:

服务器安装 LLVM 与 clangd 插件

在 FreeBSD 本地安装 LLVM 工具链,并配置 code-server:

服务器通过 daemon 命令启动 code-server

使用 daemon 工具后台启动 code-server,关闭认证并将 PID 写入指定文件:

注意

使用 root 权限操作时请注意风险,做好数据备份。

客户端通过 SSH 建立隧道并通过浏览器连接到 code-server 服务器

在客户端建立 SSH 本地端口转发,将本地 8080 端口映射到远程服务器的 127.0.0.1:8080:

在浏览器中访问 http://127.0.0.1:8080arrow-up-right

(示例)浏览器中用 code-server 打开 FreeBSD 的源码树

使用 code-server 打开 /usr/src 目录作为工作区:

(示例)在浏览器中编译最小化内核并生成 compile_commands.json 文件

安装 Bear 工具并生成内核构建的编译命令数据库:

等待编译完成并生成 compile_commands.json 文件后,即可开始阅读内核关键部分的源码。

自动化安装脚本

为便于快速获得开发环境,以下是 code-server 安装步骤的自动化脚本:

警告

此脚本会破坏现有的任何 Linux 兼容层。读者应在理解的基础上再执行,并做好备份工作。

目录结构:

欢迎测试并反馈。

补充说明

关于 HTTPS 配置

本教程未涉及如何在服务器上通过 HTTPS 提供 code-server 服务。如需配置 HTTPS,可参考 code-server 官方文档进行 SSL 证书配置。

Linux 兼容层与 Linux Jail

本教程使用 Linux 二进制兼容层,而非 Linux Jail。两者的主要区别在于:Linux 兼容层共享 FreeBSD 内核,而 Linux Jail 提供更隔离的运行环境。

课后习题

  1. 使用自动化脚本部署 code-server 环境,通过 SSH 隧道连接,打开 FreeBSD 源码树并使用 clangd 阅读内核代码。

  2. 修改自动化脚本,配置 HTTPS 认证并测试远程访问安全性。

  3. 对比 Linux 兼容层与原生 FreeBSD 方案在运行 code-server 上的实现差异,探讨为何兼容层是目前的最优解。

最后更新于