|
|
Log in / Subscribe / Register

Anything that can be userspace should be userspace

Anything that can be userspace should be userspace

Posted Aug 4, 2026 20:16 UTC (Tue) by quotemstr (subscriber, #45331)
Parent article: The beginning of a process-builder API

I wholly support the goal of providing an extensible build-and-launch process API and encouraging its use over fork/exec and posix_spawn. But does the *kernel* need to provide this API?

Imagine you had a special process-launch helper (say, a souped-up /usr/bin/env) that you could spawn, give process-building instructions, and, when done, let run? And this process would do all the environment configuration you wanted, then execve the final binary, reporting over an inherited pipe any errors in the setup process?

ISTM the Linux kernel is already too big a TCB and that we'd do well to implement new functionality where possible in userspace and userspace libraries, leaving to the kernel the jobs only a privileged executive can do.


to post comments

Anything that can be userspace should be userspace

Posted Aug 4, 2026 21:05 UTC (Tue) by geofft (subscriber, #59789) [Link] (1 responses)

I think this does need to be in kernelspace for both performance and functionality reasons.

The previous discussion that inspired this one was specifically about performance of code that spawns several helper processes, in the UNIX tradition of "do one thing and do it well." If the way to make things work is to run a userspace helper that then execs the actual userspace binary, that will make things strictly slower from the status quo.

On the functionality side, the only way you could have the userspace helper implement preserving/renumbering file descriptors is to have the helper inherit every single file descriptor and then choose which ones to close. This runs into the same problem as we currently have with the lack of O_CLOFORK, i.e., there is a period of time where another process on the system, marching at its own pace, has a file open because it happened to be open at the time that that process was spawned. Even if the parent process has since closed the file descriptor, the underlying file is still open. This impacts both things like when a flush to disk happens and also e.g. whether you can exec the file without running into ETXTBSY. A spawn-like syscall has the ability to atomically (from the perspective of userspace) create a new process that never has unwanted file descriptors open.

(I guess you can technically get this right in userspace if you have a clone flag that only preserves stdio, and you redirect stdio to a UNIX socketpair, and you pass fds over that socket and have the helper install them in the right places. But that is complicated, and I also recently learned that it fails if you compile a kernel without networking support since sockets are networking.)

We do already have fork/exec, which is actually very well designed under the constraint that you want all the complexity to be in userspace; it gets you full flexibility to run any syscall between fork and exec and to start with the entire parent process's state to borrow from as you wish. The goal of this new interface is to solve the problems that are unavoidable with that approach.

BTW, I think there is a somewhat stronger argument to move execve into userspace. The only thing that it does right now that you can't really do from userspace is set /proc/self/exe correctly. Other than that, the task of opening a file, mapping in the executable segments, unmapping everything currently in memory, and jumping to the start address is pretty straightforward from userspace, and there are both complexity and functionality arguments for moving ELF parsing out of the kernel. (The obvious downside is now you get fun cases like running a static or emulated binary that doesn't know how to parse the executables in /usr/bin. If this functionality is in the kernel, it's guaranteed to be available to all processes equally easily.)

Anything that can be userspace should be userspace

Posted Aug 4, 2026 22:13 UTC (Tue) by mb (subscriber, #50428) [Link]

>I think this does need to be in kernelspace for both performance and functionality reasons.

This sounds like the perfect job for by new invention which a abbreviate as:
BPFBPFBPF

(BPF-Blazingly-Performant-Fast-Blanket-Process-Forking)

Anything that can be userspace should be userspace

Posted Aug 4, 2026 22:19 UTC (Tue) by Cyberax (✭ supporter ✭, #52523) [Link] (4 responses)

I think it has to be a proper kernel API. Otherwise, it'll end up being unusable in generic code because of all the complexities of namespacing, labeling, privileges, etc.

It will not be able to achieve all the same semantics purely in the userspace. And an "empty shell" process is an incredibly powerful primitive in its own right. I can see tons of possible use cases.

For example, you can safely fill in the process environment (env vars, FDs, etc) in a privileged context without actually running it. Then you can pass the process descriptor to a confined runner that actually launches it with all the proper namespacing.

I can also see eventually doing it in reverse, have a running process and create its suspended "snapshot". It would make the "zygote" pattern actually useful for processes with multiple threads.

Anything that can be userspace should be userspace

Posted Aug 5, 2026 0:25 UTC (Wed) by quotemstr (subscriber, #45331) [Link] (3 responses)

> unusable in generic code because of all the complexities of namespacing, labeling, privileges, etc.

It continually disappoints me that the kernel has to do things that are really the job of userspace because in many domains in which Linux is used, userspace is so brittle and hard to modify that even linking to glibc is considered risky.

I don't think I buy a performance argument for a kernel-mode process builder: you're running the dynamic linker interpreter anyway (all your binaries are PIE, right? Right??). The interpreter has to load the actual program anyway, and I don't see why the interpreter couldn't be made to do a bit of process setup before transferring control to the main program. There are multiple ways to get correctness too.

But deployment? Yeah, I can see how that can be hard in some godforsaken Kubernetes hell. That's a good, if tragic, ecological argument for doing this kernel-side. That said, I can't wait for ten years of being unable to use this feature because gvisor won't implement it or some provider has seccomp-ed out every system call invented since 1995.

It shouldn't be this way. It doesn't have to be this way. And if I were building an OS from scratch, I'd strictly adhere to the rule that *everything* that could *plausibly* be outside the kernel *is* outside the kernel. But that's not the OS we have.

Anything that can be userspace should be userspace

Posted Aug 5, 2026 8:46 UTC (Wed) by pwfxq (subscriber, #84695) [Link]

> if I were building an OS from scratch, I'd strictly adhere to the rule that *everything* that could *plausibly* be outside the kernel *is* outside the kernel

A micro-kernel?

</sarcasm>

Anything that can be userspace should be userspace

Posted Aug 5, 2026 8:52 UTC (Wed) by chris_se (subscriber, #99706) [Link]

> It shouldn't be this way. It doesn't have to be this way. And if I were building an OS from scratch, I'd strictly adhere to the rule that *everything* that could *plausibly* be outside the kernel *is* outside the kernel. But that's not the OS we have.

Welcome to Hurd. ;-)

https://www.debian.org/ports/hurd/
https://wiki.gentoo.org/wiki/Project:Hurd

Anything that can be userspace should be userspace

Posted Aug 6, 2026 1:55 UTC (Thu) by Cyberax (✭ supporter ✭, #52523) [Link]

It's problematic to do 100% robustly without having true microkernel infrastructure.

In particular, it's impossible to tell if the wrapper process crashed or if it was the target executable. And there's no reliable way to distinguish between stuck processes and just very slow processes.

The _one_ thing I'd like to see even in a microkernel

Posted Aug 5, 2026 9:05 UTC (Wed) by chris_se (subscriber, #99706) [Link] (1 responses)

> Imagine you had a special process-launch helper (say, a souped-up /usr/bin/env) that you could spawn, give process-building instructions, and, when done, let run? And this process would do all the environment configuration you wanted, then execve the final binary, reporting over an inherited pipe any errors in the setup process?

You'd still need an additional syscall to spawn that helper program. (Otherwise you'd do the current fork/execve dance anyway, and then a helper doesn't improve your situation.)

You'd need some form of IPC in order to get that process to do what you want, and that IPC has to be powerful enough to be able to transfer file descriptors from your process to that process. (One of the main things the proposed spawning API does is give the user control over which file descriptors are in the initial set of descriptors passed to the new program.)

The operation itself would not be atomic - while the helper program is running, you do already have a subprocess, but if execve happens to fail in that program, that process ends, and error handling becomes way harder for the main process (especially if you have multiple threads and another thread has a general wait() running). Sure, that situation is the same as currently, but when designing something new you'd want to improve upon the existing.

You have to rely on that helper program existing/working in the moment you want to create a process (unlike e.g. a shared library in userspace where starting your program doesn't start up if it's not available / broken), this makes this more error-prone.

Finally, you'd still construct the helper program first, which definitely is a performance hit has compared to immediately starting the requested program.

Even from a microkernel perspective, managing processes and memory management are _the_ two key features that a microkernel should provide in the kernel directly. I believe a specific "start a new process with the following resource configuration" functionality is _the_ one functionality that would belong in a microkernel, and not a userspace helper.

Smelling blood in the water,

Posted Aug 13, 2026 9:21 UTC (Thu) by ksandstr (guest, #60862) [Link]

>Even from a microkernel perspective, managing processes and memory management are _the_ two key features that a microkernel should provide in the kernel directly. I believe a specific "start a new process with the following resource configuration" functionality is _the_ one functionality that would belong in a microkernel, and not a userspace helper.

Empirically false; on the contrary it's entirely feasible to do POSIX on top of a microkernel that knows neither process nor file. Such kernels[0] instead abstract address spaces, threads, memory mappings[1], and exception handling, with these being mediated from system level to lesser privilege by synchronous IPC that provides client atomicity. Process spawning external to the microkernel then configures the new process' pager (such as a systemwide memory server), and any filesystems implementing the files it maps, to define the new program; and this is the equivalent of calling an arbitrarily complex spawn67() over IPC. That extends to non-files such as sockets and the like.

This seems like a lot of hair splitting, but the kernel-to-system divide runs in the very definition of a microkernel. Nothing in that detail applies to monolithic kernels either: they enjoy a similar API freedom internally and so lack the gains from going usermode, but are weighed down by their POSIX externals if they went anyway.

As for solving file descriptor TOCTOU in a process with unruly threads (e.g. syscalls that started before the spawn() call) messing with open files during a running syscall, it's simple enough to retard spawn() first and then halt other threads for its duration at the point where they'd access any file. (mutexes would do in a pinch.) Lighter methods may be possible, but that one is a complete solution albeit on the petri dish scale.

[0] Where "kernel" is the supervisor component, "system" is privileged non-POSIX user mode, and "user" (or "app") is cat(1) inna terminal window, its loader, the terminal program, ..., and the horse they rode in on.
[1] And even that is more a matter of portability and security (i.e. defense in depth), where an alternative would allow privileged programs to writably map their own page tables and those of other programs. Such "exokernel" optimization is long dead.

Anything that can be userspace should be userspace

Posted Aug 5, 2026 11:45 UTC (Wed) by grmnsftphr (subscriber, #178591) [Link] (2 responses)

>ISTM the Linux kernel is already too big

There's the longtime systemd kernel that's even much bigger and will at some time encompass all known programs.

Unsubstantiated systemd bashing

Posted Aug 5, 2026 11:59 UTC (Wed) by chris_se (subscriber, #99706) [Link]

Could we please not? This feels like it's 2015 all over again.

Why?

Posted Aug 5, 2026 13:17 UTC (Wed) by corbet (editor, #1) [Link]

You seem to be determined to stir up old flame wars and be generally disruptive in the comments; I have asked you a few times now to stop. Don't do this anymore.


Copyright © 2026, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds