Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2151561
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2015-06-12 11:20:20

1. 说明
    第5.10的wait与waitpid
    当client有5个连接同时关闭时,
    server端的wait来不及处理同时发生的5个sigchld,导致僵死进程产生。
1.1 现象
  1. cong@msi:/work/test/tcpip/7tcp/client$ ./client
  2. client.c:main[59]: connect success
  3. client.c:main[59]: connect success
  4. client.c:main[59]: connect success
  5. client.c:main[59]: connect success
  6. client.c:main[59]: connect success
  7. cong@msi:/work/test/tcpip/7tcp/client$
当client连接上时,共执行了5次fork,但是只收到了3次sigchld
  1. cong@msi:/work/test/tcpip/7tcp/serv$ ./tcpserv
  2. tcpserv.c:main[50]: accept:127.0.0.1,port=45388
  3. tcpserv.c:main[50]: accept:127.0.0.1,port=45389
  4. tcpserv.c:main[58]: fork           --> fork1
  5. tcpserv.c:main[50]: accept:127.0.0.1,port=45390
  6. tcpserv.c:main[58]: fork           --> fork2
  7. tcpserv.c:main[50]: accept:127.0.0.1,port=45391
  8. tcpserv.c:main[58]: fork           --> fork3
  9. tcpserv.c:main[50]: accept:127.0.0.1,port=45392
  10. tcpserv.c:main[58]: fork           --> fork4
  11. tcpserv.c:main[58]: fork           --> fork5
  12. tcpserv.c:sig_chld[9]: child 16165 terminated     --> term1
  13. tcpserv.c:sig_chld[9]: child 16166 terminated     --> term2
  14. tcpserv.c:sig_chld[9]: child 16167 terminated     --> term3
ps查看发现,正好有2个处于僵死状态的子进程
  1. cong@msi:/work/test/tcpip/7tcp/client$ ps -u
  2. USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
  3. cong 16197 0.0 0.0 4196 504 pts/1 S+ 18:00 0:00 ./tcpserv
  4. cong 16202 0.0 0.0 0 0 pts/1 Z+ 18:01 0:00 [tcpserv] <defunct>
  5. cong 16203 0.0 0.0 0 0 pts/1 Z+ 18:01 0:00 [tcpserv] <defunct>

2. 代码
2.1 修改了的client端
  1. cong@msi:/work/test/tcpip/7tcp/client$ cat client.c
  2. #include "utils.h"

  3. //#define TIME_SERV_ADDR "192.168.4.98"
  4. #define TIME_SERV_ADDR "127.0.0.1"
  5. #if 0
  6. ssize_t readline(int fd, void* vptr, size_t maxlen)
  7. {
  8.     ssize_t n, rc;
  9.     char c,*ptr;
  10.     ptr = vptr;
  11.     for(n=1; n<maxlen; n++)
  12.     {
  13. again:
  14.         if((rc=read(fd,&c,1)) == 1)
  15.         {
  16.             *ptr++ = c;
  17.             if(c == '\n')
  18.                 break;
  19.         } else if(rc == 0) {
  20.             *ptr = 0;
  21.             return (n-1);
  22.         }else {
  23.             if( errno == EINTR)
  24.                 goto again;
  25.             return -1;
  26.         }
  27.     }
  28. }
  29. #endif
  30. ssize_t readline(int fd, void* vptr, size_t maxlen)
    {
        ssize_t n, rc;
        char c,*ptr;
        ptr = vptr;
        for(n=0; n again:
            if((rc=read(fd,&c,1)) == 1)
            {
                *ptr++ = c;
                if(c == '\n')
                    break;
            } else if(rc == 0) {
                *ptr = 0;
                return n;
            }else {
                if( errno == EINTR)
                    goto again;
                return -1;
            }
        }   
        *ptr = '\0';
        return n;
    }
  31. void str_cli(FILE* fp, int sockfd)
  32. {
  33.     char sendline[MAXLINE], recvline[MAXLINE];
  34.     while(fgets(sendline, MAXLINE, fp) != NULL)
  35.     {
  36.         write(sockfd, sendline, strlen(sendline));
  37.         if(readline(sockfd, recvline, MAXLINE) == 0)
  38.             dbmsg("server terminated");
  39.         fputs(recvline, stdout);
  40.     }

  41. }

  42. int main ( int argc, char *argv[] )
  43. {
  44.     int i;
  45.     int sockfd, n;
  46.     struct sockaddr_in servaddr;
  47.     char recvline[1024];
  48.     for(i=0; i<5; i++)     //产生了5个client
  49.     {
  50.         sockfd = socket(AF_INET, SOCK_STREAM, 0);
  51.         if(sockfd < 0)
  52.             return -1;
  53.         bzero(&servaddr, sizeof(servaddr));
  54.         servaddr.sin_family = AF_INET;
  55.         servaddr.sin_port = htons(13999);
  56.         inet_pton(AF_INET, TIME_SERV_ADDR, &servaddr.sin_addr);
  57.         connect(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr));
  58.         dbmsg("connect success");
  59.     }
  60.     str_cli(stdin, sockfd);   //str_cli返回后,进程退出,这会发送5个FIN
  61.     return EXIT_SUCCESS;
  62. }
2.2 server.c出的问题分析
  1. cong@msi:/work/test/tcpip/7tcp/serv$ cat tcpserv.c
  2. #include "utils.h"

  3. #define LISTENQ 10
  4. void sig_chld(int signo)
  5. {
  6.     pid_t pid;
  7.     int stat;
  8.     pid = wait(&stat);              //3.同时产生5个SIGCHLD,会同时调用wait
  9.     dbmsg("child %d terminated", pid); //但是wait是阻塞的,当wait在处理sigchld信号时再有sigchld信号过来
  10.     return ;                         //wait是没有办法处理的,所以导致了僵死进程产生
  11. }
  12. void str_echo(int sockfd)
  13. {
  14.     ssize_t n;
  15.     char buf[MAXLINE];
  16. again:
  17.     while( (n=read(sockfd, buf, MAXLINE)) > 0)
  18.         write(sockfd, buf, n);
  19.         
  20.     if( (n<0) && (errno==EINTR) )
  21.         goto again;
  22.     else if(n < 0)
  23.         dbmsg("read_error");
  24. }

  25. int main ( int argc, char *argv[] )
  26. {
  27.     int listenfd, connfd;
  28.     struct sockaddr_in servaddr, cliaddr;
  29.     signal(SIGCHLD, sig_chld);
  30.     pid_t childpid;
  31.     char buf[1024];
  32.     int len;
  33.     listenfd = socket(AF_INET, SOCK_STREAM, 0);
  34.     if(listenfd< 0)
  35.         return -1;
  36.     bzero(&servaddr, sizeof(servaddr));
  37.     servaddr.sin_family = AF_INET;
  38.     servaddr.sin_port = htons(13999);
  39.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  40.     //inet_pton(AF_INET, TIME_SERV_ADDR, &servaddr.sin_addr);
  41.      
  42.     bind(listenfd, (const struct sockaddr*)&servaddr, sizeof(servaddr));
  43.     listen(listenfd, LISTENQ);
  44.     while(1)
  45.     {
  46.         len = sizeof(cliaddr);
  47.         //connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
  48.         connfd = accept(listenfd, (struct sockaddr*)&cliaddr,(socklen_t*)&len);
  49.         dbmsg("accept:%s,port=%d", inet_ntop(AF_INET,&cliaddr.sin_addr,buf, sizeof(buf)), ntohs(cliaddr.sin_port));
  50.         if(errno == EINTR)
  51.         {
  52.             dbmsg("EINTR");
  53.             continue;
  54.         }
  55.         if( (childpid=fork()) == 0)
  56.         {
  57.             dbmsg("fork");
  58.             close(listenfd);
  59.             str_echo(connfd);    //1.client:str_cli返回后,进程退出,这会发送5个FIN   
  60.             exit(0);             //2.这会导致serve的5个进程同时终止,并同时产生5个SIGCHLD
  61.         }
  62.         close(connfd);
  63.     }

  64.     return EXIT_SUCCESS;
  65. }
2.3 源码
client.rar (下载后改名为client.tar.gz)
serv_old.rar (下载后改名为serv_old.tar.gz)

3. 修改后的server.c
  1. cong@msi:/work/test/tcpip/7tcp/serv$ cat tcpserv.c
  2. #include "utils.h"

  3. #define LISTENQ 10
  4. void sig_chld(int signo)
  5. {
  6.     pid_t pid;
  7.     int stat;
  8.     //pid = wait(&stat);                           //WNOHANG: return immediately if no child has exited
  9.     while( (pid = waitpid(-1,&stat,WNOHANG)) > 0)  //3.同时产生5个SIGCHLD,会同时调用waitpid,此时waitpid是非阻塞的
  10.         dbmsg("child %d terminated", pid);         //接收到sigchld信号时会立即返回并等侍下一个sigchld信号的到来,不会丢信号
  11.     return ;
  12. }
  13. void str_echo(int sockfd)
  14. {
  15.     ssize_t n;
  16.     char buf[MAXLINE];
  17. again:
  18.     while( (n=read(sockfd, buf, MAXLINE)) > 0)
  19.         write(sockfd, buf, n);
  20.         
  21.     if( (n<0) && (errno==EINTR) )
  22.         goto again;
  23.     else if(n < 0)
  24.         dbmsg("read_error");
  25. }

  26. int main ( int argc, char *argv[] )
  27. {
  28.     int listenfd, connfd;
  29.     struct sockaddr_in servaddr, cliaddr;
  30.     signal(SIGCHLD, sig_chld);
  31.     pid_t childpid;
  32.     char buf[1024];
  33.     int len;
  34.     listenfd = socket(AF_INET, SOCK_STREAM, 0);
  35.     if(listenfd< 0)
  36.         return -1;
  37.     bzero(&servaddr, sizeof(servaddr));
  38.     servaddr.sin_family = AF_INET;
  39.     servaddr.sin_port = htons(13999);
  40.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  41.     //inet_pton(AF_INET, TIME_SERV_ADDR, &servaddr.sin_addr);
  42.      
  43.     bind(listenfd, (const struct sockaddr*)&servaddr, sizeof(servaddr));
  44.     listen(listenfd, LISTENQ);
  45.     while(1)
  46.     {
  47.         len = sizeof(cliaddr);
  48.         //connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
  49.         connfd = accept(listenfd, (struct sockaddr*)&cliaddr,(socklen_t*)&len);
  50.         dbmsg("accept:%s,port=%d", inet_ntop(AF_INET,&cliaddr.sin_addr,buf, sizeof(buf)), ntohs(cliaddr.sin_port));
  51.         if(errno == EINTR)
  52.         {
  53.             dbmsg("EINTR");
  54.             continue;
  55.         }
  56.         if( (childpid=fork()) == 0)
  57.         {
  58.             dbmsg("fork");
  59.             close(listenfd);   //1.client:str_cli返回后,进程退出,这会发送5个FIN   
  60.             str_echo(connfd);  //2.这会导致serve的5个进程同时终止,并同时产生5个SIGCHLD
  61.             exit(0);
  62.         }
  63.         close(connfd);
  64.     }

  65.     return EXIT_SUCCESS;
  66. }
client.rar (下载后改名为client.tar.gz)

serv_new.rar (下载后改名为serv_new.tar.gz)

阅读(1164) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~