ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

操作系统

2022-09-01 01:00:08  阅读:185  来源: 互联网

标签:缓存 操作系统 线程 内核 进程 CPU socket


1. CPU缓存

image

  • CPU缓存分为3级结构: 寄存器 -> L1缓存(数据缓存 + 指令缓存) -> L2缓存 -> L3共享缓存
  • 缓存的最小单位: 缓存行(64kb), 这意味着对于内存连续的数据结构, 一次会将64kb的元素载入数据缓存
    • 好处是: 可以用来提升缓存命中率, 比如二维数组的行优先好于列优先遍历, 比如将同种类型的运算符聚类, 以充分利用指令缓存加速分支预测器
    • 坏处是: 造成伪共享, 降低CAS的效率, 可以使用padding消除
  • 对于多核CPU, 由于L1L2缓存不能共享, 因此也造成多线程被调度算法分到不同核上, 缓存失效的问题
  • 正如DB会在合适的时间将内存页刷回磁盘一样, CPU也会将缓存刷回内存
    • 写直达: 实时双写, 保证CPU缓存和内存的强一致性, 影响性能
    • 写回: 保证最终一致性, 缓存命中时标记为脏行但不刷内存, 缓存不命中且缓存行为脏才刷内存
      • 那么在不一致的窗口, 若其他CPU核上的线程读取内存脏数据, 就造成不一致
      • 基于总线嗅探的MESI协议: 对所有CPU核, 保证缓存更改的可见性和有序性

2. 硬中断 vs 软中断

  • 中断处理是内核的一个功能, 负责处理南向硬件的请求, 与文件, 网络, 内存管理等平级
  • 由于中断会阻塞用户进程, 因此有两种方法减小影响:
    • 中断屏蔽: 中断可以嵌套, 这样切换上下文次数多了影响效率, 中断屏蔽指令可以让中断不被抢占, 减少中断总时间
    • 中断拆分: 类似标记脏数据思想, 先被动接收中断, 但不立即处理, 然后由内核挑时间延迟集中处理

3. 巨内核 vs 微内核

image

  • 内核是对硬件指令的统一封装
  • linux是巨内核, 这意味着所有封装动作都在内核态完成
    • 缺点是封装内部的各功能耦合度高, 可移植性差, 优点是核内inline调用无需频繁切换, 性能高
  • 微内核只在内核态封装最基本的调度, 虚拟内存, 中断等硬件功能, 其他功能如驱动, 文件系统全部交给用户态实现
    • 优点是可移植性高, 自己在用户态实现的功能可以解耦, 缺点是用户请求某些硬件功能时需频繁切换到内核态, 性能差
  • 类比TCP的强治理 vs UDP的弱治理

4. 虚拟内存

  • 通过内存管理单元MMU, 将不同进程的物理内存空间做屏蔽

  • 分段
    image

    • 会产生段外碎片和段内碎片
      • 段外碎片: 需要将零散的段放到硬盘swap区, 再写回内存, 类似GC的复制算法
      • 由于需要IO与硬盘交互, 速度很慢, 产生抖动
      • 段内碎片无法避免
  • 分页
    image

    • 没有外部碎片, 内部碎片不超过单页大小
    • 额外需要MMU管理页表, 页表占一定空间
    • 根据局部性原理, 使用多级页表动态加载需要的页
      image
  • 添加快表做缓存

  • 段页式

    • 在按进程逻辑块分段的基础上, 将每个段分页
      image

5. 页面置换算法

image

  • 在缺页时, 假如第4步无法找到内存空闲页, 就需要换出脏页
  • 目标: 减少总体置换次数
    • 最佳置换, 未来最少使用
    • FIFO
    • LRU, 最久未使用
    • LFU, 最少使用
    • 时钟, 沿途访问过的标为未访问, 直到遇到未访问的

6. 进程 vs 线程

  • 单核CPU通过中断实现进程切换, 从而达到并发效果

  • 多核CPU并行执行
    image

  • 挂起: 大量处于阻塞或就绪状态的进程会占据内存空间, 因此将其换出到磁盘

  • 使用链表实现阻塞队列, 就绪队列, 单元是PCB

线程之于进程, 就好比容器之于虚拟机, Runnable之于Thread:

  • 进程是资源(包括内存、打开的⽂件等)分配的单位,线程是 CPU 调度的单位;
  • 进程拥有⼀个完整的资源平台,⽽线程只独享必不可少的资源,如寄存器和栈;
  • 线程同样具有就绪、阻塞、执⾏三种基本状态,同样具有状态之间的转换关系;
  • 线程能减少并发执⾏的时间和空间开销;
  • 线程的创建时间⽐进程快,因为进程在创建的过程中,还需要资源管理信息,⽐如内存管理信息、⽂件管理信息,⽽线程在创建的过程中,不会涉及这些资源管理信息,⽽是共享它们;
  • 线程的终⽌时间⽐进程快,因为线程释放的资源相⽐进程少很多;
  • 同⼀个进程内的线程切换⽐进程切换快,因为线程具有相同的地址空间(虚拟内存共享),这意味着
  • 同⼀个进程的线程都具有同⼀个⻚表,那么在切换的时候不需要切换⻚表。⽽对于进程之间的切换,切换的时候要把⻚表给切换掉,⽽⻚表的切换过程开销是⽐较⼤的;
  • 由于同⼀进程的各线程间共享内存和⽂件资源,那么在线程之间数据传递的时候,就不需要经过内核了,这就使得线程之间的数据交互效率更⾼了

image

进程1, 4: 内核线程
image

进程2: 用户线程
image

进程3: 轻量级线程

进程5: 混合线程

7. 僵尸进程 vs 孤儿进程 vs 守护进程

  • 正常情况下, 子进程结束后, 其大部分资源会被内核回收, 只留下现场信息(PID, timestamp, status)用于父进程调用wait()时获取子进程状态
  • 僵尸进程: 父进程未调用wait(), 子进程的现场信息删不掉
    • 解决方法, 杀掉父进程, 让init统一处理
  • 孤儿进程: 父进程结束时, 调用wait()发现子进程还在运行, 那么父进程通知init进程去回收子进程的资源, 然后挂掉. 在init收到通知并杀死子进程前的时间里, 称为孤儿进程
  • 守护进程: 开机就运行, 一直在跑, 杀不死(杀死后立即重生)

8. 进程线程调度算法

  • FIFO

  • SJF

  • 高响应比优先
    image

  • RR

  • 高优先级

  • 多级反馈队列 = RR + 高优先级
    image

9. 磁盘调度算法

  • 先来先服务 = FIFO
  • 最短寻道时间 = SJF
  • 扫描/循环扫描/LOOK/循环LOOK = RR

10. 进程通信

FIFO管道

  • 父进程创建管道 -> fork -> 父子进程间通信
    image

image

  • shell是所有进程的父进程
    image

消息队列

共享内存

  • 虚拟内存技术将不同进程的内存空间隔离
  • 共享内存则共享内存空间
  • 比如static变量, 临界资源

信号

  • kill -l

信号量

  • P消费, V生产

Socket

  • 分TCP/UDP

11. 单点锁模型

  • 生产消费者
  • 哲学家进餐
  • 读写者

12. 死锁

  • 互斥访问
  • 不可剥夺
  • 持有等待(当线程被阻塞时, 不会释放持有的临界资源, 即使该线程现在不使用)
  • 循环等待

13. 阻塞 vs 非阻塞

image

image

  • 以上两种都是同步式调用, 因为最后的拷贝阶段(内核缓冲区->用户进程缓冲区)需要用户进程阻塞, 区别仅在于内核准备数据阶段(磁盘->内核缓冲区)是阻塞还是轮询

14. I/O 多路复⽤

image

  • 上边仍然是同步式调用, 因为最后的拷贝阶段需要用户进程阻塞

image

  • 真正的异步式调用, 用户进程将最后的拷贝阶段主动权让给内核, 由内核拷贝完后通知, 用户进程在全部时间无阻塞

15. 直接内存访问DMA

  • 传统IO, 无DMA, CPU需承担数据桥的拷贝工作, 拷贝期间CPU一直被占用
    image

  • DMA, 将数据桥的角色分离出来, 但将数据从磁盘⾼速缓存拷贝到用户缓冲区仍经过CPU, 因为涉及到上下文切换, 只有CPU有最高权限

  • 涉及2次用户态到内核态的上下文切换, 1次DMA拷贝(磁盘 -> 磁盘⾼速缓存), 1次CPU拷贝(磁盘⾼速缓存 -> 用户进程缓冲区)
    image

  • DMA类似MMU, 那么磁盘高速缓存则对应快表, 假如命中, 则只需要1次CPU拷贝

16. 零拷贝

  • 业务场景:
    • 下载文件, 先从磁盘read到用户进程, 再拷到socket发给客户端, 不在用户进程允许对载入的磁盘里的文件进行再加工, 比如压缩, 转义
    • Kafka -> NIO -> Channel.transferTo() -> sendfile
    • Nginx

image

  • 共享内存mmap, 减少1次CPU拷贝
    image

  • sendfile -> 减少1次CPU拷贝 + 2次上下文切换
    image

    • 若网卡支持SG-DMA, 则能再减少1次CPU拷贝
      image

      • 若磁盘高速缓存命中, 则能再减少1次DMA拷贝①
      • 但假如传输的是大文件, 那么磁盘高速缓存区很快被占满, 而且命中率很低, 则 ①DMA拷贝 不可回避
        • 解决方案: 直接绕过磁盘高速缓存, 重新启用用户缓存取而代之. 同时, 使用异步IO, 让内核拷完后通知用户进程, 减少用户阻塞
          image
  • Nginx大小文件传输阈值:

location /path/ {
 sendfile on; // 启用零拷贝
 aio on; // 启用异步IO
 directio 1024m; // 阈值, 小于时用零拷贝
}

17. 多线程socket模型

image

  • 问题在于, 线程池里的子线程与socket队列一一对应, 无法支撑C10K

  • 思路: 一个子线程对应多socket, 采用类似并发的方式快速切换, 产生多路复用效果.

    • 这要求socket是非阻塞的, 否则在read -> 业务处理 -> socket send时一旦发生阻塞, 线程也阻塞, 无法响应对应的其他socket
  • 多路复用框架, 非阻塞同步式: 将所有socket放入内核, 然后等待事件, 最后将产生事件的socket返回给对应单一的用户态子线程处理

    • select, 在内核态进行循环轮询, 发生事件后标记该socket为可读/写, 然后将所有socket拷回用户态, 再通过轮询找到被标记socket, 最后开始处理
      • 底层数据结构bitMap, 最大1024个socket
    • poll, 将底层数据结构改为链表, 数量不受限制
    • epoll, 底层数据结构改为红黑树, 同时维护一个链表, 用来记录被触发的socket
      image
  • 多路复用框架, 非阻塞异步式: Windows下的IOCP

18. Reactor/Dispatcher vs Proactor

  • Reactor/Dispatcher即对非阻塞同步式IO进行封装
    • reactor负责监听和分发事件

    • 单线程/线程池负责处理事件

    • 单Reactor + 单线程
      image

      • handler负责处理事件
      • Redis采用此方案
    • 单Reactor + 线程池<Handler>
      image

      • handler负责转发事件到processor子线程
    • 单Reactor即负责北向监听, 又负责南向Acceptor建立连接, 还要负责将处理结果返回给客户端, 易单点瓶颈, 因此提出多Reactor + 单线程
      image

      • 主Reactor只负责监听和建立连接, 处理结果返回交给各个子Reactor
      • Netty, Memcache, Nginx(子Reactor建立连接)
  • Proactor
    image

标签:缓存,操作系统,线程,内核,进程,CPU,socket
来源: https://www.cnblogs.com/rellik96/p/16645107.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有