文章目录

  • 操作系统I/O:阻塞和非阻塞
  • 调用阻塞I/O时,应用程序需等待I/O完成才返结果
  • 调用非阻塞I/O为调用之后立即返回,
  • 轮询技术主要有以下四种:
  • select
  • poll
  • epoll
  • kequeue
  • 参考链接
  • 阻塞和非阻塞系统调用
  • 阻塞
  • 非阻塞
  • 参考链接


操作系统I/O:阻塞和非阻塞

  • OS内核对于I/O只有两方式:阻塞和非阻塞。

调用阻塞I/O时,应用程序需等待I/O完成才返结果

  • 调用之后要等到系统内核层面完成所有操作后,调用才结束。

调用非阻塞I/O为调用之后立即返回,

  • 返回后
  • CPU的时间片可用来处理其他事务,此时性能是提升的。
  • 问题是:
  • 由于完整的I/O没完成,立即返回的并不是业务层期望的数据,而仅仅是当前调用的状态。
  • 为获取完整的数据,程序需要重复调用I/O操作来确认是否完成。
  • 这种重复调用判断操作是否完成的技术叫做轮询

轮询技术主要有以下四种:

  • read
  • 最原始、性能最低,
  • 重复调用来检查I/O的状态来完成完整数据的读取。
  • 在得到数据前,CPU一直耗在等待上。

select

  • 改进read,通过对文件描述符上的事件状态来进行判断。
  • 有一个较弱的限制为
  • 它采用一个1024长的数组存储状态,
  • 所以最多可同时检查1024个文件描述符。

poll

  • 对select改进,采用链表方式避免数组长度的限制,其次它能避免不需要的检查。
  • 但当文件描述符较多时,它性能还是低。

epoll

  • LInux下效率最高的I/O事件通知机制,在进入轮询的时候如果没有检查到I/O事件,将会进行休眠,直到事件发生将它唤醒。
  • 它是真实利用了事件通知、执行回调的方式,而不是遍历查询,所以不浪费CPU,执行效率高。

kequeue

  • 与epoll类似,仅FreeBSD系统下存在。

阻塞和非阻塞系统调用

阻塞

  • 调用结果返回之前,当前线程会被挂起。
  • 函数只有在得到结果之后才会返回。
  • 有人把阻塞调用和同步调用等同起来,实际上是不同的。
  • 对同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数有返回而已。
  • 例如,我们在CSocket中调用Receive函数,如果缓冲区中没有数据,这个函数就会一直等待,直到有数据才返回。
  • 而此时,当前线程还会继续处理各种各样的消息。
  • 如果主窗口和调用函数在同一个线程中,除非你在特殊的界面操作函数中调用,其实主界面还是应该可以刷新。
  • socket接收数据的另外一个函数recv则是一个阻塞调用的例子。
  • 当socket工作在阻塞模式的时候,如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。

非阻塞

  • 指在不能立刻得到结果之前,该函数不阻塞当前线程,立刻返。
  • 对象的阻塞模式和阻塞函数调用 对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但并不是一一对应。
  • 阻塞对象上可以有非阻塞的调用方式,可通过API去轮询状态,在适当时调用阻塞函数,就可避免阻塞。
  • 对非阻塞对象,调用特殊的函数也可进入阻塞调用。
  • 函数select就是这样的一个例子。