Several ways for Linux to solve zombie processes, SIGCHLD signal setting SIG_IGN processing methods, etc.

zombie process

We know that the process under linux has a state called zombie state ;

The reason is that after the child process from the parent process fork() ends, the kernel will send a SIGCHLD signal to the parent process ;

The function of this SIGCHLD signal is to notify the parent process of the exit of the child process in time and let the parent process recycle him through a series of means ;
the meaning of recycling:
1. Avoid the generation of zombie processes and memory leaks
2. And enable the parent process to get the child process The exit status of the

If the parent process does not ignore this signal (set SIG_IGN), does not wait for (wait) the child process, and ends later than the child process (cannot be hosted and adopted by init1 process), and there is no handler to capture this signal, then the child process It will enter the zombie state ; the maintainer's own task_struct overhead, which is also a memory leak ;

solution

There are several solutions to this problem:

The parent process blocks calling wait()

#include <sys/types.h> /* 提供类型pid_t的定义 */

#include <sys/wait.h>

pid_t wait(int *status)//status 输出型参数,可以拿到子进程退出的状态码,不关心的话设置NULL即可;

Once the process calls wait, it will block itself immediately , and wait will automatically analyze whether a child process of the current process has exited. If it finds such a child process that has become a zombie, wait will collect the information of this child process , and return after completely destroying it; if no such child process is found, wait will block here until one appears.

  • Obviously, the call to wait is very unreasonable. Waiting while blocking will make the parent process unable to do other things;

The parent process calls waitpid() non-blocking

#include <sys/types.h> /* 提供类型pid_t的定义 */

#include <sys/wait.h>

pid_t waitpid(pid_t pid,int *status,int options)//pid设置为-1代表等待任意一个进程, options设置为WNOHANG 即便无子进程退出,它也会立即返回,不会像wait那样永远等下去!

According to the observation of the source code, it is not difficult to find that wait is actually a package of waitpid:

static inline pid_t wait(int * wait_stat)

{
     
     

    return waitpid(-1,wait_stat,0);//-1等待任意进程退出,0代表什么都不设置,阻塞式等待!

}
  • Although waitpid will not wait when blocked, it also needs to occupy cpu resources. Periodic training of waitpid to see if any child process is in a zombie state, so as to recycle it, also has a certain overhead

SIGCHLD signal

In fact, every time our child process terminates, the kernel will send a SIGCHLD signal to the parent process . If the parent process does not handle this signal and does not wait for the child process , it will enter the zombie state;

In this case, we can use the signal() function + waitpid() interface to process this signal, and after a child process exits, actively notify the parent process to recycle the asynchronous notification function ! (Obviously, the efficiency is higher than the synchronous processing of active wait and waitpid of the parent process!)

#include <signal.h>
typedef void (*sighandler_t)(int);//简单来说,sighandler_t是一个无返回值,参数为一个int的函数指针

sighandler_t signal(int signum, sighandler_t handler);

  • The signum parameter indicates the type of signal to be processed. It can remove any signal except SIGKILL and SIGSTOP (SIGKILL is the kill -9 command. If it is changed, it is possible that we really cannot kill a process. ”
  • The int parameter in the handler parameter is the int code of the captured corresponding signal, which can be viewed by using the kill -l command in Linux:

insert image description here

The reference code is as follows:

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>

void handler(int sig)
{
    
    
	pid_t id;
	while((id = waitpid(-1, NULL, WNOHANG)) > 0)//while循环处理需要回收的子进程
		printf(“wait child success : %d\n”, id);
}

int main()
{
    
    
	signal(SIGCHLD, handler);//指定SIGCHLD信号来到时,需要被handler函数处理
	pid_t ret = fork();
	if(ret == 0)
	{
    
    
		printf(“child : %d\n”, getpid());
		sleep(3);
		exit(0);//子进程退出,并给父进程发送SIGCHLD信号和0这个退出码
	}
	
	while(1)
	{
    
    
		printf(“father process is doing some thing!\n”);
		sleep(1);
	}
	return 0;
}

Note that the reason why the while loop is used in the handler to handle recycling tasks is: SIGCHILD is an unreliable signal !

A parent process may have many child processes. If multiple child processes exit at the same time, SIGCHILD will cause signal loss (only one exists) . If it is processed with if, only one child process will be processed. The remaining child processes become zombie processes !

Therefore, when we find that a child process exits, we use a while loop to handle it. If more than one child process exits, even if the signal is lost, we can continue to release them through the return value of waitpid. Even if only one child process exits, the second loop waitpid will return 0 and we will break out of the loop!

It's a bit like mutex+cond needs while to handle false wakeups, refer to my article

SIG_IGN signal

We all know that there is another way to solve the zombie process, which is to turn it into an "orphan process", that is, the init1 process adopts hosting, and then releases the resources of this child process!

In addition to allowing the parent process to exit earlier than the child process, we can set the processing method of the SIGCHLD signal to SIG_IGN–> ignore through the following operations , which means that the parent process ignores this signal, and the child process is equivalent to being abandoned. Adopted by process init1

signal(SIGCHLD, SIG_IGN); 

This allows the kernel to transfer the zombie child process to the init1 process to handle the release, saving the trouble of the parent process waiting for the child process;

This little trick is often used to improve the performance of concurrent servers!

Guess you like

Origin blog.csdn.net/wtl666_6/article/details/129513009