【Linux】孤儿进程、僵死进程和信号

一、孤儿进程

父进程结束,子进程还未结束,然后子进程就交给init进程照顾

二、僵死进程(僵尸进程)

父进程未结束时,子进程已经结束,并且父进程未处理子进程的退出状态。

一个进程是由进程实体和进程控制块组成,进程结束时,实现释放进程的内容,再释放进程控制块。

**僵尸进程的形成过程:**进程已经结束,进程实体已经被释放,但是系统并没有释放对应的进程控制块(PCB);

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main()
{
    
    
	pid_t pid = fork();
	assert(pid != -1);
	
	if(pid == 0)
	{
    
    	
		printf("child start\n");
		sleep(3);
		printf("child end\n");
	}
	else
	{
    
    
		sleep(10);
	}
	return 0;
}

(1)产生僵死进程

在这里插入图片描述
注意:
2.4G的内核,一个PCB大约占用1.7k的内存,而父进程一般都是服务器进程,这种进程一般是不会结束的。如果没有处理僵尸进程,他就会一直存在;bash:每执行一个命令,bash都会fork一个子进程,如果出现大量的僵尸进程,内存就会被大量消耗,拖慢速度。

(2)处理僵尸进程

操作系统提供了一组系统调用:

pid_t wait(int *result);//返回值时处理子进程的pid,参数是进程退出的状态码

pid_t waitpid(pid_t pid, int *result, int option);

pid的值可以是如下图所示:
在这里插入图片描述
wait()方法是由父进程来调用,来获取其任意的一个子进程的退出状态(第一个退出的子进程),父进程wait()的次数与子进程的个数相等。

(3)解决上面产生的僵死进程

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

int main()
{
    
    
	pid_t pid = fork();
	assert(pid != -1);
	
	if(pid == 0)
	{
    
    	
		printf("child start\n");
		sleep(3);
		printf("child end\n");
	}
	else
	{
    
    
		wait(NULL);
		sleep(10);
	}
	return 0;
}

(4)结论

  1. 虽然父进程直接调用wait方法最终可以处理子进程的僵死状态,但是因为wait的阻塞,造成了父子进程时串行执行的,效率较低;
  2. 一次wait只能处理一个僵死进程,一个父进程能产生多少的僵死进程是无法预知的;
  3. 所以就引出信号这一概念

三、信号

(1)信号定义

  1. 信号是系统预先定义好某些特殊事件。信号可以被产生,也可以被接受,产生和接收的实体都是进程;
  2. 信号就是在进程间传递某些发生的事件;
  3. 信号就是宏,每个信号都有系统指定的一个进程收到该信号是的处理方式;

(2)修改信号的响应方式

1.默认 SIG_DFL
2.忽略 SIG_IGN
3.捕获(用户自定义处理方式)
在这里插入图片描述
__sighandler_t:就是函数指针的类型

修改信号响应方式的系统调用:

在这里插入图片描述
signum:信号类型,就是信号对应的宏值

handler:函数指针,可以是用户自定义的函数,也可以是SIG_DFL(默认),SIG_IGN(忽略);

返回值:返回的是信号原来的响应方式;

(3)信号的应用

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <signal.h>

void func(int sign)
{
    
    
	printf("sign is %d\n", sign);
	printf("hello,world\n");
}
int main()
{
    
    
	signal(SIGINT, func);
	while(1)
	{
    
    
		sleep(1);
		printf("main running\n");
	
	}

	return 0;
}

运行结果:
在这里插入图片描述
结论:
在信号函数的执行过程中,该信号如果多次触发,系统只会记录一次(多余的信号会被屏蔽),也就是说系统没有为该程序维护一个消息队列;

四、发送信号

(1)系统调用kill

int kill(pid_t pid, int signum)

pid 就是要给指定的进程发信号的进程号(大于0)

signum:发送的信号类型

返回值:发送成功返回 0 ;发送失败返回 -1;

(2)仿写一个kill命令

使用kill pid就可以杀死pid对应的进程

#include <stdio.h>
#include <signal.h>
#include <string.h>

//得到目标进程的进程号pid
pid_t getDesPid(int argc, char* argv[])
{
    
    
	if(argc < 1)
		return -1;
	int pid = -1;
	sscanf(argv[1], "%d", &pid);
	return pid;
}
//发送SIGTERM信号
void sendSignal(pid_t pid)
{
    
    
	if(pid <= 0)
	{
    
    
		printf("pid不合法\n");
		return;
	}
	kill(pid, SIGTERM);
}
int main(int argc, char* argv[])
{
    
    
	pid_t pid = getDesPid(argc, argv);

	if( pid == -1)
	{
    
    
		printf("sig or pid err\n");
		return 0;
	}
	sendSignal(pid);
	return 0;
}
  • 结果:
    在这里插入图片描述

(3)使用信号异步处理僵死进程

  1. 父进程要处理僵尸进程,必须调用wait方法
  2. 子进程结束时,自动给父进程发送一个信号(SIGCHLD)。
  3. 父进程可以在信号处理函数中调用wait

具体处理方式https://www.cnblogs.com/wuchanming/p/4020463.html

Guess you like

Origin blog.csdn.net/xiaoxiaoguailou/article/details/116096102