Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does fork() know when to return 0?

Take the following example:

int main(void) {      pid_t  pid;       pid = fork();      if (pid == 0)            ChildProcess();      else            ParentProcess(); } 

So correct me if I am wrong, once fork() executes a child process is created. Now going by this answer fork() returns twice. That is once for the parent process and once for the child process.

Which means that two separate processes come into existence DURING the fork call and not after it ending.

Now I don't get it how it understands how to return 0 for the child process and the correct PID for the parent process.

This where it gets really confusing. This answer states that fork() works by copying the context information of the process and manually setting the return value to 0.

First am I right in saying that the return to any function is placed in a single register? Since in a single processor environment a process can call only one subroutine that returns only one value (correct me if I am wrong here).

Let's say I call a function foo() inside a routine and that function returns a value, that value will be stored in a register say BAR. Each time a function wants to return a value it will use a particular processor register. So if I am able to manually change the return value in the process block I am able to change the value returned to the function right?

So am I correct in thinking that is how fork() works?

like image 807
Machina333 Avatar asked Apr 15 '16 07:04

Machina333


People also ask

What does it mean when fork returns zero value?

Fork creates a duplicate process and a new process context. When it returns a 0 value it means that a child process is running, but when it returns another value that means a parent process is running.

How does fork return two values?

fork does not return two values. Right after a fork system call you simply have two independent processes executing the same code, and the returned pid from fork is the only way to distinguish which process are you in - the parent or the child.

Is child process PID 0?

the pid of your child, 0, meaning you are the child, or. -1, indicating an error occurred (and no child process was created)

What happens when fork ()?

When a process calls fork, it is deemed the parent process and the newly created process is its child. After the fork, both processes not only run the same program, but they resume execution as though both had called the system call.


2 Answers

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.

like image 190
paxdiablo Avatar answered Sep 22 '22 09:09

paxdiablo


In Linux fork() happens in kernel; the actual place is the _do_fork here. Simplified, the fork() system call could be something like

pid_t sys_fork() {     pid_t child = create_child_copy();     wait_for_child_to_start();     return child; } 

So in the kernel, fork() really returns once, into the parent process. However the kernel also creates the child process as a copy of the parent process; but instead of returning from an ordinary function, it would synthetically create a new kernel stack for the newly created thread of the child process; and then context-switch to that thread (and process); as the newly created process returns from the context switching function, it would make the child process' thread end up returning to user mode with 0 as the return value from fork().


Basically fork() in userland is just a thin wrapper returns the value that the kernel put onto its stack/into return register. The kernel sets up the new child process so that it returns 0 via this mechanism from its only thread; and the child pid is returned in the parent system call as any other return value from any system call such as read(2) would be.