What exactly is simple and elegant about it? Have you looked into how much tooling is necessary everywhere in unix to make it work? It's insane. It has a huge footprint and it does not provide standardized APIs to make it work for non covered cases (the best we have is pthread_atfork which is not portable).
Me too. I found it simple: Create new child process, copy the address space, copy the instruction pointer, let it run.
If you want to improve performance via copy-on-write, it gets more complex to implement. If you combine it with other features like threads, mmap, limits, etc use becomes complex, but the mechanism itself is simple.
You might be able to design an easy variant of fork, which handles threads, mmap, limits, etc in a sane way, but that would not be simple anymore.
Fork is actually a good example, why simple and easy are different concepts and why it is really hard to achieve both.
Even in the total absence of threads (which you never have on a modern OS) you already fucked up. Because you did not account for file handles, signal handling, pid management etc.
fork() even on the simplest real world operating system is a massive pain for the OS.
Spawn-style APIs have to take a billion parameters that are mostly set to defaults, for things like working directory, environment variables, user ID, controlling terminal. Whereas fork+exec means you can express these things in a more compositional, buildery style: fork, change the two things you actually need to change, then exec.
Python says explicit is better than implicit. I agree in this case.
Global vars set wherever in a program are preferable to explicit params when creating a new process? And there's no way to have default params or anything?
The outside world is always going to be implicit global mutable state; spawning an arbitrary process to do who-knows-what is inherently that kind of problem. If you could express it as an actual function you wouldn't need to run a separate process.
> And there's no way to have default params or anything?
Well not in C. You can sort-of do it by having a struct with fields that you fill in where all-bits-zero is the default but that's generally more trouble than it's worth.
C easily allows you to have multiple functions. exec_with_current_state or something. And a struct is a way, too.
If you care about purity you don't use spawn-like functionality at all.
> C easily allows you to have multiple functions. exec_with_current_state or something.
That doesn't help. You'd need to have one variant where you just want to change the working directory, one variant where you just wanted to set an environment variable, one variant where you want to do both, one variant where you want to run as a different UID and change working directory but not touch the environment variables...