I/O多路复用模型select,poll,epoll原理分析及对比

一、什么是io多路复用
在bio模型中,一个io请求对应一个线程,造成线程极大浪费,且没有数据发送的情况下,线程也一直阻塞等待,资源利用率不高。
在nio模型中,多个io请求归一个线程管理,当io就绪的时候,线程通知应用程序进行数据读写,这样不用创建很多线程,线程也不用一直阻塞等待io就绪,这样的技术称为io多路复用。
io多路复用通过一种机制,可以监视多个文件描述符,一旦某个文件描述就绪(),能够通知应用程序进行相应的io读写操作。
二、为什么用io多路复用
io多路复用解决了多线程io阻塞问题,避免创建很多线程,及降低线程上下文切换的开销,提升资源利用率。
三、io多路复用模型原理分析
监视多个文件描述符的机制不同,io多路复用技术主要有select,poll,epoll,本质上都是阻塞io,因为他们都需要在io就绪后,自己负责进行数据在用户空间和内核空间之间copy。非阻塞io无需自己负责数据copy,操作系统会把数据从内核空间copy到用户空间。
1.select:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
监视多个文件描述符的属性变化(可读、可写、错误异常)。select监视的文件描述符有3类:writefds,readfds,execeptfds,调用select 线程会阻塞,知道有描述符就绪,才返回,之后通过遍历fdset来找到就绪的描述符。
nfds: 要监视的文件描述符的范围,在 Linux 上最大值一般为1024。
readfd: 监视的可读描述符集合,只要有文件描述符即将进行读操作,这个文件描述符就存储到这。
writefds: 监视的可写描述符集合,只要有文件描述符即将进行写操作,这个文件描述符就存储到这里。
exceptfds: 监视的错误异常描述符集合。
timeout: 超时时间,它告知内核等待任何一个文件描述符就绪可花多少时间。有3中设置:
1)永远等待,仅有一个描述符就绪后返回;
2)不等待,检查所有描述符看其是否就绪,是一种轮训方式,返回就绪的个数;
3)等待固定时间,返回就绪的个数。
优点:所有平台都支持,不存在兼容问题。
缺点:1)每次调用select,都要把fe集合从用户态copy到内核态,fd很多时,开销很大。同时在内核态要遍历fd,看其是否就绪,开销也很大。
2.poll:int poll(struct pollfd fds, nfds_t nfds, int timeout);
poll的本质和select一样,没有太大差别,管理多个描述符也是进行轮询,根据描述符状态进行处理,但poll没有最大描述符个数限制(select限制1024个),但是随着数量的增加,性能也有下降。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
fds:不同于 select() ,使用三个位置来表示三个 fd集合,poll() 使用一个 pollfd 的指针,指向一个 pollfd 结构体数组,结构体中包括了要监视的文件描述符和事件,感兴趣的事件由结构中 events 来确定,调用后实际发生的时间将被填写在结构体的 revents 域中,如下:
struct pollfd{
int fd; /
文件描述符 /
short events; /
感兴趣的事件 /
short revents; /
实际发生了的事件 */
};
nfds: 指定第一个参数数组元素个数,并不是限制监视的文件描述符个数,poll不限制文件描述符个数。
timeout: 指定等待的毫秒数,无论 I/O 是否就绪,poll() 都会返回。当等待时间为 0 时,poll() 函数立即返回,为 -1 则使 poll() 一直阻塞直到一个感兴趣的事件发生后返回。
poll() 返回结构体中 revents 域不为 0 的文件描述符个数;如果在超时前没有任何事件发生,poll()返回 0;
返回-1说明有错误发生,具体错误有以下集中:
EBADF:一个或多个结构体中指定的文件描述符无效。
EFAULT:fds 指针指向的地址超出进程的地址空间。
EINTR:请求的事件之前产生一个信号,调用可以重新发起。
EINVAL:nfds 参数超出 PLIMIT_NOFILE 值。
ENOMEM:可用内存不足,无法完成请求。
poll和select非常类似,只是fd集合的方式不同,poll使用结构体数组,select用set,其他都差不多。
3.epoll:
int epoll_create(int size);创建一个epoll文件描述符,管理其他多个文件描述符。
size: 用来告诉内核这个监听的数目一共有多大,参数 size 并不是限制了 epoll 所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。当创建好 epoll 描述符后,它就是会占用一个 fd 值,在使用完 epoll 后,必须调用 close() 关闭,否则可能导致 fd 被耗尽。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);事件注册,它不同于 select() 是在遍历描述符时告诉内核要关注什么事件,而是在这里先注册要关注的事件。
epfd: epoll 专用的文件描述符,epoll_create()的返回值
op: 表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的 fd 到 epfd 中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从 epfd 中删除一个 fd;
fd: 需要监视的文件描述符
event: 告诉内核要监听什么事件,主要有以下事件:
EPOLLIN :表示对应的文件描述符可以读(包括对端 SOCKET 正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读;
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET :将 EPOLL 设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个 socket 的话,需要再次把这个 socket 加入到 EPOLL 队列里。
int epoll_wait( int epfd, struct epoll_event * events, int maxevents, int timeout );注册在epoll中已经发生的事件,类似于 select() 调用,轮训事件表,看是否有事件发生。
epfd: epoll 专用的文件描述符,epoll_create()的返回值
events:epoll 将会把发生的事件添加到events 数组中,events 不可以是空指针,内核只负责把数据复制到这个 events 数组中,不会去帮助我们在用户态中分配内存。
maxevents: maxevents 告之内核这个 events 有多大。
timeout: 超时时间,为 -1 时,函数为阻塞,知道有事件发生。
返回就绪的文件描述符个数。
epoll 对文件描述符的操作有两种模式:LT(level trigger:水平触发模式,default)和 ET(edge trigger:边沿触发模式)。
LT模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序后,应用程序可以不处理该事件,下次调用 epoll_wait 时,会再次向应用程序通知此事件,直到事件被处理,可能会重复处理事件。
ET 模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序后,应用程序必须立即处理该事件,如果不处理,下次调用 epoll_wait 时,不会再次向应用程序通知此事件,可能会丢失事件处理。
在 select/poll中,内核对所有监视的文件描述符进行扫描,返回就绪的个数;而 epoll() 事先通过 epoll_ctl() 来注册一个文件描述符及感兴趣的事件,一旦某个文件描述符上感兴趣的事件发生后,内核会采用类似 callback 的回调机制,将发生的事件填入epoll_wait()的结构中,当进程调用 epoll_wait() 时便得到就绪的个数。
优点:
1.监视的描述符数量不受限制。
2.效率不会随着监视 fd 的数量的增长而下降。select(),poll() 需要自己不断轮询所有 fd 集合来发现就绪设备,当fd集合很大时,开销很大。而epoll是通过事件回调机制发现就绪的设备,当设备就绪后,回进行回调,不需要轮训。
3.select(),poll() 每次调用都要把 fd 集合从用户态往内核态拷贝一次,而epoll通过内核与用户空间共享一块内存,避免了无畏的内存拷贝(mmap机制)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容