POSIX信号处理

POSIX信号处理

信号(signal)就是告知某个进程发生了某个事件的通知,有时也称软件中断(software interrupt)。注意:信号是异步发生的,也就是说进程预先并不知道信号的准确发生时刻。

查询僵死子进程的shell命令

ps -A -ostat,ppid,pid | grep -e '^[Zz]'

信号可以
1. 由一个进程发送给另外一个进程(包括自身)
2. 由内核发送给某个进程

信号处理函数

每个信号都有一个与之关联的处置。当有特定信号发生的时候,特定的函数被调用,那么这个特定函数就称为这个特定信号的信号处理函数,调用这个函数的过程称为信号捕获过程。

  1. SIGKILL和SIGSTOP这两个信号并不能被捕获
  2. 通过将信号的处置设置为SIG_IGN来忽略某个信号时需牢记,SIGKILL和SIGSTOP这两个信号同样不能被忽略。
  3. 我们可以把某个信号的处置设置为SIG_DFL来启用它的默认设置,默认处置通常是在收到信号后终止进程。SIGCHLD就是默认处置为忽略的两个信号。

信号处理函数的注册

func* signal(int signo, void *func)

signo是信号名,意为我们捕捉到的信号或者我们将要的处理的信号;func是指向函数的指针,意为我们将要处理signo信号的方式。第二个参数可以为常值SIG_IGN(忽略信号)或SIG_DFL(该信号的默认处置)。

其中func的函数格式为:

void sig_func(int signo);

处理SIGCHLD信号

僵死进程:设置僵死状态的目的是维护子进程的信息,以便父进程在以后某个时候获取。这些信息包括子进程的进程id、终止状态以及资源利用信息。如果一个进程终止,而该进程有子进程处于僵死状态,那么它的所有僵死子进程的父进程id被重置为1(init进程)。继承这些子进程的init进程将清理它们。
 

通过语句 signal(SIGCHLD,sig_chld) 注册sig_chld的信号处理函数后,服务器和客户端的表现:

  1. 客户程序终止时,会发送一个FIN给服务器
  2. 收到FIN的服务器会递送一个EOF给子进程的read函数,子进程中止
  3. 子进程结束时,会递交一个SIGCHLD信号给父进程,sig_child函数执行
  4. 由于父进程在阻塞于accept函数,即阻塞于慢系统调用时收到SIGCHLD,因此返回一个EINTR错误(被中断的系统调用)
  5. 如果EINTR错误没有被处理,程序就会终止

处理被中断的系统调用–慢系统调用

慢系统调用函数,以accept为例,在服务器程序中,bind和listen执行完毕后,未连接套接字将会变成监听套接字,此时accept函数等待一个来自客户端的连接请求。若客户端一直不运行或是一直不执行connect(tcp三次握手)函数,那么accept函数将会一直阻塞,accept调用有可能不会返回已连接的套接字。
EINTR:标志被中断的系统调用。适用于慢系统调用的基本规则是:当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能返回一个EINTR错误。当我们编写捕获信号的程序时,我们必须对慢系统调用返回EINTR有所准备,即是说,我们需要重启被中断的系统调用,一般在for循环中利用continue来实现。以下为处理被中断的accept函数的操作:

//因为accept调用需要等待客户端程序的connect连接,这个操作可以处理
        //被中断的accept调用
        if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
            if (errno == EINTR)
                continue;       /* back to for() */
            else
                err_sys("accept error");
        }


当我们判断到这个系统调用为慢系统调用即errno = EINTR,则利用continue继续重启accept函数调用。不过有一个函数我们不能重启:connect,重启就报错。


wait和waitpid函数

头文件 <sys/wait.h>

pid_t wait(int *statloc); //&stat,地址
pid_t waitpid(pid_t pid, int *statloc, int options);

两个函数返回值都是:已终止子进程的进程id号,以及通过statloc获取进程的终止状态

其中进程的终止状态包括三种:

  1. 正常终止
  2. 某个信号杀死
  3. 作业控制停止

wait函数直到有子进程结束时才会返回,否则一直阻塞

waitpid默认和wait函数一样,但是可以通过设置option来改变这一情况

waitpid的option可以设置为以下的or组合:

0: 跟wait功能一样,会阻塞

WNOHANG:如果没有任何已经结束的子进程则马上返回, 不予以等待.
WUNTRACED:如果子进程进入暂停执行情况则马上返回, 但结束状态不予以理会. 子进程的结束状态返回后存于status

建立一个信号处理函数,并且在其中调用wait函数不足以防止出现僵尸进程。

如果多个信号都在信号处理函数执行之前出现,信号处理函数只会执行一次。这时在sig_chld函数中,如果wait函数只执行一次,那么就只能处理一个子进程,剩余的未处理子进程都会变成僵尸进程;如果循环调用wait函数,那么最后一个结束的子进程处理完后,wait会一直阻塞在这个地方。

调用waitpid,并且把option设置为WHOHANG,使得waitpid在检测不到结束的进程时就返回-1,然后停止循环就可以解决问题

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值