epoll监听多FD时如何优先处理特定FD?现有方案是否可行?
epoll多FD优先级处理方案分析
问题背景
使用epoll监听多个文件描述符(FD),需求为:当高优先级FD有数据到达时,暂停低优先级FD的处理,优先处理高优先级FD。
用户提供的实现代码
n = epoll_wait(epfd, events, MAXEVENTS, -1); std::vector<int32_t> lowPriorFDs, highPriorFDs; for (i = 0; i < n; i++) { if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) { /*An error has occured on this fd, or the socket is not ready for reading (why were we notified then?) */ fprintf (stderr, "epoll error\n"); continue; //exit(-1); }else{ int fd = events[i].data.fd; if(_omsFd == fd){ highPriorFDs.push_back(fd); }else{ lowPriorFDs.push_back(fd); } } } //Here we are keeping the high priority FDs first , here we are giving the priority to the Strategies messg. std::vector<int> FDs; FDs.reserve( highPriorFDs.size() + lowPriorFDs.size() ); // preallocate memory FDs.insert( FDs.end(), highPriorFDs.begin(), highPriorFDs.end() ); FDs.insert( FDs.end(), lowPriorFDs.begin(), lowPriorFDs.end() ); for(int32_t fd : FDs){ if(fd == CHECK_FD){ // 此处应为具体处理逻辑 } }
用户问题
上述实现方案是否能满足需求?是否存在更高效的实现方式?
回答
1. 是否满足需求
这个方案基本能满足需求:
- 它将单次
epoll_wait返回的事件按FD优先级分成两组,合并时把高优先级FD放在前面,遍历处理时会先处理所有高优先级FD,再处理低优先级的,符合“暂停低优先级处理、优先处理高优先级”的要求。 - 注意:如果高优先级FD的处理耗时较长,低优先级FD的事件会被延迟到高优先级处理完成后再处理,这是优先级设计的预期结果;但如果在处理高优先级FD期间,低优先级FD又触发了新事件,这些新事件会等到下一次
epoll_wait才会被处理,这也符合需求逻辑。
不过原代码最后遍历FDs的逻辑是空的,必须补充对应的高/低优先级FD处理逻辑,否则优先级区分没有实际意义。
2. 更高效的实现方式
原方案用到了三个std::vector来存储和合并FD,存在额外的内存分配和拷贝开销,可通过以下方式优化:
方式一:分两次遍历,直接处理
不需要额外存储FD,第一次遍历只处理高优先级FD,第二次遍历处理低优先级FD,省去所有中间vector的开销:
n = epoll_wait(epfd, events, MAXEVENTS, -1); // 先处理所有高优先级FD for (int i = 0; i < n; i++) { if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) { fprintf(stderr, "epoll error\n"); continue; } int fd = events[i].data.fd; if (fd == _omsFd) { // 执行高优先级FD的处理逻辑 processHighPriority(fd); } } // 再处理所有低优先级FD for (int i = 0; i < n; i++) { if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) { continue; } int fd = events[i].data.fd; if (fd != _omsFd) { // 执行低优先级FD的处理逻辑 processLowPriority(fd); } }
方式二:单遍历,仅存储低优先级FD
遍历事件时直接处理高优先级FD,把低优先级FD存入一个vector,等高优先级处理完再处理低的,只用到一个vector:
n = epoll_wait(epfd, events, MAXEVENTS, -1); std::vector<int32_t> lowPriorFDs; lowPriorFDs.reserve(n); // 预分配最大可能空间,避免多次扩容 for (int i = 0; i < n; i++) { if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) { fprintf(stderr, "epoll error\n"); continue; } int fd = events[i].data.fd; if (fd == _omsFd) { // 直接处理高优先级FD processHighPriority(fd); } else { lowPriorFDs.push_back(fd); } } // 处理低优先级FD for (int32_t fd : lowPriorFDs) { processLowPriority(fd); }
这两种方式都减少了内存分配和数据拷贝的操作,在MAXEVENTS较大时性能提升更明显。
内容的提问来源于stack exchange,提问作者Rahul Yadav
相关产品推荐
相关产品推荐

