Linux中的僵尸进程和信号

通过处理僵死进程来引入信号的概念;

一、僵死进程:

a.概念:当父进程未结束,子进程结束且父进程未获取子进程的退出状态。

b.需要知道的一个知识点是当进程执行结束,进程主体(执行代码、数据、资源)都释放,但其PCB并未释放,只是在适当的时机才释放。

二、怎样处理僵死进程呢?

想象一下,进程号是有限的,大量的产生僵死进程,将会因为没有可用的进程号而导致系统不能产生新的进程。因此我们要处理僵死进程:

方法一:父进程调用pid_t  wait( int  *status )函数来获取子进程的退出状态。

a.函数功能:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了一个已经变成僵死进程的子进程,wait就会收 集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

b.函数返回值:当wait()与fork()配套出现时,如果在使用fork()之前调用了wait(),wait()的返回值则为-1,正常情况下返回子进程的PID。

c.参数status用来保存被收集退出时的一些状态,它是一个指向int类型的指针。如果我们对子进程是如何死掉的毫不在意,只想把这个僵死进程消灭掉,我们就可以设定这个参数为NULL;既pid=wait(NULL);调用成功,就会返回被收集的在子进程的PID,如果调用的进程没有子进程,调用失败,返回-1。


通过以上了解,我们就会想:辛苦创建了一个子进程,最终子进程和父进程成了串行运行,实际上没有达到提高效率的目的。


方法二:怎样异步处理僵死进程?

我们提出信号的概念:

(1)、信号:系统先定义好的某些特定的事件,可以被发生,也可以被接受。发生和接受的主体都是进程。

(2)、系统中信号的定义:/usr/include/bits/signum.h

(3)、信号的来源:按照产生条件的不同可以分为硬件和软件两种:

               a.硬件方式:当用户在终端上按下某键时,将产生信号;如果按下组合键(Ctrl+c 代表1)  SIGINT中断信号;Ctrl+\代表是3)  SIGQUIT退出信号;Ctrl+z代表19)  SIGTSTP信号)后将产生一个信号。

                  硬件异常产生信号:除数据、无效的存储访问等,这些事件通常由硬件(如CPU)检测到,并将其通知给Linux操作系统内核,然后内核生成相应的信号,并把信号发送给该事件发生时正在进行的程序。

                b.软件方式:用户在终端下调用kill命令向进程发送任务信号。

(4)、信号的种类:在shell下输入kill -l 可显示Linux系统支持的全部信号;信号的值定义在signal.h中。每个信号都由一个编号和宏定义名称。

(5)、信号的接收:在接受信号的进程PCB结构中有long signal;通过signal来表示接受到的信号。

(6)、进程对信号的响应:当进程发生时,用户可以要求进程以以下三种方式之一对信号做出响应:

               a.默认信号:按系统默认方式处理,大部分信号的默认操作是终止操作,且所有的实时信号的默认动作都是终止进程。

               b.忽略信号:大多数信号都可以使用这种方式进行处理,但是SIGKILL和SIGSTOP这两个信号不能被忽略,同时这两个信号也不能捕获和阻塞。此外,如果会忽略某些由硬件异常产生的信号(如非法存储访问或除以0),则进程的行为是不可预测的。

               c.捕捉信号:对于捕捉的信号,可以为其指定信号处理函数,信号发生时该函数自动被调用,在该函数内部实现对信号的处理。

(7)、修改信号的响应方式:修改PCB结构中,struct sigaction结构体数组中对应信号值作为下标的函数指针。

函数原型:void  (* signal (int   signum, void    (*fun)  ) (int) ) (int);//signum指定信号的值;int指函数指针对前面信号值得处理;

帮助理解:tepedef   void (* fun_handle)(int);

                    fun_handle   signal( int   signum,fun_handler  fun);

返回值:调用成功返回最后一次安装信号signum而调用signal()时的fun值。失败则返回SIG_ERR.

(8)、修改信号的响应方式的时机:进程刚开始就执行修改关注的信号的响应方式。

               代码中signal在何时调用呢:一般情况main函数开始第一行调用。

示例:编写程序实现用户第一次输入Ctrl+c时,输出helloworld;第二次输入Ctrl+c时程序结束:

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

void fun(int);
int count = 0;

void main()
{
	signal(SIGINT, fun);

	while(1)
	{
		sleep(2);
		printf("main running\n");
	}
}
void fun(int sign)
{
	printf("hello world\n");
	signal(SIGINT, SIG_DFL);
}

(9)、信号的发送:int   kill( pid_t  pid, int  signum);//pid表示接受信号的进程;signum表示信号类型

头文件:#include<stdio.h>

               #include<sys/types.h>

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

举例:用户传入pid向指定进程发送信号:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include<sys/types.h>
int main(int argc, char *argv[])
{
	if(argc < 2)
	{
		printf("please input pid\n");
		exit(0);
	}
	int pid = 0;
	sscanf(argv[1], "%d", &pid);
	printf("pid == %d\n", pid);
	if(kill(pid, SIGCHLD) == -1)
	{
		perror(NULL);
		exit(0);
	}
}

(10)、最后结合信号来处理僵死进程:父进程不阻塞并且处理僵死进程,那子进程结束时,只需向其父进程发送一个SIGCHLD信号。

优势:a.父进程和子进程可以并行处理;

            b.可以处理所有的僵死进程;

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

void fun(int sign)
{
	printf("fun was called\n");
	pid_t pid = wait(NULL);
	printf("pid == %d\n", pid);
	printf("fun over\n");
}

void main()
{
	pid_t pid = fork();
	assert(pid != -1);

	if(pid == 0)
	{
		printf("child start\n");
		sleep(2);
		printf("child end\n");
		// 子进程结束时会向其父进程发送SIGCHLD
		kill(getppid(), SIGINT);
	}
	else
	{
		signal(SIGCHLD, fun);
		printf("father start\n");
		sleep(10000);
		printf("father end\n");
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值