计算机知识点的理解开悟后的分享(一)

对字节的理解

对字节的理解:都知道在计算机世界01能表示任何物体,那01的表示总有数量标准的规定吧,总不能你规定0101表示苹果,我010101表示苹果,01的数量不对等,这样肯定不行,那我到底用几个位来表示一个物品呢,所以我们规定了用字节来表示,一个字节由八个比特位,八个0/1,作为一个基本计量单位,这样一个'A'规定一个字节表示,一个汉字规定三个字节表示,而依据什么规则去用字节来表示字符呢 : 字符需要通过编码规则(如ASCII、UTF-8)转换为字节序列才能在计算机中存储,这样所有物品都由规则的用01来表示了(ps:就像是我们常说的一对一样,一对是几个呢,我们规定一对是两个,用一对当作基本统计单位一样)

我们来把把这个逻辑链条梳理得更具象化一点:

  1. 物理层(比特):计算机里只有电流的开关(0和1)。
  2. 计量层(字节) :为了方便管理,我们把8个0/1打包成一个**"字节"**,作为基本搬运单位。
  3. 逻辑层(编码) :为了解释这些0/1代表什么,我们查阅**"编码表"** (如UTF-8)。
    • 查到 01000001 -> 翻译为 'A'
    • 查到 1110xxxx... -> 翻译为 '中'
  4. 应用层(万物):无数的字节组合在一起,通过不同的解释规则,就变成了你看到的文章、图片(图片是字节的颜色坐标)、视频(字节的动态序列)。

这样我们就能更好的理解------01与万物的联系


对CPU、内存、硬盘之间联系的理解

都说CPU与硬盘之间的速度差异过大,内存来缓解,那是怎么缓解的呢,为什么要缓解呢?

CPU:内存:硬盘:

这里提到的"速度"通俗来说,就是**"从发出指令到真正拿到数据所需要等待的时间"**。

  • CPU :它的"速度"是指它处理指令的周期。现在的CPU主频很高,处理一个指令可能只需要 0.3纳秒 左右。
  • 内存 :它的"速度"是指CPU喊它要数据,它多久能把数据送过来。这个时间大约是 50~100纳秒
  • 硬盘 :它的"速度"是指磁头找到数据位置(寻道)加上把数据读出来的时间。机械硬盘大约需要 5~10毫秒(即5,000,000纳秒),即便是很快的固态硬盘(SSD)也需要几十到上百微秒。

可以这样理解,a从老远的菜园摘菜送到厨房的时间,b把所有菜都洗好,切好,放好调料备用,c只需放菜到锅里,猛火爆炒5s出锅,b就相当于内存起到的作用

还有更完整的知识点点击这里


对CPU是如何不断工作的理解

CPU...(暂定)


虚拟内存空间的理解

ps :虚拟内存空间,那说白了,不就操作系同主要是出于减少内存消耗的目的"骗"一个进程有对应的内存空间,说是你这个进程的内存空间是0x0000到0xFFFF,其实也就在PCB的mm_struct 是这样写的,其实内存里啥也没有,直到CPU要拿或存这个进程的01码或者数据时,才会在内存中找一块空间把.exe文件的内容复制上来,其实也不是找一块,可能是找零零散散的内存空间,但操作系统可以内存映射啊,你这些零零散散的内存空间在内存里是这样的,但在映射前,比如说一群a1,a2,a3,a..,映射一群b1,b2,b3,b...,b1,b2,b3,b...是散的,但我a1,a2,a3,a....是完整有序的啊,它甚至有一个名字叫虚拟内存空间,从高地址到低地址,有栈,堆,.bss段,.data段,.rodata段,.text段。那就是PCB里的mm_struct里是这样写的,真找这个进程的内存数据时,是要通过mm_struct 里的记录的内容来找到进程对应的真正内存地址

  1. 程序的视角(虚拟内存):连续且有序

    操作系统为每个进程(程序)都提供了一个独立的、私有的虚拟地址空间。在这个空间里,内存布局是固定的、连续的。正如你所知,它通常被划分为几个区域:

    • 代码段 (Text Segment): 存放程序的机器指令,是只读的。
    • 数据段 (Data Segment): 存放已初始化的全局变量和静态变量。
    • BSS段: 存放未初始化的全局变量和静态变量。
    • 堆 (Heap): 用于动态内存分配(如 malloc),从低地址向高地址增长。
    • 栈 (Stack): 用于函数调用,存放局部变量、函数参数等,从高地址向低地址增长。

    从程序的角度看,这些区域就像一本页码连续的书,访问起来非常方便。

  2. 操作系统的视角(物理内存):分散且高效

    在真实的物理内存条上,情况完全不同。操作系统通过一个叫**分页(Paging)**的机制来管理内存。

    • 分页映射: 虚拟内存和物理内存都被划分成固定大小的块(例如4KB),分别叫"页"和"页框"。
    • 页表: 操作系统为每个进程维护一张"页表 ",这张表就像一个目录,记录了程序的哪个虚拟"页"对应物理内存中的哪个"页框"。
    • 非连续分配: 一个程序在虚拟空间中连续的几页,在物理内存中可以被分配到任何空闲的页框里,它们之间不需要是连续的。

    这种设计的好处非常多:

    • 突破物理限制: 可以让程序使用比实际物理内存更大的空间(部分数据可以暂时存放在硬盘上)。
    • 简化内存分配: 操作系统只需找零散的空闲页框即可,无需寻找一大块连续的空闲物理内存。
    • 内存保护: 每个进程都有自己独立的虚拟空间和页表,无法访问其他进程的内存,保证了系统的安全和稳定。

mm_struct的内容理解

mm_struct 在PCB结构体中,记录了在虚拟内存空间中的代码段,堆,栈的位置,还有虚拟地址与真实地址对应关系

  • mm_struct 是这张地图的总管家,它手里拿着两份核心文件:

    • VMA 链表 :记录了地图的区域规划 (哪里是代码,哪里是堆),以及这些区域对应硬盘上的哪个文件
    • PGD(页表) :记录了地图上的坐标(虚拟地址)和**真实世界坐标(物理地址)**的对应关系。
  • VMA(虚拟内存区域)

    • 作用 :它是**"合法性检查员"** 和**"规则制定者"**。
    • 场景:当你访问一个地址时,内核(操作系统)会先看 VMA 链表。VMA 会告诉你:"这个地址属于堆区,你是可读可写的"或者"这个地址属于代码段,你只能读不能写"。如果地址不在任何 VMA 范围内,那就是非法访问(Segmentation Fault)。
    • 记录内容:它记录了"地图上的这块区域(比如 0x400000-0x500000)对应磁盘上的哪个文件(比如 exe 文件),是数据段,还是代码段,还是栈,还是堆"。
  • PGD(页全局目录,页表的一部分)

    • 作用 :它是**"极速导航员"**。
    • 场景 :当 VMA 确认地址合法后,CPU 的 MMU(内存管理单元)硬件会直接去查 PGD(以及后面的多级页表)。PGD 不负责讲规则,它只负责翻译:直接把虚拟地址转换成物理地址。按照映射规则,通过虚拟地址在硬盘中找到物理地址。
    • 记录内容:它记录了"虚拟页号 10 对应物理页框号 888"。

对函数栈帧的理解

栈帧的创建与销毁流程

我们以一个经典的C语言函数调用为例,看看底层发生了什么:

  1. 准备阶段(在调用者函数中)

    • 参数压栈: 编译器会生成指令,将函数的参数按照从右到左的顺序依次"压入"栈中。
    • 调用函数: 执行 call 指令。这个指令会做两件关键事:
      • 将下一条指令的地址(返回地址)压入栈,这样函数执行完后才知道该回到哪里。
      • 跳转到被调用函数的入口地址。
  2. 建立新栈帧(在被调用函数的开头)

    这是最关键的一步,通常由三条汇编指令完成,称为"函数序言(Function Prologue)":

    • push ebp:保存上一层函数的栈底指针(EBP),形成一个调用链。
    • mov ebp, esp:将当前的栈顶指针(ESP)的值赋给EBP。现在,EBP就成为了当前函数栈帧的固定基准点
    • sub esp, N:将栈顶指针ESP向下移动N个字节,为当前函数的所有局部变量分配空间。

    至此,一个全新的、独立的栈帧就创建好了。

  3. 存储局部变量

    现在,栈帧已经建立,EBP是基准点。那么局部变量存在哪里呢?答案是:它们就存放在我们刚刚用 sub esp, N 开辟出来的空间里,通过相对于EBP的偏移量来访问。

    • 比如,第一个局部变量可能存放在 [ebp - 4] 这个地址。
    • 第二个局部变量可能存放在 [ebp - 8] 这个地址。
    • 而函数的参数,则可以通过 [ebp + 8][ebp + 12] 等正偏移量来访问。

    所以,栈并不是"知道"要存局部变量,而是编译器在编译时就已经计算好了需要多少空间,并生成了精确的指令来分配空间和通过偏移量访问它们。

  4. 销毁栈帧(在函数返回时)

    函数执行完毕,需要返回时,会执行"函数结语(Function Epilogue)"来清理现场:

    • mov esp, ebp:将栈顶指针ESP恢复到EBP的位置,相当于释放了所有局部变量占用的空间。
    • pop ebp:将栈中保存的上一层函数的EBP值弹出,恢复到EBP寄存器,回到了上一层函数的栈帧。
    • ret:从栈中弹出之前保存的返回地址,并跳转回去,程序继续执行。

整个过程就像搭积木,调用函数时搭上一块(创建栈帧),函数返回时拆掉这块(销毁栈帧),一切都有条不紊,完全由编译器和CPU的指令自动完成。


对数据段的理解

数据段里存放的绝对不是 01 这种指令码,而是数据的"真身"(二进制数值)。

为了让你彻底明白,我们需要区分**"动作"** 和**"对象"**。

  1. 核心区别:指令 vs 数据
  • 代码段(.text) :存放的是动作
    • 比如 01(假设的指令码)代表"加法"。这是告诉 CPU "去做什么"
  • 数据段(.data) :存放的是对象
    • 比如 10(二进制 1010)。这是告诉 CPU "操作的具体数值是多少"

打个比方:

  • 代码段 就像剧本,上面写着:"主角拿起苹果"。(这是指令)
  • 数据段 就像道具库 ,里面真的放着一个苹果。(这是数据)
  1. 深入底层:int count = 10; 到底长啥样?

当你写下 int count = 10; 这行代码,经过编译器编译后,它在磁盘上的可执行文件(比如 .data 段)里是这样的:

在数据段(.data)里:

这里只存数值,不存变量名(变量名在编译后通常就丢了,变成了地址)。

  • 内容 :就是数字 10 的二进制形式。
  • 样子 :在 32 位系统里,它通常长这样(十六进制表示):0A 00 00 00
    • 这就是 10 的真身。CPU 读到这个内存地址时,它看到的不是"指令",而是"数值 10"。

在代码段(.text)里:

这里存的是搬运指令

  • 为了让 count 变成 10,CPU 需要执行指令。
  • 指令 :类似于 MOV [地址], 10
  • 样子 :这才会出现类似 C7 05 ... 这样的操作码(Opcode)。这条指令的意思是:"把数值 10 搬运到内存的某个地址去"。

ps : 不就是代码段里的指令写好了,要去哪里拿数据,数据段在那个地址放着数据


对程序运行阶段的总结

  1. 进程创建与账本建立 :双击 exe 后,操作系统首先创建 PCB(进程控制块),并初始化 mm_struct(内存描述符),这是管理内存的"总账本"。
  2. 虚拟地址空间规划(画地图) :加载器读取 exe 文件头,在虚拟地址空间中划定代码段、数据段、堆和栈的范围,但此时不分配任何物理内存
  3. VMA 与文件映射mm_struct 通过 VMA(虚拟内存区域)记录虚拟地址与磁盘文件的对应关系(例如:虚拟地址 0x400000 对应 exe 文件的第 0 字节)。
  4. 首次执行与缺页异常:CPU 尝试执行第一条指令时,因页表为空触发"缺页异常",内核介入,分配物理页框并将代码从硬盘读入内存。
  5. 栈的自动分配:栈空间在虚拟内存中高地址向下增长。当栈指针移动触发缺页异常时,内核自动分配物理页,实现"按需增长"。
  6. 堆的延迟分配malloc 仅通过 brk/mmap 扩展虚拟地址范围,只有当程序真正读写堆内存时,才会触发缺页异常并分配物理页。
  7. 物理内存的碎片化与映射:虚拟内存是连续的,但对应的物理内存是离散的。通过页表(PGD)的映射,操作系统将零散的物理页框(Page Frame)拼接成连续的虚拟空间。
  8. 核心机制总结 :虚拟内存的本质是**"按需调页"**。操作系统通过"画饼"(虚拟地址)和"按需兑现"(缺页异常处理),实现了内存的高效利用和进程隔离。
相关推荐
独孤九剑打醒他2 小时前
#原创声明 #拒绝白嫖 #技术立场 #创作者保护
笔记
avocado_green2 小时前
【考驾照】科目一备考笔记(个人手工整理,非AI生成)
笔记
雾岛听蓝3 小时前
Qt操作指南:状态栏、浮动窗口与对话框使用
开发语言·经验分享·笔记·qt
APIshop3 小时前
小红书笔记视频详情接口深度解析:smallredbook.item_get_video_pro
数据库·笔记·音视频
y = xⁿ3 小时前
MySQL学习笔记:乐观锁VS悲观锁/八股总结
笔记·学习·mysql
d111111111d4 小时前
STM32-UART抽象层封装
笔记·stm32·单片机·嵌入式硬件·学习
chushiyunen4 小时前
notion(模块化数字工作台)笔记
笔记·notion
三品吉他手会点灯5 小时前
C语言学习笔记 - 12.C语言简介 - 一元二次方程详解
c语言·笔记·学习
Cathy Bryant6 小时前
微分几何:度规(度量)metric
笔记·线性代数·矩阵·高等数学·物理