在类unix系统中,全部的IO设备(网络,磁盘,终端...)都被模型化为文件,IO是主存(main memory)和外部设备(磁盘,终端,网络...)之间拷贝数据的过程。node
应用进程请求内核打开文件,内核返回一个文件的描述符(非负整数),内核记录打开的文件的全部信息,应用进程只需记录文件描述符。linux
内核中记录一个文件位置k,每一个打开的文件初始为0,应用进程能够经过seek操做,显式的改变文件的位置。c++
读:从文件位置k开始读取n个字节到存储器,而后将k增长到k+n。编程
写:存储器拷贝从k开始,n结束个字节到一个文件,而后更新k。缓存
应用进程请求内核关闭文件,内核释放记录的文件信息,将文件描述符释放回描述符池。网络
咱们想要了解操做系统的IO模型须要先了解几个基本的概念。并发
阻塞:请求不能当即获得应答,须要等待。异步
非阻塞:请求当即获得应答,不须要等待。函数
同步I/O:同步I/O操做引发请求进程阻塞,直到I/O操做完成。性能
异步I/O:异步I/O操做不引发请求进程阻塞。
在🌰中咱们的请求进程化身肥宅,操做系统内核化身外卖老板。
场景是肥宅周末早上在家饿了,想要吃外卖。
下面是肥宅叫外卖不一样的方式。
阻塞:肥宅打了外卖的电话,外卖的老板正在忙,因此把肥宅挂着,肥宅只能等老板忙完才能订外卖。
非阻塞:肥宅打了外卖的电话,无论有没有东西卖给肥宅,外卖老板都当即回复了肥宅。
同步I/O:肥宅打了外卖的电话,外卖老板告诉肥宅外卖何时送到,肥宅须要本身定时打电话问一下外卖到了没。
异步I/O:肥宅打了外卖的电话下了订单,肥宅就去打游戏去了。外卖老板会在指定的时间把外卖送到肥宅家里,并打电话让肥宅来取。
了解了阻塞,非阻塞,异步与同步,下面咱们的五种I/O模型都会用到这些概念。
请求进程发起系统调用后,一直等待内核数据拷贝完成,整个过程请求进程是阻塞的。
请求进程发起系统调用后,若是内核没有准备好会返回一个EWOULDBLOCK。而后请求进程会一直轮询内核,直到内核准备好,开始拷贝数据。
当进程发起一个IO操做,会向内核注册一个信号处理函数,而后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用IO读取数据。
当进程发起一个IO操做,进程返回(不阻塞),但也不能返回结果;内核把整个IO处理完后,会通知进程结果。若是IO操做成功则进程直接获取到数据。
同步阻塞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上),线程间经过信号量进行通讯。
Libevent、libev、libuv三个网络库,都是c语言实现的异步事件库Asynchronousevent library)。
ASIO在2017年进入了c++标准。
node的异步I/O底层库使用的是libuv。