1.wait和waitpid函数介绍:
一个进程正常或异常终止时,内核就向其父进程发送一个SIGCHLD信号。因为子进程终止是一个异步事件,所以这种信号也是内核向父进程发送的异步通知。父进程可以忽略该信号,或是提供一个该信号发生时即被调用的执行函数。
两个函数接口是:
-
#include <sys/wait.h>
-
-
pid_t wait(int statloc);
-
pid_t waitpid(pid_t pid, int statloc, int options);
-
两个函数返回:若成功则为进程ID, 0 如失败则为-1
如果没有任何子进程结束,那么默认阻塞。
1)如果任一子进程终止的话,那么父状态得到这个子进程终止状态返回,而子进程资源可以回收。
2)如果没有任何子进程的话,那么出错返回。
3)对于waitpid是wait的升级版本,可以选择非阻塞返回,并且可以等待一个特定的子进程返回, 而不是只是等待第一个结束的子进程返回。
对于pid来说:
==-1.任意子进程
>0.pid和pid相等的子进程
==0.组id和调用进程组id相同的任一子进程
<0.组id等于pid的任一子进程
这个关系到进程组的概念,后面会提到。
对于statloc如果不为NULL的话,那么可以获得子进程终止状态,通过宏来处理这个值:
这里关系到作业控制概念,后面会提到。
对于options有下面几个值:
WCONTINUED.
WUNTRACED.
WNOHANG.非阻塞的等待子进程结束。
-
#include <stdio.h>
-
#include <signal.h>
-
#include <sys/types.h>
-
#include <unistd.h>
-
#include <sys/wait.h>
-
-
void pr_exit(int status);
-
-
int main()
-
{
-
pid_t pid;
-
int status;
-
-
if((pid = fork()) < 0)
-
printf("fork error\n");
-
else if(pid == 0)
-
exit(7);
-
-
if(wait(&status) != pid)
-
printf("wait error\n");
-
pr_exit(status);
-
-
if((pid = fork()) < 0)
-
printf("fork error\n");
-
else if(pid == 0)
-
abort();
-
-
if(wait(&status) != pid)
-
printf("wait error\n");
-
pr_exit(status);
-
-
if((pid = fork()) < 0)
-
printf("fork error\n");
-
else if(pid == 0)
-
status /= 0;
-
-
if(wait(&status) != pid)
-
printf("wait error\n");
-
pr_exit(status);
-
-
exit(0);
-
}
-
-
void pr_exit(int status)
-
{
-
if(WIFEXITED(status))
-
printf("normal termination, exit status = %d\n",
-
WEXITSTATUS(status));
-
else if(WIFSIGNALED(status))
-
printf("abnormal termination, signal number = %d%s\n",
-
WTERMSIG(status));
-
else if(WIFSTOPPED(status))
-
printf("child stopped, signal number = %d\n",
-
WSTOPSIG(status));
-
}
显示:
-
# ./a.out
-
normal termination, exit status = 7
-
abnormal termination, signal number = 6(null)
-
abnormal termination, signal number = 8(null)
2.这两个函数的区别:
1)在一个子进程终止前,wait使其调用者阻塞, 而waitpid有一个选项可使调用者不阻塞。
2)waitpid并不等待第一个终止的子进程,它有若干选项,可以控制它所有等待的进程。
下列代码,客户建立5个服务器的连接,随后在调用str_cli函数时仅用第一个连接(sockfd[0]),建立多个连接的目的是并发服务器上派生多个子进程。
-
/*
-
**客户端
-
*/
-
#include "unp.h"
-
-
void str_cli(FILE *fp, int sockfd)
-
{
-
char sendline[MAXLINE], recvline[MAXLINE];
-
//读入一行文本,写到服务器,在未键入文本时,处于阻塞状态
-
while(Fgets(sendline, MAXLINE, fp) != NULL){
-
Writen(sockfd, sendline, strlen(sendline));
-
if(Readline(sockfd, recvline, MAXLINE) == 0)
-
err_quit("str_cli:server terminated prematurely");
-
Fputs(recvline, stdout);
-
}
-
}
-
-
int main(int argc, char *argv[])
-
{
-
int i, sockfd[5];
-
struct sockaddr_in servaddr;
-
-
if(argc != 2)
-
err_quit("usage:tcpcli");
-
//客户建立5个与服务器的连接,随后在调用str_cli函数时仅用第一个连接。
-
for (i = 0; i < 5; ++i){
-
sockfd[i] = Socket(AF_INET, SOCK_STREAM, 0);
-
-
bzero(&servaddr, sizeof(servaddr));
-
servaddr.sin_family = AF_INET;
-
servaddr.sin_port = htons(SERV_PORT);
-
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
-
-
Connect(sockfd[i], (SA*) &servaddr, sizeof(servaddr));
-
}
-
str_cli(stdin, sockfd[0]);
-
exit(0);
-
}
-
/*
-
**服务器端
-
*/
-
#include "unp.h"
-
-
void str_echo(int sockfd)
-
{
-
ssize_t n;
-
char buf[MAXLINE];
-
-
again:
-
while((n = read(sockfd, buf, MAXLINE)) > 0)
-
Writen(sockfd, buf, n);
-
//EINTR Interrupted function call (POSIX.1); see signal(7).
-
if(n < 0 && errno == EINTR)
-
goto again;
-
else if(n < 0)
-
err_sys("str_echo: read error");
-
}
-
-
/*信号处理函数,用于处理僵死进程,because 每次终止子进程将产生僵死进程,占用内核空间,耗费资源
-
用wait在多个子进程同一时刻终止时将产生僵死进程
-
*/
-
void sig_chld(int signo)
-
{
-
pid_t pid;
-
int stat;
-
-
pid = wait(&stat);
-
printf ("child %d terminated\n",pid);
-
return;
-
}
-
/*调用waitpid解决同一时刻终止多个子进程的情况
-
*/
-
-
/*void sig_chld(int signo)
-
{
-
pid_t pid;
-
int stat;
-
-
while((pid == waitpid(-1, &stat, WNOHANG)))
-
printf ("child %d terminated\n",pid);
-
return;
-
}*/
-
-
int main(int argc, char *argv[])
-
{
-
int listenfd, connfd;
-
pid_t childpid;
-
socklen_t clilen;
-
struct sockaddr_in cliaddr, servaddr;
-
//创建套接字,捆绑服务器的众所周知的端口
-
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
-
bzero(&servaddr, sizeof(servaddr));
-
servaddr.sin_family = AF_INET;
-
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
-
//SERV_PORT服务器众所周知的端口
-
servaddr.sin_port = htons(SERV_PORT);
-
-
Bind(listenfd, (SA*) &servaddr, sizeof(servaddr));
-
-
Listen(listenfd, LISTENQ);
-
Signal(SIGCHLD, sig_chld);
-
for( ; ; ){
-
//服务器阻塞于accept调用,等待客户连接的完成
-
clilen = sizeof(cliaddr);
-
//自己重启被中断的系统调用
-
if( (connfd = accept(listenfd, (SA*) &cliaddr, &clilen) )< 0 ){
-
if(errno == EINTR)
-
continue;
-
else
-
err_sys("accept error");
-
}
-
//并发服务器,fork为每一个客户派生一个处理他们的子进程。子进程关闭监听套接字,父进程关闭已连接的套接字
-
if((childpid = fork()) == 0){
-
Close(listenfd);
-
str_echo(connfd);
-
exit(0);
-
}
-
Close(connfd);
-
}
-
}
当客户终止时,所有打开的文件描述符由内核自动关闭,且所有5个连接在同一时刻终止。这就引发了5个FIN,每个连接一个,它们反过来使用服务器的5个子进程基本子在同一时刻终止。
这又导致差不多在同一时刻又5个SIGCHLD信号递送个父进程。
在后台运行服务器,接着运行新的客户:
# ./SimpleServer &
[1] 19806
$ ./SimpleClient 127.0.0.1
hello world
hello world
^D
此时的5个子进程都终止了,如果运行ps,会发现(根据机器的不同显示的结果也不相同)
我的机器有2个僵死进程存在。
所以建立信号处理函数并在其中调用wait并不足以防止出现僵死进程。(根据机器的不同产生的僵死进程不同),正确的解决办法是调用waitpid而不是wait。原因在于:我们在循环内调用waitpid,以获得所有
已终止子进程的状态。我们必须指定WNOHANG选项,它告诉waitpid在有尚未终止的子进程在运行时不要阻塞。
“去大理“很好听^_^
阅读(2115) | 评论(0) | 转发(0) |