首先奉上这两个函数
pid_t wait(int *statloc);
pid_t waitpid(pid_t pid, int *statloc, int options);
wait和waitpid都是用来处理终止进程的,这两个函数都返回两个值:已终止的进程ID号,以及通过statloc指针返回的子进程的状态(一个整数)。如果调用wait()的进程没有终止的进程而还有一个或多个子进程还在运行,函数将会一直阻塞到现有子进程第一个终止。waitpid函数则有选项可供控制,waitpid在一个循环里可以获取所有的终止进程。pid指需要等待的进程ID,如果值为-1表示等待第一个终止的进程,options是附加选项,常用的选项是WNOHANG,即没有子进程终止时不阻塞,至于其他选项自己百度吧。
接下来看两个简单的程序看他们的区别吧
首先来看看wait()函数
这是一个简单服务器程序,返回客户端发来的内容
#include"../unix.h"//这个头文件里面放的都是会用到的头文件,太懒了就不一一列出了
void str_echo(int );//在此函数中处理客户段发来的消息
void sig_child(int );//此函数处理终止子进程
int main(int argc, char ** argv)
{
int listenfd , connfd;
pid_t pid;
socklen_t cli_len;
struct sockaddr_in cliaddr,servaddr;
listenfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr) );
servaddr.sin_family=AF_INET;//设置协议族
servaddr.sin_port=htons(55555);//设置端口
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//设置ip地址
bind(listenfd,(struct sockaddr *)&servaddr, sizeof(servaddr));//绑定套接字
listen(listenfd,1024);//监听套接字
signal( SIGCHLD, sig_child );//捕获信号
for(;;){
cli_len=sizeof(cliaddr);
connfd=accept(listenfd,(struct sockaddr *)&cliaddr, &cli_len);//接受链接
if((pid=fork()) == 0){//当有新链接时fork()一个子进程处理链接,父进程继续等待新连接的到来
close(listenfd);//在子进程之嗯关闭监听套接字
str_echo(connfd);//处理链接
exit(0);
}
close(connfd);
}
return 0;
}
void sig_child( int signal){
pid_t pid;
int stat;
pid = wait(&stat);
std::cout<<"child "<<pid<<" terminated"<<std::endl;
return;
}
void str_echo(int fd){
ssize_t n;
char buf[1024],buf1[1024];
again:
while( ( n= read( fd,buf,1024) ) >0 ){
write(fd,buf,n-1);
bzero(&buf,sizeof(buf));
std::cout<<buf<<n<<std::endl;
}
}
这是客户代码
#include"../unix.h"
#define MAXSIZE 1024
void str_cli(FILE*,int );
int main(int argc ,char ** argv)
{
int sockfd[5],i;
struct sockaddr_in servaddr;
if(argc!=2){
std::cout<<"useage: tcpcil<IPaddress>"<<std::endl;
}
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(55555);
inet_pton(AF_INET,argv[1], &servaddr.sin_addr);
connect(sockfd[i],(struct sockaddr *)&servaddr, sizeof(servaddr) );
}
str_cli(stdin,sockfd[0]);
exit(0);
}
void str_cli(FILE* fp,int sockfd)
{
char sendline[MAXSIZE], recvline[MAXSIZE];
while(fgets(sendline,MAXSIZE,fp) !=NULL){
unsigned long int len= strlen(sendline);
write(sockfd, sendline, len );
if(read(sockfd,recvline,MAXSIZE)==0 ){
std::cout<<"server terminated premature"<<std::endl;
exit(0);
}
std::cout<<recvline<<std::endl;
}
}
接下来让我们运行一下来看看会出现什么结果
正如我们所期望的服务器创建了五个子进程
然后我们试着终止客户端看会发生什么
哎呦,明明五个子进程为什么只处理了四个,那一个怎么变成僵尸进程了?重来,重新运行一次再看看结果
*,我想骂人了,这下怎么只处理了三个子进程,剩下两个僵尸,这还是随机想处理几个就处理几个啊?是不是链接断开的太快了,wait()处理不过来了把一些进程忘记了?辣么把客户端改一下,不让她一次断开,而让他一个一个来,在客户端代码str_cli(stdin,sockfd[0]);前加上如下代码
for(i=0;i<5;i++){
close(sockfd[i]);
}
然后在来看一下运行结果
……此处省略1w字,这下怎么还就成两个了,还是很快?那好吧,让他每断开一次睡上一会再看看
for(i=0;i<5;i++){ //每隔一秒断开一个连接
close(sockfd[i]);
sleep(1);
}
再次运行
看,这下结果就正常了,所以你得慢慢来,不要急,心急是吃不了热豆腐滴。
接下来看看waitpid()函数是怎么处理的
附上服务端代码,客户端代码和未改动前的代码一样
将原服务端代码
void sig_child( int signal){
pid_t pid;
int stat;
pid = wait(&stat);
std::cout<<"child "<<pid<<" terminated"<<std::endl;
return;
}
改为如下代码
void sig_child( int signal){
pid_t pid;
int stat;
while((pid=waitpid(-1,&stat , WNOHANG)) > 0)
std::cout<<"child "<<pid<<" terminated"<<std::endl;
return;
}
然后运行,看看结果如何
看,一气呵成,五个子进程一次搞定,毫不拖泥带水,五个太少,辣么看看20个咋样
麼辦法,就是这么屌,依然一次搞定
现在,wait和waitpid的区别就显而易见了吧。wait不管来多少都会接受,但是能力有限,只处理一次,至于能处理多少就看运气了,运气好,多出里一些,运气不好就呵呵了,不过一个一个来还是能全部处理的,只要不蜂拥而至就行,不然就真的呵呵了。而waitpid却不一样,即使你蜂拥而至,所有来的信号都排好队,一个一个来,保证每个都会处理。