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

图解系统-小林coding笔记

整体架构图

操作系统
硬件结构
CPU是如何执行程序的
冯诺依曼模型
内存
中央处理器CPU
寄存器
通用寄存器
程序技术器
指令寄存器
控制单元
负责控制CPU工作
逻辑运算
复杂计算
总线
地址总线
数据总线
控制总线
输入设备
输出设备
存储器金字塔
存储器的层次结构
CPU Cache
L1 Cache
指令缓存
数据缓存
L2 Cache
L3 Cache
SSD/HDD磁盘
存储器的层次关系
存储器之间的实际价格和性能差距
如何写出让CPU跑得更快的代码
数据缓存_按照内存布局的顺序操作
指令缓存_CPU的分支预测器
多核CPU将线程绑定到某一个CPU核心中
CPU缓存一致性
CPU Cache的数据写入
写直达
写回
CPU缓存一致性问题
写传播
事务的串行化
总线嗅探
MESI协议
Modified-已修改
Exclusive-独占
Shared-共享
Invalidated-已失效
CPU是如何执行任务的
CPU如何读写数据的
CPU架构
CPU读写单位
CPU伪共享问题
避免伪共享方法
CPU如何选择线程的
进程与线程
普通任务与实时任务,根据优先级
调度类
Deadline/实时任务
Realtime/实时任务
Fair/普通任务
完全公平调度
CPU运行队列
调整优先级
软中断
为什么0.1+0.2不等于0.3
操作系统结构
Linux内核 vs Windows内核
内存管理
虚拟内存
内存分段
内存分页
段页式内存管理
Linux内存管理
进程 线程基础知识
进程
进程的概念
进程的状态
进程的控制结构
进程的控制
进程的上下文切换
线程
为什么要使用线程
什么是线程
线程与进程的比较
线程的上下文切换
线程的实现
调度
调度时机
调度原则
调度算法
进程间通信
多线程同步
死锁
悲观锁与乐观锁
进程调度
页面置换
磁盘调度算法
文件系统
设备管理
键盘敲入A字母时 操作系统期间发生了什么
网络系统
Linux系统是如何收发网络包的
零拷贝
I/O多路复用select/poll/epoll
高性能网络模式:Reactor和Proactor
Linux命令
如何查看网络的性能指标
如何从日志分析PV\UV

一、硬件结构

当CPU要读写内存数据的时候,一般需要通过两个总线:

  • 首先要通过地址总线来指定内存的地址;
  • 再通过数据总线来传输数据。

线路位宽与CPU位宽

硬件的64位和32位指的是CPU的位宽,软件的64位和32位指的是指令到位宽。

程序执行的基本过程

在这里插入图片描述
⼀个程序执⾏的时候, CPU 会根据程序计数器⾥的内存地址,从内存⾥⾯把需要执⾏的指令读取到指令寄存器⾥⾯执⾏,然后根据指令⻓度⾃增,开始顺序读取下⼀条指令。
CPU 从程序计数器读取指令、到执⾏、再到下⼀条指令,这个过程会不断循环,直到程序执⾏结束,这个不断循环的过程被称为 CPU 的指令周期

a=1+2执行具体过程

在这里插入图片描述

指令

不同的 CPU 有不同的指令集,也就是对应着不同的汇编语⾔和不同的机器码,接下来选⽤最简单的 MIPS指集,来看看机器码是如何⽣成的,这样也能明⽩⼆进制的机器码的具体含义。

编译器在编译程序的时候,会构造指令,这个过程叫做指令的编码。 CPU 执⾏程序的时候,就会解析指令,这个过程叫作指令的解码。

现代⼤多数 CPU 都使⽤来流⽔线的⽅式来执⾏指令,所谓的流⽔线就是把⼀个任务拆分成多个⼩任务,于是⼀条指令通常分为 4 个阶段,称为 4 级流⽔线,如下图:
在这里插入图片描述CPU 通过程序计数器读取对应内存地址的指令,这个部分称为 Fetch(取得指令) ;
2. CPU 对指令进⾏解码,这个部分称为 Decode(指令译码) ;
3. CPU 执⾏指令,这个部分称为 Execution(执⾏指令) ;
4. CPU 将计算结果存回寄存器或者将寄存器的值存⼊内存,这个部分称为 Store(数据回写) ;
上⾯这 4 个阶段,我们称为指令周期(Instrution Cycle) , CPU 的⼯作就是⼀个周期接着⼀个周期,周⽽复始。

指令的类型

指令从功能⻆度划分,可以分为 5 ⼤类:
数据传输类型的指令,⽐如 store/load 是寄存器与内存间数据传输的指令, mov 是将⼀个内存地
址的数据移动到另⼀个内存地址的指令;
运算类型的指令,⽐如加减乘除、位运算、⽐较⼤⼩等等,它们最多只能处理两个寄存器中的数据;
跳转类型的指令,通过修改程序计数器的值来达到跳转执⾏指令的过程,⽐如编程中常⻅的 ifelse 、 swtich-case 、函数调⽤等。
信号类型的指令,⽐如发⽣中断的指令 trap ;
闲置类型的指令,⽐如指令 nop ,执⾏后 CPU 会空转⼀个周期

指令的执行速度

CPU 的硬件参数都会有 GHz 这个参数,比如⼀个 1 GHz 的 CPU,指的是时钟频率是 1 G,代表着 1 秒会产⽣ 1G 次数的脉冲信号,每⼀次脉冲信号⾼低电平的转换就是⼀个周期,称为时钟周期。
对于 CPU 来说,在⼀个时钟周期内, CPU 仅能完成⼀个最基本的动作,时钟频率越⾼,时钟周期就越短,⼯作速度也就越快。

在这里插入图片描述
CPU时钟周期:指令数 ×\times× 每条指令的平均时钟周期数(CPI),于是:
在这里插入图片描述
要想程序跑得更快,优化这三者即可:

  • 指令数,表示执⾏程序所需要多少条指令,以及哪些指令。这个层⾯是基本靠编译器来优化,毕竟同样的代码,在不同的编译器,编译出来的计算机指令会有各种不同的表示⽅式。
  • 每条指令的平均时钟周期数CPI,表示⼀条指令需要多少个时钟周期数,现代⼤多数 CPU 通过流⽔
    线技术(Pipline),让⼀条指令需要的 CPU 时钟周期数尽可能的少;
  • 时钟周期时间,表示计算机主频,取决于计算机硬件。有的 CPU ⽀持超频技术,打开了超频意味着把 CPU 内部的时钟给调快了,于是 CPU ⼯作速度就变快了,但是也是有代价的, CPU 跑的越快,散热的压⼒就会越⼤, CPU 会很容易奔溃。

存储器金字塔

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

CPU Cache的数据结构和读取过程是什么样的?

CPU怎么知道要访问的内存数据,是否在Cache里,如果在的话,如何找到Cache对应的数据呢?

直接映射Cache

CPU 访问内存数据时,是⼀⼩块⼀⼩块数据读取的,具体这⼀⼩块数据的⼤⼩,取决于coherency_line_size 的值,⼀般 64 字节。在内存中,这⼀块的数据我们称为内存块(Block) ,读取的时候我们要拿到数据所在内存块的地址。

直接映射Cache采用取模运算策略,取模运算的结果就是内存块地址对应的 CPU Line(缓存块) 的地址。
CPU 在从 CPU Cache 读取数据的时候,并不是读取 CPU Line 中的整个数据块,⽽是读取 CPU 所需要的
⼀个数据⽚段,这样的数据统称为⼀个字(Word) 。那怎么在对应的 CPU Line 中数据块中找到所需的字
呢?答案是,需要⼀个偏移量(Offset)

在这里插入图片描述
⼀个内存的访问地址,包括组标记、 CPU Line 索引、偏移量这三种信息,于是 CPU 就能通过这些信息,在 CPU Cache 中找到缓存的数据。⽽对于 CPU Cache ⾥的数据结构,则是由索引 + 有效位 + 组标记 + 数据块组成。
在这里插入图片描述

全相连Cache(可了解)
组相连Cache(可了解)

CPU缓存一致性

在什么时机才把Cache中的数据写回到内存中:

  • 写直达( 把数据同时写⼊内存和 Cache 中,效率问题)
  • 写回(在把数据写⼊到 Cache 的时候,只有在缓存不命中,同时数据对应的 Cache 中的 Cache Block 为<>脏标记的情况下,才会将数据写到内存中,⽽在缓存命中的情况下,则在写⼊后 Cache后,只需把该数据对应的 Cache Block 标记为脏即可,⽽不⽤写到内存⾥。)
写回
策略
效率问题
缓存一致性问题
通知到其他核心
不能保证串形化
多个线程同时读写同一个Cache Line
缓存一致性问题
Cache
内存
写直达
写回
1.写传播
总线嗅探
MSEI协议
伪共享
2.事务的串形化

解决写回引起的一致性问题:
1、写传播:某个 CPU 核⼼⾥的 Cache 数据更新时,必须要传播到其他核⼼的 Cache;
2、事务的串形化:某个 CPU 核⼼⾥对数据的操作顺序,必须在其他核⼼看起来顺序是⼀样的。

要实现事务串行化:
CPU 核⼼对于 Cache 中数据的操作,需要同步给其他 CPU 核⼼;
要引⼊「锁」的概念,如果两个 CPU 核⼼⾥有相同数据的 Cache,那么对于这个 Cache 数据的更新,只有拿到了「锁」,才能进⾏对应的数据更新。

MESI协议

MESI协议其实是 4 个状态单词的开头字⺟缩写,分别是:

  • Modified,已修改(脏标记)
  • Exclusive,独占
  • Shared,共享
  • Invalidated,已失效

这四个状态来标记 Cache Line 四个不同的状态。
在这里插入图片描述

CPU是如何执行任务的?

现代CPU的架构图:
在这里插入图片描述
CPU 从内存中读取数据到 Cache 的时候,并不是⼀个字节⼀个字节读取,⽽是⼀块⼀块的⽅式来读取数
据的,这⼀块⼀块的数据被称为 CPU Line(缓存⾏),所以 CPU Line 是 CPU 从内存读取数据到 Cache的单位

伪共享

伪共享:因为多个线程同时读写同一个Cache Line的不同变量时,而导致CPU Cache失效的现象。
解决办法:用空间换时间,对齐地址。
有⼀个 Java 并发框架 Disruptor 使⽤「字节填充 + 继承」的⽅式,来避免伪共享的问题。

CPU如何选择线程的?

调度类

由于任务有优先级之分, Linux 系统为了保障⾼优先级的任务能够尽可能早的被执⾏,于是分为了这⼏种调度类:
在这里插入图片描述
Deadline 和 Realtime 这两个调度类,都是应⽤于实时任务的,这两个调度类的调度策略合起来共有这三种,它们的作⽤如下:

  • SCHED_DEADLINE:是按照 deadline 进⾏调度的,距离当前时间点最近的 deadline 的任务会被优先调度;
  • SCHED_FIFO:对于相同优先级的任务,按先来先服务的原则,但是优先级更⾼的任务,可以抢占低优先级的任务,也就是优先级⾼的可以「插队」;
  • SCHED_RR:对于相同优先级的任务,轮流着运⾏,每个任务都有⼀定的时间⽚,当⽤完时间⽚的任务会被放到队列尾部,以保证相同优先级任务的公平性,但是⾼优先级的任务依然可以抢占低优先级的任务;

而 Fair 调度类是应⽤于普通任务,都是由 CFS 调度器管理的,分为两种调度策略:

  • SCHED_NORMAL:普通任务使⽤的调度策略;
  • SCHED_BATCH:后台任务的调度策略,不和终端进⾏交互,因此在不影响其他需要交互的任务,可以适当降低它的优先级。
完全公平调度

我们平⽇⾥遇到的基本都是普通任务,对于普通任务来说,公平性最重要,在 Linux ⾥⾯,实现了⼀个基于 CFS 的调度算法,也就是完全公平调度(Completely Fair Scheduling) 。
这个算法的理念是想让分配给每个任务的 CPU 时间是⼀样,于是它为每个任务安排⼀个虚拟运⾏时间vruntime,如果⼀个任务在运⾏,其运⾏的越久,该任务的 vruntime ⾃然就会越⼤,⽽没有被运⾏的任务, vruntime 是不会变化的。
那么, 在 CFS 算法调度的时候,会优先选择 vruntime 少的任务,以保证每个任务的公平性。
在这里插入图片描述

CPU运行队列

⼀个系统通常都会运⾏着很多任务,多任务的数量基本都是远超 CPU 核⼼数量,因此这时候就需要排队。
事实上,每个 CPU 都有⾃⼰的运⾏队列(Run Queue, rq) ,⽤于描述在此 CPU 上所运⾏的所有进程,其队列包含三个运⾏队列, Deadline 运⾏队列 dl_rq、实时任务运⾏队列 rt_rq 和 CFS 运⾏队列 csf_rq,其中 csf_rq 是⽤红⿊树来描述的,按 vruntime ⼤⼩来排序的,最左侧的叶⼦节点,就是下次会被调度的任务。
在这里插入图片描述
Deadline > Realtime > Fair
实时任务总是会⽐普通任务优先被执⾏

调整优先级

如果我们启动任务的时候,没有特意去指定优先级的话,默认情况下都是普通任务,普通任务的调度类是Fail,由 CFS 调度器来进⾏管理。 CFS 调度器的⽬的是实现任务运⾏的公平性,也就是保障每个任务的运⾏的时间是差不多的。

如果你想让某个普通任务有更多的执⾏时间,可以调整任务的 nice 值,从⽽让优先级⾼⼀些的任务执⾏更多时间。 nice 的值能设置的范围是 -20~19 , 值越低,表明优先级越⾼,因此 -20 是最⾼优先级, 19则是最低优先级,默认优先级是 0。
在这里插入图片描述
在这里插入图片描述
nice 调整的是普通任务的优先级,所以不管怎么缩⼩ nice 值,任务永远都是普通任务,如果某些任务要求实时性⽐较⾼,那么你可以考虑改变任务的优先级以及调度策略,使得它变成实时任务,⽐如:
在这里插入图片描述

软中断

中断

在计算机中,中断是系统⽤来响应硬件设备请求的⼀种机制,操作系统收到硬件的中断请求,会打断正在执⾏的进程,然后调⽤内核中的中断处理程序来响应请求。

中断请求的响应程序,也就是中断处理程序,要尽可能快的执⾏完,这样可以减少对正常进程运⾏调度地影响。

什么是软中断?
两部分
处理硬件请求
两部分
由内核触发
中断
上半部用来快速处理中断
硬中断
下半部用来延迟处理上半部未完成的工作
软中断
内核自定义事件

硬中断:主要是负责耗时短的⼯作,特点是快速执⾏;
软中断:主要是负责上半部未完成的⼯作,通常都是耗时⽐较⻓的事情,特点是延迟执⾏;

系统里有哪些软中断?

在 Linux 系统⾥,我们可以通过查看 /proc/softirqs 的 内容来知晓「软中断」的运⾏情况,以及/proc/interrupts 的 内容来知晓「硬中断」的运⾏情况。

每个 CPU核⼼都对应着⼀个内核线程。

如何定位软中断CPU使用率过高的问题?

每⼀个 CPU 都有各⾃的软中断内核线程,我们还可以⽤ ps 命令来查看内核线程,⼀般名字在中括号⾥⾯到,都认为是内核线程。

如果在 top 命令发现, CPU 在软中断上的使⽤率⽐较⾼,⽽且 CPU 使⽤率最⾼的进程也是软中断ksoftirqd 的时候,这种⼀般可以认为系统的开销被软中断占据了。

这时我们就可以分析是哪种软中断类型导致的,⼀般来说都是因为⽹络接收软中断导致的,如果是的话,可以⽤ sar 命令查看是哪个⽹卡的有⼤量的⽹络包接收,再⽤ tcpdump 抓⽹络包,做进⼀步分析该⽹络包的源头是不是⾮法地址,如果是就需要考虑防⽕墙增加规则,如果不是,则考虑硬件升级等。

为什么0.1+0.2不等于0.3?

由于计算机的资源是有限的,所以是没办法⽤⼆进制精确的表示 0.1,只能⽤「近似值」来表示,就是在有限的精度情况下,最⼤化接近 0.1 的⼆进制数,于是就会造成精度缺失的情况。

为什么负数要用补码表示?

补码:所谓的补码就是把正数的⼆进制全部取反再加 1。

负数之所以⽤补码的⽅式来表示,主要是为了统⼀和正数的加减法操作⼀样,毕竟数字的加减法是很常⽤的⼀个操作,就不要搞特殊化,尽量以统⼀的⽅式来运算。

计算机是怎么存小数的?

在这里插入图片描述

二、操作系统结构

什么是内核?

对于内核的架构⼀般有这三种类型:

  • 宏内核,包含多个模块,整个内核像⼀个完整的程序;
  • 微内核,有⼀个最小版本的内核,⼀些模块和服务则由⽤户态管理;
  • 混合内核,是宏内核和微内核的结合体,内核中抽象出了微内核的概念,也就是内核中会有⼀个小型的内核,其他模块就在这个基础上搭建,整个内核是个完整的程序;

Linux 的内核设计是采⽤了宏内核, Window 的内核设计则是采⽤了混合内核。
这两个操作系统的可执⾏⽂件格式也不⼀样, Linux 可执⾏⽂件格式叫作 ELF, Windows 可执⾏⽂件格式叫作 PE。

三、内存管理

四、进程与线程

五、调度算法

六、文件系统

七、设备管理

八、网络系统

九、Linux命令

后记

持续更新积累。

http://www.dtcms.com/a/287557.html

相关文章:

  • 从零入门:云迁移原理详解与华为Rainbow实战指南
  • Linux进程通信——共享内存:System V 进程间通信的极速方案
  • FreeRTOS学习笔记之软件定时器
  • C语言菜鸟入门·浅析strdup和strcpy的区别
  • 1.初始化
  • 【电脑】声卡的基础知识
  • CTF misc之数字取证
  • 我做的基础服务项目,是如何实现 API 安全与限流的(短信、邮件、文件上传、钉钉通知)
  • lazyvim配置
  • 教育科技产品设计:从公司背景到 MVP 方案的落地思路
  • laravel RedisException: Connection refused优雅草PMS项目管理系统报错解决-以及Redis 详细指南-优雅草卓伊凡
  • 传统行业和AIGC的结合及应用
  • Spring AI 项目实战(十八):Spring Boot + AI + Vue3 + OSS + DashScope 实现高效语音识别系统(附完整源码)
  • PyQt5—Qt QDialog 学习笔记
  • 【RK3576】【Android14】SDK源码编译
  • 【RK3576】【Android14】UART开发调试
  • JavaScript基础语法和简单数据结构
  • 【小沐学GIS】基于Rust绘制三维数字地球Earth(Rust、OpenGL、GIS)
  • RPC(Remote Procedure Call,远程过程调用)介绍
  • MySQL数据丢失救援办法
  • 下一场范式革命:Transformer架构≠最终解法
  • 《全栈博客系统的技术肌理:从接口构建到体验升维的实践路径》
  • 美国VPS服务器Linux内核参数调优的实践与验证
  • 第二次总结(xss、js原型链)
  • 【2025最新】使用neo4j实现GraphRAG所需的向量检索
  • OAIF:基于在线 AI 反馈的语言模型直接对齐
  • [MarkdownGithub] 使用块引用高亮显示“注意“和“警告“和其他注意方式的选项
  • Django母婴商城项目实践(九)- 商品列表页模块
  • vue2 面试题及详细答案150道(121 - 130)
  • [Python] -实用技巧10- 时间处理:datetime 和 time 模块入门