深刻浅出IO模型

什么是IO?

在类unix系统中,全部的IO设备(网络,磁盘,终端...)都被模型化为文件,IO是主存(main memory)和外部设备(磁盘,终端,网络...)之间拷贝数据的过程。node

I/O的过程

  • 打开文件

应用进程请求内核打开文件,内核返回一个文件的描述符(非负整数),内核记录打开的文件的全部信息,应用进程只需记录文件描述符。linux

  • 改变当前文件的位置

内核中记录一个文件位置k,每一个打开的文件初始为0,应用进程能够经过seek操做,显式的改变文件的位置。c++

  • 读写文件

读:从文件位置k开始读取n个字节到存储器,而后将k增长到k+n。编程

写:存储器拷贝从k开始,n结束个字节到一个文件,而后更新k。缓存

  • 关闭文件

应用进程请求内核关闭文件,内核释放记录的文件信息,将文件描述符释放回描述符池。网络

IO模型

咱们想要了解操做系统的IO模型须要先了解几个基本的概念。并发

  • 阻塞:请求不能当即获得应答,须要等待。异步

  • 非阻塞:请求当即获得应答,不须要等待。函数

  • 同步I/O:同步I/O操做引发请求进程阻塞,直到I/O操做完成。性能

  • 异步I/O:异步I/O操做不引发请求进程阻塞。

举个🌰

在🌰中咱们的请求进程化身肥宅,操做系统内核化身外卖老板。

场景是肥宅周末早上在家饿了,想要吃外卖。

下面是肥宅叫外卖不一样的方式。

阻塞:肥宅打了外卖的电话,外卖的老板正在忙,因此把肥宅挂着,肥宅只能等老板忙完才能订外卖。

非阻塞:肥宅打了外卖的电话,无论有没有东西卖给肥宅,外卖老板都当即回复了肥宅。

同步I/O:肥宅打了外卖的电话,外卖老板告诉肥宅外卖何时送到,肥宅须要本身定时打电话问一下外卖到了没。

异步I/O:肥宅打了外卖的电话下了订单,肥宅就去打游戏去了。外卖老板会在指定的时间把外卖送到肥宅家里,并打电话让肥宅来取。

五种I/O模型

了解了阻塞,非阻塞,异步与同步,下面咱们的五种I/O模型都会用到这些概念。

阻塞I/O模型

阻塞I/O模型

请求进程发起系统调用后,一直等待内核数据拷贝完成,整个过程请求进程是阻塞的。

非阻塞I/O模型

非阻塞I/O模型

请求进程发起系统调用后,若是内核没有准备好会返回一个EWOULDBLOCK。而后请求进程会一直轮询内核,直到内核准备好,开始拷贝数据。

I/O复用模型

I/O复用模型

  • select: 多个的进程的IO能够注册到一个复用器(select)上,而后用一个进程调用该select, select会监听全部注册进来的IO。单个进程所能打开的文件描述符最大只能是1024。使用该技术会有C10K并发问题
  • poll: 对select的一种优化,基于链表实现,没有了1024的限制,可是对链表的遍历使性能低下。
  • epoll: 不须要线性遍历,经过callback进行IO完成后的通知。(因为AIO不成熟,在linux大多使用epoll)
  • kqueue: 与epoll相似,存在FreeBSD中。

信号驱动I/O模型

信号驱动I/O模型

当进程发起一个IO操做,会向内核注册一个信号处理函数,而后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用IO读取数据。

异步I/O模型

异步I/O

当进程发起一个IO操做,进程返回(不阻塞),但也不能返回结果;内核把整个IO处理完后,会通知进程结果。若是IO操做成功则进程直接获取到数据。

I/O模型的比较

I/O模型的比较

同步阻塞IO、同步非阻塞IO、IO多路复用、信号驱动IO都属于同步IO。

只有异步IO属于异步IO。

linux异步I/O有基于线程池的,但AIO有缺陷(没法利用系统缓存等),使用比较多的是epoll。(AIO是posix标准)

win异步I/O使用比较多的是IOCP。

异步I/O线程池主流的实现是IO线程负责I/O(大部分时间阻塞在I/O上),线程间经过信号量进行通讯。

异步I/O库

  • glibc aio: 缺陷是自带单独的执行线程去执行回调函数,这对使用者来讲很难控制。
  • ACE:ACE 过于复杂,甚至比它试图封装的对象更复杂。
  • libevent : 名气最大,应用最普遍,历史悠久的跨平台事件库。(缺点:使用全局变量,定时器没法处理时间跳变等设计缺陷。)
  • libev : 较libevent而言,设计更简练,性能更好。(缺点:对Windows支持不够好)
  • libuv : 开发node的过程当中须要一个跨平台的事件库,他们首选了libev,但又要支持Windows,故从新封装了一套,*nix下早期用libev实现(现已本身实现),Windows下用IOCP实现;(缺点:没有正确的处理 TCP 关闭,容许空回调,用户不知道出错在哪里)。
  • ASIO:在epoll的基础上用iocp的思想垫了一层,而后封装为统一接口(缺点:在linux上会损失一部分效率)。

Libevent、libev、libuv三个网络库,都是c语言实现的异步事件库Asynchronousevent library)。

ASIO在2017年进入了c++标准。

node的异步I/O底层库使用的是libuv。

参考连接

unix网络编程

那些年咱们追过的网络库

相关文章
相关标签/搜索