I/O相关概念记录

I/O模式

对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:

等待数据准备 (Waiting for the data to be ready)
将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)
正式因为这两个阶段,linux系统产生了下面五种网络模式的方案。

  • 阻塞 I/O(blocking IO)
  • 非阻塞 I/O(nonblocking IO)
  • I/O 多路复用( IO multiplexing)
  • 信号驱动 I/O( signal driven IO)
  • 异步 I/O(asynchronous IO)

阻塞I/O

在linux中,所有的socket默认都是blocking的


当用户进程调用了recvfrom这个系统调用,kernel就开始了I/O的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来

  • blocking I/O的特点是I/O执行的两个阶段全部都被block了

非阻塞I/O(nonBlocking I/O)

在linux下,可以通过设置socket使其变成non-blocking。当对一个non-blocking socket执行读操作时,流程如下:


当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回

  • non-blocking的特点是用户进程需要不断地主动询问kernel数据准备好了没有

I/O多路复用(I/O multiplexing)

IO多路复用指内核一旦发现进程指定的一个或者多个IO条件准备读取,他就通知该进程,有时候也被称为事件驱动I/O(event driven I/O)

IO多路复用适用于以下场景:

  • 当客户处理多个描述符(一般是交互式输入和网络套接口),必须使用IO多路复用
  • 当一个客户同时处理多个套接口时
  • 当一个TCP服务器既要处理监听套接口,又要处理已连接套接口
  • 当一个服务器既要处理TCP,又要处理UDP
  • 当一个服务器要处理多个服务或多个协议

与多进程和多线程相比,IO多路复用的优势是系统开销小,系统不必创建线程或者进程,也不必维护这些线程或进程

IO多路复用就是通过一种机制,一个进程可以监控多个描述符,一旦某个描述符就绪(读/写就绪),能够通知程序进行相应的读写操作

目前支持I/O多路复用的系统调用有select,poll,epoll,pselect,本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的

select

select函数监视的文件描述符有3类,分别是writefds,readfds,exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据读、写、except)或者超时,函数返回。当select函数返回后,可以遍历fdset,来找到就绪的描述符。

  • 优点:支持跨平台
  • 缺点:单个进程能监视的描述符数量存在最大限制,Linux上一般为1024。

select本质上是通过设置或检查描述符标志位的数据结构进行下一步处理,这样带来几个缺点:

  • 单个进程监控的描述符最大数量有限制,32位机1024,64位机2048
  • 多socket进行扫描是线性的,采用轮训,效率低下

    当socket比较多时,每次select都要遍历FD_SETSIZE个socket,不管哪个是活跃的,都要遍历一遍,会浪费很多时间(epoll和kqueue对此有改进

  • 需要维护一个存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
select调度过程
poll

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。

  • poll没有最大连接数,因为它是基于链表来存储的,其余的和select没有多大区别
epoll

epoll有3个系统调用:

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event event);
int epoll_wait(int* epfd, struct epoll_event events, int* maxevents, int timeout)
  • epoll_create建立一个epoll对象,参数size是内核保证能正确处理的最大句柄数,多于这个数内核不保证效果)
  • epoll_ctl可以操作上面建立的epoll对象。 将socket放入epoll让其监控,或者把监控的某个socket句柄移除,不再监控(将I/O流放到内核)
  • epoll_wait在调用时,在给定的timeout时间内,当在监控的句柄中有事件发生时,就返回用户态的进程(在内核层面捕获可读写的I/O事件)

epoll高效的地方还在于:epoll里面有个内核高速cache,被监控的socket在cache里面以红黑树的结构存储,同时,epoll还有个list链表,存储准备就绪的事件

在调用epoll_create时,内核会建立红黑树存储socket,建立链表存储准备就绪的事件;在调用epoll_wait时,仅仅观察这个链表有没有数据,没有数据就sleep,有数据就返回,等到timeout没数据也返回;在执行epoll_ctl时,如果增加socket,则检查红黑树是否存在,存在就立即返回,不存在就添加到红黑树,然后向内核注册回调函数,用于当中断事件来临时向准备就绪的链表中插入数据。

epoll水平触发和边缘触发的实现
epoll_wait返回用户态时是否清空链表,清空了就是边缘触发,未清空就是水平触发

select/poll和epoll的区别:

  • select/poll 监控的socket句柄列表在用户态,每次调用都需要从用户态将句柄列表拷贝到内核态;但是epoll监控的句柄就是建立在内核态,减少了内核和用户态的拷贝
  • select/poll 在用户态和内核之间的拷贝每次都是全部列表,同时遍历效率低;epoll则只需要将准备就绪的句柄返回即可,数量较少

异步I/O(asynchronous I/O)


用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

最后

就好比去买一件商品
  1)阻塞I/O:你自己跑去商店下单(只能一个一个来),等有了物品还要自己去拿回来
  2)非阻塞I/O:你可以网上下单了,而且网上看有没有货,有的话自己去拿回来
  3)I/O多路复用:和第一种情况差不多,但是这个商店下单窗口多,可以同时多个人跑来下单,谁的货到了,自己来取
  4)异步I/O:你只要网上下个单,其余就不管了。商家不仅发快递,快递小哥还把商品直接送到你家里

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

推荐阅读更多精彩内容

  • IO模型介绍 为了更好地了解IO模型,我们需要事先回顾下:同步、异步、阻塞、非阻塞 同步(synchronous)...
    可笑的黑耀斑阅读 1,176评论 0 2
  • 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的。所...
    Daniel521阅读 1,376评论 0 6
  • 必备的理论基础 1.操作系统作用: 隐藏丑陋复杂的硬件接口,提供良好的抽象接口。 管理调度进程,并将多个进程对硬件...
    drfung阅读 3,531评论 0 5
  • 对于io模型这块内容之前基本完全没有接触过,有了些许了解之后还是很困昏,select、poll、epoll的关系以...
    8714f2c3f1b0阅读 11,469评论 1 8
  • title: 论事件驱动与异步IOtag: 事件驱动 异步IOcategories: notes 转载自人云思云 ...
    cmustard阅读 960评论 0 0