core: add systemd-executor binary
Currently we spawn services by forking a child process, doing a bunch of work, and then exec'ing the service executable. There are some advantages to this approach: - quick: we immediately have access to all the enourmous amount of state simply by virtue of sharing the memory with the parent - easy to refactor and add features - part of the same binary, will never be out of sync There are however significant drawbacks: - doing work after fork and before exec is against glibc's supported case for several APIs we call - copy-on-write trap: anytime any memory is touched in either parent or child, a copy of that page will be triggered - memory footprint of the child process will be memory footprint of PID1, but using the cgroup memory limits of the unit The last issue is especially problematic on resource constrained systems where hard memory caps are enforced and swap is not allowed. As soon as PID1 is under load, with no page out due to no swap, and a service with a low MemoryMax= tries to start, hilarity ensues. Add a new systemd-executor binary, that is able to receive all the required state via memfd, deserialize it, prepare the appropriate data structures and call exec_child. Use posix_spawn which uses CLONE_VM + CLONE_VFORK, to ensure there is no copy-on-write (same address space will be used, and parent process will be frozen, until exec). The sd-executor binary is pinned by FD on startup, so that we can guarantee there will be no incompatibilities during upgrades.
This commit is contained in:
@@ -201,3 +201,25 @@ can be found under various directories such as `factory/`, `modprobe.d/`, `netwo
|
||||
`tools/`, `coccinelle/`, `.github/`, `.semaphore/`, `.mkosi/` host various
|
||||
utilities and scripts that are used by maintainers and developers. They are not
|
||||
shipped or installed.
|
||||
|
||||
# Service Manager Overview
|
||||
|
||||
The Service Manager takes configuration in the form of unit files, credentials,
|
||||
kernel command line options and D-Bus commands, and based on those manages the
|
||||
system and spawns other processes. It runs in system mode as PID1, and in user
|
||||
mode with one instance per user session.
|
||||
|
||||
When starting a unit requires forking a new process, configuration for the new
|
||||
process will be serialized and passed over to the new process, created via a
|
||||
posix_spawn() call. This is done in order to avoid excessive processing after
|
||||
a fork() but before an exec(), which is against glibc's best practices and can
|
||||
also result in a copy-on-write trap. The new process will start as the
|
||||
`systemd-executor` binary, which will deserialize the configuration and apply
|
||||
all the options (sandboxing, namespacing, cgroup, etc.) before exec'ing the
|
||||
configured executable.
|
||||
|
||||
```
|
||||
┌──────┐posix_spawn() ┌───────────┐execve() ┌────────┐
|
||||
│ PID1 ├─────────────►│sd-executor├────────►│program │
|
||||
└──────┘ (memfd) └───────────┘ └────────┘
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user