select poll epoll相关知识速记
缘起
面试的时候经常被问的一个很蛋疼的问题,经常被问,但是知识很零散,难记忆,看完就忘
select
作用
可以监视文件描述符是否可以读写,要求监视的文件描述符是非阻塞的
诞生背景
产生与上个世纪80年代的UNIX系统,到1993年写入POSIX1.b规范(一个操作系统的编程接口的规范,你要是写个操作系统想被兼容得遵守这个规范)。由于那个年代还没有多线程(2年后线程相关的内容才写入POSIX1.c规范),还没有什么C10K问题,所以在设计select的时候体现了那个年代的特点。
接口
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds 是参数2,3,4中最大的文件描述符 + 1
readfds 是要检测fd的可读事件,当fd可读的时候select就返回
writefds 是要检测fd的写事件,当fd可写的时候select就返回
excpetfds 是要检测fd的出错事件,当fd出错的时候select就返回,当是NULL的时候,就是检测readfds和writefds的出错事件,所以一般都写NULL
timeout 是一个纳秒级的超时时间
想使用这个函数需要设置个fd_set结构,所以就用到void FD_SET(int fd, fd_set *set);这个宏,用来设置fd_set
使用过程
fd_set fd_in, fd_out;
struct timeval tv; // 初始化fd_set
FD_ZERO( &fd_in );
FD_ZERO( &fd_out ); // 把网络IO复制到fd_set
FD_SET( sock1, &fd_in );
FD_SET( sock1, &fd_out ); // 用户初始化select
int largest_sock = sock1 > sock2 ? sock1 : sock2; // 设置select的超时时间
tv.tv_sec = ;
tv.tv_usec = ; // 调用select 并阻塞在这里等待 IO事件
int ret = select( largest_sock, &fd_in, &fd_out, NULL, &tv ); // 检查返回值的状态
if ( ret == - )
// 异常情况
else if ( ret == )
// 超时或者没有可以监控的fd
else
{
// 检测每个IO事件是否可以读写
if ( FD_ISSET( sock1, &fd_in ) )
// IO可读 if ( FD_ISSET( sock2, &fd_out ) )
// IO可写
}
可以看到使用select的时候,每个fd对应一个fd_set结构,然后调用FD_SET,调用select以后进入polling,等返回以后通过FD_ISSET对每个fd_set检测是否可读可写。
存在问题
是不是会儿还没有现在nginx几万并发的场景,select只能对1024个fd进行监控
select 函数会修改fd_set,所以每次调用完select以后需要重新通过FD_SET设置fd_set
select 返回以后并不知道具体哪个fd可以读写,需要使用FD_ISSET把所有的fd检测一遍才知道具体是哪个可读可写
那年代估计不像现在这么广泛的用多线程,所以select中的fd_set在调用select的时候相当于被独占的
优点
使用简单,POSIX标准所以跨平台比较好
POLL
功能和select相同,但是主要解决select的一些限制
接口
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
fds 是一个pollfd的数组,和select的fd_set差不多,下面具体解释
nfds 是fds数组的长度,可以看到没有select还需要求一个fd最大值再加1那么麻烦
timeout 是超时的毫秒数
pollfd的结构
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 需要监听的事件 */
short revents; /* 返回的事件 */
};
对比一下select,接口上更加优雅,首先,pollfd 通过单独的events来区分了监控的是什么样的事件,而不是像select那样通过参数来区分。
使用过程
// 创建pollfd
struct pollfd fds[]; // 设置pollfd的fd和要监控的事件,sock1监控读,sock2监控写
fds[].fd = sock1;
fds[].events = POLLIN;
fds[].fd = sock2;
fds[].events = POLLOUT; // 10秒超时,开始等待sock1和sock2上的事件
int ret = poll( &fds, , );
// 有事件返回
if ( ret == - )
// 出错了
else if ( ret == )
// 超时
else
{
// 对每个pollfd检测是否有就绪的事件
if ( pfd[].revents & POLLIN )
pfd[].revents = ;
// 可读 if ( pfd[].revents & POLLOUT )
pfd[].revents = ;
// 可写
}
与select相同,都是创建结构,设置,开始polling,逐个检测事件
相比于SELECT的改进
对于可以监控fd的数量没有限制,而不是像select那样最大才1024个
每次poll之后不需要重新设置pollfd,而不像fd_set需要重新设置
兼容性
vista之前的windows上没有poll
#if defined (WIN32)
static inline int poll( struct pollfd *pfd, int nfds, int timeout) { return WSAPoll ( pfd, nfds, timeout ); }
#endif
EPOLL
linux平台上最新的polling技术,出现与linux2.6版本,linux2.6发布是在2003年(居然epoll出现已经12年了)。
接口
int epoll_create(int size);
用于创建一个size大小的epoll,返回一个epfd的描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
修改某个文件描述符的状态
epfd 是创建的epoll
op 是修改的操作类型,可以是EPOLL_CTL_ADD 或者 EPOLL_CTL_DEL,代表添加和删除
fd 是要操作的文件描述符
event 是文件描述符fd上挂的一个context,是一个epoll_event结构体,下面是epoll_event的结构体内容:
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 */
};
其中epoll_event.events 和 pollfd中的events 差不多,不过事件更加丰富,data是对于文件描述符上可以挂的卫星数据,也更加灵活。
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
epdf 是epoll_create时候返回的
events 是polling 返回的结果会赋值在events
maxevents 是一次通知用户最大的events数量,一般就是events的数组长度
timeout 是超时时间
相比select/poll,epoll_wait 只返回可读写的事件,而不是全部返回
关于几个size的理解
epoll_create 时候的size是指 epoll在核心态监控fd的最大数量
epoll_wait 时候的events 是指一次通知的数据,这个数量认为是一次批量,肯定是小于等于epoll_create时候的大小,比这个再大也没用了
epoll_wait 时候的maxevents 是为了防止一次通知events溢出的一个边界,如果设置的比events的数组长度小,那就相当于批量变小,比这个大会溢出,所以应该是相等就可以了
使用过程
// 创建
int pollingfd = epoll_create( 0xCAFE ); // 创建一个epoll_event 用来一会儿epoll_ctl的时候EPOLL_CTL_ADD用
struct epoll_event ev = { }; // 假设sock1是个网络连接
int sock1 = pConnection1->getSocket(); // 给这个sock1挂一点卫星数据,这里可以是任意的东西,我们就放个他的connection
ev.data.ptr = pConnection1; // 来监控sock1的可读事件
ev.events = EPOLLIN | EPOLLONESHOT; // 把设置好的epoll_event 添加到创建的epollfd
if ( epoll_ctl( epollfd, EPOLL_CTL_ADD, sock1, &ev ) != )
// 出错 // 创建一些epoll_event用来在用户态来接收
struct epoll_event pevents[ ]; // 等待可读事件
int ready = epoll_wait( pollingfd, pevents, , );
if ( ret == - )
// 出错
else if ( ret == )
// 超时
else
{
// ret是返回了多少个可以读写的事件
for ( int i = ; i < ret; i++ )
{
// 判断通知到用户这个到底是个什么事件
if ( pevents[i].events & EPOLLIN )
{
// 取到当初我们挂在上面的卫星数据
Connection * c = (Connection*) pevents[i].data.ptr;
// 对这个socket进行一些操作
c->handleReadEvent();
}
}
}
epoll的使用过程还是比select和poll复杂不少的,首先你得创建一个epoll,然后创建和设置epoll_event,再通过epoll_ctl添加到epoll,最后epoll_wait,遍历通知过来的events
比select和poll的改进
最大的改进就是不需要在遍历所有事件了,不需要FD_ISSET,也不需要遍历所有pollfd.revents,取而代之的是,内核帮我们把active的fd赋值到epoll_wait的events上
pollfd封装了一个event,而不是像select的fd_set只有一个fd属性,epoll_event 比pollfd又多了一个data的卫星数据,可以放任意的东西上去
select和poll一旦进入polling阶段,就没法对fd做修改了,但是epoll_ctl可以在任意的线程里在任意事件动态的添加,删除epoll_event
缺点
改变epoll中fd的监听事件类型需要epoll_ctl的系统调用,而在poll中只需要在用户态做BITMASK
只能在linux上用,虽然有libevent这种东西
epoll的api比select和poll复杂
该如何选择
如果连接数很低小于1024,epoll对比select和poll是没有性能提升的,选择select还是poll就看个人喜好了,一般select就行,比如fpm,epoll早就出了fpm也没改,PHP很少有人能worker开1000以上
如果连接是短连接,经常accept出一些fd添加到epoll中的系统调用开销需要考虑,具体性能还需要再综合考虑,比如nginx,虽然都是短连接,但是有高并发,几万并发select每次遍历一遍所有fd更耗
如果是长连接,并且都是idle的,例如一些聊天的服务器,一个连接,半天才说一句话,都是挂机的,但是连接几十万,那有个人说句话,服务区需要读,你遍历几十万个fd就不值了
如果你的应用是多线程来处理网络的,那么为了利用多线程还是使用epoll比较好,可以用多线程配合边缘触发(如果可读只通知一次,不管读完没读完,水平触发没读完就一直通知,所以效率会比边缘触发低一些),这也是边缘触发推荐的使用方式。
为什么epoll高效
简单来说是这样的select和poll当检测到fd就绪以后,就通知到用户态了,函数也就返回了。而epoll在add的时候就开始监听,发现他就绪以后就放到一个就绪表里,epoll_wait只是定时查看一下这个就绪表里的数据。
参考文章
https://cs.uwaterloo.ca/~brecht/papers/getpaper.php?file=ols-2004.pdf
http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/
http://www.unix.org/what_is_unix/history_timeline.html
http://en.wikipedia.org/wiki/Asynchronous_I/O
http://en.wikipedia.org/wiki/POSIX
http://blog.csdn.net/vividonly/article/details/7539342
select poll epoll相关知识速记的更多相关文章
- select, poll, epoll的实现分析
select, poll, epoll都是Linux上的IO多路复用机制.知其然知其所以然,为了更好地理解其底层实现,这几天我阅读了这三个系统调用的源码. 以下源代码摘自Linux4.4.0内核. 预 ...
- 转--select/poll/epoll到底是什么一回事
面试题:说说select/poll/epoll的区别. 这是面试后台开发时的高频面试题,属于网络编程和IO那一块的知识.Android里面的Handler消息处理机制的底层实现就用到了epoll. 为 ...
- Python之路-python(Queue队列、进程、Gevent协程、Select\Poll\Epoll异步IO与事件驱动)
一.进程: 1.语法 2.进程间通讯 3.进程池 二.Gevent协程 三.Select\Poll\Epoll异步IO与事件驱动 一.进程: 1.语法 简单的启动线程语法 def run(name): ...
- Python自动化 【第十篇】:Python进阶-多进程/协程/事件驱动与Select\Poll\Epoll异步IO
本节内容: 多进程 协程 事件驱动与Select\Poll\Epoll异步IO 1. 多进程 启动多个进程 进程中启进程 父进程与子进程 进程间通信 不同进程间内存是不共享的,要想实现两个进程间 ...
- select.poll,epoll的区别与应用
先讲讲同步I/O的五大模型 阻塞式I/O, 非阻塞式I/O, I/O复用,信号驱动I/O(SIGIO),异步I/O模型 而select/poll/epoll属于I/O复用模型 select函数 该函数 ...
- select poll epoll三者之间的比较
一.概述 说到Linux下的IO复用,系统提供了三个系统调用,分别是select poll epoll.那么这三者之间有什么不同呢,什么时候使用三个之间的其中一个呢? 下面,我将从系统调用原型来分析其 ...
- 笔记-select,poll,epoll
笔记-select,poll,epoll 1. I/O多路复用 I/O多路复用是指:通过一种机制或一个进程,可以监视多个文件描述符,一旦描述符就绪(写或读),能够通知程序进行相应的读写操作. ...
- Linux内核中网络数据包的接收-第二部分 select/poll/epoll
和前面文章的第一部分一样,这些文字是为了帮别人或者自己理清思路的.而不是所谓的源代码分析.想分析源代码的,还是直接debug源代码最好,看不论什么文档以及书都是下策. 因此这类帮人理清思路的文章尽可能 ...
- Linux IO模式以及select poll epoll详解
一 背景 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network ...
随机推荐
- ansile 命令解释选项
1, -a MODULE_ARGS --args=MODULE_ARGS 作用传递参数给模块使用 2, --ask-vault-pass 执行时询问vault的密码 3, -B SECONDS --b ...
- (转)shell脚本之文件测试操作符及整数比较符
shell脚本之文件测试操作符及整数比较符 原文:http://www.cnblogs.com/Steward-Xu/p/6722592.html 一.文件测试操作符: 在书写测试表达式是,可以使用一 ...
- elasticsearch清空type下的所有数据
DELETE /twitter/tweet/_query{"query": { "match_all": {} }} 注:twitter为index,tweet ...
- IE7不兼容slideDown()
IE7下,使用slideDown()方法,可能出现以下两种问题: 一.下拉动画变形,最终定格时正常 二.下拉动画正常,最终定格时消失
- (一)安装Python
一.安装python 打开 Python官网,找到“Download”, 在其下拉菜单中选择自己的平台(Windows/Mac),一般的Linux平台已经自带的Python,所以不需要安装,通过打开“ ...
- Video标签事件与属性
事件与属性 属性 描述 audioTracks 返回可用的音轨列表(MultipleTrackList对象) autoplay 媒体加载后自动播放 buffered 返回缓冲部件的时间范围(TimeR ...
- avalon实现分页组件
前言 分页组件比较常见,但是用avalon实现的见的不多,这个分页组件,可以适配2种分页方式, 第一种是每次点击下一页,就请求一次后台,并返回当页数据和总条数,我称之为假分页: 第二种是一次性把所有数 ...
- java程序: 从kernel.ubuntu.com下载kernel - HttpURLConnection
用java实现从kernel.ubuntu.com下载内核的小工具 现在的最新的LTS版本是4.19,目前已经更新到4.19.13了. Kernel的更新通知里,经常有下面的陈述: 作为一个听劝的人, ...
- python的if语句
1.条件测试 (1)概念: 每条if语句的核心都是一个值为True或False的表达式,这种表达式被称为条件测试.Python 根据条件测试的值为True还是False来决定是否执行if语句中的代码. ...
- SGI STL红黑树中迭代器的边界值分析
前言 一段程序最容易出错的就是在判断或者是情况分类的边界地方,所以,应该对于许多判断或者是情况分类的边界要格外的注意.下面,就分析下STL中红黑树的迭代器的各种边界情况.(注意:分析中STL使用的版本 ...