处理僵死进程
- 一个进程使用fork创建子进程后,如果子进程退出时,父进程并没有调用wait或waitpid来获取子进程的状态信息,那么子进程的进程描述符将一直保持在系统中。这种进程就称为僵死进程。
- 显然,僵死进程会占用内核空间,所以,无论何时我们fork子进程都得wait它们,以防它们变成僵死进程。
- 处理僵死进程,即捕获SIGCHLD信号,在信号处理函数中调用wait或waitpid,即子进程退出时,在父进程中处理其退出信息。
在多进程中,正确的处理子进程僵死状态的方法是调用waitpid而不是wait。原因在于我们在一个循环内waitpid,以获得所有已终止子进程的状态。我们必须指定WNOHANG选项(若pid指定的进程没有结束,则waitpid立即返回0,不等待。若结束,返回其进程ID),它告知waitpid在有尚未终止的子进程在运行时不要阻塞。采用while来进行轮询。
void sig_chld(int signo){ pid_t pid; int stat; while((pid = waitpid(-1, &stat, WNOHANG)) > 0){ printf("child %d terminated.\n", pid); } return; }
处理被中断的系统调用
- 慢系统调用是指可能会使系统永远阻塞的系统调用。
- 适用于慢系统调用的基本规则是:当阻塞于某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能会返回一个EINTR错误。
- accept函数就属于这类,为了处理被中断的accept,我们常采用以下方式:
cpp for( ; ; ){ clilen = sizeof(cliaddr); if((connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) < 0){ if(errno == EINTR) continue; else{ err_sys("accept error"); } } }
- 上述代码所做的事就是自己重启被中断的系统调用。对于accept以及诸如read、write、select和open之类函数来说,都是适用的。
- 但是有一个函数我们不能重启:connect。如果该函数返回EINTR,我们就不能再次调用它,否则将立即返回一个错误。当connect被一个捕获的信号中断而不自动重启时,我们必须调用select来等待连接完成。