如何让 bubblewrap 子进程进入父进程的 systemd cgroup?
结论
Linux 子进程通常继承父进程创建子进程时所在的 cgroup。因此,如果 bwrap 已进入 my-unit.scope,它之后创建的 bash 一般也会位于该 scope 中。你观察到 bwrap 和 bash 都位于 kitty-122960-0.scope,所以需要先确认:被 systemd 移入 scope 的 PID,是否就是随后执行 exec 并启动 bwrap 的进程。目前的信息还不足以确定具体是哪一步导致归属不同。
先核对进程的 cgroup
在调用 StartTransientUnit 前、调用成功后,以及执行 exec 前,分别记录当前进程的 PID 和 /proc/self/cgroup。然后检查 bwrap 和 bash 对应的 /proc/<pid>/cgroup。重点核实迁移前后 PID 是否一致,以及 scope 启动成功后,该进程是否确实显示在 my-unit.scope 中。
如果创建 scope 后、执行 exec 前,/proc/self/cgroup 仍显示终端的 scope,问题出在进程加入 scope 的阶段。如果此时已经显示为 my-unit.scope,但实际启动的 bwrap 不在其中,就需要确认执行 exec 的进程是否与刚才检查的是同一个。
检查执行路径
你的代码先取得 getpid(),再通过异步 D-Bus 调用创建 scope,最后调用 command.exec()。需要确认 PidFDs 指向的进程,在 D-Bus 调用返回后是否仍是执行 exec 的同一个进程。尤其要检查 setup_process 的调用位置:它是否在预期的进程中运行,期间是否有其他代码创建子进程,或者是否由另一个进程实际启动了 bwrap。
你提到改用 nix::unistd::execvpe(...) 后仍出现相同现象,因此目前没有依据将问题单纯归因于 Command 的参数构造。关键还是对照 PID 和 cgroup 记录,确认迁移对象与执行 exec 的对象是否一致。
一个便于定位的时序
可以按以下顺序检查:
- 记录准备放入 scope 的 PID,以及该 PID 的
/proc/<pid>/cgroup。 - 调用
StartTransientUnit,等待调用成功返回。 - 再次读取同一 PID 的
/proc/<pid>/cgroup,确认它已属于my-unit.scope。 - 由该进程执行
exec启动bwrap,然后检查bwrap及其子进程的 cgroup。
如果实际要移入 scope 的是新创建的子进程,应确保 systemd 注册的是该子进程的 PID 或 pidfd,并在它执行 exec 前确认迁移完成。仅将包装进程的 pidfd 交给 systemd,不会自动将一个独立创建、且未作为其后代启动的进程加入 scope。
注意事项
--unshare-all和--die-with-parent是 bubblewrap 的参数。现有信息不足以据此判断它们是否导致宿主侧的 cgroup 归属发生变化。- 对照检查时应使用 PID 和
/proc/<pid>/cgroup,不要只根据进程树的显示形式推断归属。 - 如果 PID 在异步调用前后保持一致,且调用返回后已显示为
my-unit.scope,但随后启动的进程仍出现在终端 scope,应继续检查实际执行exec的代码路径和进程创建位置。目前的信息还无法确定更具体的根因。
备注:内容仅供参考。
