多进程服务器中,epoll的创建应该在创建子进程之后

看我的测试代码,似乎应该是在创建子进程之后创建epoll的fd,否则程序将会有问题,试将代码中两个CreateWorker函数的调用位置分别调用,一个在创建epoll fd之前,一个在之后,在调用在创建之前的代码会出问题,在我的机器上(linux内核2.6.26)表现的症状就是所有进程的epoll_wait函数返回0, 而客户端似乎被阻塞了:

服务器端:


view plain copy to clipboard print ?
  1. #include <iostream>

  2. #include <sys/socket.h>

  3. #include <sys/epoll.h>

  4. #include <netinet/in.h>

  5. #include <arpa/inet.h>

  6. #include <fcntl.h>

  7. #include <unistd.h>

  8. #include <stdio.h>

  9. #include <errno.h>

  10. #include <sys/types.h>

  11. #include <sys/wait.h>

  12. usingnamespace std;

  13. #define MAXLINE 5

  14. #define OPEN_MAX 100

  15. #define LISTENQ 20

  16. #define SERV_PORT 5000

  17. #define INFTIM 1000

  18. typedefstruct task_t

  19. {

  20. int fd;

  21. char buffer[100];

  22. int n;

  23. }task_t;

  24. int CreateWorker(int nWorker)

  25. {

  26. if (0 < nWorker)

  27. {

  28. bool bIsChild;

  29. pid_t nPid;

  30. while (!bIsChild)

  31. {

  32. if (0 < nWorker)

  33. {

  34. nPid = ::fork();

  35. if (nPid > 0)

  36. {

  37. bIsChild = false;

  38. --nWorker;

  39. }

  40. elseif (0 == nPid)

  41. {

  42. bIsChild = true;

  43. printf("create worker %d success!/n", ::getpid());

  44. }

  45. else

  46. {

  47. printf("fork error: %s/n", ::strerror(errno));

  48. return -1;

  49. }

  50. }

  51. else

  52. {

  53. int nStatus;

  54. if (-1 == ::wait(&nStatus))

  55. {

  56. ++nWorker;

  57. }

  58. }

  59. }

  60. }

  61. return 0;

  62. }

  63. void setnonblocking(int sock)

  64. {

  65. int opts;

  66. opts=fcntl(sock,F_GETFL);

  67. if(opts<0)

  68. {

  69. perror("fcntl(sock,GETFL)");

  70. exit(1);

  71. }

  72. opts = opts|O_NONBLOCK;

  73. if(fcntl(sock,F_SETFL,opts)<0)

  74. {

  75. perror("fcntl(sock,SETFL,opts)");

  76. exit(1);

  77. }

  78. }

  79. int main()

  80. {

  81. int i, maxi, listenfd, connfd, sockfd,epfd,nfds;

  82. ssize_t n;

  83. char line[MAXLINE];

  84. socklen_t clilen;

  85. struct epoll_event ev,events[20];

  86. struct sockaddr_in clientaddr;

  87. struct sockaddr_in serveraddr;

  88. listenfd = socket(AF_INET, SOCK_STREAM, 0);

  89. bzero(&serveraddr, sizeof(serveraddr));

  90. serveraddr.sin_family = AF_INET;

  91. char *local_addr="127.0.0.1";

  92. inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);

  93. serveraddr.sin_port=htons(SERV_PORT);

  94. // 地址重用

  95. int nOptVal = 1;

  96. socklen_t nOptLen = sizeof(int);

  97. if (-1 == ::setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &nOptVal, nOptLen))

  98. {

  99. return -1;

  100. }

  101. setnonblocking(listenfd);

  102. bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));

  103. listen(listenfd, LISTENQ);

  104. CreateWorker(5);

  105. //把socket设置为非阻塞方式

  106. //生成用于处理accept的epoll专用的文件描述符

  107. epfd=epoll_create(256);

  108. //设置与要处理的事件相关的文件描述符

  109. ev.data.fd=listenfd;

  110. //设置要处理的事件类型

  111. ev.events=EPOLLIN|EPOLLET;

  112. //ev.events=EPOLLIN;

  113. //注册epoll事件

  114. epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

  115. //CreateWorker(5);

  116. maxi = 0;

  117. task_t task;

  118. task_t *ptask;

  119. while(true)

  120. {

  121. //等待epoll事件的发生

  122. nfds=epoll_wait(epfd,events,20,500);

  123. //处理所发生的所有事件

  124. for(i=0;i<nfds;++i)

  125. {

  126. if(events[i].data.fd==listenfd)

  127. {

  128. connfd = accept(listenfd,NULL, NULL);

  129. if(connfd<0){

  130. printf("connfd<0, listenfd = %d/n", listenfd);

  131. printf("error = %s/n", strerror(errno));

  132. exit(1);

  133. }

  134. setnonblocking(connfd);

  135. //设置用于读操作的文件描述符

  136. memset(&task, 0, sizeof(task));

  137. task.fd = connfd;

  138. ev.data.ptr = &task;

  139. //设置用于注册的读操作事件

  140. ev.events=EPOLLIN|EPOLLET;

  141. //ev.events=EPOLLIN;

  142. //注册ev

  143. epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);

  144. }

  145. elseif(events[i].events&EPOLLIN)

  146. {

  147. cout << "EPOLLIN" << endl;

  148. ptask = (task_t*)events[i].data.ptr;

  149. sockfd = ptask->fd;

  150. if ( (ptask->n = read(sockfd, ptask->buffer, 100)) < 0) {

  151. if (errno == ECONNRESET) {

  152. close(sockfd);

  153. events[i].data.ptr = NULL;

  154. } else

  155. std::cout<<"readline error"<<std::endl;

  156. } elseif (ptask->n == 0) {

  157. close(sockfd);

  158. events[i].data.ptr = NULL;

  159. }

  160. ptask->buffer[ptask->n] = '/0';

  161. cout << "read " << ptask->buffer << endl;

  162. //设置用于写操作的文件描述符

  163. ev.data.ptr = ptask;

  164. //设置用于注测的写操作事件

  165. ev.events=EPOLLOUT|EPOLLET;

  166. //修改sockfd上要处理的事件为EPOLLOUT

  167. epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

  168. }

  169. elseif(events[i].events&EPOLLOUT)

  170. {

  171. cout << "EPOLLOUT" << endl;

  172. ptask = (task_t*)events[i].data.ptr;

  173. sockfd = ptask->fd;

  174. write(sockfd, ptask->buffer, ptask->n);

  175. //设置用于读操作的文件描述符

  176. ev.data.ptr = ptask;

  177. //修改sockfd上要处理的事件为EPOLIN

  178. epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,&ev);

  179. cout << "write " << ptask->buffer;

  180. memset(ptask, 0, sizeof(*ptask));

  181. close(sockfd);

  182. }

  183. }

  184. }

  185. return 0;

  186. }


测试客户端:


view plain copy to clipboard print ?
  1. #!/usr/bin/perl

  2. use strict;

  3. use Socket;

  4. use IO::Handle;

  5. sub echoclient

  6. {

  7. my $host = "127.0.0.1";

  8. my $port = 5000;

  9. my $protocol = getprotobyname("TCP");

  10. $host = inet_aton($host);

  11. socket(SOCK, AF_INET, SOCK_STREAM, $protocol) or die "socket() failed: $!";

  12. my $dest_addr = sockaddr_in($port, $host);

  13. connect(SOCK, $dest_addr) or die "connect() failed: $!";

  14. SOCK->autoflush(1);

  15. my $msg_out = "hello world/n";

  16. print "out = ", $msg_out;

  17. print SOCK $msg_out;

  18. my $msg_in = <SOCK>;

  19. print "in = ", $msg_in;

  20. close SOCK;

  21. }

  22. #&echoclient;

  23. #exit(0);

  24. for (my $i = 0; $i < 9999; $i++)

  25. {

  26. echoclient;

  27. }


lighttpd 也是在创建完子进程后创建的epoll的fd.


你可能感兴趣的:(linux,创建,epoll)