1. Fork
1.1 The parent-child process created by Fork for the FD that was opened before fork, the shared file offset. this is because, after the parent process has forked a child process, it has its own process table entry, so they have a set of identical file descriptor tables that share the file table entries and thus share the offset. In addition , Close is closed with a reference count, and when Close is executed, it is the reference countof the file representation in the kernel that suffocated that FD is reduced by 1, and the structure is truly destroyed only if the reference count is 0 o'clock.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #define ERR_EXIT ( m) do { perror (m); Exit (exit_failure); } while (0)/* * Parent Inherits Shared file offset */int main (int argc, const char *argv[]) { int fd = open ("Test.txt", o_rdonly); if (fd = =-1) { err_exit ("open"); } pid_t pid; if (PID = fork ()) < 0) { err_exit ("fork"); } else if (PID = = 0) { char buf[10] = {0}; Read (FD, buf, 3); printf ("in child buf =%s\n", buf); Close (FD); Here the reference count minus 1 } else{ sleep (3); Char buf[10] = {0}; Read (FD, buf, 3); printf ("in Parent BUF =%s\n", buf); Close (FD); } return 0;}
1.2 There are 2 types of shared file offsets that we are currently encountering:
A) replication of FD by means of DUP, at this time two FD shared file offset (file table entries);
b)Fork The parent-child process, which shares the file offset.
How the 1.3 Shell works: when we type "ls" on the keyboard,
A) shell (bash, zsh) Fork a child process first
b) Replace the child process's code with "LS" with exec
c) Shell is responsible for the recycling of the child process
1.4 for the classic fork+exec combination mode , fork out the sub-process and then replace, then copy the full child process address space is meaningless. Two solutions are proposed:
A) The purpose of vfork:vfork is to exec;
b) use write-time replication technology for fork .
1.5 Fork's write-time replication technology:
A) When you fork a child process, only the page table entries are copied, not the specific process space. The address space is also set to read-only .
b) Whenever either party attempts to modify the address space, it copies one copy .
1.6 copy-on-write (COW) causes the parent-child process to physically share the address space, but the logical address space is independent of each other .
2. Processing of parent-child processes
2.1 ways to deal with zombie processes:
A) processing SIGCHLD signals;
b) using wait, waitpid.
2 . 2 If there are no child processes , then when you do wait, it returns 1 immediately, while errno is echld. otherwise blocked , use Wnohang to avoid blocking.
2.3 Waitpid does not recycle child processes in order.
Examples of 2.3.1 that are not sequentially recycled.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/ types.h> #include <sys/wait.h> #define ERR_EXIT (m) do { perror (m); Exit (exit_failure); } while (0) #define N 10int Main (int argc, const char *argv[]) { int i; pid_t pid; for (i = 0; i < N; i++) { if (PID = fork ()) < 0) { err_exit ("fork"); } else if (PID = = 0) { exit (+ i);//child process return exit code } } int status; while (PID = Waitpid ( -1, &status, 0)) > 0) {//Recycle all sub-processes if (wifexited (status)) {//Determine if child processes exit normally printf (" Child%d return success%d\n ", PID, Wexitstatus (status)); } else printf ("Chidl%d return errno\n", PID); } if (errno! = echild) { err_exit ("Waitpid"); } return 0;}
2.3.2 to sequentially recycle, You can use waitpid one time to wait for each specific PID, if not wait, will always block.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/ types.h> #include <sys/wait.h> #define ERR_EXIT (m) do { perror (m); Exit (exit_failure); } while (0) #define N 10/* * Sequential recovery * */int Main (int argc, const char *argv[]) { int i; pid_t Pid[n]; for (i = 0; i < N; i++) { if ((pid[i] = fork ()) < 0) { err_exit ("fork"); } else if (pid[i] = = 0) { exit (+ i);//child process return exit code } } int status; i = 0; pid_t ret; while (ret = Waitpid (Pid[i], &status, 0)) > 0) {//Recycle all sub-processes if (wifexited (status)) {//Determine if child process exits normally printf ("Child%d return success%d\n", ret, Wexitstatus (status)); } else printf ("Chidl%d return errno\n", Pid[i]); i++; } if (errno! = echild) { err_exit ("Waitpid"); } return 0;}
2.4 system differs from exec:
A) Exec replaces the current process
b) system is to create a child process and then call exec replace
Example of 2.4.1 exec.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include < Errno.h> #define ERR_EXIT (m) do { perror (m); Exit (exit_failure); } while (0) int main (int argc, const char *argv[]) { printf ("Enter main\n"); EXECLP ("ls", "ls", "-l", NULL);//Replace the current child process printf ("Leave main\n");}
Example of the 2.4.2 system.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include < Errno.h> #define ERR_EXIT (m) do { perror (m); Exit (exit_failure); } while (0) int main (int argc, const char *argv[]) { printf ("Enter main\n"); System ("Ls-l"); printf ("Leave main\n");}
Implementation of the 2.5 system:
A) Creating child processes
b) subprocess takes exec for process substitution
c) Parent process reclaims child processes, note eintr
2.6 The difference between a daemon and a normal process: (this time to see it again)
A) daemon does not belong to the conversation group where the shell is located
b) The daemon will not be affected when the shell exits