使用Linux epoll模型的LT水平触发模式,当socket可写时,会不停的触发socket可写的事件,如何处理?
-----网络流传的腾讯面试题
方法一:每一次需要写入时,将fd加入epoll,轮询到可写并写完数据后,将fd移除epoll管理
既然当socket可写时,会不停触发事件,那么从一开始就将需要写入的fd不加入epoll的EPOLLOUT事件中,当需要写入的时候,再加入epoll管理,轮询fd可写,数据全部写完后,将fd移除epoll管理,避免反复通知
这种方法需要反复添加和删除
方法二:需要写入时,直接调用send或write,当返回错误码EAGAIN时,才将fd加入epoll管理,等待fd可写后写入数据,写入完成后,将fd移出epoll
改进的做法相当于认为socket在大部分时候是可写的,不能写了再让epoll帮忙监控
鄙人根据方法二写的send函数:
int epoll_LT_send(int fd, char *buf, int length) { int ret = send(fd, buf, length, 0); // 需要写入时,直接调用send if (ret < 0) { // 当返回错误码EAGAIN时,才将fd加入epoll管理 if(errno == EAGAIN || errno == EWOULDBLOCK) { int epfd = epoll_create(1); if (epfd == -1) { perror("epoll_create"); return -1; } struct epoll_event ev; ev.data.fd = fd; ev.events = EPOLLOUT; // 将fd加入epoll管理 if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) { perror("epoll_ctl"); close(epfd); return -1; } ret = 0; while(1) { // 等待fd可写后写入数据 struct epoll_event events; int nready = epoll_wait(epfd, &events, 1, -1); if (nready == -1) { perror("epoll_wait"); close(epfd); return -1; } else if (nready == 0) { continue; } do { // 写入数据 int r = send(fd, buf+ret, (length - ret), 0); if (r > 0) { ret += r; } else if (r == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { close(epfd); return ret; } else { perror("send"); close(epfd); return -1; } } } while (ret < length); break; } // 写入完成后,将fd移出epoll epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev); } return ret; } else if (ret == 0) { // disconnect perror("send"); return -1; } return ret; }