IO模型

基本概念

内核空间是操作系统内核访问的区域,独立于普通的应用程序,是受保护的内存空间。
用户空间是普通应用程序可访问的内存区域,用户态的程序不能随意操作内核地址空间,这样对操作系统具有一定的安全保护作用。

网络IO

网络I/O的本质是socket的读取,socket在Linux系统被抽象为流,I/O可以理解为对流的操作。这个操作又分为两个阶段:

  • 等待流数据准备(waiting for the data to be ready)
  • 从内核向进程复制数据(copying the data from the kernel to the process)

对于socket流而言:

  • 第一步通常涉及等待网络上的数据分组到达,然后被复制到内核空间
  • 第二步把数据从内核空间复制到用户空间

Unix网络编程中的五种IO模型

  • Blocking IO - 阻塞IO
  • NoneBlocking IO - 非阻塞IO
  • IO multiplexing - IO多路复用
  • signal driven IO - 信号驱动IO
  • asynchronous IO - 异步IO

Blocking IO - 阻塞IO

阻塞IO,指的是需要内核IO操作彻底完成后,才返回到用户空间,执行用户的操作。阻塞指的是用户空间程序的执行状态,用户空间程序需等到IO操作彻底完成。传统的IO模型都是同步阻塞IO。在java中,默认创建的socket都是阻塞的。

优点:程序简单,在阻塞等待数据期间,用户线程挂起。用户线程基本不会占用 CPU 资源。
缺点:一般情况下,会为每个连接配套一条独立的线程,或者说一条线程维护一个连接成功的IO流的读写。在并发量小的情况下,这个 没有什么问题。但是,当在高并发的场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常巨大。因此,BIO模型在高并发场景下是不可用的

NoneBlocking IO - 非阻塞IO

非阻塞IO,指的是用户程序不需要等待内核IO操作完成后,内核立即返回给用户一个状态值,用户空间无需等到内核的IO操作彻底完成,可以立即返回用户空间,执行用户的操作,处于非阻塞的状态,但是需要不断轮询内核以确认IO状态

优点:每次发起的 IO 系统调用,在内核的等待数据过程中可以立即返回。用户线程不会阻塞
缺点:需要不断的重复发起IO系统调用,这种不断的轮询,将会不断地询问内核,这将占用大量的 CPU 时间,系统资源利用率较低,NIO模型在高并发场景下,也是不可用的。一般 Web 服务器不使用这种 IO 模型。一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。java的实际开发中,也不会涉及这种IO模型

IO multiplexing - IO多路复用

IO多路复用指利用采用单个(或少量)线程来处理多个网络连接IO请求

IO 多路复用其实就是基于 NIO 的基础上加入了事件机制,程序会注册一组 socket 文件描述符给操作系统,然后监视这些 fd 是否有 IO 事件发生,如果有,程序会被通知,IO 多路复用的方式主要有 select、poll、epoll,这三个函数都会进行阻塞,所以可以放在 while(true)循环里使用,不会造成 CPU 的空转,后续文章单独介绍三者的区别

优点:用极少的线程处理大量的连接

signal driven IO - 信号驱动IO

信号驱动式 I/O 是指进程预先告知内核,向内核注册一个信号处理函数,然后进程返回不阻塞,当内核数据就绪时会发送一个信号给进程,用户进程便在信号处理函数中调用IO读取数据,实际上IO内核拷贝到用户进程的过程还是阻塞的,信号驱动IO并没有实现真正的异步

优点:进程没有收到SIGIO信号之前,不被阻塞,可以做其他事情。
缺点:当数据量变大时,信号产生太频繁,性能非常低,内核需要不断的把数据复制到用户态。

asynchronous IO - 异步IO

应用进程执行 aio_read 系统调用会立即返回,应用进程可以继续执行,不会被阻塞,内核会在所有操作完成之后向应用进程发送信号。
异步 I/O 与信号驱动 I/O 的区别在于,异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O。

区别

image.png

你可能感兴趣的:(IO模型)