Process Control (Linux)

process control

fork

In Linux, the fork function is a very important function, which creates a new process from an existing process. The new process is the child process, and the original process is the parent process.

Return value:
Return 0 in the child process, return the PID of the child process in the parent process, and return -1 if the child process fails to create.

A process calls fork, and when control is transferred to the fork code in the kernel, the kernel does:

  • 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 the child process to the system process list.
  • fork returns and starts the scheduler scheduling.
    insert image description here

process terminated

process exit scenario

In the program we wrote, there will be three situations when the code runs:

  1. The code runs to completion, correct - end of code
  2. The code runs to completion with incorrect results
  3. Abnormal code termination

The return value of return in the main function is the exit code of the process!

insert image description here

After entering the test summary - the value returned by return is the exit code of the main function!

> [External link picture transfer failed, the source site may have an anti-leeching mechanism, it is recommended to save the picture and upload it directly (img-Gk5jqfJM-1685794022709)(G:\Mynote\image\image-20230526182653011.png)]
The meaning of the return value of return (there are many more, but what I want to express is that only 0 is success and the others are error reasons):

insert image description here

Abnormal code termination

In layman's terms: the code ran halfway and reported an error and terminated the operation-the program crashed!
The return value of the program after the crash is meaningless! example:

insert image description here

How the process exits

  • The main function return means that the process exits
  • Non-main function return - function return
  • Calling exit anywhere means terminating the process, and the parameters are all exit codes!

[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-REiidq7G-1685794022710)(G:\Mynote\image\image-20230526193200792.png)]

  • exit is the same as return to exit the process
    insert image description hereinsert image description here

  • _exit terminates the process, forcibly terminates the process, do not finish the process, such as not refreshing the buffer! exit() will flush the buffer (user buffer)
    insert image description here

What did the process do at the OS level when it exited?

  • At the system level, there is one less process: free PCB, free mm_struct, free page table and various mapping relationships, and the space for code + data application is also given to free. The process exits
    abnormally

Case 1: A signal to the process causes the process to exit abnormally.

For example, when a process is running, a kill -9 signal is sent to the process to cause the process to exit abnormally, or to use Ctrl+C to cause the process to exit abnormally.

Situation 2: A code error causes the process to exit abnormally when it is running.

For example, there is a wild pointer problem in the code that causes the process to exit abnormally when it is running, or the situation of dividing by 0 causes the process to exit abnormally when it is running, etc.

process waiting

What is process wait?

fork() ;
child process: help the parent process to complete certain tasks
parent process: if the parent process wants to know what tasks the child process has done for me, it must use wait/waitpid to wait for the child process to exit

Why should the parent process wait for the child process?
1. By obtaining the exit information of the child process, you can know the execution result of the process!
2. It can be guaranteed: timing problems, the child process exits first, and the parent process exits later.
3. When the process exits, it will enter the zombie state first, which will cause memory leaks. It is necessary to release the resources occupied by the child process through the parent process wait!

Note: Once the process becomes a zombie process, it will become invincible. Kill-9 cannot kill him, because there is no way to kill a dead process!

How to solve zombie process?

Use wait to recycle child processes

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
  pid_t id = fork();
  if(id == 0)
  {
    //child
    int cnt = 5;
    while(cnt)
    {
      printf("child[%d] is running: cnt- is:%d\n",getpid(),cnt);
      cnt--;
      sleep(1);
    }
    exit(0);//子进程在这里结束
  }

  sleep(10);
  printf("fahter wait begin\n");
  pid_t ret = wait(NULL);
  if(ret > 0)
  {
  
  printf("fahter wait :%d, success\n", ret);
  }
  else{
    printf("father wait failed!\n");
  }
  sleep(10);
  return 0;
}

The effect is as follows:

insert image description here
Summary: wait can recycle zombie processes

insert image description here

status can help us receive three kinds of feedback when the child process ends!

The result of our program code running is right or wrong is judged by the process exit code, but how to prove that our code is running to the end, instead of "the first emperor died halfway through his business " ? - Other signals received by the program if a program should terminate for code exceptions. So we can judge whether a process has received a signal to determine whether the process is terminated abnormally! How should we look at the following table?
insert image description here
Through a series of bit operations, we can get the exit code and exit signal of the process according to the status.
Explanation: The bottom 8 digits are the exit status - the exit code, and the bottom 7 digits are the termination signal! So we only need to judge the exit code, its bottom 7 bits are 0, it will not receive the signal

exitCode = (status >> 8) & 0xFF; //exit code
exitSignal = status & 0x7F; //exit signal

The system provides two macros to get the exit code and exit signal.

  1. WIFEXITED(status): Used to check whether the process exited normally, the essence is to check whether a signal is received.
  2. WEXITSTATUS(status): Used to get the exit code of the process.

exitNormal = WIFEXITED(status); //Whether exited normally
exitCode = WEXITSTATUS(status); //Get the exit code

It should be noted that when a process exits abnormally, it means that the process was killed by a signal, so the exit code of the process is meaningless.
How the process waits

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
  pid_t id = fork();
  if(id == 0)
  {
    //child
    int cnt = 5;
    while(cnt)
    {
      printf("child[%d] is running: cnt- is:%d\n",getpid(),cnt);
      cnt--;
      sleep(1);
    }
    exit(0);
  }

  sleep(10);
  printf("fahter wait begin\n");
 // pid_t ret = wait(NULL);
 // pid_t ret = waitpid(id,NULL,0);等待指定一个进程
 // pid_t ret = waitpid(-1,NULL,0);//等待任意一个子进程
  int status = 0;
  pid_t ret = waitpid(-1,&status,0);
  if(ret > 0)
  { 
  printf("fahter wait :%d, success, status exit code:%d ,status exit signal%d\n", ret,
  (status>>8)&0xFF, status & 0x7F);//看信号位与退出码
  }
  else{
    printf("father wait failed!\n");
  }
  sleep(10);
  return 0;

insert image description here

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
  printf("i am a child process!,pid :%d ,ppid: %d \n",getpid(),getppid());
  exit(10);
}

result:

i am a child process!,pid :20681 ,ppid: 19703

insert image description here

We found that the parent process of process /a.out is actually -bash——bash is the parent process of all startup processes! How does bash get the exit result of the process? The exit result of the process must be obtained through wait! So we can use echo to view the exit code of the child process!

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
  pid_t id = fork();
  if(id == 0)
  {
    //child
    int cnt = 5;
    while(cnt)
    {
      printf("child[%d] is running: cnt- is:%d\n",getpid(),cnt);
      cnt--;
      sleep(1);
    }
    exit(0);
  }

  sleep(5);
  printf("fahter wait begin\n");
 // pid_t ret = wait(NULL);
 // pid_t ret = waitpid(id,NULL,0);等待指定一个进程
 // pid_t ret = waitpid(-1,NULL,0);//等待任意一个子进程
  int status = 0;
  pid_t ret = waitpid(id,&status,0);
  if(ret>0)
  {
    if(WIFEXITED(status))//没有收到任何的退出信号的
    {
    //正常结束的,获取对应的退出码!
      printf("exit code: %d\n",WEXITSTATUS(status));
    }
    else{
      printf("error, get s signal!\n");
    }
  }
}

whitepit

作用:waitpid会暂时停止进程的执行,直到有信号来到或子进程结束。
函数说明:

If the child process has ended when waitpid() is called, waitpid() will immediately return the child process end status value.
The end status value of the child process will be returned by the parameter status, and the process identification code of the child process will also be returned together.
If you don't care about the end status value, the parameter status can be set to NULL.

Parameters:
The parameter pid is the identification code of the child process to be waited for, and the meanings of other values ​​are as follows:

  1. pid<-1 Wait for any child process whose process group ID is the absolute value of pid.
  2. pid=-1 waits for any child process, equivalent to wait().
  3. pid=0 waits for any child process with the same process group id as the process.
  4. pid>0 waits for any subprocess whose subprocess ID is pid.

Parameter status: output parameter, get the exit status of the child process, if you don't care, it can be set to NULL.
Parameter options: When it is set to WNOHANG, if the waiting child process has not ended, the waitpid function will directly return 0 without waiting. If it ends normally, the pid of the child process is returned.

understand waitpit

insert image description here

block

blocking and non-blocking

* Blocking in layman's terms means that you go out with your girlfriend but she wants to put on makeup and you have to wait for her to wait.

Non-blocking for you to hang out with your girlfriend but she needs makeup and you have to wait for her, but call and ask in five minutes until she comes down. ——This method is based on non-blocking polling scheme! *

Note: both blocking and non-blocking are a method of waiting

The essence of blocking: In fact, the PCB of the process is put into the waiting queue, and the state of the process is changed to the S state. The
essence of return: the PCB of the process is taken from the waiting queue to the R queue, and then it is scheduled by the CPU.

non-blocking wait

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
  pid_t id = fork();
  if(id == 0)
  {
    //child
    int cnt = 3;
    while(cnt)
    {
      printf("child[%d] is running: cnt- is:%d\n",getpid(),cnt);
      cnt--;
      sleep(1);
    }
    exit(0);
  }

  int status = 0;
  while(1)
  {
     pid_t ret = waitpid(id,&status,WNOHANG);                                                                                                                                                                  
      if(ret == 0)                            
      {                                                                   
        // 子进程没有退出,但是waitpid等待是成功的,需要父进程重新进行等待
        printf("父进程运行");
      }                                                                   
      else if (ret > 0)                                                   
      {                                                                   
        // 子进程退出,waitpid也成功l,获取到对应的结果了
        printf("获得子进程的退出码%d,子进程的退出信号%d",(status>>8)&0xFF,status&0x7F);
        break;
      }                                                     
      else                                               
      {                                                  
        // 等待进程失败
        perror("waitpid");
        break;
      }
  }
  sleep(1);
}

process program replacement

After using fork to create a child process, the child process executes the same program as the parent process (but may execute a different code branch). If you want the child process to execute another program, you often need to call an exec function.

When a process calls an exec function, the process's user-space code and data are completely replaced by the new program (resurrection), and execution starts from the new program's startup routine.

insert image description here

Looking at the above picture, the address space (virtual memory) and page table of the child process have not changed, only the physical memory has changed. By modifying the data and code of the physical memory, the child process can be changed to run a new content (resurrection) , but pay attention Originally, the child process and the parent process share a common space (code and data), but the data of the child process will be copied realistically after being changed. From then on, the data of the child process and the parent process are no longer related** (the father is a teacher and the son runs a company)* *

Some people have to ask how to replace it? ? (Kids, do you have a lot of question marks?)

Replacement function (exec series function)

There are six replacement functions starting with exec, which are collectively called exec functions:

execl

int execl(const char *path, const char *arg, ...);

The first parameter is the path to execute the program, the second parameter is a variable parameter list, indicating how you want to execute the program, and ends with NULL.

For example, the ls program is to be executed.

execl("/usr/bin/ls", "ls", "-a", "-i", "-l", NULL);

can also

execl("/usr/bin/ls", "ls", "-ali", NULL);

execlp

int execlp(const char *file, const char *arg, ...);

The first parameter is the name of the program to be executed, and the second parameter is a list of variable parameters, indicating how you want to execute the program, and ends with NULL.

For example, the ls program is to be executed.

execlp("ls", "ls", "-a", "-i", "-l", NULL);

execle

int execle(const char *path, const char *arg, ..., char *const envp[]);

The first parameter is the path of the program to be executed, the second parameter is a variable parameter list, indicating how you want to execute the program, and ends with NULL, and the third parameter is the environment variable you set yourself.

For example, if you set the MYVAL environment variable, the environment variable can be used inside the mycmd program.

char* myenvp[] = { "MYVAL=qwe", NULL };
execle("./mycmd", "mycmd", NULL, myenvp);

execv

int execv(const char *path, char *const argv[]);

The first parameter is the path to execute the program, the second parameter is an array of pointers, the contents of the array indicate how you want to execute the program, and the array ends with NULL.

For example, the ls program is to be executed.

char* myargv[] = { "ls", "-a", "-i", "-l", NULL };
execv("/usr/bin/ls", myargv);
12

execvp

int execvp(const char *file, char *const argv[]);

The first parameter is the name of the program to be executed, and the second parameter is an array of pointers. The contents of the array indicate how you want to execute the program, and the array ends with NULL.

For example, the ls program is to be executed.

char* myargv[] = { "ls", "-a", "-i", "-l", NULL };

execve

int execve(const char *path, char *const argv[], char *const envp[]);

The first parameter is the path to execute the program, the second parameter is an array of pointers, the content in the array indicates how you want to execute the program, the array ends with NULL, and the third parameter is the environment variable you set yourself.

For example, if you set the MYVAL environment variable, the environment variable can be used inside the mycmd program.

char* myargv[] = {
    
     "mycmd", NULL };
char* myenvp[] = {
    
     "MYVAL=qwe", NULL };
execve("./mycmd", myargv, myenvp);

Guess you like

Origin blog.csdn.net/qq_45591898/article/details/131025132