[Linux] Process creation, termination, waiting, replacement

Process creation

The fork function creates a new process from an existing process. The new process is a child process, and the original process is its parent process.

The header file of the fork function is: #include <unistd.h>
Its return value pid_tis an unsigned integer

In Linux, pid_tit is a data type used to represent process identifiers (Process IDentifier). A process identifier is an integer value that uniquely identifies a running process.
pid_tThe data type is a signed integer type, which is usually signed intthe type. Its value can be positive, zero, or negative.
In Linux programming, pid_tit is often used for operations such as obtaining the identifier of the current process, creating a new process, and inter-process communication. Each process has a unique process identifier, which can be used to uniquely identify and operate a specific process.

Process = kernel data structure + process code and data

  • Allocate new memory blocks and kernel data structures to the child process
  • Copy part of the data structure content of the parent process to the child process
  • Add child process to system process list
  • fork returns and starts scheduler scheduling

When a process calls fork, there are two processes with the same binary code. And they all run to the same place. But each process starts its own journey
. When the fork is successful, 0 is returned to the child process, the parent process returns the child process id, and -1 is returned on error.

 int main()
 {
    
    
   pid_t pid;
 
   printf("Before: pid is %d\n", getpid());
   if ( (pid=fork()) == -1 ) return 0;
   printf("After:pid is %d, fork return %d\n", getpid(), pid);
   sleep(1);
   return 0;
 }   

Insert image description here
Why after the child process is created, where does the parent and child process run, and where does the child process run?
This is because there is also a register (EIP) in our cpu. After each line of code is loaded into the memory, there is a corresponding address, which will be recorded in the register. When our program terminates unexpectedly, we can come back through the cpu register. Rewrite the operation of the retrieval address interface.
Each process thinks it has a register, so after a fork, the starting position of the register is the code after the fork.

Creating a child process and assigning the corresponding kernel structure to the child process must be unique to the child process itself, because the process is independent.
Because the child process also has its own code and data, but if we create a child process, if our child process does not run immediately and does not modify the content in the parent process, the two processes use the same data space Yes, when the data of the parent process or the child process is modified, copy-on-write will occur
Insert image description here

Why do we need to use copy-on-write?
Because copy-on-write can completely separate its own process, the independence of the process is completed.
Copy-on-write is also a delayed application technology, which can improve the utilization rate of the whole machine memory.

process terminated

When the process terminates, the system will release the relevant kernel data organization and data codes requested by the process.

Why the return value of main function is 0

Each number returned by return has a corresponding exit error reason. The number 0 means successful exit. Next, let's see how many error return values ​​the main function has.
The process exit code is helpful for us to quickly find and solve errors

Insert image description here

How to terminate a process

When talking about how to terminate a process, we need to extract a new concept, that is, how to find out the exit code of the previous process?
Can be used echo &?to access the exit code of the previous process.
Generally speaking, return corresponds to the exit code.

Insert image description here

return terminate

The termination in the main function is the real process termination.

 1: process.c ? ?                                                                                                                                           ?? buffers 
  1 #include <unistd.h>
  2 #include <sys/types.h>
  3 #include <stdio.h>
  4 #include <string.h>
  5 
  6 int sum(int n)
  7 {
    
    
  8   int s = 0;
  9   int i = 0;
 10   for(i = 1;i<n;i++)
 11   {
    
    
 12     s+=i;
 13   }
 14   return s;
 15 }
 16 
 17 int main()
 18 {
    
    
 19   int res = sum(10);
 20   if(res != 5050)
 21   {
    
    
 22     //函数运行结果不正确,直接退出
 23     return 1;//返回错误信息                                                                                                                                         
 24   }
 25   return 0;
 26 }

Insert image description here
exit terminates the process

The exit termination process is different from the return termination process. When exit terminates the process, it will call the cleanup function, flush the buffer, close the stream, etc. We will explain this in the difference with _exit below.

Insert image description here

 1: process.c  ⮀                                                                                                                                           ⮂⮂ buffers 
  1 #include <unistd.h>  
  2 #include <sys/types.h>                                                    
  3 #include <stdio.h>  
  4 #include <string.h>  
  5 #include <stdlib.h>  
  6 int sum(int n)  
  7 {
    
      
  8   int s = 0;  
  9   int i = 0;  
 10   for(i = 1;i<n;i++)  
 11   {
    
      
 12     s+=i;  
 13   }  
 14   exit(1);  
 15 }  
 16   
 17 int main()  
 18 {
    
      
 19   int res = sum(10);  
 20   if(res != 5050)  
 21   {
    
      
 22     //函数运行结果不正确,直接退出  
 23    exit(2) ;//返回错误信息                                                                                                                                        
 24   }                                                                                             
 25   return 0;                                                                                      
 26 }  

_exit terminate

As long as the process encounters _exit, the process will be terminated immediately without any operation.

 1: process.c ? ?                                                                                                                                           ?? buffers 
  1 #include <unistd.h>
  2 #include <sys/types.h>
  3 #include <stdio.h>
  4 #include <string.h>
  5 #include <stdlib.h>
  6 int sum(int n)
  7 {
    
    
  8   int s = 0;
  9   int i = 0;
 10   for(i = 1;i<n;i++)
 11   {
    
    
 12     s+=i;
 13   }
 14   _exit(3);
 15 }                                                                                                                                                                   
 16 
 17 int main()
 18 {
    
    
 19   int res = sum(10);
 20   if(res != 5050)
 21   {
    
    
 22     //函数运行结果不正确,直接退出
 23    _exit(2) ;//返回错误信息
 24   }
 25   return 0;
 26 }

Insert image description here
The difference between exit and _exit

   int main()
   {
    
    
     printf("hello");
    sleep(3);
    exit(13);                                                                                                                                                        
  }

Insert image description here

   int main()
   {
    
    
     printf("hello");
    sleep(3);
    _exit(13);                                                                                                                                                        
  }

Insert image description here

We can see from the case that when exit exits the termination process, the buffer will be flushed, but _exit will not

Insert image description here

Process waiting

Why do we need to wait for the process?
When the child process exits before the parent process, our child process will become a zombie process. In this case, we have to use wait to recycle the child process.
Waiting can also allow the parent process to recycle the exit code of the child process and understand the execution status of the child process.

wait blocking wait

When all child processes are terminated, the second half of the parent process wait will be run again. And the child process will also be recycled and is no longer a zombie process.
Returns the pid of the waiting child process after success, returns -1 on failure.

Insert image description here

 1 #include <unistd.h>
  2 #include <sys/types.h>
  3 #include <stdio.h>
  4 #include <string.h>
  5 #include <stdlib.h>
  6 #include <sys/wait.h>
  7 int main()
  8 {
    
    
  9   pid_t id = fork();
 10   if(id < 0)
 11   {
    
    
 12     perror("fork");
 13     exit(1);
 14   }
 15   else if(id == 0)
 16   {
    
    
 17     //子进程
 18     int cnt = 5;
 19     while(cnt)
 20     {
    
    
 21       printf("cnt: %d, 我是子进程, pid: %d, ppid : %d\n", cnt, getpid(), getppid());
 22       sleep(1);
 23       cnt--;
 24     }
 25     exit(0);                            
 26   }                    
 27   else
 28   {
    
                                                                    
 29     //父进程                                                     
 30     printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());
 		sleep(7);                                                                                                 
 31     pid_t ret = wait(NULL);//阻塞式等待
 32     if(ret > 0)                                          
 33     {
    
                                             
 34       printf("等待子进程成功,ret : %d\n",ret);                                                                                 
 35     }                 
 36     while(1)
 37     {
    
    
 38       printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());
 39       sleep(1);
 40     }
 4142   }
 43 }

Process search method:while :; do ps ajx | head -1 && ps ajx | grep process | grep -v grep;sleep 1;echo "***************************************";done Insert image description here

waitpid blocks and waits

pid:
pid = -1, wait for any child process, equivalent to wait.
pid > 0, wait for a child process whose process ID is equal to pid.
status:
WIFEXITED(status): True if the status returned by a normally terminated child process. ( Check whether the process exited normally )
WEXITSTATUS (status): If WIFEXITED is non-zero, extract the exit code of the child process. ( Check the process exit code ) The status is not used as an integer, but is divided into 32 bits
according to the way of bits. We only learn the lower 16 bits of options: WNOHANG: If the child process specified by pid has not ended, Then the waitpid() function returns 0 and does not wait. If it ends normally, the ID of the child process is returned. 0: Always block until the child process terminates.




Insert image description here
Insert image description here

Insert image description here

 1 #include <unistd.h>
  2 #include <sys/types.h>
  3 #include <stdio.h>
  4 #include <string.h>
  5 #include <stdlib.h>
  6 #include <sys/wait.h>
  7 int main()
  8 {
    
    
  9   pid_t id = fork();
 10   if(id < 0)
 11   {
    
    
 12     perror("fork");
 13     exit(1);
 14   }
 15   else if(id == 0)
 16   {
    
    
 17     //子进程
 18     int cnt = 5;
 19     while(cnt)
 20     {
    
    
 21       printf("cnt: %d, 我是子进程, pid: %d, ppid : %d\n", cnt, getpid(), getppid());
 22       sleep(1);
 23       cnt--;
 24     }                                                 
 25     exit(12);     
 26   }                                                    
 27   else               
 28   {
    
                           
 29     //父进程                            
 30     printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());                                                                                                 
 31    sleep(7);
 32    int status = 0;
 33                                                                  
 34    // pid_t ret = wait(NULL);//阻塞式等待                 
 35    pid_t ret = waitpid(id,&status,0);
 36    if(ret > 0)
 37     {
    
    
 38       // 0x7F -> 0000.000 111 1111
 39       printf("等待子进程成功, ret: %d, 子进程收到的信号编号: %d,子进程退出码: %d\n",\
 40                     ret, status & 0x7F ,(status >> 8)&0xFF); //0xff --> 0000...000 1111 1111
 41     }
 42     while(1)
 43     {
    
    
 44       printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());
 45       sleep(1);
 46     }
 47     
 48   }
 49 }
 50 

Insert image description here

Insert image description here

Perform non-blocking testing
Insert image description here

 1 #include <unistd.h>
  2 #include <sys/types.h>
  3 #include <stdio.h>
  4 #include <string.h>
  5 #include <stdlib.h>
  6 #include <sys/wait.h>
  7 int main()
  8 {
    
    
  9   pid_t id = fork();
 10   if(id < 0)
 11   {
    
    
 12     perror("fork");
 13     exit(1);
 14   }
 15   else if(id == 0)
 16   {
    
    
 17     //子进程
 18     int cnt = 5;
 19     while(cnt)
 20     {
    
    
 21       printf("cnt: %d, 我是子进程, pid: %d, ppid : %d\n", cnt, getpid(), getppid());
 22       sleep(1);
 23       cnt--;
 24     }
 25     exit(12);
 26   }
 27   else 
 28   {
    
    
 29     //父进程
 30     printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());                                                                                                 
 31    int Exit = 0;
 32    while(!Exit)
 33    {
    
    
 34 
 35    int status = 0;
36 
 37    // pid_t ret = wait(NULL);//阻塞式等待
 38    pid_t ret = waitpid(-1,&status,WNOHANG);
 39                                                                                                                                                                     
 40    if(ret > 0)
 41     {
    
    
 42       if(WIFEXITED(status))
 43       {
    
    
 44         // 0x7F -> 0000.000 111 1111
 45            printf("等待子进程成功, ret: %d, 子进程收到的信号编号: %d,子进程退出码: %d\n",
 46                     ret, status & 0x7F ,WEXITSTATUS(status)); //0xff --> 0000...000 1111 1111
 47           Exit = 1;
 48       }
 49     }
 50    else if(ret == 0)
 51    {
    
    
 52       printf("子进程还在运行中,暂时还没有退出,父进程可以在等一等, 处理一下其他事情??\n");
 53       printf("这里还可以执行其他命令\n");
 54       sleep(1);
 55    }
 56    else{
    
    
 57      //等待失败
 58      printf("wait失败!\n");
 59      Exit = 1;
 60    }
 61    }
 62     while(1)
 63     {
    
    
 64       printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());
 65       sleep(1);
 66     }
 67     
 68   }
 69 }

wait 和 waitpid

wait and waitpid can get the exit information of the child process, so why use the wait and waitpid functions?
This is because there is a copy-on-write function between processes, and the processes are independent, so you need to use the wait and waitpid functions to read the information of the child process. So why can wait
and waitpid get the information of the child process? They are all independent in the first place, how is that possible?
This is because wait and waitpid read the task_struct structure of the child process. When the child process is a zombie process, the child process must at least retain the PCB information of the process, and the task_struct retains the exit result information when any process exits
. wait and waitpid are system calls, so they have this power to complete.

Process replacement

What is it?
If we create a new subprocess and the subprocess wants to execute a completely new one, we need to perform process replacement.
Process replacement is to load a privileged program (code and data) on the disk through a specific interface, load it into the address space of the calling process, and re-establish the mapping with the page table of the current process.
How to operate?

  1. Do not create child processes
  2. Create child process

#include <unistd.h>`
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
int execle(const char *path, const char *arg, …,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);

The letters after exec represent

  • l(list) : Indicates that the parameter takes a list
  • v(vector) : array for parameters
  • p(path): There is p to automatically search the environment variable PATH
  • e(env): Indicates that you maintain environment variables yourself

Insert image description here

Create subprocess to replace
1. Do not create child processes

int execl(const char *path, const char *arg, …);
Insert image description here

  1 #include <unistd.h>
  2 #include <sys/types.h>
  3 #include <stdio.h>
  4 #include <string.h>
  5 #include <stdlib.h>
  6 #include <sys/wait.h>
  7 
  8 int main()
  9 {
    
    
 10   printf("当前进程开始\n");
 11   execl("/usr/bin/ls", "ls", "--color=auto","-l", NULL);
 12     exit(1);                                                                                                                                  
 13   printf("当前进程结束\n");
 14   return 0;
 15 }

Insert image description here

After the replacement is successful, why is the following code not executed?
This is because after execl is successfully replaced, all code and data of the current process will be replaced. Including executed and unexecuted.
Therefore, once the replacement is successful, all subsequent codes will not be executed.
Insert image description here

2. Create a child process

When we replace the process, why do we need to create a child process?

  1. In order not to affect the parent process, because when replacing, all the code and data are replaced, so we choose to create a child process and replace it in the child process. At this time, the code and data of the father-child process and the child process are completely separated, although there is no conflict between them.
  2. We want the parent process to focus on reading data, parsing data, and assigning processes to execute code.

int execl(const char *path, const char *arg, …);
Insert image description here
Insert image description here

int execlp(const char *file, const char *arg, …)
Insert image description here

Insert image description here
Insert image description here
Insert image description here
int execv(const char *path, char *const argv[])
Insert image description here

int execvp(const char *file, char *const argv[])
Insert image description here
int execle(const char *path, const char *arg, …,char *const envp[])
Insert image description here

	exec.c
    1 #include <stdio.h>
    2 #include <unistd.h>
    3 #include <wait.h>
    4 #include <stdlib.h>
    5 
    6 #define NUM 16
    7 int main()
    8 {
    
    
    9   const char* myfile = "./mycmd";
   10   
   11   pid_t id = fork();
   12   if(id == 0)
   13   {
    
    
   14     //子进程
   15    printf("子进程开始运行,pid:%d\n",getpid());
   16    sleep(2);
   17    char* const _env[NUM]{
    
    
   18      (char*)"MY_105_VAL=888777666555",
   19     NULL
   20    };
   21    
W> 22    char* const  _argv[NUM]{
    
    
   23     (char*)"ls",
   24     (char*)"-a",
   25     (char*)"-l",
   26     (char*)"-i",
   27     NULL
   28    };
   29    execle(myfile,"mycmd","-a",NULL,_env);
   30    // execvp("ls",_argv);
   31    //execv("/usr/bin/ls",_argv);//和下面的execl只有传参方式的区别
   32    //execlp("bash","bash","test.sh",NULL); 
   33    //execlp("python", "python", "test.py", NULL);                                                                                           
   34    //execlp("ls","ls","-a","-l",NULL);
   35   // execl(myfile, "mycmd", "-b", NULL);
   36   // execl("/usr/bin/ls","ls","-a","-l",NULL);
   37    //如果替换失败就会走下面的程序
   38    exit(-1);
   39   }
   40   else 
   41   {
    
    
   42     //父进程
   43     printf("父进程开始运行,pid:%d\n",getpid());
   44     int status = 0;
   45     pid_t id = waitpid(-1,&status,0);
   46     //阻塞等待, 一定是子进程先运行完毕,然后父进程获取之后,才退出!
   47     if(id > 0)
   48     {
    
    
   49       printf("wait success,exit code: %d\n",WEXITSTATUS(status));
   50     }
   51   }
   52   return 0;
   53 }

 mycmd.c                                                                                                                    
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 int main(int argc,char *argv[])
  5 {
    
    
  6   if(argc != 2)
  7   {
    
    
  8     printf("can not execute!\n");
  9     exit(1);
 10   }
 11   printf("获取环境变量: MY_105_VAL: %s\n", getenv("MY_105_VAL"));
 12                                                                                                                                               
 13   if(strcmp(argv[1],"-a") == 0)
 14   {
    
    
 15     printf("hello a!\n");
 16   }
 17   else if(strcmp(argv[1],"-b") == 0)
 18   {
    
    
 19     printf("hello b!\n");
 20   }
 21   else
 22   {
    
    
 23     printf("default!\n");
 24   }
 25   return 0;
 26 }

test.py                                                                                                                    
  1 #! /usr/bin/python3.6                                                           
  2                                                                                 
  3 print("hello python");                                                          
  4 print("hello python");                                                          
  5 print("hello python");                                                          
  6 print("hello python");                                                          
  7 print("hello python");                                                          
  8 print("hello python");                                                          
  9 print("hello python");                                                          
 10 print("hello python");                                                          
 11 print("hello python");                                                          
 12 print("hello python");    
test.py                                                                                                                    
  1 #! /usr/bin/python3.6                                                           
  2                                                                                 
  3 print("hello python");                                                          
  4 print("hello python");                                                          
  5 print("hello python");                                                          
  6 print("hello python");                                                          
  7 print("hello python");                                                          
  8 print("hello python");                                                          
  9 print("hello python");                                                          
 10 print("hello python");                                                          
 11 print("hello python");                                                          
 12 print("hello python");    

Guess you like

Origin blog.csdn.net/wh9109/article/details/132257708