为了了解更多关于 linux 中的 epoll 通知功能,我一直在使用 http 服务器。服务器的结构基本上是有一个表示请求的事件结构数组,然后在一些嵌套的 for 和 while 循环中对其进行迭代。
当我第一次编写这个原始事件循环时,它运行得相当好。但是,当我重构代码时,循环变得不那么可靠了。特别是,我开始收到一堆(大约 75% 的请求)epoll_ctl 错误,其中 errno 被设置为 BADFD。epoll_ctl 显然认为我的套接字文件描述符并不是真正的套接字文件描述符。
然而,我真的很困惑为什么性能会如此下降,因为重构只包括 a) 清理请求的解析和 b) 将 main 函数移到程序的开头。我检查了服务器的先前提交,并将主要功能与新版本进行了比较——它是相同的。
有没有人对这里可能发生的事情有所了解?将不胜感激。
下面是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);
}