I was writing a lengthy article on the difficulties of replacing fork() in a unixoid OS, when an idea popped into my head. But let me start at the beginning.
fork() has many conceptual and architectural problems. The general use of any fork() like API is something like:
Code: Select all
pid = fork();
if (!pid) { /* child */
do_something();
exit();
}
Usually the alternative to fork() is given as a kind of spawn() system call; something that creates a fully formed process with a new program. These APIs generally tend to have tons of arguments to set specific things about the new process, and tend to be cumbersome to use (fork() has no arguments, CreateProcess() on Windows has around a dozen), and they are never comprehensive (some inheritable thing is inevitably forgotten), and never extensible (if a new property to be set comes along, you need a new version of the API).
So someone brought up "eggs" in the past: Some kind of operating system object a process can allocate, then use system calls to set certain attributes of the process, before hatching the egg into a full new process. The idea has merit, as it gets around most of the problems of fork() and spawn(), but it does mean adding a whole new set of system calls to set all those attributes. I also don't know if you really want to do a system call for all of those things. Another problem is that "eggs" don't fit into UNIX's idea of resources. UNIX knows processes and files. Memory only exists in form of processes using it up. When a process dies, all of its still-open files will be closed, thus cleaning them up, but all of its still-running child processes are simply re-parented to the init process. This is because presumably these processes are still running and will find some kind of exit sooner or later. An egg would fall into the category of "process", but would be unable to run, since it is not hatched yet. So re-parenting them is pointless, since they can never run. So I would have to change the process exit code to ensure all unhatched eggs of a process are cleaned up. Or I could make them files, but then they would be inheritable, and suddenly that whole thing gets a lot more complicated.
I was about to despair and ask for advice when I noticed that essentially, what I wanted to have was some kind of fork() I could hand a code pointer to. Then a small program could run to set various things about the child process and then call execve(). But the code would have to be restricted. It would have to somehow always ensure termination. And I also really don't like the idea of two processes running in the same address space while not being threads. So a real code pointer is out of the question; any subroutine above two bytes in size can potentially loop infinitely. And then it hit me: I need a byte code. I can set all the things a child process could ever want to set in the byte code, and if something is forgotten, I can add support in the byte code later. Maybe add a system call to query whether a given number is a valid opcode, so processes can negotiate what kind of byte code programs they need to create, and I have an extensible API that ought to be easy to use, feature complete, and be able to make do with five parameters (program file, argument list, environment list, byte code program pointer, byte code length).
That also gets rid of another problem I have with spawn(): Sometimes the order of operations is important. For example, a terminal emulator might want to spawn their child process with the slave console being the controlling terminal. The simplest way to do so is to call setsid() in the child, then open the slave console. setsid() removes the controlling terminal (that is otherwise inherited from the parent process), and opening the slave console sets it again, since a terminal is opened while the process has no controlling terminal. But this requires the setsid() to be done before the open(). posix_spawn() likely makes such a promise, but the interface really does not make it clear. But with a byte code the programmer can set the order of operations however they want.
So, what do you guys think, is that a sensible way to go?
P.S.: Regarding thread creation, I would just create another system call for that job. One that takes a new code and stack pointer and argument. While threads and processes are similar objects to the operating system, they are very different for the userspace creating them.