Before introducing the creation process, let's briefly introduce the process memory layout under Linux.
Stack-The memory area for storing local variables, arguments and return addresses of all functions
Heap-dynamically allocated memory area
bss-The memory area for all uninitialized global variables and static variables
data-the storage memory area for all initialized global variables and static variables
[^1] Typical memory layout of a process on Linux/x86-32
In Linux system, a new process can be created by calling fork(). The calling process is the parent process, and the new process that is born is the child process.
fork() is special because it will return twice, which means there will be two return values. We can distinguish between parent and child processes by these two return values. In the parent process, fock() will return the process ID of the child process. In the child process, it returns 0 if it succeeds, and returns -1 if it fails (refer to the manual for the failure reason). The child process can call getpid()
to get the process ID.
After the child process is born, it will get a copy of the data from the parent process, including bss, data segment and stack segment. It is worth noting that CentOS 8 cannot guarantee the execution order of parent and child processes after calling fork().
We can know from the output that the data of the two processes are independent.
# include<unistd.h>#include<iostream>auto global_value =11;// stored in data segmentintmain(){auto local_value =66;// stored in stack segmentdouble* dPtr =newdouble(3.14);switch(fork()){case-1:
std::cout <<"failed to fork.\n";_exit(-1);case0:*dPtr =5.12;
std::cout <<"child process ID: "<<getpid()<<"\n";
std::cout <<"global value: "<<(++global_value)<<"\n";
std::cout <<"local value: "<<(++local_value)<<"\n";
std::cout <<"double pointer value: "<<(*dPtr)<<"\n";
std::cout <<"----------------------------------\n";_exit(0);}
std::cout <<"parent process ID: "<<getpid()<<"\n";
std::cout <<"global value: "<< global_value <<"\n";
std::cout <<"local value: "<< local_value <<"\n";
std::cout <<"double pointer value: "<<(*dPtr)<<"\n";
std::cout <<"----------------------------------\n";exit(0);}
Output result:
[ me@localhost Documents]$ ./exe
parent process ID:3961
global value:11
local value:66
double pointer value:3.14----------------------------------
child process ID:3962
global value:12
local value:67
double pointer value:5.12----------------------------------[me@localhost Documents]$ ./exe
parent process ID:3988
global value:11
local value:66
double pointer value:3.14----------------------------------
child process ID:3989
global value:12
local value:67
double pointer value:5.12----------------------------------
Inherit the file descriptor##
When executing fork(), the child process will get a copy of the file descriptor from the parent process. Just like copying data, although file descriptors are also copies, in essence these file descriptors point to the open file table maintained by the kernel. Therefore, the offsets and file status flags of the corresponding file descriptions are the same.
[^2] Duplication of file descriptors during fork(), and closing of unused descriptors
# include<unistd.h>#include<fcntl.h>#include<iostream>intmain(){auto fd =open("/home/usr1/Documents/reading.txt", O_NONBLOCK);switch(fork()){case-1:
std::cout <<"failed to fork.\n";_exit(-1);case0:auto flags =fcntl(fd, F_GETFL);if(O_NONBLOCK & flags)
std::cout <<"O_NONBLOCK flag is on\n";_exit(0);}auto flags =fcntl(fd, F_GETFL);if(O_NONBLOCK & flags)
std::cout <<"O_NONBLOCK flag is on\n";close(fd);return0;}
Output result:
O_NONBLOCK flag is on
O_NONBLOCK flag is on
Because the child process may execute the exec()
family function immediately after birth. This means that all the data copied by the child process from the parent process will be washed away, and the copying effort will be wasted. For efficiency reasons, COW was put into use. The principle is very simple. After calling fork(), the parent and child processes share read only memory images. If no process modifies this memory image, then they all have the same memory impact. If any process wants to modify the data, then the kernel will copy a new memory image for the process for independent use by the process.
reference:
[^1] 6.4 Virtual Memory Management, The Linux Programming Interface.
[^2] 24.2.1 File Sharing Between Parent and Child, The Linux Programming Interface.
Recommended Posts