Fork () and vfock () Both create a process. What is the difference between them? There are three differences:
1. Fork (): The child process copies the data segment of the parent process, code segment
Vfork (): The child process shares the data segment with the parent process.
2. Fork (): the execution order of parent and child processes is unknown.
Vfork ensures that the sub-process runs first. Data is shared with the parent process before exec or exit is called. After it calls exec or exit, the parent process can be scheduled to run.
3. vfork ensures that the sub-process runs first. After she calls exec or exit, the parent process can be scheduled to run. If the sub-process depends on the further action of the parent process before calling these two functions, a deadlock will occur.
The following are examples:
1. Example of a code segment where a child process copies a parent process:
# Include <sys/types. h>
# Include <unistd. h>
# Include <stdio. h>
Int main ()
{
Pid_t PID;
PID = fork ();
If (PID <0)
Printf ("error in fork! ");
Else if (pid = 0)
Printf ("I am the child process, ID is % d/N", getpid ());
Else
Printf ("I am the parent process, ID is % d/N", getpid ());
}
Running result:
[Root @ localhost chenyunsong] # GCC fork1.c-O fork1
[Root @ localhost chenyunsong] #./fork1
I am the child process, ID is 4238
I am the parent process, ID is 4237
Why are two statements printed? The previous blog has roughly discussed it. Here I will explain it in detail.
First ,. /fork1 is an executable file. When we run it in a Linux Shell environment, the CPU registers are related to fork1 before the PID = fork () command is executed, in the user State, for example, CS points to the base address of the user State code segment; EIP points to the offset of the next instruction in the user State code segment, that is, the fork () database function; DS points to the base address of the user State data segment; ESI/EDI points to the full/partial offset of the current operand in the user State data segment; SS points to the base address of the stack segment in the user State data segment; esp points to the offset position of the user State stack top unit; the EBP points to the full/partial offset of the current unit in the user State stack segment, and may also store 32-bit or 16-bit operands or operation results.
Okay. After fork (), an interruption occurs and enters the kernel state. CS and EIP are the logical addresses of the registers containing the next command to be executed (sys_fork (regs. After entering the kernel state, the control unit must start to use command segments and stacks related to the new privileged level. This process includes loading the ss0 and esp0 registers from the TSS segment, and saving the user-state SS and ESP values in the new kernel state stack, load CS and EIP registers (sys_fork (regs) with the command address that causes the exception, and save the eflags, Cs, and EIP contents in the kernel state stack.
Then, save_all saves all other registers (most General registers) to the kernel state stack. At this time, CS: eip and SS: ESP are all kernel states, but note that at this time, the DS segment register is still _ user_ds, because the previous blog post mentioned that this is a problem of efficiency. As for how to use static user-state data, Linux uses another copy mechanism. We will discuss it in related topics, which is also very important.
Go to the practical function do_fork () called by the fork () system to create a new process from an existing process. The new process is called a sub-process, the original process is called a parent process. Fork () returns two values: the sub-process returns 0, the parent process returns the sub-process number, and the process number is a non-zero positive integer, therefore, the value returned by the parent process must be greater than zero. Before the PID = fork () Statement, only the parent process is running, and after the PID = fork () statement, the parent process and the newly created child process are running. Therefore, if PID = 0, it must be a child process. Why?
Because after do_fork () is completed, we have a complete sub-process that is running. However, it is not actually running yet. Wait until the scheduler schedule () decides when to hand over the CPU to this sub-process. In future blog posts, we will elaborate on the scheduling mechanism of Linux. Here we only need to know that it is possible to switch immediately, that is, the sub-process runs first, or it may take some time to switch, that is, the parent process runs first. During the process switching of the scheduler, the scheduler continues to improve the sub-process: load the sub-process descriptor thread field value (TSS value) into several CPU registers (see the previous blog post for details ). Especially thread. ESP is loaded into the ESP register, and the address of the function ret_from_fork () is loaded into the EIP register (these tasks will be done later, the value of the eax register is the value of the subprocess PID returned after the parent process executes the do_fork function ). This assembly language function calls the schedule_tail () function, loads all registers with the values stored in the stack, and returns the process to the user State. In this way, the eax register has two values: one is the value of the parent process-the PID of the child process (at this time, the parent process is running, because eax retains the PID value of the sub-process before the switchover) and the value of the sub-process is 0 (at this time, the sub-process is running, overwrite the original content of eax );.
If PID! = 0 (in fact, it must be greater than 0), so the parent process is running. We know that the fork () function sub-process copies the code segment of the parent process, so the sub-process also has
If (PID <0)
Printf ("error in fork! ");
Else if (pid = 0)
Printf ("I am the child process, ID is % d/N", getpid ());
Else
Printf ("I am the parent process, ID is % d/N", getpid ());
Therefore, the above Code will be executed by the parent process and the child process each time. The first sentence is printed because the child process's pid = 0, the PID of the parent process is greater than 0, and the second sentence is printed. The above running result is obtained.
Note that not the sub-process executes a segment, but the parent process executes a segment, so it only runs according to the PID (the C compiler locates the eax register) to determine the specific execution segment.
Let's take a look at an example of copying data segments:
# Include <unistd. h>
# Include <stdio. h>
Int main (void)
{
Pid_t PID;
Int COUNT = 0;
PID = fork ();
Count ++;
Printf ("Count = % d/N", count );
Return 0;
}
What is the printed value? Is it 2? Let's take a look at the running results first.
[Root @ localhost chenyunsong] # GCC fork2.c-O fork2
[Root @ localhost chenyunsong] #./fork2
Count = 1
Count = 1
Why not 2? Because we once stressed that the fork () function sub-process copies the data segment code segment of the parent process
Count ++;
Printf ("Count = % d/N", count );
Return 0
The sub-process will execute each time by the parent and child processes, but the sub-process will execute the same data segment (this data segment is copied from the parent process) Count + 1, similarly, when the parent process is executed, the Count + 1 in its data segment does not affect each other.
Let's take a look at vfork. If you change fork () in the above program to vfork (), what is the running result?
[Root @ localhost chenyunsong] # GCC fork2.c-O fork2
[Root @ localhost chenyunsong] #./fork2
Count = 1
Count = 1
Or error. Originally, vfock () is a shared data segment and the result is expected to be 2. Why is it not expected 2? Let's first look at one knowledge point: Another difference between vfork and fork is that vfork ensures that the child process runs first. After she calls exec or exit, the parent process can be scheduled to run. If the sub-process depends on the further action of the parent process before calling these two functions, a deadlock will occur.
In this way, after fork () in the above program is changed to vfork (), vfork () creates a sub-process and does not call exec or exit. Therefore, a deadlock will eventually occur.
How to change it? Check the following program:
# Include <unistd. h>
# Include <stdio. h>
# Include <sys/types. h>
Int main (void)
{
Pid_t PID;
Int COUNT = 0;
PID = vfork ();
If (pid = 0)
{
Count ++;
_ Exit (0 );
} Else {
Count ++;
}
Printf ("Count = % d/N", count );
Return 0;
}
If no _ exit (0) exists, the child process does not call exec or exit, so the parent process cannot be executed, the parent process can be scheduled to run only after the child process calls exec or exit.
Therefore, we add _ exit (0); to exit the sub-process and execute the parent process, so that the statements after else will be executed by the parent process, because data is shared with the parent process before the child process calls exec or exit, the Data Segment count of the parent process is changed to 1 after the child process exits. After the child process exits, when the parent process is executed again, the Count value is changed to 2. Check the actual running result:
[Root @ localhost chenyunsong] # GCC fork2.c-O fork2
[Root @ localhost chenyunsong] #./fork2
Count = 2
You can further understand a section on the Internet:
Why is there vfork? Because the previous fork was silly. When it creates a sub-process, it will create a new address space and copy the resources of the parent process, the exec call is often executed in the sub-process. In this way, the previous copy operation is in vain, and no one has thought of writing copy (write-on-copy) under the mechanism, smart people come up with vfork. The child process generated by it shares the address space with the parent process at the beginning (in fact, it is the concept of a thread ), at this time, the sub-process runs in the address space of the parent process, so the sub-process cannot perform write operations, and when the son "occupies" Lao Tzu's house, he will be wronged, let him stop (blocking) outside. Once the son executes exec or exit, it is equivalent to buying his own house, and then it is equivalent to dividing the house.