2023年epoll学习笔记大全_第1页
2023年epoll学习笔记大全_第2页
2023年epoll学习笔记大全_第3页
2023年epoll学习笔记大全_第4页
2023年epoll学习笔记大全_第5页
已阅读5页,还剩37页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

epoH学习笔记

epo11有两种模式,EdgeTriggered(简称ET)和Leve1Triggered

(简称LT).在采用这两种模式时要注意的是,假如采用ET模式,那么仅当状态发

生变化时才会告知,而采用LT模式类似于本来的select/p。11操作,只要尚

有没有解决的事件就会一直告知.△

以代码来说明问题:

一方面给出server的代码,需要说明的是每次accept的连接,加入可读集的时

候采用的都是ET模式,并且接受缓冲区是5字节的,也就是每次只接受5字节

的数据:

#include<iostrnclude<sys/socket.h>

#include<sys/epol1.h>A#include<netinet/in.h>

#include<arpa/inet.h>A#inc1ude<fcnt1.h>

#inc1ude<unistd.h>A#include<stdio.h>

#inelude<errno.h>AAUSingnamespacestd;

用defineMAXLINE5A#C1efine0PEN_MAX100

^defineLISTENQ2OA#defineSERV_PORT5000

WdefineINFTIM1000

Avoidsetnonb1ocking(intsock)A,intopts;

opts=fcntl(sock,F_GETFL);

if(opts<0)

(

perror("fent1(sock,GETFL)");

exit(1);

}Aopts=opts10_N0NBLOCK;

if(fcntl(sock,F_SETFL,opts)<0)A{perrorC'fcnt1(soc

k,SETFL,opts)”);Aexit(1);

}

intmainO

(

inti,maxi,1istenfd,connfd,sockfd,epfd,nfds;

ssize_tn;

char1ine[MAXLINE];

socklen_tc1ilen;A//声明epo11_event结构体的变量,ev用于注册事件,数

组用于回传要解决的事件Astructepo1l_eventev,events[20];

//生成用于解决accept的epoll专用的文献描述符Aepfd=epo11create(256);

structsockaddr_inc1ientaddr;

structsockaddr_inserveraddr;

1istenfd=socket(AF_INET,SOCK_STREAM,0);

//把socket设立为非阻塞方式

//setnonb1ocking(1istenfd);A//设立与要解决的事件相关的文献描述符

Aev.data,fd=listenfd:

//设立要解决的事件类型Aev.events=EPOLLINIEPOLLE

T;A//ev.events=EPOLLIN;•*//注册epoll事件

epo1l_ct1(epfd,EPOLL_CTL_ADD,listenfd,&ev);Abzero(&serverad

dr,sizeof(serveraddr));Aserveraddr.sinfamily=AF

_INETchar*local_addr=//127.0.0.1";Ainet_aton(local_addr,&(se

rveraddr.sin_addr));//htons(SERV_PORT)SerVeraddr.sin_port=htons(S

ERVPORT);Abind(listenfd,(sockaddr*)&serveraddr,sizeof(serve

raddr));Alisten(listenfd,LISTENQ);

maxi0型for()(

〃等待epol1事件的发生④nfds=epol1wait(epfd,event

s,20,500);

//解决所发生的所有事件for(i=0;i<nfds;++i)

if(events[i].data.fd=1istenfd)

A

connfdaccept(1istenfd,(sockaddr*)&clien

taddr,&c1ilen);

if(connfd<

Perror(〃con

nfd<0");Aexit(1);

//setnonb1ocking(connfd)

char*str二inetntoa(c1ientaddr.sin_add

■Acout«accaptaconnectionfro

m«str«endl;

〃设立用于读操作的文献描述符

ev.data.fd=connfd;

〃设立用于注测的读操作事件Ae

v.events二EPOLLINIEPOLLET;

//ev.events=EPOLLIN;A〃注册ev

epol1_ct1(epfd,EP01」,_CTLAIX),connfd,&ev)泠)

Aelseevents[i].events&EPOLLIN)

cout«HEPOLLIN"«end1

f((sockfd=events[i].data.fd)<0)

continue;

if((nread(sockfd,line,MAXLIN

E))<0)if(errnoECONNRESE

T){Aclose(sockfd);

events[i].data.fd=-1;

)e

]seAstd::cout<</zreadlineerror”

«std::end1;

}e1s

eif(n==0){Aclose(sockf

d);Aevents[i].data.fd=-

1;A}A1ine[n]='\0';

cout<<"read〃«1ine<<end1;

//设立用于写操作的文献描述符

ev.data.fd=sockfd;

//设立用于注测的写操作事件

ev.events=EPOLL0UT1EP0LLET;

//修改sockfd上要解决的事件为EPOLLOUT

//epol1_ct1(epfd,EPOLL_CTLMOD,sockfd,&ev):

A}Aelseif(events[i].events&EP

OLLOUT)^{Asockfd=eve

nts[i].data.fd;

write(sockfd,line,n);

〃设立用于读操作的文献描述符

ev.data.fd=sockfd;A//

设立用于注测的读操作事件

ev.events二EPOLLINEP

()LLET;A〃修改sockfd上要解决的事件为EPOLIN

epol1_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

}

}

}Breturn0;

)

下面给出测试所用的Perl写的client端,在client中发送10字节的数据,

同时让client在发送完数据之后进入死循环,也就是在发送完之后连接的状

态不发生改变一既不再发送数据,也不关闭连接,这样才干观测出server的状

态:

#!/usr/bin/per1

use10::Socket;A

myShost=n127.0.0.l";Amy$port=5000;Mny$socket=I0::Socket::IN

ET—>new("$host:$port〃)ordie"createsocketerror$矿;

my$msg_out=〃”;

print$soeket$msg_out;

printnowsendover,gotosleep____l\n”;

whi1e(1)A{ASleep(1);

)

运营server和client发现,server仅仅读取了5字节的数据,而c1

ient其实发送了10字节的数据,也就是说,server仅当第一次监听到了EP

OLLIN事件,由于没有读取完数据,并且采用的是ET模式,状态在此之后不发

生变化,因此server再也接受不到EPOLLIN事件了.■*(友情提醒:上面的这

个测试客户端,当你关闭它的时候会再次出发10可读事件给server,此时

server就会去读取剩下的5字节数据了,但是这一事件与前面描述的ET性质并

不矛盾.)

假如我们把client改为这样:

#!/usr/bin/perl^

useI0::Socket;&my$host-"12”;

my$port=5000;Mmy$socket=I0::Socket::INET->new(z,$host:$port")o

rdie"createsocketerror:Amy$msgout=〃〃:

print$socket$msg_out;Aprint"nowsendover,gotosieepl

sleep(5);

print〃5secondanotherline\n,z;Aprint$socketSmsg_out;

while(1)

sleep(l);A}

可以发现,在server接受完5字节的数据之后一直监听不到client的事件,而

当client休眠5秒之后重新发送数据,server再次监听到了变化,只但是由于

只是读取了5个字节,仍然有10个字节的数据(client第二次发送的数据)没有

接受完.

A假如上面的实验中,对accept的socket都采用的是LT模式,那么只要尚

有数据留在buffer中,server就会继续得到告知,读者可以自行改动代码进行

实验.

A基于这两个实验,可以得出这样的结论:ET模式仅当状态发生变化的时候才获

得告知,这里所谓的状态的变化并不涉及缓冲区中尚有未解决的数据,也就是说,

假如要采用ET模式,需要一直read/write直到犯错为止,很多人反映为什

么采用ET模式只接受了一部分数据就再也得不到告知了,大多由于这样;而

LT模式是只要有数据没有解决就会一直告知下去的.

补充说明一下这里一直强调的〃状态变化''是什么:31)对于监听可读事件时,

假如是socket是监听socket,那么当有新的积极连接到来为状态发生变化;

对一般的socket而言,协议栈中相应的缓冲区有新的数据为状态发生变化.但是,

假如在一个时间同时接受了N个连接(N>1),但是监听socket只accept了一

个连接,那么其它未accept的连接将不会在ET模式下给监听socket发出

告知,此时状态不发生变化;对于一般的socket,就如例子中而言,假如相应

的缓冲区自身已有了N字节的数据,而只取出了小于N字节的数据,那么残存

的数据不会导致状态发生变化.A

2)对于监听可写事件时,同理可推,不再详述.

而不管是监听可读还是可写,对方关闭socket连接都将导致状态发生变化,

比如在例子中,假如强行中断client脚本,也就是积极中断了soeket连接,那

么都将导致server端发生状态的变化,从而server得到告知,将已经在本

方缓冲区中的数据读出.A

把前面的描述可以总结如下:仅当对方的动作(发出数据,关闭连接等)导致的

事件才干导致状态发生变化,而本方协议栈中已经解决的事件(涉及接受了对方

的数据,接受了对方的积极连接请求)并不是导致状态发生变化的必要条件,状态

变化一定是对方导致的.所以在ET模式下的,必须一直解决到犯错或者完全解

决完毕,才干进行下一个动作,否则也许会发生错误.

A此外,从这个例子中,也可以阐述一些基本的网络编程概念.一方面,连接的两

端中,一端发送成功并不代表着对方上层应用程序接受成功,就拿上面的

client测试程序来说,10字节的数据已经发送成功,但是上层的server并没

有调用read读取数据,因此发送成功仅仅说明了数据被对方的协议栈接受存

放在了相应的buffer中,而上层的应用程序是否接受了这部分数据不得而知;

同样的,读取数据时也只代表着本方协议栈的相应buffer中有数据可读,而此

时时候在对端是否在发送数据也不得而知.

epoll精髓

在1inux的网络编程中,很长的时间都在使用select来做事件触发。在

linux新的内核中,有了一种替换它的机制,就是epollo

相比于seiect,epoll最大的好处在于它不会随着监听fd数目的增长而减少

效率。由于在内核中的select实现中,它是采用轮询来解决的,轮询的fd

数目越多,自然耗时越多。并且,在linux/posix_types.h头文献有这样

的声明:赋define_FD_SETSIZE1024A表达select最多同时监

听1024个fd,当然,可以通过修改头文献再重编译内核来扩大这个数目,但这

似乎并不治本。A

epo11的接口非常简朴,一共就三个函数:

1.intepol1_create(intsize);

创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这

个参数不同于select。中的第一个参数,给出最大监听的fd+1的值。需要

注意的是,当创建好ep。11句柄后,它就是会占用一个fd值,在linux下假

如查看/proc/进程id/fd/,是可以看到这个fd的,所以在使用完epoll

后,必须调用close()关闭,否则也许导致fd被耗尽。足

2.intepoll_ct1(intepfd,intop,intfd,structepol

1_event*event);

epol1的事件注册函数,它不同与se1ect()是在监听事件时告诉内核要

监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是

epo1l_create()的返回值,第二个参数表达动作,用三个宏来表达:

EP0LL_CTL_ADD:注册新的fd到epfd中;

EP0LL_CTL_M0D:修改已经注册的fd的监听事件;AEP0LL_CTL_DEL:从

epfd中删除一个fd内第三个参数是需要监听的fd,第四个参数是告诉内核需

要监听什么事,structepoll_event结构如下:AStructepo11_event

{4___uint32_tevents;/*Epo11events*/

epo11_data_tdata;/*Userdatavariab1e*/

);

Mvents可以是以下几个宏的集合:

EPOLLIN:表达相应的文献描述符可以读(涉及对端SOCKET正常关闭);

EP0LLOUT:表达相应的文献描述符可以写;

EPOLLPRI:表达相应的文献描述符有紧急的数据可读(这里应当表达有带外数

据到来);4POLLERR:表达相应的文献描述符发生错误;AEPOLLHUP:表达相

应的文献描述符被挂断;AEPOLLET:将EPOLL设为边沿触发(Edge

Triggered)模式,这是相对于水平触发(LevelTriggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,假如还需要继续监听

这个socket的话,需要再次把这个soeket加入到EP0LL队列里

A

3.intepoll_wait(intepfd,structepo11_event*

events,intmaxevents,inttime。ut);A等待事件的产生,类似于

select()调用。参数events用来从内核得到事件的集合,maxevents告之

内核这个events有多大,这个maxevents的值不能大于创建epoll_cr

eate()时的size,参数timeout是超时时间(毫秒,。会立即返回,T将

不拟定,也有说法说是永久阻塞)。该函数返回需要解决的事件数目,如返回0表

达已超时。

A一

A从man手册中,得到ET和LT的具体描述如下*EPOLL事件有两种模型:^Ed

geTriggered(ET)MevelTriggered(LT)2假如有这样一个例子:

1.我们已经把一个用来从管道中读取数据的文献句柄(RFD)添加到epol1描

述符%.这个时候从管道的另一端被写入了2KB的数据调用epoll_w

ait(2),并且它会返回RFD,说明它已经准备好读取操作4A然后我们读取了1K

B的数据

5.调用epo1l_wait(2)...........

EdgeTriggered工作模式:A假如我们在第1步将RFD添加|至UepoH描

述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait(2)之

后将有也许会挂起,由于剩余的数据还存在于文献的输入缓冲区内,并且数据发

出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文献句柄上发

生了某个事件的时候ET工作模式才会报告事件。因此在第5步的时候,调用者

也许会放弃等待仍在存在于文献输入缓冲区内的剩余数据。在上面的例子中,

会有一个事件产生在RFD句柄上,由于在第2步执行了一个写操作,然后,事

件将会在第3步被销毁。由于第4步的读取操作没有读空文献输入缓冲区内的

数据,因此我们在第5步调用epoll_wait(2)完毕后,是否挂起是不拟定

的。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文

献句柄的阻塞读/阻塞写操作把解决多个文献描述符的任务饿死。最佳以下面

的方式调用ET模式的epoll接口,在后面会介绍避免也许的缺陷。

Ai基于非阻塞文献句柄

ii只有当read(2)或者write(2)返回EAGAIN时才需要挂起,

等待。但这并不是说每次read()时都需要循环读,直到读到产生一个EAGAIN

才认为本次事件解决完毕,当read。返回的读到的数据长度小于请求的数据

长度时,就可以拟定此时缓冲中已没有数据了,也就可以认为此事读事件已解决

完毕。

Leve1Triggered工作模式

相反的,以LT方式调用epol1接口的时候,它就相称于一个速度比较快的pol

1(2),并且无论后面的数据是否被使用,因此他们具有同样的职能。由于即使

使用ET模式的epoll,在收到多个chunk的数据的时候仍然会产生多个事

件。调用者可以设定EPOLLONESHOT标志,在epol1_wait(2)收到事件后

epol1会与事件关联的文献句柄从epoll描述符中严禁掉。因此当EPOLLON

ESHOT设定后,使用带有EPOLL_CTL_MOD标志的epo1l_ctl(2)解决

文献句柄就成为调用者必须作的事情。

A

然后具体解释ET,LT:

ALT(1eveltriggered)是缺省的工作方式,并且同时支持biock和no-

blocksocket.在这种做法中,内核告诉你一个文献描述符是否就绪了,然后

你可以对这个就绪的fd进行10操作。假如你不作任何操作,内核还是会继续

告知你的,所以,这种模式编程犯错误也许性要小一点。传统的seiec

t/poll都是这种模型的代表.AAET(edge-triggered)是高速工作方式,

只支持no-blocksoeket„在这种模式下,当描述符从未就绪变为就绪

时,内核通过epol1告诉你。然后它会假设你知道文献描述符已经就绪,并且不

会再为那个文献描述符发送更多的就绪告知,直到你做了某些操作导致那个文

献描述符不再为就绪状态了(比如,你在发送,接受或者接受请求,或者发送接

受的数据少于一定量时导致了一个EWOULDBLOCK错误)。但是请注意,假如一

直不对这个fd作I0操作(从而导致它再次变成未就绪),内核不会发送更多的

告知(onlyonce),但是在TCP协议中,ET模式的加速效用仍需要更多的

benchmark确认这句话不理解)

在许多测试中我们会看到假如没有大量的idle-connection或者dead-

connection,epo11的效率并不会比select/poll高很多,但是当我们碰

到大量的idle-connection(例如WAN环境中存在大量的慢速连接),就会

发现epoll的效率大大高于select/poll.(未测试)

4

A此外,当使用epoll的ET模型来工作时,当产生了一个EPOLLIN事件后,

读数据的时候需要考虑的是当recv()返回的大小假如等于请求的大小,那么

很有也许是缓冲区尚有数据未读完,也意味着该次事件还没有解决完,所以还

需要再次读取

whie(rs)A

buf1en=recv(activeevents[i].data.fd,buf,sizeof(b

uf),0);

if(buf1en0)

//由于是非阻塞的模式,所以当errno为EAGAIN时,表达当前缓冲区

已无数据可读

在这里就当作是该次事件已解决处.

if(errno==EAGAIN)Abreak;Aels

e.Areturn;

elseif(buf1en==0)A{A//这里表达对端

的socket已正常关闭.

}Aif(buflen==sizeof(uf)Ars=

1;〃需要再次读取Aelse*rs=0;

)

A尚有,假如发送端流量大于接受端的流量(意思是epoll所在的程序读比转

发的socket要快),由于是非阻塞的socket,那么send()函数虽然返

回,但实际缓冲区的数据并未真正发给接受端,这样不断的读和发,当缓冲区

满后会产生EAGAIN错误(参考mansend),同时,不理睬这次请求发送的数

据.所以,需要封装socket_send()的函数用来解决这种情况,该函数会尽量

将数据写完再返回,返回T表达犯错。在socket_send()内部,当写缓冲已满

(sendO返回-1,且errn。为EAGAIN),那么会等待后再重试.这种方式并不

很完美,在理论上也许会长时间的阻塞在socket_send()内部,但暂没有更好的

办法.4

ssize_tsocket_send(intsockfd,constchar*buffer,

size_tbulen)

ssize_ttmp;Asize_ttotal=buflen;Aconstchar

*p=buffe

Awhile(1)

tmp=send(sockfd,p,tota1,0)if(tmp<

0”{A〃当send收到信号时,可以继续写,但这里返回-

1.4if(errno==EINTR)

etun-1

//当socket是非阻塞时,如返回此错误,表达写缓冲队列已满,

//在这里做延时后再重试.

if(errno==EAGAIN)A{AUSleep(10

00);

continue;

return-1;A}

4if((size_t)tmp==tota1)Areturnbufien;

Atota1-=tmp;

p+=tmp;

}greturntmp;

)

在linux的网络编程中,很长的时间都在使用select来做事件触发。在li

nux新的内核中,有了一种替换它的机制,就是epoll。A相比于selec

t,epoll最大的好处在于它不会随着监听fd数目的增长而减少效率。由于在

内核中的select实现中,它是采用轮询来解决的,轮询的fd数目越多,自然

耗时越多。并且,在1inux/posix_types.h头文献有这样的声明:&#de

fine_FD_SETSIZE1024A表达select最多同时监听1024个

fd,当然,可以通过修改头文献再重编译内核来扩大这个数目,但这似乎并

不治本。

aepol1的接口非常简朴,一共就三个函数:

1.intepo1lereate(intsize);A创建一个epoll的句柄,size

用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第

一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好ep。11句

柄后,它就是会占用一个fd值,在1inux下假如查看/proc/进程id/fd/,是

可以看到这个fd的,所以在使用完epol1后,必须调用close。关闭,否则也

许导致fd被耗尽。山2.intepo1l^ctl(intepfd,intop,intfd,

structepoll_event*event);

epoll的事件注册函数,它不同与seiect()是在监听事件时告诉内核要监

听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epo1

l_create()的返回值,第二个参数表达动作,用三个宏来表达:AEPOLL_CT

L_ADD:注册新的fd到epfd中;AEP0LL_CTL_M0D:修改已经注册的fd的监

听事件;

EPOLL_CTL_DEL:从epfd中删除一个fd;

第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct

epol1_event结构如下:

structepo1l_event{A_uint32_tevents;/*Epo11

events*/Aepo11_data_tdata;/*Userdatavariable*/

A};

^events可以是以下几个宏的集合:

EP0LLIN:表达相应的文献描述符可以读(涉及对端SOCKET正常关闭);

AEPOLLOUT:表达相应的文献描述符可以写;

EPOLLPRI:表达相应的文献描述符有紧急的数据可读(这里应当表达有带外数

据到来);AEPOLLERR:表达相应的文献描述符发生错误;

EPOLLHUP:表达相应的文献描述符被挂断;

EP0LLET:将EPOLL设为边沿触发(EdgeTriggered)模式,这是相对于

水平触发(Leve1Triggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,假如还需要继续监

听这个socket的话,需要再次把这个socket加入到EPOLL队列里

A3.intepoll_wait(intepfd,structepo11_event*eve

nts,intmaxevents,inttimeout);A等待事件的产生,类似于

select()调用。参数events用来从内核得到事件的集合,maxevents

告之内核这个events有多大,这个maxevents的值不能大于创建epoil—

ereate()时的size,参数timeout是超时时间(毫秒,0会立即返回,-

1将不拟定,也有说法说是永久阻塞)。该函数返回需要解决的事件数目,如返

回0表达已超时。

从man手册中,得到ET和LT的具体描述如下

AEPOLL事件有两种模型:

EdgeTriggered(ET)

Leve1Triggered(LT)g假如有这样一个例子:

1.我们已经把一个用来从管道中读取数据的文献句柄(RFD)添加到epoll

描述符

2.这个时候从管道的另一端被写入了2KB的数据

3.调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作

4.然后我们读取了1KB的数据

5.调用epoll_wait(2)....

EdgeTriggered工作模式:A假如我们在第1步将RFD添加到epoll描

述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait(2)之后将有

也许会挂起,由于剩余的数据还存在于文献的输入缓冲区内,并且数据发出端还

在等待一个针对已经发出数据的反馈信息。只有在监视的文献句柄上发生了某

个事件的时候ET工作模式才会报告事件。因此在第5步的时候,调用者也许会

放弃等待仍在存在于文献输入缓冲区内的剩余数据。在上面的例子中,会有一个

事件产生在RFD句柄上,由于在第2步执行了一个写操作,然后,事件将会在第

3步被销毁。由于第4步的读取操作没有读空文献输入缓冲区内的数据,因此我

们在第5步调用epoll_wait(2)完毕后,是否挂起是不拟定的。epoll工

作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文献句柄的阻塞

读/阻塞写操作把解决多个文献描述符的任务饿死。最佳以下面的方式调用ET

模式的epoll接口,在后面会介绍避免也许的缺陷。

i基于非阻塞文献句柄

ii只有当read⑵或者write(2)返回EAGAIN时才需要挂起,

等待。但这并不是说每次read。时都需要循环读,直到读到产生一个EAGAIN

才认为本次事件解决完毕,当read()返回的读到的数据长度小于请求的数据

长度时,就可以拟定此时缓冲中已没有数据了,也就可以认为此事读事件已解

决完毕。3

LevelTriggered工作模式A相反的,以LT方式调用epo11接口的时候,

它就相称于一个速度比较快的pol1(2),并且无论后面的数据是否被使用,

因此他们具有同样的职能。由于即使使用ET模式的epoll,在收到多个chu

nk的数据的时候仍然会产生多个事件。调用者可以设定EPOLLONESHOT标

志,在epoll_wait(2)收到事件后epoll会与事件关联的文献句柄从

epoll描述符中严禁掉。因此当EPOLLONESHOT设定后,使用带有

EP0LL_CTL_M0D标志的epoll_ctl(2)解决文献句柄就成为调用者必须作

的事情。

她然后具体解释ET,LT:

△LT(leve1triggered)是缺省的工作方式,并且同时支持b1ock和no-

blocksocket.在这种做法中,内核告诉你一个文献描述符是否就绪了,然后

你可以对这个就绪的fd进行10操作。假如你不作任何操作,内核还是会继续

告知你的,所以,这种模式编程犯错误也许性要小一点。传统的select/po

11都是这种模型的代表.

ET(edge-triggered)是高速工作方式,只支持no-blocksocket。在这

种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假

设你知道文献描述符已经就绪,并且不会再为那个文献描述符发送更多的就绪

告知,直到你做了某些操作导致那个文献描述符不再为就绪状态了(比如,你在发

送,接受或者接受请求,或者发送接受的数据少于一定量时导致了一个EW0ULD

BLOCK错误)。但是请注意,假如一直不对这个fd作10操作(从而导致它再

次变成未就绪),内核不会发送更多的告知(onlyonce),但是在TCP协议

中,ET模式的加速效用仍需要更多的benehmark确认(这句话不理解)。

在许多测试中我们会看到假如没有大量的idle-connection或者dead-c

onnection,epoll的效率并不会比select/poll高很多,但是当我们碰到

大量的idle-connection(例如WAN环境中存在大量的慢速连接),就会发

现epoil的效率大大高于seiect/poil。(未测试)A

M此外,当使用epo11的ET模型来工作时,当产生了一个EP0LLIN事件

后,&读数据的时候需要考虑的是当recv()返回的大小假如等于请求的大

小,那么很有也许是缓冲区尚有数据未读完,也意味着该次事件还没有解决完,

所以还需要再次读取:Awhile(rs)

{4buflen=recv(activeevents[i].data.fd,buf,sizeof(buf),

0);Aif(buflen<0)

{A//由于是非阻塞的模式,所以当errn。为EAGAIN时,表达

当前缓冲区已无数据可读

//在这里就当作是该次事件已解决处.劣if(errno==

EAGAIN)Abreak;

else

return;

}Ae1seif(buflen==0)A{

//这里表达对端的socket已正常关闭.

}Aif(buf1en==sizeof(buf)Ars=

1;//需要再次读取

e1seArs=0;

}

A

尚有,假如发送端流量大于接受端的流量(意思是epoll所在的程序读比转发

的socket要快),由于是非阻塞的socket,那么send()函数虽然返回,但实

际缓冲区的数据并未真正发给接受端,这样不断的读和发,当缓冲区满后会产生

EAGAIN错误(参考mansend),同时,不理睬这次请求发送的数据.所以,需要

封装s。cket_send()的函数用来解决这种情况,该函数会尽量将数据写完

再返回,返回T表达犯错。在socket_send()内部,当写缓冲已满(send()

返回-1,且errno为EAGAIN),那么会等待后再重试.这种方式并不很完美,在理

论上也许会长时间的阻塞在socket_send()内部,但暂没有更好的办法.3

ssize_tsocket_send(intsockfd,constchar*buffer,

size_tbuflen)A{

ssize_ttmp;

size_ttotal=buf1en;Aconstchar*p=buffer;

gwhile(l)

{Atmp=send(sockfd,p,totai,0);Aif

(tmp<0)

(A//当send收到信号时,可以继续写,但这里返回一1.

if(errno==EINTR)

return-1;

A//当socket是非阻塞时,如返回此错误,表达写缓冲队列已满,

//在这里做延时后再重试.Aif(errno==EAGAIN)

A

us1eep(1000);Acontinue;A

return-1;

}aif((size_t)tmp==tota1)Areturn

buflen;3total—=tmp;

p+=tmp;

}

4returntmp;A}

epoll有两种模式,EdgeTriggered(简称ET)和LevelTriggered(简

称LT).在采用这两种模式时要注意的是,假如采用ET模式,那么仅当状态发生

变化时才会告知,而采用L

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论