From 56988d860593a5fd8153d02a0ca5469508378626 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Wed, 28 Jan 2026 12:41:26 +0100 Subject: [PATCH] libstore: use vfork for linux sandbox launch wrappers this halves sandbox launch overhead, reducing the build time for 3000 trivial runCommand derivations on our machine from 80 seconds to 44s. as a nice side effect we also get better error message propagation in some cases, most notably setgroups failing when run in lix sandboxes. Change-Id: Ia7c50a844915d9b8a20475b90b2d0179fd2fff34 --- .../rl-next/linux-sandbox-launch-overhead.md | 12 +++++++ lix/libstore/platform/linux.cc | 33 ++++++------------- tests/functional/supplementary-groups.sh | 4 +-- 3 files changed, 24 insertions(+), 25 deletions(-) create mode 100644 doc/manual/rl-next/linux-sandbox-launch-overhead.md diff --git a/doc/manual/rl-next/linux-sandbox-launch-overhead.md b/doc/manual/rl-next/linux-sandbox-launch-overhead.md new file mode 100644 index 000000000..41aca49ae --- /dev/null +++ b/doc/manual/rl-next/linux-sandbox-launch-overhead.md @@ -0,0 +1,12 @@ +--- +synopsis: "Linux sandbox launch overhead greatly reduced" +cls: [5030] +category: "Improvements" +credits: [horrors] +--- + +Sandboxed builds are now much cheaper to launch on Linux with 50% lower management +overhead. This will mostly be noticeable when building derivation trees containing +many small derivations like nixpkgs' `writeFile` or `runCommand` with scripts that +very quickly. In synthetic tests we have seen build times of 3000 small runCommand +drop from 80 seconds to 44 seconds, which is the most optimistic case in practice. diff --git a/lix/libstore/platform/linux.cc b/lix/libstore/platform/linux.cc index 86c103a73..b3f3a8e1d 100644 --- a/lix/libstore/platform/linux.cc +++ b/lix/libstore/platform/linux.cc @@ -1471,15 +1471,10 @@ Pid LinuxLocalDerivationGoal::startChild( return {std::move(userns), std::move(netns)}; }(); - Pipe sendPid; - sendPid.create(); - - Pid helper = startProcess([&]() { + Pid pid = inVFork(/* flags*/ 0, [&]() { if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) throw SysError("setting death signal"); - sendPid.readSide.close(); - if (dup2(logPTY.get(), STDERR_FILENO) == -1) { throw SysError("failed to redirect build output to log file"); } @@ -1504,25 +1499,17 @@ Pid LinuxLocalDerivationGoal::startChild( options.cloneFlags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; - pid_t child = startProcess([&]() { - if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) - throw SysError("setting death signal"); - runChild(builder, envStrs, args); - }, options).release(); - - writeFull(sendPid.writeSide.get(), fmt("%d\n", child)); - _exit(0); + return startProcess( + [&]() { + if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) { + throw SysError("setting death signal"); + } + runChild(builder, envStrs, args); + }, + options + ); }); - sendPid.writeSide.close(); - - if (helper.wait() != 0) - throw Error("unable to start build process"); - - auto ss = tokenizeString>(readLine(sendPid.readSide.get())); - assert(ss.size() == 1); - Pid pid = Pid{string2Int(ss[0]).value()}; - /* Migrate the child inside the available control group. */ if (context.cgroup) { context.cgroup->adoptProcess(pid.get()); diff --git a/tests/functional/supplementary-groups.sh b/tests/functional/supplementary-groups.sh index c1a949eb4..9a2068a5f 100644 --- a/tests/functional/supplementary-groups.sh +++ b/tests/functional/supplementary-groups.sh @@ -21,13 +21,13 @@ unshare --mount --map-root-user bash <