IO 模型

2018 年 10 月 12 日 • 阅读数: 109

IO 模型

并发与并行

  • 并发是指一个时间段内,有几个程序在同一个cpu上运行,但是任意时刻只有一个程序在cpu上运行
  • 并行是指任意时刻点上,有多个程序同时运行在多个cpu上

同步与异步

  • 同步是指代码调用IO操作时,必须等待IO操作完成才返回的调用方式
  • 异步是指代码调用IO操作时,不必等待IO操作完成就返回的调用方式

阻塞和非阻塞

  • 阻塞是指调用函数的时候,当前进程被挂起
  • 非阻塞是指调用函数的时候当前线程不会被挂起,而是立即返回

C10K问题

  • 如何在一颗1GHz CPU,2G内存,1Gbps网络环境下,让单台服务器同时为1万个客户端提供FTP服务

Unix下五种I/O模型

  • 阻塞式I/O
  • 非阻塞式I/O
  • I/O复用
  • 信号驱动式I/O
  • 异步I/O(POSIX的aio_系列函数)

阻塞式I/O

  • 应用程序调用一个IO函数,导致应用程序阻塞并等待数据准备就绪
  • 如果数据没有准备好,一直等待,如果数据准备好了,则从内核拷贝到用户空间拷贝数据,IO函数返回成功指示
  • 进程调用recvfrom,此系统调用直到数据报到达且被复制到应用进程的缓冲区中或发生错误才返回
  • 常见的错误如系统调用被信号中断
  • 进程在调用recvfrom开始到它返回的整段时间内是被阻塞的,该函数成功返回后,应用进程开始处理数据报

IO01.png

非阻塞式I/O

  • 我们把一个套接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠
  • 而是返回一个错误,这样我们的I/O操作函数将不断的测试
  • 数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止
  • 在这个不断测试的过程中,会大量的占用CPU的时间
  • 前三次调用recvfrom时仍无数据返回,因此内核立即返回一个EWOULDBLOCK错误
  • 第四次调用recvfrom时,数据报已准备好,被拷贝到应用缓冲区,recvfrom 返回成功指示,接着就是处理数据
  • 当一个应用进程像这样对一个非阻塞描述字循环调用recvfrom 时,我们称此过程为轮询(polling)
  • 由于应用进程像这样连续不断地查询内核,这对CPU时间是极大的浪费,所以这种模型只是偶尔才会遇到

IO02.png

I/O复用

  • I/O复用模型会用到select或者poll函数,这两个函数也会使进程阻塞
  • 但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作
  • 而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数
  • 只要有数据就绪,select调用返回,应用程序调用recvfrom将数据从内核区拷贝至用户区
  • 和阻塞IO模型相比,selectI/O复用模型相当于提前阻塞了
  • 等到有数据到来时,再调用recv就不会因为要等数据就绪而发生阻塞

IO03.png

信号驱动I/O

  • 我们可以用信号,让内核在数据就绪时用信号SIGIO通知我们,将此方法称为信号驱动I/O
  • 首先,我们允许套接字进行信号驱动I/O,并通过系统调用 sigaction 安装一个信号处理程序
  • 此系统调用立即返回,进程继续工作,它是非阻塞的
  • 当数据报准备好被读时,就为该进程生成一个SIGIO信号
  • 我们随即可以在信号处理程序中调用 recvfrom 来取读数据报

IO04.png

异步I/O

  • 我们让内核启动操作,并在整个操作完成后(包括将数据从内核拷贝到我们自己的缓冲区)通知我们
  • 调用aio_read函数,告诉内核描述字,缓冲区指针,缓冲区大小,文件偏移以及通知的方式,然后立即返回
  • 当内核将数据拷贝到缓冲区后,再通知应用程序

IO05.png

select、poll、epoll

  • select、poll、epoll都是I/O多路复用的机制
  • I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般读就绪或者写就绪)
  • 能狗通知程序进行相应的读写操作
  • 但select、poll、epoll本质上都是同步I/O
  • 因为它们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的
  • 而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据重内核拷贝到用户空间

select

  • select 函数监视的文件描述符分3类,分别是writefds、readfds和exceptfds
  • 调用后select函数会阻塞,直到描述符就绪,或者超时,函数返回
  • 当select函数返回后,可以通过遍历fdset,来找到就绪的描述符
  • select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点
  • select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,Linux一般为1024
  • 可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样会造成效率的降低

Poll

  • 不同于select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现
  • pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式
  • 同时,pollfd并没有最大数量限制(但是数量过大后,性能也是会降低)
  • 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符
  • 事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态
  • 因此随着监视的描述符数量的增长,其效率也会线性下降

epoll

  • epoll是在2.6内核中提出的,是之前的select和poll的增强版本
  • 相对于select和poll来说,epoll更加灵活,没有描述符限制
  • epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中
  • 这样在用户空间和内核空间的copy只需要一次
标签: IO

召唤伊斯特瓦尔