当前位置: 首页 > news >正文

秋招突击——7/29——操作系统——网络IO

文章目录

    • 引言
    • 基础知识
      • 零拷贝
        • 传统文件读取
        • 传统文件传输
        • 零拷贝
          • mmap + write
          • sendifle
      • 网络通信IO模型
        • 阻塞IO
        • 非阻塞IO
      • IO多路复用模型
        • select
        • poll
        • select和poll的总结
        • epoll
          • 边缘触发ET和水平触发LT
      • 信号驱动IO模型
      • 异步IO
    • 面试题库
      • 1、说一下Linux五种IO模型
      • 2、阻塞IO和非阻塞IO应用场景问题,有一个计算密集型场景,和一个给用户传视频的场景,分别应该使用什么IO?
      • 3、谈谈你对IO复用的理解
      • 4、select、poll、epoll有什么区别?
      • 5、epoll ET模式和LT模式有什么区别?哪个更加高效?
      • 6、零拷贝技术了解过吗?说一下原理
    • 总结

引言

  • 头一次在面试的时候被问到,之前总觉得不会考,就没有看,导致灵魂三连问,全部懵逼!
  • 还是和以前的思路一样,先整理一下基础知识,然后再整理一下面试题!
  • 这章感觉还是挺难的,当初学操作系统的时候,就是直接跳过了,没想到在面试这里等着我!
  • 下述信息是参考以下资料
    • 100%弄明白多路复用

基础知识

零拷贝

传统文件读取

原始技术

  • 需要CPU参与,并且不能干其他的事情
  • 针对此,就有了下文,直接内存访问技术

DMA直接内存访问技术

  • 使用DMA替代CPU的工作
    • 在进行IO设备和内存的数据传输时,数据搬运工作交给DMA控制器,CPU不再参与任何与数据搬运相关的事情,这样CPU可以去处理别的事情了
传统文件传输
  • 在传统的情况下,如果需要通过网络应用传输数据,需要经过如下的流程

在这里插入图片描述

  • 特点
    • 4次用户态和内核态的上下文交换
    • 4次数据拷贝
      • 第一次:将磁盘中的数据拷贝到操作系统的内核缓冲区中,通过DMA搬运
      • 第二次:将内核缓冲区的数据拷贝到用户缓冲区中,应用程序可以使用这部分数据,由CPU完成
      • 第三次:将刚才拷贝到用户缓冲区的数据,再次拷贝到socket缓冲区中,CPU完成
      • 第四次:将内核缓冲区的数据拷贝到网卡缓冲区的数据中,DMA完成

专门针对网络传输,不需要将数据拷贝到用户缓冲区,因为不会修改数据,所以就有了零拷贝技术

零拷贝
  • 两种实现方式
    • mmap + write
    • sendfile
mmap + write
  • 使用mmap替代read

    • 直接将内核缓冲区的数据映射到用户空间,而不是复制到用户空间
      • 操作系统内核与用户空间不再进行任何数据拷贝操作
      • 应用进程和操作系统内核共享缓冲区
  • 调用write函数,将内核缓冲区的数据复制到Socket缓冲区中

    • 在内核态中,将内核缓冲区的数据复制到Socket缓冲区,都是在内核态
      在这里插入图片描述

通过上述过程,减少一次拷贝的过程

sendifle
  • 不涉及到用户态的操作,直接将内核态的数据复制到内核态的Socket缓冲区中,专门用来网络传输的命令

网络通信IO模型

  • 网络通信中,双方应用都是通过各自TCP发送缓冲区和TCP接受缓冲区,进行发送消息和接受消息。
    • IO通信模型主要是解决某一端应用和TCP接受缓冲区和发送缓冲区的交互情况。
    • 发送数据和接受数据是需要时间的,应用需要知道缓冲区的数据是否已经准备好了,以便于及时获取数据

在这里插入图片描述
图片链接

  • 阻塞IO和非阻塞IO也都是针对应用从缓冲区获取数据和接受数据而设计的一些模式,具体如下。
阻塞IO

阻塞的IO的基本流程

  • 应用调用recvfrom读取数据时,询问内核是否准备好数据
  • 内核准备数据报,应用程序陷入阻塞等待
  • 内核准备好数据报,并将数据复制到应用空间
  • 返回revfrom的调用结果,成功返回,线程从阻塞中恢复
    在这里插入图片描述

缺点

  • 一个程序能够开辟的线程是有限的,而且开辟线程的开销是比较大的
    • 会导致一个应用程序可以处理的客户端请求优先,针对百万级连接也是无法处理的
非阻塞IO

非阻塞和阻塞的最大区别,是调用了recvfrom会立刻返回结果,线程可以执行别的工作,不用一直阻塞等待

  • 应用程序向内核发起recvfrom请求读取数据
    • 数据没有准备好,返回EWOULDBLOCK错误码
      • 线程去干别的,知道数据没有准备好,下次再问
    • 数据准备好了,同上面的阻塞IO准备数据的过程
      • 内核将数据报准备好
      • 复制数据到应用空间
      • 返回成功指示

在这里插入图片描述

非阻塞IO的优点

  • 解决了每一个连接一个线程处理的问题,最大的有带你是一个线程可以处理多个连接

非阻塞IO的缺点

  • 用户需要多次发起系统容调用,频繁调用会消耗系统资源

IO多路复用模型

在这里插入图片描述
图片链接

共用的TCP缓冲区的必要性

  • 之前将的所有的样例都是一个线程对应一个TCP缓冲区,但是实际应用中,会有多个线程共用一个TCP缓冲区,一般来说这种情况在服务器中很常见,服务器会为每一个客户端链接创建一个线程,这些线程会共享一个TCP缓冲区尽进行数据的读取和处理。
  • 但是对于客户端而言,每一个TCP连接都是有独立的缓冲区
  • IO多路复用的情况,常出现在如下应用中
    • 高性能的网络服务器
    • 数据采集服务器
    • 实时流媒体服务器
  • 具体如上图,就是多个线程共用一个TCP缓冲区,应用B可能是一个服务器程序!但是如果像上图一样,每次都创建对应的线程去询问对应的TCP缓冲区,会出现浪费了很多资源,并且线程数有限。
  • 所以将这部分工作抽象出来,单独交特定的几个线程完成
    • 监控并询问多个网络请求(fd文件描述付),数据准备完毕后,分配对应的应用线程来读取数据。

多路复用用的是什么?

  • 通过有限次的系统调用来实现管理多个网络连接

    • 复用的是系统调用==》通过有限次系统调用,判断海量连接的数据是否准备好!!!
  • 具体见下图

在这里插入图片描述
图片链接

  • 应用程序会让询问线程调用select函数
    • 这个函数实现上述多路复用的功能,他会同时检测多个fd操作,其所监视的fd只要有一个数据准备完毕,就会返回可读状态,
    • 然后该询问线程再去通知处理数据的线程
    • 对应线程在发起recvform请求读取数据
      在这里插入图片描述图片链接

总结

  • 复用的基本思路就是通过设计不同的监听函数,来监控多个fd,不必为每一个fd都创建一个监控线程,减少线程资源创建的目的。
    • 常见实现版本有select、poll、epoll
select

具体过程

  • 将已连接的socket都放到一个文件描述符集合,然后调用select函数将文件描述符拷贝到内核里

  • 让内核通过轮询遍历的方式来检查是否有网络事件的产生

    • 检查到事件之后,将socket标记为可读可写,然后把整个文件描述符集合拷贝回用户态
  • 用户态再通过遍历的方法找到可读可写的Socket方法,对其进行处理

特点

  • 需要两次遍历文件描述符集合
    • 用户态和内核态
  • 两次拷贝文件描述符集合
    • 用户空间传入内核空间,然后内核空间修改之后,在传回用户空间

特点

  • 跨平台性好
  • 能够监视的文件描述符数量有限制,1024
poll

监视对象

  • 使用动态数据,以链表的形式进行组织,没有数量的限制

特点

  • 需要遍历文件描述符来返回已经就绪的socket
    • 一般来说效率很低,连接数很多,但是有效的连接就只有几个
select和poll的总结

本质

  • 批量socket fds通过系统调用传递给内核,让内核循环遍历判断哪些fd上的数据准备就绪了
  • 然后将就绪readyfds返回给用户,再由用户进行遍历读取好的fds,读取或者写入数据

缺点

  • 海量数据开销大
    • 用户需要将海量的socket fds从用户态传到内核态,由内核态检测那些网络连接数据就绪了,开销大

select和poll的区别

  • select能够处理的最大连接数有限,poll理论上能够支撑无限个
  • select和poll在处理海量连接的时候,会频繁的从用户态拷贝到内核态,比较消耗资源
  • 都是使用线性结构存储进程关注的Socket集合,都需要遍历文件描述符集合才能找到可读可写的Socket,时间复杂度是O(n)
epoll

epoll对于select和poll的改进

  • 第一步

    • 内核里使用红黑树来跟踪进程所有待检测的文件描述符
      • 将需要监控的socket加入到内核中的红黑树中,红黑树的增删改查效率是O(logn)
      • 可以将Socket保存在内核中,不需要每次都复制
  • 第二步

    • 使用事件驱动替代遍历轮询
      • 内核里维护了一个链表来记录就绪事件
      • 有事件发生时,通过回调函数,内核会将其加入到就绪事件列表中,用户调用epoll_wait函数,返回有事件发生的文件描述符链表

具体流程如下

在这里插入图片描述

边缘触发ET和水平触发LT

边缘触发ET

  • 当被监控的Socket描述符有可读事件发生时,服务端只会从epoll_wait中苏醒一次
    • 即使没有调用read函数,也依然只会苏醒一次
    • 保证一次性将内核缓冲区的数据读取完

水平触发LT

  • 被监控的Socket上有刻度事件发生时,服务端不断从epoll_wait中苏醒,直到内核缓冲区数据被read函数读完才结束
    • 只要满足条件,就会苏醒,告诉要取数据

区别

  • 持续性传递和一次性传递
    • 水平触发,是只要内核中有数据读,就会一直不断把这个事件传递给用户
      • 会循环读取数据,因而搭配非阻塞IO一块使用
    • 边缘触发,只有第一次满足条件才会触发,之后就不会传递同样的事件了

信号驱动IO模型

  • 之前的多路复用IO中的select是通过暴力轮询的方式实现fd监控,效率低下,于是提出基于信号的IO模型。
    • 运作原理
      • 对应网络IO的数据准备完毕后,主动通知对应的绑定的询问线程,准备好了,然后询问线程再去通知数据处理线程。
      • 类似设计模式中的观察者模式
    • 具体实现
      • 监控链接建立
        • 询问线程调用sigaction函数和对应的需要检测的网络IO(fd描述符),建立SIGIO信号联系
      • 通知询问线程
        • 内核准备好数据后,通过SIGIO信号通知询问线程准备好后的可读状态
      • 读取数据
        • 对应线程调用recvform读取数据。

注意

  • 信号驱动IO模型下,询问线程建立监控链接后,即刻返回,不会陷入阻塞

具体如下图

在这里插入图片描述图片链接

  • 具体实现流程如下图
    在这里插入图片描述图片链接

总结

  • 不同于IO多路复用中select是需要线程去轮询每一个fd,不能干别的事,而且很多都是无效的轮询。
  • 通过建立这种信号驱动IO模型,建立线程和fd之间的信号关联关系,避免大量无效的数据状态轮询操作

异步IO

提出原由

  • 无论是多路复用IO还是信号驱动IO,读取数据都需要发送两段请求
    • 多路复用,第一次select请求,第二次rcvform
    • 信号驱动,第一次sigaction建立链接,第二次rcvform
  • 如何将整个过程压缩为一个请求?不要进行两次访问,直接发送一个读数据请求,内核什么时候准备好数据,什么时候复制过来,然后告诉我复制完成即可。
  • 上述过程可以通过异步IO实现

运行过程

  • 读数据请求
    • 应用向内核发送read请求,告诉内核读取数据后,立刻返回
  • 内核返回数据
    • 内核收到请求建立信号连接
    • 当数据准备就绪,内核会主动把数据从内核复制到用户空间
    • 所有操作完成后,内核发起通知告诉应用数据完成复制

具体如下图
在这里插入图片描述图片链接
具体流程见下图

在这里插入图片描述图片链接

总结

  • 异步IO解决了应用程序需要连续两次发送请求才能获取数据的模式
    • 在异步IO中只需要向内核发送一次请求,就可以完成状态询问和数据拷贝的操作

面试题库

1、说一下Linux五种IO模型

  • 五种分别是

    • 阻塞IO
      • 应用程序执行IO操作时,数据还没有准备好,应用程序会阻塞,直到数据准备好,让出CPU使用权
    • 非阻塞IO
      • 应用程序执行IO操作时,数据还没准备好,会返回一个错误,而不是阻塞应用程序,应用程序可以执行其他的操作
    • 信号驱动式IO
      • 应用程序向操作系统注册一个信号处理函数,当数据准备好时,才做系统会发送一个信号,应用程序可以在接受到信号的时候读取数据,避免阻塞和轮询
    • IO多路复用
      • 事件驱动IO,应用程序可以同时监控多个IO描述符,当一个IO描述符准备好数据时,应用程序可以对其进行处理。select、poll、epoll都是模型的实现
    • 异步IO
      • 应用程序发起IO操作之后,可以立刻去做其他的事情,当数据准备好之后,操作系统会将数据复制到应用程序的缓冲区中,并通知应用程序。
  • 阻塞IO和非阻塞IO区别

    • 进程发起系统调用后,是会被挂起直到收到数据后在返回,还是立即返回成功货错误
  • 同步IO和异步IO的区别在于

    • 将数据从内核复制到用户空间时,用户进程是否会阻塞
    • 如果用户进程会阻塞,就是同步IO
    • 如果用户进程不会阻塞,就是异步IO

2、阻塞IO和非阻塞IO应用场景问题,有一个计算密集型场景,和一个给用户传视频的场景,分别应该使用什么IO?

阻塞IO

  • 读写过程中发生阻塞现象,当用户线程发出IO请求后,内核会去查看数据是否准备就绪,如果没有就绪就会等待数据就绪
    • 线程处于阻塞状态,并交出CPU
    • 数据就绪后,内核会将数据拷贝到用户线程,并返回结果给用户线程,重新等待获取CPU

非阻塞IO

  • 读写过程中,发起read请求之后,并不会一直等待,会立刻返回。
    • 不间断询问内核数据是否准备就绪,非阻塞IO不会交出CPU

计算密集型场景

  • 该场景下需要消耗的是CPU资源,用阻塞IO比较好,非阻塞IO会一直占用CPU做无意义的轮询

用户传输视频

  • 瓶颈不是CPU资源,是数据传输过程,所以使用非阻塞IO会更好,避免线程阻塞在数据传输上,提高程序的并发性和响应时间。

3、谈谈你对IO复用的理解

不使用IO复用,服务端支持多客户端IO存在什么问题

  • 如果没有IO复用,每一连接都需要使用使用一个子进程或者线程来处理,但是进程创建和线程创建开销很大,不能维护太多IO。
  • 系统每一次进行IO都需要进行一次IO请求,直到获取数据为为止,如何降低系统调用地次数十分关键

多路复用IO是怎么解决上述问题的

  • IO多路复用可以实习那多个IO复用一个进程,只需要一个进程或者线程就能并发处理多个客户端地IO事件
    • 进程可以通过select、poll、epoll这类IO多路复用系统调用接口从内核获取发生事件的Socket集合。
    • 应用程序可以遍历这个集合,对每一个Socket进行处理。

redis单线程实现高性能,就是IO多路复用地典型实现

4、select、poll、epoll有什么区别?

select和poll

  • select和poll都是使用线性结构来存储进程关注的Socket集合,在使用的时候

    • 首先把需要关注的Socket集合通过select/poll系统调用从用户态拷贝到内核态
    • 然后,在由内核态进行遍历检测事件,并设置对应状态为可读/可写
    • 接着,将从内核态复制到用户态,由用户态遍历找到其中可读可写的Socket,进行处理
  • 缺点

    • 客户端越多,Socket集合越大
    • Socket集合的遍历和拷贝都要很大的开销

epoll

  • 内核中使用红黑树关注Socket,避免大量复制和拷贝

    • 在内核中使用红黑树来关注进程所偶待检测Socket
      • 红黑树增删改查的时间复杂度是O(logn)
    • 通过对于红黑树的管理,不用每次都传入整个集合,减少内核和用户空间大量的数据拷贝和内存分配
  • 事件驱动代理轮询扫描

    • 使用事件驱动机制,内核维护链表记录就绪事件
    • 只有将事件发生的socket传递给应用程序,不需要poll和select一样轮询整个Socket集合,提高了效率

5、epoll ET模式和LT模式有什么区别?哪个更加高效?

  • ET边缘触发

    • 当描述符从未就绪变为就绪时,只会通知一次,之后就不会通知,保证程序能够一次性将事件处理完

    • 效率更高,减少epoll_wait系统调用次数

  • LT水平触发

    • 当文件描述符就绪时,就会触发通知,直到用户把所有数组读完为止,都会一直发通知

6、零拷贝技术了解过吗?说一下原理

传统文件传输

  • 设计两次系统调用,read和write函数,4次用户态和内核态的切换以及数据拷贝。

零拷贝

  • 只需要一次sendfile或者两次系统调用mmap和write,减少了两次用户态和内核态的切换次数,再加上DMA技术,数据拷贝只需要两次。

通过零拷贝能够提高文件传输的速率,Kafka消息队列IO的吞吐量高的原因,就是使用了零拷贝技术

总结

  • 好家伙,终于结束了,下一个知识点,在不断补充完整!
  • 这个终于结束了,耗时两天,明天抽时间好好背一下,后续不需要在花时间了!
  • 能应付面试就行了!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 系统移植(八)u-boot源码解析(未整理)
  • 聊聊基于Alink库的主成分分析(PCA)
  • K210视觉识别模块学习笔记8:Mx_yolo3本地模型训练环境搭建_部署模型到亚博canmv(失败)
  • 2-48 基于matlab的EM算法聚类可视化程序
  • 【面试题】喔影网络科技面试题复盘
  • Unity Android接入SDK 遇到的问题
  • springcloud RocketMQ 客户端是怎么走到消费业务逻辑的 - debug step by step
  • 19.延迟队列优化
  • 高性能响应式UI部件DevExtreme v24.1.4全新发布
  • TCP程序设计
  • Linux基础操作(下)
  • 基于Flink SQL CDC的实时数据同步
  • wire和reg的区别
  • 使用eclipse在新建的java项目中编辑xml文件时Unhandled event loop exception No more handles
  • 力扣 二分查找
  • #Java异常处理
  • Iterator 和 for...of 循环
  • JS数组方法汇总
  • js数组之filter
  • Linux中的硬链接与软链接
  • maven工程打包jar以及java jar命令的classpath使用
  • Selenium实战教程系列(二)---元素定位
  • Spark学习笔记之相关记录
  • supervisor 永不挂掉的进程 安装以及使用
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 简单数学运算程序(不定期更新)
  • 前端面试之闭包
  • 深入浅出Node.js
  • 小程序01:wepy框架整合iview webapp UI
  • postgresql行列转换函数
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • #ifdef 的技巧用法
  • #Java第九次作业--输入输出流和文件操作
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • #传输# #传输数据判断#
  • (12)Linux 常见的三种进程状态
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (zt)最盛行的警世狂言(爆笑)
  • (zz)子曾经曰过:先有司,赦小过,举贤才
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (附源码)php投票系统 毕业设计 121500
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (每日一问)设计模式:设计模式的原则与分类——如何提升代码质量?
  • (转)Scala的“=”符号简介
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • (自用)网络编程
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能