IO

192 阅读9分钟

Io 相关

一、Java IO 模型

(1)IO相关名称

  1. IO模型主要分类:
  • 同步(synchronous) IO和异步(asynchronous) IO
  • 阻塞(blocking) IO和非阻塞(non-blocking)IO
  • 同步阻塞(blocking-IO)简称BIO
  • 同步非阻塞(non-blocking-IO)简称NIO
  • 异步非阻塞(Asynchronous-non-blocking-IO)简称AIO
  1. 同步和异步的区别:
    • 同步: 发送一个请求,等待返回,再发送下一个请求,同步可以避免出现死锁,脏读的发生
    • 异步:发送一个请求,不等待返回,随时可以再发送下一个请求,可以提高效率,保证并发。
    • 主要区别:
      在发起IO请求和实际IO操作时,实际IO操作是否阻塞。如果实际IO 操作阻塞请求进程(__请求进程需要等待或者轮询查看IO操作状态 __)就是同步IO。如果实际IO操作不阻塞请求进程,由OS进行操作 后将结果返回,这是异步IO
  2. 阻塞和非阻塞的区别:
    • 阻塞:当视图对文件描述符或者网络套接字进行读写时,如果当时没有东西可读,或者暂时不可写,程序就进入等待状态,直到有东西读或者写。即阻塞即等待
    • 非阻塞:当视图对文件描述符或者网络套接字进行读写时,如果没有东西可读,或者不可写,读写函数马上返回,无须等待。
    • 主要区别:
      IO 操作主要分为两个步骤,即发起 IO 请求和实际 IO 操作,阻 塞与非阻塞的区别就在于第一个步骤是否阻塞。 若发起 IO 请求后请求线程一直等待实际 IO 操作完成,则为阻 塞 IO;若发起 IO 请求后请求线程返回而不会一直等待,即为非 阻塞 IO。

(2)BIO

  1. 概念说明:
    • 定义:数据的读取必须阻塞在一个线程内等待其完成
  2. TCSC(实例代码):BIO 的code
  3. 场景:
    • 例子:这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做
    • 使用范围:BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择。

(3)NIO

  1. 概念说明:
    • non-blocking的简称
  2. TCSC(实例代码):NIO的code
  3. 场景:
    • 例子:还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作
    • 使用范围:NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂。

(4)AIO

  1. 概念说明:
    • 异步IO的简称
  2. TCSC(实例代码):AIO的code
  3. 场景:
    • 例子:异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
    • 使用范围:AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

二、Linux IO模型

(1)相关知识

  1. 用户空间和内核空间:
    • 现代的操作系统都是采用虚拟存储器。
    • 系统的寻址空间取决于系统的位数,32位的寻址空间为4G(2^32)。
    • 操作系统的核心是内核,它独立于普通的应用程序,可以访问受保护的内存地址,并且拥有所有的硬件的访问权限
    • 针对操作系统,一部分的供内核使用的内存叫做内核空间
    • 供用户程序使用的内存叫做用户空间
  2. 缓存IO:
    • 缓存IO:被称为标准IO,基本所有的操作系统都是缓存IO。在Linux中的缓存IO中,操作系统会将Io的数据缓存到文件系统的页缓存中(Page Cache)
    • 一般的IO操作会先将数据Copy到内核空间的缓存区,然后在copy到用户空间
    • 数据传输中需要在内核空间和用户空间来回copy,CPU和内存的开销比较大,由此衍生出zero-copy技术
  3. 一般IO操作的流程:
    • 第一阶段:等待数据准备
    • 第二阶段:将数据从内核空间copy到进程中

(2)IO模型

  1. 同步阻塞IO:

    • 在同步阻塞IO的情况下,用户空间执行一个系统调用(从内核空间),此时,该进程会block住,直到数据准备好,然后从内核空间Copy到用户空间,再进行数据处理逻辑
    • 流程图: BIO流程图
    • 代码:code
  2. 同步非阻塞IO:

    • 在同步非阻塞IO的情况下,用户执行一个系统调用,进程不会被block住,内核空间会立刻返回一个flag,然后进程可以先执行其他的任务,再发起系统调用,重复上述过程。一般这过程被称为轮询。此处的内核空间到用户空间的数据copy依旧是block的。
    • 流程图: NIO流程图)
    • 代码:code
  3. IO 多路复用:

    • 事件驱动IO
    • 流程图: 20210504222427
    • 代码:code
  4. 信号驱动式IO:

    • 首先我们允许Socket进行信号驱动IO,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据
    • 流程图: 20210504222636
    • 代码: code
  5. 异步非阻塞 IO:

    • 用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO两个阶段,进程都是非阻塞的。
    • 流程图: 20210504222815
    • 代码: code1 code2
  6. IO 模型的比较: 20210504223109

(3)jvm 中对linux io的封装(需要openjdk源码)

  1. jvm 中对io操作的封装
    • io_util_md.h ,io_util_md.c ,io_util.c ,libnio
  2. jvm中的native方法包:share/native

Linux 内存相关

内存

  1. 基本概念:
    • 地址空间:操作系统对物理内存的抽象。可以来访问内存的地址集合,每个进程都有自己的地址空间,并且在每个进程看来都是等于物理内存大小的空间,进程可以任意的去访问,不需要担心影响其他的进程,因为访问的都是虚拟地址。
    • 地址空间的划分:4GB的内存中,有1GB的是共享空间,也叫做内核空间,其他的都是进程独享的又叫做用户空间。当4GB的空间都满了,就会把一部分的无用的内存数据保存到磁盘中来腾出空间,这种技术叫做虚拟内存。
    • 内核空间/用户空间:内核空间的数据在用户模式下的进程是不可见的,当进程切换到内核模式的时候才可以对内核空间的数据访问。
  2. 页:
    • 32位系统中,每个进程都有4G的地址空间,但是程序在运行的时候并不需要程序中所有的内容,需要的只是当前正在执行的一部分和相关的数据就可以,因此可以将整个地址空间分成一个个连续的大小固定的块(比如4KB),只要那些需要的块在内存中即可,这一个个的块就是页(page)。这样的好处就是当程序运行或者停止时,系统不用把整个进程的内容移进移出,而只要移动一个个页就可以,这样既提高了效率又节省了空间,才使得多个进程能够同时存在于内存中。注意,这里的页是指虚拟地址空间里连续的一段,而物理内存中这样连续的段则称为page frame,它们的大小相同,进程的页放进内存中时就放到一个个的page frame中。
    • 逻辑地址到物理地址的转换:
      Linux为每个进程维护了一个page table,这个表里的每一条记录表示一个page. 21230624-9974355e8d9c4f058b87a422cd0d5576 每条记录中通过一些位标识了这个页是否存在于实际内存中,允许什么方式的访问,是否被修改过,是否正在被使用,是否进行缓存等信息,其中最重要的就是page frame number,系统就是由此得到的该页在实际内存中的位置。
    • 页的回收:在系统运行期间,随着越来越多的程序的启动和运行,越来越多的物理内存会被占用,而系统必须保证有空闲的内存来维持正常的运行,Linux使用了一种叫PFRA(page frame reclaiming algorithm) 的算法将一些暂时没用的page frame释放来腾出空间,的目标就是从内存中选出要释放的page frame。
  3. 地址转换:
    • 基于硬件的动态地址转换:
      cpu中添加俩个硬件寄存器(MMU),基址寄存器(保存进程物理地址的起始地址),界限寄存器(进程的地址边界)。通过这俩寄存器可以做到地址转换,具体的实现如下公式:
      物理地址 = 虚拟地址 + 基本地址

    • 快速地址转换(TLB): 参考:TLB的作用和工作原理

参考:

  1. 聊聊Linux 五种IO模型
  2. LinuxIo 概览
  3. 从操作系统层面理解Linux下的网络IO模型
  4. 如何查找 jdk 中的 native 实现
  5. 揭秘java虚拟机 第一版
  6. 通过Linux理解操作系统(四):内存管理(上)
  7. 通过Linux理解操作系统(五):内存管理(下)
  8. TLB的作用和工作原理
  9. 操作系统导论 第一版