异想天开

What's the true meaning of light, Could you tell me why

linux下的epoll

日期:2015-04-11 11:47:47
  
最后更新日期:2017-09-08 18:09:09
【技术】
对于目前的复用IO的模型主要有select/poll,epoll。对于不是所有的连接都活跃的情况下,epoll比select和poll有优势的地方在于:epoll只会拷贝有事件(可读/可写/错误)发生的套接字。epoll的事件定义的结构体如下:
[code lang="cpp"]
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;

struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
[/code]
epoll_data定义是个联合体,添加时可以使用套接字fd,还是存放一个指针进去作为事件的标识符。网络上epoll源码和目前man手册也是用fd作为标识符,故这里需要注意还可以自定义自己的事件结构,用fd作为标识符是简单需要。看下epoll_ctl的接口: [code lang="cpp"]
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
[/code]
op支持3个命令EPOLL_CTL_ADD,EPOLL_CTL_MOD,EPOLL_CTL_DEL对应为在epoll的实例中添加事件,修改事件,删除事件。使用epoll的流程大概,服务端首先往epoll实例里面注册监测的事件,然后epoll_wait。
[code lang="cpp"]
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
[/code]
一.什么是可读事件和可写事件?
个人的看法,未求证。可读就是新建立连接的时候或套接字缓冲有内容过来了,哪怕是只有一个字节,也会触发可读事件。可写事件,就是当输出缓冲有一定空间时就会触发可写事件,这个时间点比如第一次创建socket时候,会分配输出缓冲,还是一下写入的数据超过了输出缓冲的大小,比如1M,内核会分几次写入,当缓冲空间有足够大小时(这个值应该可以设置),就有一个可写事件。对于可写事件,我是有疑惑的,怎么利用可写事件呢?
[code lang="cpp"]
int fd=socket(...);
int efd=epoll_create(...);
struct epoll_event event;
struct foo{
int fd;
void (*callback)();
};
struct foo bar;
bar.fd=fd;
bar.callback=my_print;
event.ptr=&bar;
epoll_ctl(efd,EPOLL_CTL_ADD,fd,event);
[/code]
创建socket会分配接收和发送缓冲,然后添加到epoll的实例,后面会触发epoll_wait一个可写事件的。这层机制也许是内核对套接字有个标记,epoll_wait的时候,会检测这个标记。当发送缓冲可用时有可写事件怎么利用?
<----------------------百度一番后的答案---------------------------------->
当使用非阻塞的socket时,一次性写大块数据时,write操作会返回实际写入的数据的大小,当发送缓冲空间又变足够时,就会触发可写事件。
会不会有这样的问题,假设发送缓冲只有128k,可用空间大于96k时会有可写事件,两种情况:
1.创建socket的时候
2.写入了发送缓冲100k数据,内核发送完这个100k数据后
水平触发时,当触发可写事件时,会不停的触发可写事件,所以这个需要根据逻辑,判断如果没有可写的数据块,那么需要屏蔽掉可写事件。

二.水平触发和边沿触发
区别在于边沿触发在于套接字的缓冲内容发生变化时,才会触发事件,而水平触发,是只要套接字缓冲有内容时,就会触发事件。水平模式是默认的模式,所以没有EPOLLLT这个值。
三. 注意事项
一般添加事件时,不添加EPOLLERR或EPOLLHUB事件,也会默认触发的。一个合理的epoll应该考虑fd是socket fd,listen fd, fifo fd等情况。对于fifo fd接收到EPOLLHUB事件时,不应该关闭fifo fd, 因为对方发生了异常导致关闭了写端,而作为读端应该持续等对端连上恢复过来。