系统将虚拟空间(内存)分为了内核空间、用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间
举例说下进程运行时用到数据时,所需要的操作:
对于一个read操作经历两个阶段:一是将数据copy到内核空间,二是将数据从内核空间copy到用户空间,这样read才算完成
对于socket流而言经历两个阶段:一是将等待网络数据到达网络缓冲区,将数据copy到内核空降,二是将数据从内核空间copy到用户空间
1 阻塞IO
执行流程如下所示:
上图中的 “无数据准备好” 就是说数据还没有到达内核空间
阻塞IO模型属于同步阻塞类型,由图中可知,同步体现在:调用端需要主动去拿返回成功指示;阻塞体现在两个阶段:一是无数据准备时会阻塞,二是将数据从内核空间copy到用户空间时会阻塞
2 非阻塞IO
执行流程如下所示:
非阻塞IO模型属于同步非阻塞(但不是真正意义上的非阻塞)类型,注意这里的非阻塞仅仅指的是在无数据准备的时候,代码不会阻塞,但是在将数据由内核copy到用户空间时还是会阻塞的
由上图可以知道,同步体现在:调用端需要主动去拿返回成功指示;非阻塞体现在:当调用recvfrom知道无数据准备时,会直接返回,不会阻塞;同时需要不断调用函数去询问数据是否准备好,这个不断去调用询问的过程就叫轮询
3 IO多路复用
执行流程如下:
从上图中直观的看起来IO多路复用属于同步阻塞(升级版,可同时对多个状态轮询)型,因为在无数据准备时阻塞在了select函数、copy时也阻塞,但是要注意的是select函数是对多个状态轮询
[IO多路复用有两个特别的系统调用select、poll、epoll函数
。select调用是内核级别的,select轮询相对非阻塞的轮询的区别在于---前者可以等待多个socket,能实现同时对多个IO端口进行监听
,当其中任何一个socket的数据准好了,就能返回进行可读
,然后进程再进行recvform系统调用,将数据由内核拷贝到用户进程,当然这个过程是阻塞的
。select或poll调用之后,会阻塞进程,与blocking IO阻塞不同在于,此时的select不是等到socket数据全部到达再处理, 而是有了一部分数据就会调用用户进程来处理]
IO多路复用用单线程实现了对多个状态的轮询监测
4 信号驱动IO
数据准备不阻塞,将数据从内核空间copy到用户空间阻塞,属于异步非阻塞(不是严格意义上的非阻塞)
5 异步IO
数据准备、将数据从内核空降copy到用户空间都不阻塞,属于真正意义上的异步非阻塞
总结:
所以最后的总结图对应的是:
阻塞I/O:同步阻塞
非阻塞I/O:同步(轮询)非阻塞
I/O多路复用:同步阻塞(不过可以同时监听多个socket状态,效率高了)
信号驱动I/O:异步非阻塞
异步I/O:真正意义上的异步非阻塞(上面的都只是数据准备阶段,这个是数据准备和数据处理阶段)
参考文章:
https://www.jianshu.com/p/486b0965c296(此文举的例子所说的同步跟阻塞一般是针对数据准备阶段的)