Moving beyond fork() + exec() [LWN.net]
LWN<br>.net<br>News from the source
Content Weekly Edition<br>Archives<br>Search<br>Kernel<br>Security<br>Events calendar<br>Unread comments
LWN FAQ<br>Write for us
User:<br>Password: |
Log in /<br>Subscribe /<br>Register
Moving beyond fork() + exec()
[LWN subscriber-only content]
By Jonathan Corbet<br>June 5, 2026
Since the earliest days of Unix, two of the core process-oriented system<br>calls have been fork(), which creates a child process as a copy of<br>the parent, and exec(), which runs a new program in the place of<br>the current one. In Linux kernels, those system calls are better known as<br>clone()<br>and execve(),<br>but the core functionality remains the same. While there is elegance to<br>this process-creation model, there are shortcomings as well. A recent proposal from<br>Li Chen to add "spawn templates" to the kernel will not be accepted in its<br>current form, but it may point the way toward a new process-creation<br>primitive in the future.
fork() is a relatively expensive system call; it must copy the<br>entire process state (including memory) for the child process. Many<br>optimizations have been made over the years, but a fork is still a<br>fundamentally costly operation. To make things worse, a fork()<br>call is often immediately followed by an exec(), which will<br>discard all of that memory that was so carefully copied for the child.<br>Attempts (such as vfork())<br>have been made over the years to optimize for this case, but the pattern<br>still is more expensive than it could be.
The LWN kernel-source database is the definitive source of information about kernel releases. Try a one-month free trial subscription for immediate access to LWN's kernel content and KSDB as well.
Spawn templates
Chen's patch set takes an interesting approach to optimize the<br>fork() and exec() pattern. It is focused on applications<br>that repeatedly launch processes running the same executable; imagine, for<br>example, a program that must run Git repeatedly to obtain information about<br>the contents of a repository. In such cases, the program could establish a<br>template to accelerate those invocations, spreading the setup cost across<br>multiple operations. This template would be created with the<br>spawn_template_create() system call:
struct spawn_template_create_args {<br>__aligned_u64 flags;<br>__s32 execfd;<br>__u32 exec_flags;<br>__aligned_u64 filename;<br>/* Some fields elided */<br>};
int spawn_template_create(struct spawn_template_create_args *args, size_t args_size);
This call will return a file descriptor representing a template for the<br>executable file, which can be specified as either a file descriptor<br>(execfd) or an absolute path (filename), but not both.<br>To create the template, the kernel will open the indicated file and cache a<br>bunch of information that will allow a process to run that file more<br>quickly in the future.
The application in question may run a given executable many times, but each<br>invocation is different in a number of ways. The details of a specific<br>invocation must be placed into an instance of this structure:
struct spawn_template_spawn_args {<br>__aligned_u64 flags;<br>__aligned_u64 pidfd;<br>__aligned_u64 argv;<br>__aligned_u64 envp;<br>__aligned_u64 actions;<br>__aligned_u64 actions_len;<br>__aligned_u64 reserved[4];<br>};
The argv field is a pointer to the argument list to be passed to<br>the program, while envp points to its environment. Changes to<br>file descriptors and signal handling, instead, are passed through<br>actions, which is a pointer to an array of:
struct spawn_template_action {<br>__u32 type;<br>__u32 flags;<br>__s32 fd;<br>__s32 newfd;<br>__aligned_u64 arg;<br>};
If, for example, file descriptor four should be closed in the child, the<br>associated spawn_template_action structure would have<br>type set to SPAWN_TEMPLATE_ACTION_CLOSE and fd<br>set to four. Other actions exist for duplicating file descriptors, opening<br>files, changing the working directory, and changing signal handling.
Once the spawn_template_spawn_args structure has been filled in,<br>the new process can be run with:
int spawn_template_spawn(int template_fd,<br>struct spawn_template_spawn_args *args, int args_size);
Internally, this system call follows something close to the normal<br>fork()/exec() path. Chen is careful to point out that<br>all of the normal checks applied when executing a new file remain in place.<br>But the cached information in the template makes the whole process faster<br>than it was before.
How much faster? Benchmark results provided in the cover letter show an<br>improvement of about 2%, which may not seem like a lot, but it may<br>make a difference for applications that fit the expected pattern.
Toward posix_spawn()
The most detailed review of this work was posted<br>by Mateusz Guzik, who said: "This problem is dear to my heart and I<br>have been pondering it on and off for some time now. The entire fork + exec<br>idiom is terrible and needs to be retired". He pointed out that the<br>focus of the patch set was a bit strange in that it left the<br>fork() part of the problem untouched. That is where most of the<br>cost lies,...