Fork,vfork,clone
UNIX standard replication process system calls fork (that is, fork), but Linux,bsd and other operating systems do not only implement this one, specifically, the Linux implementation of three, Fork,vfork,clone (specifically Vfork created is a lightweight process, also called a thread, Is the process of sharing resources)
system Calls |
Description |
Fork |
The child process created by Fork is a complete copy of the parent process, replicating the resources of the father process, including the contents of the memory task_struct content |
Vfork |
Vfork creates a child process that shares data segments with the parent process, and the child processes created by Vfork () are run before the parent process |
Clone |
Creating threads on Linux generally uses the Pthread library actually Linux also gives us a system call to create a thread, which is clone |
Fork
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>intMainvoid){intCount =1;intChild Child = fork ();if(Child <0) {perror ("Fork Error:"); }Else if(Child = =0)//Fork return 0 in the child process because child can get hid PID by getpid (){printf("This is son, he count is:%d (%p). And his PID is:%d\n ", ++count, &count, Getpid ()); }Else //The PID of the child process was returned in the parent ' s thread of execution{printf("This is father, he count is:%d (%p), he pid is:%d\n", Count, &count, Getpid ()); }returnexit_success;}
From the running results, we can see that the PID of the parent-child two processes is different, and the stack and data resources are completely duplicated.
The child process changed the value of count, and count in the parent process was not changed.
The child process is the same as the address (virtual address) of the parent process count (note that they are mapped in the kernel in different physical addresses)
Copy when writing
Some people think that such a large volume of replication can lead to inefficient execution. In fact, during the replication process, Linux uses a copy-on-write strategy.
The child process replicates the task_struct of the parent process, the system stack space and the page table, which means that the above program, we did not execute count++ before, actually the child process and the parent process of the count point to the same piece of memory. When a sub-process changes the variable (that is, the variable is written), a new copy of the page involved is created by means of copy_on_write.
So when we execute the ++count, this time the child process creates a new page to copy the contents of the original page, the basic resource replication is necessary, and is efficient. The whole looks like a separate storage space for the parent process, and it's replicated again.
copy-on-write (Copy-on-write) is an optimization strategy that is used in the field of program design. The underlying idea is that if multiple callers (callers) require the same resources simultaneously, they will collectively get the same metric pointing to the same resource until a caller (caller) attempts to modify the resource, and the system will actually replicate a copy to the caller. To avoid being directly aware of the modified resources, this process is only transparent to the other calls (transparently). The main advantage of this approach is that if the caller does not modify the resource, no replica (private copy) is created.
The first-generation Unix system implements a fool-like process creation: when a fork () system call is made, the kernel copies the entire address space of the parent process as-is and assigns the copied copy to the child process. This behavior is very time-consuming because it requires:
Assigning page frames to a child process's page table
Assigning page frames to the pages of a child process
Initializing a page table for a child process
Copy the page of the parent process to the corresponding page of the child process
This method of creating address space involves many memory accesses, consumes many CPU cycles, and completely destroys the content in the cache. In most cases, this is often meaningless because many child processes start their execution by loading a new program, which completely discards the inherited address space.
The Linux kernel now uses a more efficient method called write-time replication (copy on Write,cow). The idea is quite simple: the parent process and the child process share the page frame instead of copying the page frame. However, as long as the page frames are shared, they cannot be modified, that is, page frames are protected. When a parent or child process attempts to write a shared page frame, an exception occurs, and the kernel copies the page to a new page frame and marks it as writable. The original page frame is still write-protected: When another process attempts to write, the kernel checks whether the write process is the only owner of the page frame, and if so, marks the page frame as writable for the process.
When process a uses system call fork to create a child process B, because child process B is actually a copy of parent process A,
Therefore, it will have the same physical page as the parent process. To save memory and speed up creation, the fork () function causes child process B to share the physical page of parent process A in a read-only manner. The access rights of parent process A to these physical pages are also set to read-only.
Thus, when either parent process A or child process B performs a write operation on these shared physical pages, a page error exception (Page_fault int14) interrupt occurs, and the CPU executes the system-supplied exception handler Do_wp_page () to resolve the exception.
Do_wp_page () cancels the shared operation on the physical page that caused the write exception to be interrupted, copying a new physical page for the write process, and having the parent process A and child process B each own a physical page of the same content. Finally, when returned from the exception handler, The CPU will re-execute the write instructions that led to the exception, and the process continues.
Vfork
If the fork Simple vfork () is more popular, the virtual address space structure of the kernel connecting child process is not created, and the virtual space of the parent process is shared directly, of course, this practice yielded the physical space of the parent process.
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>intMainvoid){intCount =1;intChild//child = Vfork (); printf("Before create son, the Father ' s count is:%d\n", count);if(Child = Vfork ()) <0) {perror ("Fork Error:"); }Else if(Child = =0)//Fork return 0 in the child process because child can get hid PID by getpid (){printf("This is son, he count is:%d (%p). And his PID is:%d\n ", ++count, &count, Getpid ());Exit(0); }Else //The PID of the child process was returned in the parent ' s thread of execution{printf(After son, this is father, he count is:%d (%p), he pid is:%d\n ", ++count, &count, Getpid ());Exit(0); }returnexit_success;}
From the running result you can see that the child process created by vfork (thread) shares the count variable of the parent process, and the count of 2 points to the same memory, so the child process modifies the count variable, and the parent process's count variable is also affected.
See Man-vfork (2)
A child process created by Vfork also causes the parent process to hang, unless the child process exit or EXECVE will invoke the parent process
Child processes created by Vfok share all the memory of the parent process, including the stack address, until the child process launches a new application using EXECVE
The child process created by vfork should not use return to return the caller, or exit with exit (), but it can use the _exit () function to exit
If we use return to exit, you will find that the program is in a state of repetitive vfork that is logically chaotic.
See the code below
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <unistd.h>intMainvoid){intCount =1;intChild//child = Vfork (); printf("Before create son, the Father ' s count is:%d\n", count);if(Child = Vfork ()) <0) {perror ("Fork Error:"); }Else if(Child = =0)//Fork return 0 in the child process because child can get hid PID by getpid (){printf("This is son, he count is:%d (%p). And his PID is:%d\n ", ++count, &count, Getpid ()); }Else //The PID of the child process was returned in the parent ' s thread of execution{printf(After son, this is father, he count is:%d (%p), he pid is:%d\n ", Count, &count, Getpid ()); Sleep2); }returnexit_success;}
We will find that Vfork's child process returns to the call after using return, so the parent process creates a new vfork process,
The way to solve this problem is not to use return in the process, but to use exit or _exit instead
The difference and connection between fork and Vfork
Vfork () usage is similar to fork (). But there are differences, the specific difference boils down to the following 3 points
The fork () child process copies the data segment of the parent process, the code snippet.
The vfork () child process shares the data segment with the parent process.
The execution order of the fork () parent-child process is indeterminate.
Vfork (): Ensure that the child process runs first,
Vfork () guarantees that the child process runs first, and the parent process may be scheduled to run after she calls exec or _exit. If the
The child process relies on further actions of the parent process before invoking these two functions, which results in a deadlock.
It is shared with the parent process data before calling exec or _exit, and the parent process may be scheduled to run after it calls exec or _exit. If the child process relies on further actions of the parent process before invoking these two functions, it causes a deadlock. When you need to change the value of a variable in a shared data segment, copy the parent process
Vfork is used to create a new process, and the purpose of the new process is to exec a new process, creating a child process like vfork and fork, but it does not copy the address space of the parent process completely into the child process and does not replicate the page table. Because the child process calls exec immediately, the address space is not stored. However, before calling exec or exit in a child process, he runs in the space of the parent process.
If a child process relies on a further action of the parent process when calling Vfork, it causes a deadlock. Thus, this system call is used to launch a new application. Second, the child process runs directly in the stack space of the parent process after vfork () returns and uses the parent process's memory and data. This means that the child process may break the data structure or stack of the parent process, resulting in a failure.
To avoid these problems, you need to make sure that once you call Vfork (), the child process does not return from the current stack frame, and the Exit function cannot be called if the child process changes the data structure of the parent process.
Child processes must also avoid altering any information in global data structures or global variables, because these changes can make it impossible for the parent process to continue. In general, if an application does not call EXEC () immediately after fork (), it is necessary to do a careful check before fork () is replaced with vfork ().
Why would there be vfork?
Because of the previous fork when it creates a child process, it creates a new address space and copies the parent process's resources, often executing the exec call in the child process, so that the previous copy work is a waste of effort, in which case the wise man came up with a vfork, It produces a child process that initially shares the address space with the parent process (which is actually the concept of the thread) because the child process is running in the address space of the parent process, so the child process cannot write.
And in the son "Occupy" the House of Lao Tzu, to wronged Lao Tzu, let him rest in the outside (blocking), once the son executed exec or exit, the equivalent of the son bought their own house, this time is tantamount to separation. At this point vfork guarantees that the child process runs first, and the parent process may be scheduled to run after she calls exec or exit.
So Vfork is designed to execute EXECVE system calls to load new programs immediately after the child process is created. The kernel guarantees that the parent process is blocked until the child process exits or starts a new program
After a child process is created with the Vfork function, the child process often calls an EXEC function to execute another program, and when the process calls an EXEC function, the process is completely substituted by the new program, and the new program starts from its main function because calling exec does not create a new process, so the process ID Does not change, exec simply replaces the current process's body, data, heap, and stack with another new program.
Clone
See
Man Handbook
The Clone function is powerful and has many parameters, so the process created by him is more complex than the previous 2 methods.
Clone allows you to selectively inherit the resources of the parent process, and you can choose to share a virtual space with the parent process like vfork, so that you can create a thread, you may not share it with the parent process, you can even choose to create the process and the parent process is no longer a parent-child relationship, but a sibling relationship.
It is necessary to say the structure of this function first.
··· C
int clone (int (fn) (void ), void *child_stack, int flags, void *arg);
···
Here fn is a function pointer, we know the process of 4 elements, this is a pointer to the program, is called the "script", Child_stack is obviously for the sub-process allocation system stack space (in Linux, the system stack space is 2 pages, is 8K of memory, which in this memory, A value is placed on the low address, which is the value of the process Control block task_struct, and flags is the flag that describes the resources you need to inherit from the parent process, ARG is the parameter passed to the child process. Here is the value that flags can take
New Ket OJ |
Nine degrees OJ |
csdn |
GitHub Code |
Sign |
Meaning |
|
|
Clone_parent |
The parent process of the child process that is created is the parent of the caller, and the new process and the process that created it become "brothers" rather than "father and son" |
|
|
Clone_fs |
The child process shares the same file system as the parent process, including root, current directory, umask |
|
|
Clone_files |
The child process shares the same file descriptor (descriptor) table as the parent process |
|
|
Clone_newns |
In the new namespace start Subprocess, namespace describes the file of the process hierarchy |
|
|
Clone_sighand |
The child process shares the same signal processing (signal handler) table as the parent process |
|
|
Clone_ptrace |
If the parent process is trace, the child process is also trace |
|
|
Clone_vfork |
The parent process is suspended until the child process frees the virtual memory resource |
|
|
Clone_vm |
The child process runs in the same memory space as the parent process |
|
|
Clone_pid |
When a child process is created, the PID is consistent with the parent process |
|
|
Clone_thread |
Added in Linux 2.4 to support POSIX threading standards, child processes share the same thread as the parent process Cheng |
|
|
The following example creates a thread (the child process shares the virtual memory space of the parent process and does not have its own independent virtual storage space that cannot be called a process). The parent process is suspended when the sub-thread frees the virtual storage resource before continuing execution.
#include <stdio.h>#include <malloc.h>#include <sched.h>#include <signal.h>#include <sys/types.h>#include <unistd.h>#define Fiber_stack 8192intAvoid*Stack;intDo_something () {printf("This was son, the PID is:%d, the A is:%d\n", Getpid (), ++a); Free(Stack);//Here I also do not know, if not released here, do not know if the child thread died after the memory will be released, the person can tell the next, thank you Exit(1);}intMain () {void*Stack; A =1;Stack=malloc(Fiber_stack);//Request system stack for child process if(!Stack) {printf("The Stack failed\n");Exit(0); }printf("Creating son thread!!! \ n "); Clone (&do_something, (Char*)Stack+ Fiber_stack, clone_vm| Clone_vfork,0);//Create Child threads printf(father, my pid is:%d, the A is:%d\n ", Getpid (), a);Exit(1);}
Clone, fork, vfork difference and contact
implementations see
The way to realize the idea
The system calls the service routine sys_clone, Sys_fork, and sys_vfork all end up calling the Do_fork function to complete.
The parameters of the do_fork are similar to the parameters of the clone system call, but one more regs (the user-mode register saved by the kernel stack). In fact, all the other parameters are taken with regs.
Specific implementation parameters are different
-
- Clone
Clone's API cloak, which presses FN, arg into the user stack, and then raises the system call. After returning to user mode, the next instruction is FN.
Sysclone:parent_tidptr, child_tidptr all passed into Do_fork's parameters.
Sysclone: Check if there is a new stack, if not, use the parent process stack (start address is REGS.ESP)
Fork, Vfork:
The service routine calls Do_fork directly, but the parameters are modified slightly
Clone_flags:
SYS_FORK:SIGCHLD, 0, 0, NULL, NULL, 0
Sys_vfork:clone_vfork | CLONE_VM | SIGCHLD, 0, 0, NULL, NULL, 0
User stack: Both are the stacks of the parent process.
PARENT_TIDPTR, child_ctidptr are null.
Fork,vfork and Clone in Linux (Difference and contact)