免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 7775 | 回复: 7
打印 上一主题 下一主题

多个进程epoll_wait同一个epoll fd是否有问题? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-03-29 10:35 |只看该作者 |倒序浏览
本帖最后由 tajial 于 2010-03-29 10:43 编辑

服务器代码如下:
  1. #include <stdlib.h>
  2. #include <strings.h>
  3. #include <sys/epoll.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <sys/socket.h>
  7. #include <arpa/inet.h>
  8. #include <errno.h>

  9. void setnonblocking(int fd)
  10. {
  11.     int val;

  12.     if ((val = fcntl(fd, F_GETFL)) < 0)
  13.     {
  14.         perror("GETFL");
  15.         exit(1);
  16.     }

  17.     val |= O_NONBLOCK;

  18.     if (fcntl(fd, F_SETFL, val) < 0)
  19.     {
  20.         perror("SETFL");
  21.         exit(1);
  22.     }
  23. }

  24. int main()
  25. {
  26.     int fd, listener, clifd, epfd, i, ret, size;
  27.     struct epoll_event ev, events[16];
  28.     char buf[1024];
  29.     int parent = 1;
  30.     int status;
  31.     int pid;
  32.    
  33.     if ((listener = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  34.     {
  35.         perror("socket");
  36.         exit(1);
  37.     }

  38.     setnonblocking(listener);

  39.     epfd = epoll_create(256);
  40.     if (epfd < 0)
  41.     {
  42.         perror("epoll_create");
  43.         exit(1);
  44.     }

  45.     ev.data.fd = listener;
  46.     ev.events = EPOLLIN | EPOLLET;

  47.     if (epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev) < 0)
  48.     {
  49.         perror("epoll_ctl");
  50.         exit(1);
  51.     }
  52.    
  53.     struct sockaddr_in servaddr, cliaddr;
  54.     bzero(&servaddr, sizeof(servaddr));
  55.     servaddr.sin_family = AF_INET;
  56.     servaddr.sin_addr.s_addr = INADDR_ANY;
  57.     servaddr.sin_port = htons(5432);

  58.     if (bind(listener, &servaddr, sizeof(servaddr)) < 0)
  59.     {
  60.         perror("bind");
  61.         exit(1);
  62.     }

  63.     if (listen(listener, 16) < 0)
  64.     {
  65.         perror("listen");
  66.         exit(1);
  67.     }

  68.     for (i = 0; i < 3 && parent; i++)
  69.     {
  70.         switch (fork())
  71.         {
  72.             case -1:
  73.                 perror("fork");
  74.                 exit(1);
  75.             case 0:
  76.                 parent = 0;
  77.                 pid = getpid();
  78.                 break;
  79.             default:
  80.                 break;
  81.         }
  82.     }

  83.     if (parent)
  84.     {
  85.         while ((ret = waitpid(-1, &status, 0)))
  86.         {
  87.             if (ret > 0)
  88.                 printf("child %d has quit with %d...\n", ret, status);
  89.         }
  90.     }

  91.     socklen_t len;
  92.     while (1)
  93.     {
  94.         if ((ret = epoll_wait(epfd, events, 1, 600000)) < 0)
  95.         {
  96.             perror("epoll_wait");
  97.             exit(1);
  98.         }
  99.         else if (ret == 0)
  100.         {
  101.             printf("[%d]600s elapsed...\n", pid);
  102.         }
  103.         else
  104.         {
  105.             printf("\n[%d]%d events checkin\n", pid, ret);
  106.         }

  107.         for (i = 0; i < ret; i++)
  108.         {
  109.             if ((fd = events[i].data.fd) < 0)
  110.             {
  111.                 printf("Invalid fd!\n");
  112.                 exit(1);
  113.             }
  114.             
  115.             if (fd == listener)
  116.             {
  117.                 len = sizeof(cliaddr);
  118.                 clifd = accept(fd, &cliaddr, &len);
  119.                 if (clifd < 0)
  120.                 {
  121.                     perror("accept");
  122.                     continue;
  123.                 }

  124.                 printf("[%d]New connection from %s:%d,fd=%d\n",pid, inet_ntoa(cliaddr.sin_addr),
  125.                     ntohs(cliaddr.sin_port), clifd);

  126.                 setnonblocking(clifd);
  127.                 ev.data.fd = clifd;
  128.                 ev.events = EPOLLIN | EPOLLET;

  129.                 if (epoll_ctl(epfd, EPOLL_CTL_ADD, clifd, &ev) < 0)
  130.                 {
  131.                     perror("epoll_ctl");
  132.                     exit(1);
  133.                 }
  134.             }
  135.             else if (events[i].events & EPOLLIN)
  136.             {
  137.                 while ((size = recv(fd, buf, sizeof(buf), 0)) > 0)
  138.                 {
  139.                     buf[size] = 0;
  140.                     printf("[%d]%d bytes recved from fd %d:%s\n", pid, size, fd, buf);
  141.                 }

  142.                 printf("[%d]size=%d,fd=%d:%s\n", pid, size, fd, strerror(errno));
  143.                 if (size == 0)
  144.                 {
  145.                     close(fd);
  146.                 }
  147.             }
  148.         }
  149.     }

  150.     return 0;
  151. }
复制代码

论坛徽章:
0
2 [报告]
发表于 2010-03-29 10:35 |只看该作者
本帖最后由 tajial 于 2010-03-29 10:47 编辑

回复 1# tajial

服务器主进程创建一个监听fd和一个epoll_fd,把监听fd加到epoll_fd中,然后创建3个子进程,父进程然后什么都不干,等待子进程退出;子进程继承epoll_fd,等待新连接并接受数据

客户端也是fork 3个子进程,建立连接后发送几个字节的数据,sleep 3s之后退出。
从客户端观测到每次建立连接和发送数据都成功,但是服务器的epoll_wait经常返回到另外的子进程了,造成检测不到连接断开事件(recv返回0)
  1. [11912]1 events checkin
  2. [11912]New connection from 10.167.27.11:47246,fd=5

  3. [11912]1 events checkin
  4. [11912]9 bytes recved from fd 5:==12025==
  5. [11912]size=-1,fd=5:Resource temporarily unavailable

  6. [11912]1 events checkin
  7. [11912]New connection from 10.167.27.11:47247,fd=6

  8. [11912]1 events checkin
  9. [11912]9 bytes recved from fd 6:==12026==
  10. [11912]size=-1,fd=6:Resource temporarily unavailable

  11. [11912]1 events checkin
  12. [11912]New connection from 10.167.27.11:47248,fd=7

  13. [11912]1 events checkin
  14. [11912]9 bytes recved from fd 7:==12027==
  15. [11912]size=-1,fd=7:Resource temporarily unavailable

  16. [11911]1 events checkin               
  17. [11911]size=-1,fd=5:Bad file descriptor   //应该给进程号为11912的子进程处理

  18. [11912]1 events checkin
  19. [11912]size=0,fd=6:Resource temporarily unavailable

  20. [11911]1 events checkin
  21. [11911]size=-1,fd=7:Bad file descriptor   //应该给进程号为11912的子进程处理
复制代码
由于服务器侧没有关闭连接,产生两条处于CLOSE_WAIT状态的连接
  1. $ netstat -an | grep 5432
  2. tcp  0  0 0.0.0.0:5432                0.0.0.0:*       LISTEN
  3. tcp  1  0 10.167.27.11:5432    10.167.27.11:47248    CLOSE_WAIT
  4. tcp  1  0 10.167.27.11:5432    10.167.27.11:47246    CLOSE_WAIT
复制代码
如果确实有问题,为什么每次都出现在关闭连接的时候?接收数据总是能够上报到正确的子进程的。

论坛徽章:
0
3 [报告]
发表于 2010-03-29 10:44 |只看该作者
{:3_190:}

论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:53:172015亚冠之水原三星
日期:2015-06-02 16:34:202015年亚冠纪念徽章
日期:2015-10-19 18:13:37程序设计版块每日发帖之星
日期:2015-11-08 06:20:00
4 [报告]
发表于 2010-03-29 11:29 |只看该作者
这个epfd是多个进程公享的,从一个进程加入,从另外一个进程返回是很正常的,但是不应该这么设计

论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:53:172015亚冠之水原三星
日期:2015-06-02 16:34:202015年亚冠纪念徽章
日期:2015-10-19 18:13:37程序设计版块每日发帖之星
日期:2015-11-08 06:20:00
5 [报告]
发表于 2010-03-29 11:30 |只看该作者
recv()返回0,代表对方关闭

论坛徽章:
0
6 [报告]
发表于 2010-03-29 13:59 |只看该作者
回复 4# xinglp
那应该如何设计,每个子进程创建自己的epfd,把监听fd加入进来吗?
recv返回0表示对方关闭我知道,服务器端就是检测到这种情况才close fd的,现在的问题是读事件没有上报到正确的进程导致检测不到这种情况。

论坛徽章:
0
7 [报告]
发表于 2010-03-29 16:41 |只看该作者
man 7 epoll

论坛徽章:
0
8 [报告]
发表于 2010-03-30 09:29 |只看该作者

epoll_create这段(46-60)放到子进程里面就没问题了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP