要了解有关linux中的epoll通知工具的更多信息,我一直在研究http服务器。服务器的结构基本上有一个表示请求的事件结构数组,然后在一些嵌套的for和while循环中迭代。
当我第一次写这个原始事件循环时,它工作得相当好。但是,当我重构代码时,循环变得不那么可靠了。特别是,我开始得到一堆(大约75%的请求)epoll_ctl错误,并将errno设置为BADFD。 epoll_ctl显然认为我的套接字文件描述符实际上不是套接字文件描述符。
我真的很困惑,为什么应该会有这样的性能下降,因为重构只包括a)清理请求的解析和b)将main函数移动到程序的开头。我查看了之前提交的服务器,并将主要功能与新版本进行了比较 - 它是相同的。
有没有人对这里可能发生的事情有所了解?不胜感激。
以下是供参考的主要功能的代码:
int main(int argc, char *argv[]) {
c("in main");
char *progname=argv[0];
int sockfd, newsockfd, portno, clilen, n, pid, epollfd;
struct sockaddr_in serv_addr, cli_addr;
initFt();
if (argc < 2 ) {
fprintf(stderr, "\nERROR: No Port Provided\n");
exit(EXIT_FAILURE);
}
if (!createSocket(&sockfd)) {
fprintf(stderr, "%s ERROR, COULD NOT CREATE SERVER SOCKET\n", progname);
exit(EXIT_FAILURE);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(atoi(argv[1]));
serv_addr.sin_addr.s_addr = INADDR_ANY;
Bind(&sockfd, &serv_addr);
listen(sockfd, 5);
struct epoll_event *events = calloc(SOMAXCONN, sizeof(struct epoll_event));
struct epoll_event event;
makeSocketNB(&sockfd);
event.events = EPOLLIN | EPOLLET;
event.data.fd = sockfd;
if ((epollfd = createpoll())<0) {
fprintf(stderr, "epoll create error\n");
exit(EXIT_FAILURE);
}
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) == -1) {
fprintf(stderr, "error with epoll_ctl on sockfd");
exit(EXIT_FAILURE);
}
while (1) {
int n, e; //e for events
e = epoll_wait(epollfd, events, SOMAXCONN, -1);
for (n=0; n<e; n++) {
if ((events[n].events & EPOLLERR) || (events[n].events & EPOLLHUP) || !(events[n].events & EPOLLIN)) {
fprintf (stderr, "epoll error\n");
close (events[n].data.fd);
continue;
}
else if (events[n].data.fd == sockfd) {
while (1) {
newsockfd=accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd>0) {
makeSocketNB(&newsockfd);
fprintf(stderr, "Accepted a new connection on fd %d, made nonblocking\n", newsockfd);
}
else if (errno == EAGAIN || errno == EWOULDBLOCK) {
fprintf (stderr, "we have accepted all the clients on this event\n");
break;
}
event.data.fd = newsockfd;
event.events = EPOLLIN | EPOLLET;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, newsockfd, &event)<0) {
fprintf(stderr, "epoll_ctl error\n");
if (errno == EEXIST) {
fprintf(stderr, "fd already registered\n");
}
else if (errno == EBADF) {
fprintf(stderr, "fd bad\n%d\n", newsockfd);
break;
}
else if (errno == ENOMEM) {
fprintf(stderr, "no memory\n");
}
else if (errno == ENOSPC) {
fprintf(stderr, "enospc\n");
}
exit(EXIT_FAILURE);
}
continue;
}
}
else { // there is stuff for us to read
handleResponse(&events[n].data.fd);
fprintf(stderr, "we are about to close file descriptor %d\n", events[n].data.fd);
close (events[n].data.fd);
fprintf(stderr, "connection closed\n");
}
}
}
exit(EXIT_SUCCESS);
}
答案 0 :(得分:2)
你的accept
代码并没有真正处理错误:它只处理“成功”案例和willblock案例。
if (newsockfd>0) {
/* success */
} else if (errno == EAGAIN || errno == EWOULDBLOCK) {
/* wouldblock */
}
/* Could be error but you call `epoll_ctl` anyway. */