Chinaunix首页 | 论坛 | 博客
  • 博客访问: 611004
  • 博文数量: 66
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1810
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-23 13:53
个人简介

linux

文章分类

全部博文(66)

文章存档

2016年(1)

2015年(14)

2014年(32)

2013年(19)

分类: C/C++

2014-12-05 09:30:32

1.wait和waitpid函数介绍:
一个进程正常或异常终止时,内核就向其父进程发送一个SIGCHLD信号。因为子进程终止是一个异步事件,所以这种信号也是内核向父进程发送的异步通知。父进程可以忽略该信号,或是提供一个该信号发生时即被调用的执行函数。

两个函数接口是:
  1. #include <sys/wait.h>

  2. pid_t wait(int statloc);
  3. pid_t waitpid(pid_t pid, int statloc, int options);
  4.     两个函数返回:若成功则为进程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.非阻塞的等待子进程结束。

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <sys/wait.h>

  6. void pr_exit(int status);

  7. int main()
  8. {
  9.         pid_t pid;
  10.         int status;

  11.         if((pid = fork()) < 0)
  12.                 printf("fork error\n");
  13.         else if(pid == 0)
  14.                 exit(7);

  15.         if(wait(&status) != pid)
  16.                 printf("wait error\n");
  17.         pr_exit(status);

  18.         if((pid = fork()) < 0)
  19.                 printf("fork error\n");
  20.         else if(pid == 0)
  21.                 abort();

  22.         if(wait(&status) != pid)
  23.                 printf("wait error\n");
  24.         pr_exit(status);

  25.         if((pid = fork()) < 0)
  26.                 printf("fork error\n");
  27.         else if(pid == 0)
  28.                 status /= 0;

  29.         if(wait(&status) != pid)
  30.                 printf("wait error\n");
  31.          pr_exit(status);

  32.         exit(0);
  33. }

  34. void pr_exit(int status)
  35. {
  36.         if(WIFEXITED(status))
  37.                 printf("normal termination, exit status = %d\n",
  38.                          WEXITSTATUS(status));
  39.         else if(WIFSIGNALED(status))
  40.                 printf("abnormal termination, signal number = %d%s\n",
  41.                         WTERMSIG(status));
  42.         else if(WIFSTOPPED(status))
  43.                 printf("child stopped, signal number = %d\n",
  44.                         WSTOPSIG(status));
  45. }
显示:
  1. # ./a.out
  2. normal termination, exit status = 7
  3. abnormal termination, signal number = 6(null)
  4. abnormal termination, signal number = 8(null)
2.这两个函数的区别:
    1)在一个子进程终止前,wait使其调用者阻塞, 而waitpid有一个选项可使调用者不阻塞。
    2)waitpid并不等待第一个终止的子进程,它有若干选项,可以控制它所有等待的进程。

下列代码,客户建立5个服务器的连接,随后在调用str_cli函数时仅用第一个连接(sockfd[0]),建立多个连接的目的是并发服务器上派生多个子进程。

  1. /*
  2. **客户端
  3. */
  4. #include "unp.h"

  5. void str_cli(FILE *fp, int sockfd)
  6. {
  7.   char sendline[MAXLINE], recvline[MAXLINE];
  8.   //读入一行文本,写到服务器,在未键入文本时,处于阻塞状态
  9.   while(Fgets(sendline, MAXLINE, fp) != NULL){
  10.     Writen(sockfd, sendline, strlen(sendline));
  11.     if(Readline(sockfd, recvline, MAXLINE) == 0)
  12.       err_quit("str_cli:server terminated prematurely");
  13.     Fputs(recvline, stdout);
  14.   }
  15. }

  16. int main(int argc, char *argv[])
  17. {
  18.   int i, sockfd[5];
  19.   struct sockaddr_in servaddr;

  20.   if(argc != 2)
  21.     err_quit("usage:tcpcli");
  22.   //客户建立5个与服务器的连接,随后在调用str_cli函数时仅用第一个连接。
  23.   for (i = 0; i < 5; ++i){
  24.     sockfd[i] = Socket(AF_INET, SOCK_STREAM, 0);
  25.     
  26.     bzero(&servaddr, sizeof(servaddr));
  27.     servaddr.sin_family = AF_INET;
  28.     servaddr.sin_port = htons(SERV_PORT);
  29.     Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

  30.     Connect(sockfd[i], (SA*) &servaddr, sizeof(servaddr));
  31.    }
  32.   str_cli(stdin, sockfd[0]);
  33.   exit(0);
  34. }
  1. /*
  2. **服务器端
  3. */
  4. #include "unp.h"

  5. void str_echo(int sockfd)
  6. {
  7.   ssize_t n;
  8.   char buf[MAXLINE];

  9.  again:
  10.   while((n = read(sockfd, buf, MAXLINE)) > 0)
  11.     Writen(sockfd, buf, n);
  12.   //EINTR Interrupted function call (POSIX.1); see signal(7).
  13.   if(n < 0 && errno == EINTR)
  14.     goto again;
  15.   else if(n < 0)
  16.     err_sys("str_echo: read error");
  17. }

  18. /*信号处理函数,用于处理僵死进程,because 每次终止子进程将产生僵死进程,占用内核空间,耗费资源
  19.   用wait在多个子进程同一时刻终止时将产生僵死进程
  20.  */
  21. void sig_chld(int signo)
  22. {
  23.   pid_t pid;
  24.   int stat;
  25.   
  26.   pid = wait(&stat);
  27.   printf ("child %d terminated\n",pid);
  28.   return;
  29.   }
  30. /*调用waitpid解决同一时刻终止多个子进程的情况
  31.  */

  32. /*void sig_chld(int signo)
  33. {
  34.   pid_t pid;
  35.   int stat;
  36.   
  37.   while((pid == waitpid(-1, &stat, WNOHANG)))
  38.     printf ("child %d terminated\n",pid);
  39.   return;
  40. }*/

  41. int main(int argc, char *argv[])
  42. {
  43.   int listenfd, connfd;
  44.   pid_t childpid;
  45.   socklen_t clilen;
  46.   struct sockaddr_in cliaddr, servaddr;
  47.   //创建套接字,捆绑服务器的众所周知的端口
  48.   listenfd = Socket(AF_INET, SOCK_STREAM, 0);
  49.   bzero(&servaddr, sizeof(servaddr));
  50.   servaddr.sin_family = AF_INET;
  51.   servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  52.   //SERV_PORT服务器众所周知的端口
  53.   servaddr.sin_port = htons(SERV_PORT);
  54.   
  55.   Bind(listenfd, (SA*) &servaddr, sizeof(servaddr));
  56.   
  57.   Listen(listenfd, LISTENQ);
  58.   Signal(SIGCHLD, sig_chld);
  59.   for( ; ; ){
  60.     //服务器阻塞于accept调用,等待客户连接的完成
  61.     clilen = sizeof(cliaddr);
  62.     //自己重启被中断的系统调用
  63.     if( (connfd = accept(listenfd, (SA*) &cliaddr, &clilen) )< 0 ){
  64.       if(errno == EINTR)
  65.     continue;
  66.       else
  67.     err_sys("accept error");
  68.     }
  69.     //并发服务器,fork为每一个客户派生一个处理他们的子进程。子进程关闭监听套接字,父进程关闭已连接的套接字
  70.     if((childpid = fork()) == 0){
  71.       Close(listenfd);
  72.       str_echo(connfd);
  73.      exit(0);
  74.     }
  75.     Close(connfd);
  76.   }
  77. }
当客户终止时,所有打开的文件描述符由内核自动关闭,且所有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) |
1

上一篇:atexit函数

下一篇:时间和日期

给主人留下些什么吧!~~