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 the child process. Now the transition from answer fork () is returned twice. This is once for the parent process and once for the child process.

This means that there are two separate processes DURING calling the plug, and not after it ends.

Now I don’t understand how he understands how to return 0 for the child process and the correct PID for the parent process.

This is 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.

At first, I correctly say that returning to any function is placed in one 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).

Suppose I call the function foo () inside a subroutine and this function returns a value, this value will be stored in a register, say, BAR. Each time a function wants to return a value, it will use a specific processor register. So, if I can manually change the return value in the process block, can I change the value returned by the function on the right?

How do I understand correctly how fork () works?

+47
c unix process internals fork
Apr 15 '16 at 7:00
source share
5 answers

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; // make sure child receives zero 

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.

+53
Apr 15 '16 at 7:08
source share

On Linux, fork() happens in the kernel; actual place here is _do_fork here . A simplified, fork() system call might 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 to the parent process. However, the kernel also creates the child process as a copy of the parent process; but instead of returning from a regular function, he synthetically created a new kernel stack for the newly created thread of the child process; and then context-switch to this thread (and process); since the newly created process returns from the context switching function, this will cause the thread of the child process to return to user mode with 0 as the return value from fork() .




Basically fork() in userland, only a thin wrapper returns a value that the kernel puts on its stack / in the return register. The kernel sets up a new child process so that it returns 0 through this mechanism from a single thread; and the child pid is returned in the parent system call, since any other return value from any system call, such as read(2) , will be.

+29
Apr 15 '16 at 7:39
source share

First you need to know how multitasking works. It is impractical to understand all the details, but each process runs on some kind of virtual machine controlled by the kernel: the process has its own memory, processor and registers, etc. There is a comparison of these virtual objects with real ones (magic is in the core), and there are several mechanisms that change virtual contexts (processes) to a physical machine over time.

Then, when the kernel starts the process ( fork() is a record in the kernel) and creates a copy of almost everything in the parent process of the child process, it can modify everything necessary. One of them is modifying the corresponding structures to return 0 for the child and the pid of the parent of the parent from the current call to fork.

Note: nether says that "fork is returned twice", a function call is returned only once.

Just think of a cloning machine: you go in alone, but two people go out, one is you and the other is your clone (very slightly different); while cloning a machine is able to set a name other than yours to a clone.

+10
Apr 15 '16 at 10:19
source share

The fork system call creates a new process and copies many states from the parent process. Things like the file descriptor table are copied, memory mappings and their contents, etc. This state is inside the kernel.

One of the things that the kernel monitors for each process is the values ​​of the registers that this process must restore when it returns from a system call, interrupt, interrupt, or context switch (most context switches occur during system calls or interrupts). in syscall / trap / interrupt and then restored when returning to the user area. System calls return values ​​by writing to this state. This is what the plug does. The parent fork gets one value, the child process gets another.

Since the forked process is different from the parent process, the kernel can do something for it. Give him any values ​​in the registers, give him any memory mappings. To make sure that almost everything except the return value is the same as in the parent process, it takes more effort.

+8
Apr 15 '16 at 7:11
source share

For each running process, the kernel has a register table to load back when creating the context switch. fork() - system call; a special call, which, when it is made, processes the context switch, and the kernel code making the call is launched in another (kernel) thread.

The value returned by system calls is placed in a special register (EAX in x86), which your application reads after the call. When fork() is called, the kernel copies the process, and in each register table of each process descriptor is written the corresponding value: 0 and pid.

+2
Apr 15 '16 at 15:56
source share



All Articles