什么是epollWord文件下载.docx
- 文档编号:987174
- 上传时间:2023-04-29
- 格式:DOCX
- 页数:27
- 大小:29.92KB
什么是epollWord文件下载.docx
《什么是epollWord文件下载.docx》由会员分享,可在线阅读,更多相关《什么是epollWord文件下载.docx(27页珍藏版)》请在冰点文库上搜索。
//感兴趣的事件和被触发的事件
10.struct
epoll_event
11.
events;
/*
Epoll
events
*/
12.
epoll_data_t
data;
User
data
variable
13.};
events可以是以下几个宏的集合:
EPOLLIN
:
表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:
表示对应的文件描述符可以写;
EPOLLPRI:
表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:
表示对应的文件描述符发生错误;
EPOLLHUP:
表示对应的文件描述符被挂断;
EPOLLET:
将EPOLL设为边缘触发(EdgeTriggered)模式,这是相对于水平触发(LevelTriggered)来说的。
EPOLLONESHOT:
只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
3.intepoll_wait(intepfd,structepoll_event*events,intmaxevents,inttimeout);
收集在epoll监控的事件中已经发送的事件。
参数events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。
maxevents告之内核这个events有多大,这个
maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。
如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时。
epoll工作原理
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。
在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
Epoll的2种工作方式-水平触发(LT)和边缘触发(ET)
假如有这样一个例子:
1.我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符
2.这个时候从管道的另一端被写入了2KB的数据
3.调用epoll_wait
(2),并且它会返回RFD,说明它已经准备好读取操作
4.然后我们读取了1KB的数据
5.调用epoll_wait
(2)......
EdgeTriggered工作模式:
如果我们在第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
(2)或者write
(2)返回EAGAIN时才需要挂起,等待。
但这并不是说每次read()时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read()返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。
LevelTriggered工作模式
相反的,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll
(2),并且无论后面的数据是否被使用,因此他们具有同样的职能。
因为即使使用ET模式的epoll,在收到多个chunk的数据的时候仍然会产生多个事件。
调用者可以设定EPOLLONESHOT标志,在epoll_wait
(2)收到事件后epoll会与事件关联的文件句柄从epoll描述符中禁止掉。
因此当EPOLLONESHOT设定后,使用带有EPOLL_CTL_MOD标志的epoll_ctl
(2)处理文件句柄就成为调用者必须作的事情。
LT(leveltriggered)是epoll缺省的工作方式,并且同时支持block和no-blocksocket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。
如果你不作任何操作,内核还是会继续通知你
的,所以,这种模式编程出错误可能性要小一点。
传统的select/poll都是这种模型的代表.
ET(edge-triggered)是高速工作方式,只支持no-blocksocket,它效率要比LT更高。
ET与LT的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。
而LT模式正好相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。
因此,LT模式下开发基于epoll的应用要简单些,不太容易出错。
而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。
Nginx默认采用ET模式来使用epoll。
epoll的优点:
1.支持一个进程打开大数目的socket描述符(FD)
select
最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是2048。
对于那些需要支持的上万连接数目的IM服务器来说显然太少了。
这时候你一是可以选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降,二是可以选择多进程的解决方案(传统的
Apache方案),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。
不过
epoll则没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat/proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。
2.IO效率不随FD数目增加而线性下降
传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是"
活跃"
的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。
但是epoll不存在这个问题,它只会对"
的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。
那么,只有"
的socket才会主动的去调用
callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个"
伪"
AIO,因为这时候推动力在os内核。
在一些
benchmark中,如果所有的socket基本上都是活跃的---比如一个高速LAN环境,epoll并不比select/poll有什么效率,相反,如果过多使用epoll_ctl,效率相比还有稍微的下降。
但是一旦使用idleconnections模拟WAN环境,epoll的效率就远在select/poll之上了。
3.使用mmap加速内核与用户空间的消息传递
这点实际上涉及到epoll的具体实现了。
无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。
而如果你想我一样从2.5内核就关注epoll的话,一定不会忘记手工
mmap这一步的。
4.内核微调
这一点其实不算epoll的优点了,而是整个linux平台的优点。
也许你可以怀疑linux平台,但是你无法回避linux平台赋予你微调内核的能力。
比如,内核TCP/IP协议栈使用内存池管理sk_buff结构,那么可以在运行时期动态调整这个内存pool(skb_head_pool)的大小---
通过echoXXXX>
/proc/sys/net/core/hot_list_length完成。
再比如listen函数的第2个参数(TCP完成3次握手的数据包队列长度),也可以根据你平台内存大小动态调整。
更甚至在一个数据包面数目巨大但同时每个数据包本身大小却很小的特殊系统上尝试最新的NAPI网卡驱动架构。
linux下epoll如何实现高效处理百万句柄的
开发高性能网络程序时,windows开发者们言必称iocp,linux开发者们则言必称epoll。
大家都明白epoll是一种IO多路复用技术,可以非常高效的处理数以百万计的socket句柄,比起以前的select和poll效率高大发了。
我们用起epoll来都感觉挺爽,确实快,那么,它到底为什么可以高速处理这么多并发连接呢?
使用起来很清晰,首先要调用epoll_create建立一个epoll对象。
参数size是内核保证能够正确处理的最大句柄数,多于这个最大数时内核可不保证效果。
epoll_ctl可以操作上面建立的epoll,例如,将刚建立的socket加入到epoll中让其监控,或者把
epoll正在监控的某个socket句柄移出epoll,不再监控它等等。
epoll_wait在调用时,在给定的timeout时间内,当在监控的所有句柄中有事件发生时,就返回用户态的进程。
从上面的调用方式就可以看到epoll比select/poll的优越之处:
因为后者每次调用时都要传递你所要监控的所有socket给select/poll系统调用,这意味着需要将用户态的socket列表copy到内核态,如果以万计的句柄会导致每次都要copy几十几百KB的内存到内核态,非常低效。
而我们调用epoll_wait时就相当于以往调用select/poll,但是这时却不用传递socket句柄给内核,因为内核已经在epoll_ctl中拿到了要监控的句柄列表。
所以,实际上在你调用epoll_create后,内核就已经在内核态开始准备帮你存储要监控的句柄了,每次调用epoll_ctl只是在往内核的数据结构里塞入新的socket句柄。
当一个进程调用epoll_creaqte方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关:
1./*
3.
171
*
This
structure
is
stored
inside
the
"
private_data"
member
of
file
172
and
represents
main
for
eventpoll
173
interface.
8.
174
10.
175struct
13.
176
Protect
access
to
this
14.
15.
177
spinlock_t
lock;
16.
17.
178
18.
19.
179
20.
21.
180
mutex
used
ensure
that
files
are
not
removed
22.
23.
181
while
epoll
using
them.
held
during
event
24.
25.
182
collection
loop,
cleanup
path,
exit
26.
27.
183
code
ctl
operations.
28.
29.
184
30.
31.
185
struct
mtx;
32.
33.
186
34.
35.
187
Wait
queue
by
sys_epoll_wait()
36.
37.
188
wait_queue_head_t
wq;
38.
39.
189
40.
41.
190
file->
poll()
42.
43.
191
poll_wait;
44.
45.
192
46.
47.
193
List
ready
descriptors
48.
49.
194
list_head
rdllist;
50.
51.
195
52.
53.
196
RB
tree
root
store
monitored
fd
structs
54.
55.
197
rb_root
rbr;
//红黑树根节点,这棵树存储着所有添加到epoll中的事件,也就是这个epoll监控的事件
56.
198
57.
199
58.
200
a
single
linked
list
chains
all
epitem"
59.
201
happened
transferring
userspace
w/out
60.
202
holding
->
lock.
61.
203
62.
204
epitem
*ovflist;
63.
205
64.
206
wakeup_source
when
ep_scan_ready_list
running
65.
207
*ws;
66.
208
67.
209
The
user
created
descriptor
68.
210
user_struct
*user;
69.
211
70.
212
*file;
71.
213
72.
214
optimize
loop
detection
check
73.
215
visited;
74.
216
visited_list_link;
//双向链表中保存着将要通过epoll_wait返回给用户的、满足条件的事件
75.
217};
每一个epoll对象都有一个独立的eventpoll结构体,这个结构体会在内核空间中创造独立的内存,用于存储使用epoll_ctl方法向epoll对象中添加进来的事件。
这样,重复的事件就可以通过红黑树而高效的识别出来。
在epoll中,对于每一个事件都会建立一个epitem结构体:
130
Each
added
interface
will
131
have
an
entry
type
rbr"
tree.
132
Avoid
increasing
size
struct,
there
can
be
many
thousands
133
these
on
server
we
do
want
take
another
cache
line.
134
135struct
136
node
link
137
rb_node
rbn;
138
139
header
140
rdllink;
141
142
143
Works
together
eventpoll"
ovflist
in
keeping
144
chain
items.
145
146
*next;
147
148
information
item
refers
149
epoll_filefd
ffd;
150
151
Number
active
wait
attached
poll
operations
152
nwait;
153
154
containing
queues
155
pwqlist;
156
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 什么是 epoll