HI!我是小小,我们又见面了,今天是国庆假期的最后一天,在这最后一天,举国同庆的时刻。。。。我们一块来学习IO,这一次,学习 NIO,BIO,AIO等各种IO
Linux 基础知识回顾
用户空间和内核空间
现在的操作系统都采用虚拟寻址,处理器先产生一个虚拟地址,然后通过地址翻译成为物理地址,再通过总线的传递,最后处理器拿到某个物理地址返回的字节。
对于32位操作系统来说,它的寻址空间,为4G,操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限,为了保证用户进程不能直接操作内核,保证内核安全,操作系统把虚拟空间划分为两个部分,一部分为内核空间,一部分为用户空间。
对于Linux系统来说,把高直接,1G,给内核使用,称之为内核空间。把低的3G字节,送给进程使用,称之为用户空间。
直接IO和缓存IO
文件系统IO分为直接IO和缓存IO。
缓存IO
读操作
操作系统会检查内核的缓冲区有没有需要的数据,如果已经有缓存了,那么就直接从缓存中返回,否则会从磁盘中读取,然后缓存在操作系统的缓存中。
写操作
把数据从用户空间复制到内核空间的缓存中,并提示用户写操作完成,什么时候写入磁盘,由操作系统随机决定。除非强制调用sync命令。
举个栗子,对于write命令来说,数据会先被拷贝进入缓冲区,在拷贝到缓冲区以后才会写入到设备中。
直接IO
阻塞和同步
这一小节介绍什么是阻塞,什么是同步
阻塞和非阻塞
阻塞:往往需要等待缓冲区中的数据准备好过后才处理其他事情,否则一直等待在哪。
非阻塞: 当我们的进程访问我们的数据缓冲区的时候,如果数据没有准备好则直接返回,当数据读取完毕以后,会直接触发,提前监听好的事件的回调函数。若准备好,也会直接返回。
同步异步
同步: 应用程序要直接参与IO的读写操作。
异步: 所有的IO读写交给系统处理,应用程序秩序要等待通知即可,当读的时候,会调用之前设定好的应用程序的回调函数,当写的时候,会调用之前设定好的应用程序的回调函数。
常见的IO模型
常见的IO模型有,阻塞IO,非阻塞IO,I/O复用,信号驱动I/O,异步I/O
比较如下图
以读取数据为例子
BIO: 如果数据源没有数据会进入阻塞状态,直到获取到数据
NIO,如果数据源没有数据,会直接返回0,不阻塞。
AIO: 全称异步+非阻塞
详细的见下一节
BIO,NIO,AIO
BIO
同步阻塞IO,服务器实现模式为一个连接一个线程,客户端有连接请求时服务器就需要启动一个线程进行处理,后期可以通过线程池的方式提高性能。适用于连接数目比较小,固定的架构。
NIO
客户端发送的链接请求都会注册到多路复用器上,多路复用器会轮询到连接有IO请求时才启动一个线程进行处理。NIO方式适用于连接数目多,且连接比较短的架构,例如聊天服务器。
IO多路复用模型
IO多路复用,IO就是指我们网络IO,多路指多个TCP连接,复用指复用一个或少量线程,串起来理解就是多个网络IO复用一个或少量的线程处理这些连接。
核心: 能处理更多的链接
常用的复用模型,select poll,epoll
select
select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述符就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以通过遍历fdset,来找到就绪的描述符。
poll
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。
epoll
epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。
比较
应用和框架
Jetty、Mina、Netty、Dubbo、ZooKeeper 之间的通信,都是基于NIO方式实现。
常用的框架有Netty
AIO
一个有效请求一个线程,客户端IO请求都是由OS完成监听和执行的,常用的应用场景有相册服务器。
关于作者
我是小小,一个生于二线,活在一线的程序猿,该程序猿相当相当的佛系,我是小小,我们下期再见。