Chinaunix
标题:
多个进程epoll_wait同一个epoll fd是否有问题?
[打印本页]
作者:
tajial
时间:
2010-03-29 10:35
标题:
多个进程epoll_wait同一个epoll fd是否有问题?
本帖最后由 tajial 于 2010-03-29 10:43 编辑
服务器代码如下:
#include <stdlib.h>
#include <strings.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
void setnonblocking(int fd)
{
int val;
if ((val = fcntl(fd, F_GETFL)) < 0)
{
perror("GETFL");
exit(1);
}
val |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, val) < 0)
{
perror("SETFL");
exit(1);
}
}
int main()
{
int fd, listener, clifd, epfd, i, ret, size;
struct epoll_event ev, events[16];
char buf[1024];
int parent = 1;
int status;
int pid;
if ((listener = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
exit(1);
}
setnonblocking(listener);
epfd = epoll_create(256);
if (epfd < 0)
{
perror("epoll_create");
exit(1);
}
ev.data.fd = listener;
ev.events = EPOLLIN | EPOLLET;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev) < 0)
{
perror("epoll_ctl");
exit(1);
}
struct sockaddr_in servaddr, cliaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(5432);
if (bind(listener, &servaddr, sizeof(servaddr)) < 0)
{
perror("bind");
exit(1);
}
if (listen(listener, 16) < 0)
{
perror("listen");
exit(1);
}
for (i = 0; i < 3 && parent; i++)
{
switch (fork())
{
case -1:
perror("fork");
exit(1);
case 0:
parent = 0;
pid = getpid();
break;
default:
break;
}
}
if (parent)
{
while ((ret = waitpid(-1, &status, 0)))
{
if (ret > 0)
printf("child %d has quit with %d...\n", ret, status);
}
}
socklen_t len;
while (1)
{
if ((ret = epoll_wait(epfd, events, 1, 600000)) < 0)
{
perror("epoll_wait");
exit(1);
}
else if (ret == 0)
{
printf("[%d]600s elapsed...\n", pid);
}
else
{
printf("\n[%d]%d events checkin\n", pid, ret);
}
for (i = 0; i < ret; i++)
{
if ((fd = events[i].data.fd) < 0)
{
printf("Invalid fd!\n");
exit(1);
}
if (fd == listener)
{
len = sizeof(cliaddr);
clifd = accept(fd, &cliaddr, &len);
if (clifd < 0)
{
perror("accept");
continue;
}
printf("[%d]New connection from %s:%d,fd=%d\n",pid, inet_ntoa(cliaddr.sin_addr),
ntohs(cliaddr.sin_port), clifd);
setnonblocking(clifd);
ev.data.fd = clifd;
ev.events = EPOLLIN | EPOLLET;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, clifd, &ev) < 0)
{
perror("epoll_ctl");
exit(1);
}
}
else if (events[i].events & EPOLLIN)
{
while ((size = recv(fd, buf, sizeof(buf), 0)) > 0)
{
buf[size] = 0;
printf("[%d]%d bytes recved from fd %d:%s\n", pid, size, fd, buf);
}
printf("[%d]size=%d,fd=%d:%s\n", pid, size, fd, strerror(errno));
if (size == 0)
{
close(fd);
}
}
}
}
return 0;
}
复制代码
作者:
tajial
时间:
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)
[11912]1 events checkin
[11912]New connection from 10.167.27.11:47246,fd=5
[11912]1 events checkin
[11912]9 bytes recved from fd 5:==12025==
[11912]size=-1,fd=5:Resource temporarily unavailable
[11912]1 events checkin
[11912]New connection from 10.167.27.11:47247,fd=6
[11912]1 events checkin
[11912]9 bytes recved from fd 6:==12026==
[11912]size=-1,fd=6:Resource temporarily unavailable
[11912]1 events checkin
[11912]New connection from 10.167.27.11:47248,fd=7
[11912]1 events checkin
[11912]9 bytes recved from fd 7:==12027==
[11912]size=-1,fd=7:Resource temporarily unavailable
[11911]1 events checkin
[11911]size=-1,fd=5:Bad file descriptor //应该给进程号为11912的子进程处理
[11912]1 events checkin
[11912]size=0,fd=6:Resource temporarily unavailable
[11911]1 events checkin
[11911]size=-1,fd=7:Bad file descriptor //应该给进程号为11912的子进程处理
复制代码
由于服务器侧没有关闭连接,产生两条处于CLOSE_WAIT状态的连接
$ netstat -an | grep 5432
tcp 0 0 0.0.0.0:5432 0.0.0.0:* LISTEN
tcp 1 0 10.167.27.11:5432 10.167.27.11:47248 CLOSE_WAIT
tcp 1 0 10.167.27.11:5432 10.167.27.11:47246 CLOSE_WAIT
复制代码
如果确实有问题,为什么每次都出现在关闭连接的时候?接收数据总是能够上报到正确的子进程的。
作者:
linuxlixk
时间:
2010-03-29 10:44
{:3_190:}
作者:
xinglp
时间:
2010-03-29 11:29
这个epfd是多个进程公享的,从一个进程加入,从另外一个进程返回是很正常的,但是不应该这么设计
作者:
xinglp
时间:
2010-03-29 11:30
recv()返回0,代表对方关闭
作者:
tajial
时间:
2010-03-29 13:59
回复
4#
xinglp
那应该如何设计,每个子进程创建自己的epfd,把监听fd加入进来吗?
recv返回0表示对方关闭我知道,服务器端就是检测到这种情况才close fd的,现在的问题是读事件没有上报到正确的进程导致检测不到这种情况。
作者:
JohnBull
时间:
2010-03-29 16:41
man 7 epoll
作者:
sunceenjoy
时间:
2010-03-30 09:29
把
epoll_create这段(46-60)放到子进程里面就没问题了。
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2