首页 > 编程学习 > BIO NIO AIO 多路复用

BIO NIO AIO 多路复用

发布时间:2022/1/17 12:25:49

先来说说四个概念:

  • 同步
    同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。
  • 异步
    异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
  • 阻塞
    阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
  • 非阻塞
    非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。

是不是还有点模糊,通俗的话讲就是:

这里只考虑两个实体(客户端、服务端)
一个事件(客户端向服务端请求数据)

同步和异步是针对于服务端来讲

  • 同步 客户端找服务器 (客户端轮询的查询服务器,看是否有结果返回)
  • 异步 服务器找客户端 (服务器有数据返回时,通过回调等机制通知客户端有数据返回)

阻塞和非阻塞是针对于客户端来讲的

  • 阻塞: 客户端发起请求时,服务端不给结果,客户端线程挂起,不做其他事情
  • 非阻塞: 客户端发起请求时,服务端不给结果,客户端可以先去干其他的事情

还没明白?你再品:

举个生活中简单的例子,你妈妈让你烧水,小时候你比较笨啊,在哪里傻等着水开(同步阻塞)。等你稍微再长大一点,你知道每次烧水的空隙可以去干点其他事,然后只需要时不时来看看水开了没有(同步非阻塞)。后来,你们家用上了水开了会发出声音的壶,这样你就只需要听到响声后就知道水开了,在这期间你可以随便干自己的事情,你需要去倒水了(异步非阻塞)。

你就是客户端 水壶 就是服务端

BIO 同步阻塞

  • 流程解析: 用户线程通过系统调用read发起IO读操作,由用户空间转到内核空间。内核等到数据包到达后,然后将接收的数据拷贝到用户空间,完成read操作。
  • 同步阻塞,当没有数据返回的时候,当前线程处于等待状态
    在这里插入图片描述

在这里插入图片描述

同步非阻塞IO

  • 流程解析: 由于socket是非阻塞的方式,因此用户线程发起IO请求时立即返回。但并未读取到任何数据,用户线程需要不断地发起IO请求,直到数据到达后,才真正读取到数据,继续执行。
  • 相比于BIO 用户线程请求内核后立马收到返回,用户线程为非阻塞状态,可以执行别的操作,但是需要不停的轮询调用内核,查看数据是否准备完成,比较耗费CPU性能
    在这里插入图片描述

在这里插入图片描述

多路复用 NIO

形成原因: 如果一个I/O流进来,我们就开启一个进程处理这个I/O流。那么假设现在有一百万个I/O流进来,那我们就需要开启一百万个进程一一对应处理这些I/O流(——这就是传统意义下的多进程并发处理)。思考一下,一百万个进程,你的CPU占有率会多高,这个实现方式及其的不合理。所以人们提出了I/O多路复用这个模型,一个线程,通过记录I/O流的状态来同时管理多个I/O,可以提高服务器的吞吐能力。

  • 当进程调用select,进程就会被阻塞
  • 此时内核会监视所有select负责的的socket,当socket的数据准备好后,就立即返回。
  • 进程再调用read操作,数据就会从内核拷贝到进程。
    在这里插入图片描述

其实多路复用的实现有多种方式:select、poll、epoll

select的基本原理:

  • 监视文件3类描述符: writefds、readfds、和exceptfds;
  • 调用后select函数会阻塞住,等有数据 可读、可写、出异常 或者 超时 就会返回
  • select函数正常返回后,通过遍历fdset整个数组才能发现哪些句柄发生了事件,来找到就绪的描述符fd,然后进行对应的IO操作
  • 几乎在所有的平台上支持,跨平台支持性好

缺点:

  • select采用轮询的方式扫描文件描述符,全部扫描,随着文件描述符FD数量增多而性能下降
  • 每次调用 select(),需要把 fd 集合从用户态拷贝到内核态,并进行遍历(消息传递都是从内核到用户空间)
  • 最大的缺陷就是单个进程打开的FD有限制,默认是1024 (可修改宏定义,但是效率仍然慢)
  • static final int MAX_FD = 1024

poll的基本流程:

  • select() 和 poll() 系统调用的大体一样,处理多个描述符也是使用轮询的方式,根据描述符的状态进行处理
  • 一样需要把 fd 集合从用户态拷贝到内核态,并进行遍历。
  • 最大区别是: poll没有最大文件描述符限制(使用链表的方式存储fd)

epoll基本原理:

在2.6内核中提出的,对比select和poll,epoll更加灵活,没有描述符限制,用户态拷贝到内核态只需要一次使用事件通知,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用callback的回调机制来激活对应的fd

优点:

  • 没fd这个限制,所支持的FD上限是操作系统的最大文件句柄数,1G内存大概支持10万个句柄
  • 效率提高,使用回调通知而不是轮询的方式,不会随着FD数目的增加效率下降
  • 通过callback机制通知,内核和用户空间mmap同一块内存实现(减少了数据复制)

句柄是什么?
句柄就是一个标识符,只要获得对象的句柄,我们就可以对对象进行任意的操作。
句柄不是指针,操作系统用句柄可以找到一块内存,这个句柄可能是标识符,map的key,也可能是指针,看操作系统怎么处理的了。fd算是在某种程度上替代句柄吧;Linux 有相应机制,但没有统一的句柄类型,各种类型的系统资源由各自的类型来标识,由各自的接口操作。
在操作系统层面上,文件操作也有类似于FILE的一个概念,在Linux里,这叫做文件描述符(File Descriptor),而在Windows里,叫做句柄(Handle)(以下在没有歧义的时候统称为句柄)。用户通过某个函数打开文件以获得句柄,此后用户操纵文件皆通过该句柄进行。

参考原文链接:
https://blog.csdn.net/zhuyanlin09/article/details/102730344
https://blog.csdn.net/weililansehudiefei/article/details/70885515

Copyright © 2010-2022 ngui.cc 版权所有 |关于我们| 联系方式| 豫B2-20100000