高并发IO底层原理

文章目录

  • IO读写基本原理
  • 四种IO模型
    • 同步阻塞IO
    • 同步非阻塞IO(Non-Blocking IO, NIO)
    • IO多路复用
    • 异步IO

IO读写基本原理

为了避免用户进程直接操作内涵,保证内核安全,操作系统将内存(虚拟内存)划分为两部分:

  • 内核空间(Kenel-Snace)
  • 用户空间(User-Space)

在Linux系统中,内核模块运行在内核空间,对应的进程处于内核态:用户程序运行在用户空间,对应的进程处于:用户态。操作系统的核心是内核程序,它独立于普通的应用程序,有权限访问受保护的内核空间和硬件设备,而普通的应用程序并没有这样的权限。内核态进程可以执行任意命令,调用系统的一切资源,而用户态进程只能执行简单的运算不能直接调用系统资源,用户态进程执行系统调用必须通过系统调用(System Call)向内核发出指令,完成调用系统资源之类的操作。

操作系统层面的read系统调用并不是直接从物理设备把数据读取到应用的内存中,write系统调用也不是直接把数据写入物理设备。上层应用无论是调用操作系统的read还是调用操作系统的write,都会涉及缓冲区。具体来说,上层应用通过操作系统的read系统调用把数据从内核缓冲区复制到应用程序的进程缓冲区,通过操作系统的 write系统调用把数据从应用程序的进程缓冲区复制到操作系统的内核缓冲区

应用程序的IO操作实际上不是物理设备级别的读写,而是缓存的复制。read和write两大系统调用都不负责数据在内核缓冲区和物理设备(如磁盘、网卡等)之间的交换。这个底层的读写交换操作是由操作系统内核(Kernel)来完成的。所以,在应用程序中,无论是对socket的IO操作还是对文件的IO操作,都属于上层应用的开发,它们在输入(Input)和输出(Output)维度上的执行流程是类似的,都是在内核缓冲区和进程缓冲区之间进行数据交换。

高并发IO底层原理_第1张图片

  • 客户端发送请求:Java客户端程序通过write系统调用将数据复制到内核缓冲区,Linux内核缓冲区的请求数据通过客户端机器的网卡发送出去。在服务端,这份请求数据会从接收网卡中读取到服务端机器的内核缓冲区。
  • 服务端获取请求:Java服务端程序通过read系统调用从Linux内核缓冲区读取数据,再进入 Java进程缓冲区。
  • 服务端业务处理:Java服务器在自己的用户空间中完成客户端的请求所对应的业务处理
  • 服务端返回数据: Java服务器完成处理后,构建好的响应数据格从用户缓冲区写入内核缓冲区,这里用到的是 write系统调用,操作系统会负责将内核缓冲区的数据发送出去。
  • 发送给客户端:服务端Linux将内核缓冲区的数据写入网卡,网卡通过底层的通讯协议将数据发送给目标客户端。

四种IO模型

同步阻塞IO

高并发IO底层原理_第2张图片

  • 从Java进行IO读后发起read系统调用开始,用户线程就进入阻塞状态。
  • 当系统内核收到read系统调用后就开始准备数据。一开始,数据可能还没到达内核缓冲区(例如,还没有收到一个完整的 socket数据包),这时内核就要等待。
  • 内核一直等到完整的数据到达,就会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到用户缓冲区中的字节数)。
  • 直到内核返回后用户线程才会解除阻塞的状态,重新运行起来。

同步非阻塞IO(Non-Blocking IO, NIO)

Java编程的NIO(NEW IO)类库组件所归属的不是基础IO模型中的NIO,而是IO多路复用

高并发IO底层原理_第3张图片

  • 在内核数据没有准备好的阶段,用户线程发起IO请求时立即返回。所以,为了读取最终的数据,用户进程(或者线程)需要不断地发起IO系统调用。
  • 内核数据到达后,用户进程(或者线程)发起系统调用,用户进程(或者线程)阻塞。内核开始复制数据,它会将数据从内核缓冲区复制到用户缓冲区,然后内核返回结果(例如返回复制到的用户缓冲区的字节数)。
  • 用户进程(或者线程)读到数据后,才会解除阻塞状态,重新运行起来,用户空间需要经过多次尝试才能确保最终真正读到数据

IO多路复用

高并发IO底层原理_第4张图片

  • 选择器注册。首先,将需要read操作的目标文件描述符(socket连接)提前注册到Linux的select/epoll 选择器中,在Java 中所对应的选择器类是 Selector类。然后,开启整个IO多路复用模型的轮询流程。
  • 就绪状态的轮询。通过选择器的查询方法,查询所有提前注册过的目标文件描述符( socket连接)的IO就绪状态。通过查询的系统调用,内核会返回一个就绪的socket列表。当任何一个注册过的socket 中的数据准备好或者就绪了就说明内核缓冲区有数据了,内核将该socket加入就绪的列表中,并且返回就绪事件。
  • 用户线程获得了就绪状态的列表后,根据其中的socket连接发起read系统调用,用户线程阻塞。内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区。
  • 复制完成后,内核返回结果,用户线程才会解除阻寒的状态,用户线程读取到了数据继续执行。

异步IO

高并发IO底层原理_第5张图片

  • 当用户线程发起了read系统调用后,立刻就可以去做其他的事,用户线程不阻塞。
  • 内核开始IO的第一个阶段:准备数据。准备好数据,内核就会将数据从内核缓冲区复制到用户缓冲区。
  • 内核会给用户线程发送一个信号(Signal),或者回调用户线程注册的回调方法,告诉用户线程read系统调用已经完成,数据已经读入用户缓冲区。
  • 用户线程读取用户缓冲区的数据,完成后续的业务操作。

你可能感兴趣的:(juc,linux)