How this works is largely irrelevant - as a developer working at a certain level (for example, coding for the UNIX API), you really only need to know that it works.
Having said that, however, and recognizing that curiosity or the need to understand at a certain depth, is usually a good feature, there are many ways in which this could be done.
First, your statement that a function can only return one value is true as much as possible, but you need to remember that after the process has been split, there are actually two instances of the function, one in each process. They are mostly independent of each other and can follow different codes. The following diagram may help in understanding this:
Process 314159 | Process 271828 -------------- | -------------- runs for a bit | calls fork | | comes into existence returns 271828 | returns 0
It's safe to see that a single fork instance can return only one value (like any other C function), but in fact multiple instances are executed, so he said that it returns several values ββin the documentation.
There is one possibility here about how it can work.
When the fork() function is started, it saves the current process identifier (PID).
Then, when the time comes to return, if the PID matches the stored one, this is the parent. Otherwise, it is a child. Following is the pseudo code:
def fork(): saved_pid = getpid() # Magic here, returns PID of other process or -1 on failure. other_pid = split_proc_into_two(); if other_pid == -1: # fork failed -> return -1 return -1 if saved_pid == getpid(): # pid same, parent -> return child PID return other_pid return 0 # pid changed, child, return zero
Please note that there is a lot of magic in calling split_proc_into_two() , and it will almost certainly not work under covers (a) . This is just to illustrate the concepts around him, which is basically:
- get the original PID before separation, which will remain identical for both processes after separation.
- perform the split.
- get the current PID after the split, which will differ in two processes.
You can also take a look at this answer , it explains the philosophy of fork/exec .
(a) This is almost certainly more complicated than I explained. For example, on MINIX, the fork call terminates in the kernel, which has access to the entire process tree.
It simply copies the parent structure of the process to a free slot for the child element line by line:
sptr = (char *) proc_addr (k1); // parent pointer chld = (char *) proc_addr (k2); // child pointer dptr = chld; bytes = sizeof (struct proc); // bytes to copy while (bytes--) // copy the structure *dptr++ = *sptr++;
Then it makes small changes to the child structure to ensure that it will be suitable, including the line:
chld->p_reg[RET_REG] = 0;
So, basically, identical to the scheme that I set, but using data modifications, rather than choosing a code path, to decide what to return to the caller - in other words, you will see something like:
return rpc->p_reg[RET_REG];
at the end of fork() so that the return value is returned depending on whether it is a parent or child process.