最近开始接触Linux,那么必然的也开始接触epoll.
epoll其实很简单,就三个函数epoll_create, epoll_ctl和epoll_wait。当有网络事件触发的时候由内核主动通知应用程序,它只会对“活跃”的socket进行操作。- -!都是废话,下面进入正题(以ET来说)。
下面是主要的处理线程(有些是伪代码 大概意思就是这样)。
int fd_count; SOCKET sock;
while(1) {
//等待一个网络事件 fd_count = epoll_wait(mgr->epoll_fd, events, THREAD_EVENT_SIZE, 5000); for(i = 0; i < fd_count; ++i) {//fd_count是有多少个事件被触发 sock = events[i].data.fd;
if(sock == INVALID_SOCK) {
continue;
} if( sock == listenfds[events[i].data.fd])) {//listenfds是监听socket的数组用sock做索引 OnAccept(sock );
//PostEvent是epoll_ctl /*PostEvent(sock,EPOLLIN | EPOLLET); */<--------------------------------------------1
}
if(events[i].events & EPOLLHUP || events[i].events & EPOLLERR) { Disconnect(sock); continue; } else if(events[i].events & EPOLLIN) {//读事件 ReadCallback(0); // 触发回调函数.
if(CanSend(sock)) PostEvent(sock,EPOLLOUT);<--------------------------------------------2 // PostEvent(sock, EPOLLIN | EPOLLET); //<--------------------------------3
} else if(events[i].events & EPOLLOUT) {//写事件 WriteCallback(); // 写回调 PostEvent(sock ,EPOLLIN); //<---------------------------------------------------4 } } }
以下的测试环境为 64位Redhat 双核 1G内存。
这是一个比较常见的模型,先说监听吧. <------1处屏蔽的时候会发生这么个情况,同时有2个连接事件发生的时候 有一个连接不会被处理,这是ET本身的规则所导致,它认为已经通知过你有事件到达了,至于怎么处理完 那是你的事。处理接收还有个用的比较常用的方法,就是起一个接收线程,但是这样有一个坏处---当一个应用程序有多个接收sock的时候会启动多个接收线程,会带来额外的线程切换开销。在测试建立连接的时候同时起了8个客户端(5台机器),建立6w个连接,发现<------1处如果屏蔽了处理速度非常慢,而且还有不能处理的连接,而接收线程的方式和放开<-------1的方式速度差不多,也不存在连接丢失的情况。
<------2 <--------3 <-------4 说的其实是一个问题,刚开始的时候<------2 和 <--------4 处其实是屏蔽的 <--------3 处放开,因为我想处理完一个recv事件后应该继续投递下一个recv事件,这样才能把所有数据读完而投递写事件没有什么用。在简单的测试程序里这样是没有什么问题,但是放在应用的工程里发现了毛病.一般正常严谨的工程里,发送数据应该是将数据写在一个缓存里然后投递写事件就不管了,那么这样问题就出来了,你投递的着个 写事件 或者 <------3处的读事件并不会共存,而会被后来者覆盖,即epoll_ctl EPOLLOUT后在消息还没被处理的时候又投递了个EPOLLIN,那么EPOLLOUT将不会被触发了。而屏蔽<------3 打开<------2和 <--------4 则2边的事件始终都能被执行。
呃...大概就是这样在后来的测试中基本程序算是比较稳定的,没有出现过丢包和丢连接的现象。
PS:关于测试的客户端,一台机器的连接数其实是有限制的,1是会有65535的端口限制(客户端的端口其实也是默认给分配的),2是每个连接会占用一定得非分页内存,这样一台硬件机器客户端的连接数不会非常大。