一、什么是IO?我们都知道unix世界里、一切皆文件、而文件是什么呢?文件就是一串二进制流而已、不管socket、还是FIFO、管道、终端、对我们来说、一切都是文件、一切都是流、在信息交换的过程中、我们都是对这些流进行数据的收发操作、简称为I/O操作(input and output)、往流中读出数据、系统调用read、写入数据、系统调用write、不过话说回来了、计算机里有这么多的流、我怎么知道要操作哪个流呢?做到这个的就是文件描述符、即通常所说的fd、一个fd就是一个整数、所以对这个整数的操作、就是对这个文件(流)的操作、我们创建一个socket、通过系统调用会返回一个文件描述符、那么剩下对socket的操作就会转化为对这个描述符的操作、不能不说这又是一种分层和抽象的思想、 二、IO交互通常用户进程中的一个完整IO分为两阶段:
所以、对于一个网络输入操作通常包括两个不同阶段: IO有内存IO、网络IO和磁盘IO三种、通常我们说的IO指的是后两者 三、POSIX对IO底层交互感兴趣的小伙伴可以好好了解一下POSIX(Portable Operating System Interface for Computing System)、我对深沉次原理也不怎么熟、之所以写此篇博文也是为了后面的Java IO学习、深入浅出点到即可、此章节给有兴趣的朋友一个引子、 四、IO模型《UNIX网络编程》说得很清楚、5种IO模型分别是阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型、异步IO模型、前4种为同步IO操作、只有异步IO模型是异步IO操作、请仔细阅读IO交互便于理解IO模型 (一)阻塞IO模型当用户进程调用了recvfrom这个系统调用、内核就开始了IO的第一个阶段:准备数据、对于网络IO来说、很多时候数据在一开始还没有到达(比如、还没有收到一个完整的UDP包)、这个时候内核就要等待足够的数据到来、而在用户进程这边、整个进程会被阻塞、当内核一直等到数据准备好了、它就会将数据从内核中拷贝到用户内存、然后返回结果、用户进程才解除阻塞的状态、重新运行起来、几乎所有的程序员第一次接触到的网络编程都是从listen()、send()、recv()等接口开始的、这些接口都是阻塞型的、
(二)非阻塞IO模型当用户进程发出read操作时、如果内核中的数据还没有准备好、那么它并不会block用户进程、而是立刻返回一个error、从用户进程角度讲、它发起一个read操作后、并不需要等待、而是马上就得到了一个结果、用户进程判断结果是一个error时、它就知道数据还没有准备好、于是它可以再次发送read操作、一旦内核中的数据准备好了、并且又再次收到了用户进程的系统调用、那么它马上就将数据拷贝到了用户内存、然后返回、非阻塞的接口相比于阻塞型接口的显著差异在于、在被调用之后立即返回、
(三)IO复用模型多个的进程的IO可以注册到一个复用器(select)上、当用户进程调用该select、select会监听所有注册进来的IO、如果select所有监听的IO在内核缓冲区都没有可读数据、select调用进程会被阻塞、而当任一IO在内核缓冲区中有可数据时、select调用就会返回、而后select调用进程可以自己或通知另外的进程(注册进程)来再次发起读取IO、读取内核中准备好的数据、多个进程注册IO后、只有一个select调用进程被阻塞 IO复用相对阻塞和非阻塞更难简单说明、所以额外解释一段、其实IO复用模型和阻塞IO模型并没有太大的不同、事实上、还更差一些、因为这里需要使用两个系统调用(select和 recvfrom)、而阻塞IO模型只有一次系统调用(recvfrom)、但是、用select的优势在于它可以同时处理多个连接、所以如果处理的连接数不是很高的话、使用select/epoll的web server不一定比使用多线程加阻塞IO的web server性能更好、可能延迟还更大、select/epoll的优势并不是对于单个连接能处理得更快、而是在于能处理更多的连接
(四)信号驱动式IO模型信号驱动式IO就是指进程预先告知内核、向内核注册一个信号处理函数、然后用户进程返回不阻塞、当内核数据就绪时会发送一个信号给进程、用户进程便在信号处理函数中调用IO读取数据、从图中明白实际IO内核拷贝到用户进程的过程还是阻塞的、信号驱动式IO并没有实现真正的异步、因为通知到进程之后、依然是由进程来完成IO操作、 这和后面的异步IO模型很容易混淆、需要理解IO交互并结合五种IO模型的比较阅读
(五)异步IO模型用户进程发起aio_read(POSIX异步IO函数aio_或者lio_开头)操作之后、给内核传递描述符、缓冲区指针、缓冲区大小和read相同的三个参数以及文件偏移(与lseek类似)、告诉内核当整个操作完成时、如何通知我们、立刻就可以开始去做其它的事、而另一方面、从内核的角度、当它受到一个aio_read之后、首先它会立刻返回、所以不会对用户进程产生任何阻塞、然后、内核会等待数据准备完成、然后将数据拷贝到用户内存、当这一切都完成之后、内核会给用户进程发送一个信号、告诉它aio_read操作完成了 异步IO的工作机制是:告知内核启动某个操作、并让内核在整个操作完成后通知我们、这种模型与信号驱动的IO区别在于、信号驱动IO是由内核通知我们何时可以启动一个IO操作、这个IO操作由用户自定义的信号函数来实现、而异步IO模型是由内核告知我们IO操作何时完成、 这和前面的信号驱动式IO模型很容易混淆、需要理解IO交互并结合五种IO模型的比较阅读
(六)五种IO模型的比较
五、总结全篇最大的难点在于真正理解数据是如何从设备空间扭转到内核空间再到用户空间
|
|