# 安装与升级 bm2

bm2 由两个二进制组成：命令行 `bm2` 与守护进程 `bm2d`。

两者一起安装，也必须留在同一目录并在 `PATH` 里，因为 `bm2` 是按名字启动 `bm2d` 的。

这一页讲三件事：第一次怎么把 bm2 装上、以后怎么升级 bm2、换了二进制之后怎么让它真正生效。

## 运行 bm2 需要什么

- **仅支持 Linux。** 非 Linux 构建会拒绝运行并说明原因。
- **内核 >= 5.4**，pidfd 进程跟踪需要。5.4 才有 `waitid(P_PIDFD)`，低一档的 5.3 只能判断存活，拿不到退出码。
- **C 语言工具链。** `cc`、`binutils` 与 libc 头文件，装 bm2 时在本机编译要用。Debian 系装 `build-essential`，RHEL 系装 `gcc make`。
- **[MoonBit](https://www.moonbitlang.com/)** 工具链：安装与升级 bm2 时需要，运行 bm2 不需要。
- **[Bun](https://bun.sh/)** `1.4.0` 及以上：`runtime = "bun"` 的项目需要。
- **[Node.js](https://nodejs.org/)** `24.0.0` 及以上：只有 `runtime = "node"` 的项目需要。

bm2 只经 MoonBit 的工具链分发，没有预编译包，安装与升级都是在你机器上现编一次。所以 C 工具链是硬前置，缺了 `moon install` 会在编译阶段失败。

Bun 与 Node.js 的版本在启动项目那一刻实测，低于下限直接拒绝启动并说清缺什么。

这条检查比「先起再崩」有用得多：它把问题定位在启动之前，而不是让你从一堆崩溃日志里猜。

## 首次安装 bm2

分两步：先装 MoonBit 工具链，再用它安装 bm2。

### 第一步：安装 MoonBit 工具链

```bash
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
```

bm2 的安装与升级都经 MoonBit 的工具链分发，所以 `moon` 命令是前置条件。

### 第二步：安装 bm2

```bash
moon install chensuiyi/bm2/...
```

`bm2` 与 `bm2d` 两个二进制一起落到 `~/.moon/bin`，这个目录通常已经在 `PATH` 里，不需要额外配置。

国内直连 mooncakes 会比较慢，可以按[网络与国内镜像](/guide/registry-mirror)配一个下载镜像再装。

确认 bm2 装好了。

```bash
bm2 version
which bm2 bm2d
```

两条命令都指向同一个目录才算正确。

`bm2d` 找不到时的表现很明确：任何需要守护进程的命令都会报 `bm2: bm2d did not start`，并告诉你去看 `~/.bm2/bm2d.log`。

## 升级 bm2

```bash
bm2 upgrade
```

它做的事按顺序是：查 mooncakes 上 bm2 的最新版本（15 秒超时）、用 `moon install chensuiyi/bm2/...` 换入两个二进制、把守护进程交给新二进制。

应用全程继续运行，PID 不变。

四种情况各有明确结果：

| 情况 | 结果 |
| --- | --- |
| 已是最新 | 打印当前版本，不做任何安装动作 |
| 查不到 mooncakes | 退出 `1`，说明无法访问 |
| 机器上没有 `moon` | 退出 `1`，提示先安装 MoonBit 工具链 |
| `moon install` 失败 | 退出 `1`，旧版本原样保留 |

同一时刻只允许一个升级。`~/.bm2/upgrade.lock` 被占用时第二条命令立即退出，避免两个升级互相覆盖文件。

升级依赖本机的 `moon` 命令。它不在 `PATH` 里时，命令会直接说明，不会自己去找别的下载通道——bm2 只通过 mooncakes 分发。

安装成功后如果换班没有成功（旧守护进程不肯退场），命令会提示执行 `bm2 reload`，此时新二进制已经就位。

## 换了 bm2 二进制后如何生效

二进制已经换好（例如刚跑完 `moon install`），只想让它立刻生效时。

```bash
bm2 reload
```

旧守护进程退场，新守护进程接管仍在运行的实例，PID 不变。

`bm2` 与 `bm2d` 会互相校验协议版本。版本不一致时，提示会直接指向这一条命令，而不是给一个查不到原因的错误。

## 装了新版 Bun 或 Node.js 之后

这一步换的不是 bm2，而是 bm2 启动项目时用的运行时。

守护进程的 `PATH` 在它启动那一刻定格。

装了新版 Bun 或 Node.js 之后，先用 `bm2 reload` 换入新守护进程，用到它的项目才能看到新版本。

跳过这一步的典型症状是：`which bun` 已经是新版，`bm2 list` 里的项目却还在报版本过低。

## 守护进程 bm2d

`bm2d` 是 bm2 的守护进程，但它不是给你日常调用的。

- 它**不接受任何参数**，多传一个就报用法错误。
- 一个用户只能有一个：第二个会因 `~/.bm2/bm2d.lock` 被占用而拒绝启动。
- `bm2` 在端点文件不存在时会按 `PATH` 找到它并拉起，所以你不需要手工启动。
- 它接管 `SIGTERM` 与 `SIGINT`，收到后优雅停下所有项目、注销它们、删除端点文件。

想让新版本立刻生效时不要直接杀进程，用上面的 `bm2 reload`。

## 多用户与权限

一个用户一个守护进程，状态目录是 `~/.bm2`，权限 `0700`。

被托管的应用以守护进程的用户身份运行，文件权限、环境变量与日志归属都跟着走。

不要把 bm2 装在 root 下管理属于其它用户的应用，也不要让两个用户共享同一个状态目录。

## 在 WSL 里运行 bm2

可以跑，前提是 WSL2：它提供完整内核，pidfd 可用。WSL1 不支持。

两点差异要知道：WSL 与 Windows 共享端口空间，端口冲突要在两边一起看。WSL 实例关闭后里面的进程会停止，长期服务不要放在本机的 WSL 里。

## 卸载 bm2

先停掉全部项目与守护进程。

```bash
bm2 kill -y
```

再删二进制与状态目录。

```bash
rm -f ~/.moon/bin/bm2 ~/.moon/bin/bm2d
rm -rf ~/.bm2
```

`~/.bm2` 里有日志与事件记录，需要留档就先备份再删。

## bm2 安装与升级的常见问题

`bm2: bm2d did not start`：两个二进制不在同一目录，或 `bm2d` 没有执行权限。

`Permission denied`：`~/.bm2` 属于别的用户，或者当前用户没有写权限。

`bm2: refusing to stop bm2d and all apps`：`kill` 需要 `-y` 确认，这是防止误杀的拦截。

更细的排障见[排障与常见问题](/guide/troubleshooting)。
