How does fork() know when to return 0?

后端 未结 5 533
[愿得一人]
[愿得一人] 2020-12-13 06:15

Take the following example:

int main(void)
{
     pid_t  pid;

     pid = fork();
     if (pid == 0) 
          ChildProcess();
     else 
          ParentPr         


        
5条回答
  •  抹茶落季
    2020-12-13 06:46

    How it works is largely irrelevant - as a developer working at a certain level (ie, coding to the UNIX APIs), you really only need to know that it works.

    Having said that however, and recognising that curiosity or a need to understand at some depth is generally a good trait to have, there are any number of ways that this could be done.

    First off, your contention that a function can only return one value is correct as far as it goes but you need to remember that, after the process split, there are actually two instances of the function running, one in each process. They're mostly independent of each other and can follow different code paths. 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
    

    You can hopefully see there that a single instance of fork can only return one value (as per any other C function) but there are actually multiple instances running, which is why it's said to return multiple values in the documentation.


    Here's one possibility on how it could work.

    When the fork() function starts running, it stores the current process ID (PID).

    Then, when it comes time to return, if the PID is the same as that stored, it's the parent. Otherwise it's the child. Pseudo-code follows:

    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
    

    Note that there's a lot of magic in the split_proc_into_two() call and it almost certainly won't work that way at all under the covers(a). It's just to illustrate the concepts around it, which is basically:

    • get the original PID before the split, which will remain identical for both processes after they split.
    • do the split.
    • get the current PID after the split, which will be different in the two processes.

    You may also want to take a look at this answer, it explains the fork/exec philosophy.


    (a) It's almost certainly more complex than I've explained. For example, in MINIX, the call to fork ends up running in the kernel, which has access to the entire process tree.

    It simply copies the parent process structure into a free slot for the child, along the lines of:

    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 slight modifications to the child structure to ensure it will be suitable, including the line:

    chld->p_reg[RET_REG] = 0;       // make sure child receives zero
    

    So, basically identical to the scheme I posited, but using data modifications rather than code path selection to decide what to return to the caller - in other words, you'd see something like:

    return rpc->p_reg[RET_REG];
    

    at the end of fork() so that the correct value gets returned depending on whether it's the parent or child process.

提交回复
热议问题