计算机底层理解:字节、CPU、虚拟内存、mm_struct、函数栈帧、数据段、程序运行

计算机底层理解:字节、CPU、虚拟内存、mm_struct、函数栈帧、数据段、程序运行
对字节的理解对字节的理解都知道在计算机世界01能表示任何物体那01的表示总有数量标准的规定吧总不能你规定0101表示苹果我010101表示苹果01的数量不对等这样肯定不行那我到底用几个位来表示一个物品呢所以我们规定了用字节来表示一个字节由八个比特位八个0/1作为一个基本计量单位这样一个A规定一个字节表示一个汉字规定三个字节表示而依据什么规则去用字节来表示字符呢 字符需要通过编码规则如ASCII、UTF-8转换为字节序列才能在计算机中存储这样所有物品都由规则的用01来表示了ps:就像是我们常说的一对一样一对是几个呢我们规定一对是两个用一对当作基本统计单位一样我们来把把这个逻辑链条梳理得更具象化一点物理层比特计算机里只有电流的开关0和1。计量层字节为了方便管理我们把8个0/1打包成一个“字节”作为基本搬运单位。逻辑层编码为了解释这些0/1代表什么我们查阅“编码表”如UTF-8。查到01000001- 翻译为A查到1110xxxx...- 翻译为中应用层万物无数的字节组合在一起通过不同的解释规则就变成了你看到的文章、图片图片是字节的颜色坐标、视频字节的动态序列。这样我们就能更好的理解——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 里的记录的内容来找到进程对应的真正内存地址程序的视角虚拟内存连续且有序操作系统为每个进程程序都提供了一个独立的、私有的虚拟地址空间。在这个空间里内存布局是固定的、连续的。正如你所知它通常被划分为几个区域代码段 (Text Segment):存放程序的机器指令是只读的。数据段 (Data Segment):存放已初始化的全局变量和静态变量。BSS段:存放未初始化的全局变量和静态变量。堆 (Heap):用于动态内存分配如malloc从低地址向高地址增长。栈 (Stack):用于函数调用存放局部变量、函数参数等从高地址向低地址增长。从程序的角度看这些区域就像一本页码连续的书访问起来非常方便。操作系统的视角物理内存分散且高效在真实的物理内存条上情况完全不同。操作系统通过一个叫分页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语言函数调用为例看看底层发生了什么准备阶段在调用者函数中参数压栈编译器会生成指令将函数的参数按照从右到左的顺序依次“压入”栈中。调用函数执行call指令。这个指令会做两件关键事将下一条指令的地址返回地址压入栈这样函数执行完后才知道该回到哪里。跳转到被调用函数的入口地址。建立新栈帧在被调用函数的开头这是最关键的一步通常由三条汇编指令完成称为“函数序言Function Prologue”push ebp保存上一层函数的栈底指针EBP形成一个调用链。mov ebp, esp将当前的栈顶指针ESP的值赋给EBP。现在EBP就成为了当前函数栈帧的固定基准点。sub esp, N将栈顶指针ESP向下移动N个字节为当前函数的所有局部变量分配空间。至此一个全新的、独立的栈帧就创建好了。存储局部变量现在栈帧已经建立EBP是基准点。那么局部变量存在哪里呢答案是它们就存放在我们刚刚用sub esp, N开辟出来的空间里通过相对于EBP的偏移量来访问。比如第一个局部变量可能存放在[ebp - 4]这个地址。第二个局部变量可能存放在[ebp - 8]这个地址。而函数的参数则可以通过[ebp 8]、[ebp 12]等正偏移量来访问。所以栈并不是“知道”要存局部变量而是编译器在编译时就已经计算好了需要多少空间并生成了精确的指令来分配空间和通过偏移量访问它们。销毁栈帧在函数返回时函数执行完毕需要返回时会执行“函数结语Function Epilogue”来清理现场mov esp, ebp将栈顶指针ESP恢复到EBP的位置相当于释放了所有局部变量占用的空间。pop ebp将栈中保存的上一层函数的EBP值弹出恢复到EBP寄存器回到了上一层函数的栈帧。ret从栈中弹出之前保存的返回地址并跳转回去程序继续执行。整个过程就像搭积木调用函数时搭上一块创建栈帧函数返回时拆掉这块销毁栈帧一切都有条不紊完全由编译器和CPU的指令自动完成。对数据段的理解数据段里存放的绝对不是01这种指令码而是数据的“真身”二进制数值。为了让你彻底明白我们需要区分“动作”和“对象”。1. 核心区别指令 vs 数据代码段.text存放的是动作。比如01假设的指令码代表“加法”。这是告诉 CPU“去做什么”。数据段.data存放的是对象。比如10二进制1010。这是告诉 CPU“操作的具体数值是多少”。打个比方代码段就像剧本上面写着“主角拿起苹果”。这是指令数据段就像道具库里面真的放着一个苹果。这是数据2. 深入底层int count 10;到底长啥样当你写下int count 10;这行代码经过编译器编译后它在磁盘上的可执行文件比如.data段里是这样的在数据段.data里这里只存数值不存变量名变量名在编译后通常就丢了变成了地址。内容就是数字10的二进制形式。样子在 32 位系统里它通常长这样十六进制表示0A 00 00 00。这就是10的真身。CPU 读到这个内存地址时它看到的不是“指令”而是“数值 10”。在代码段.text里这里存的是搬运指令。为了让count变成 10CPU 需要执行指令。指令类似于MOV [地址], 10。样子这才会出现类似C7 05 ...这样的操作码Opcode。这条指令的意思是“把数值 10 搬运到内存的某个地址去”。ps : 不就是代码段里的指令写好了要去哪里拿数据数据段在那个地址放着数据对程序运行阶段的总结进程创建与账本建立双击 exe 后操作系统首先创建 PCB进程控制块并初始化mm_struct内存描述符这是管理内存的“总账本”。虚拟地址空间规划画地图加载器读取 exe 文件头在虚拟地址空间中划定代码段、数据段、堆和栈的范围但此时不分配任何物理内存。VMA 与文件映射mm_struct通过 VMA虚拟内存区域记录虚拟地址与磁盘文件的对应关系例如虚拟地址 0x400000 对应 exe 文件的第 0 字节。首次执行与缺页异常CPU 尝试执行第一条指令时因页表为空触发“缺页异常”内核介入分配物理页框并将代码从硬盘读入内存。栈的自动分配栈空间在虚拟内存中高地址向下增长。当栈指针移动触发缺页异常时内核自动分配物理页实现“按需增长”。堆的延迟分配malloc仅通过brk/mmap扩展虚拟地址范围只有当程序真正读写堆内存时才会触发缺页异常并分配物理页。物理内存的碎片化与映射虚拟内存是连续的但对应的物理内存是离散的。通过页表PGD的映射操作系统将零散的物理页框Page Frame拼接成连续的虚拟空间。核心机制总结虚拟内存的本质是“按需调页”。操作系统通过“画饼”虚拟地址和“按需兑现”缺页异常处理实现了内存的高效利用和进程隔离。