Post

overview

overview

1. 计算机系统概述

1.1 CPU核心架构

现代CPU是一个精密的执行引擎,其核心组件包括:

image-20250929105005528

内存子系统

  • 内存可以类比为快递箱系统:支持数据的存储和读取
  • 存储的最小单元是字节(byte)
  • 内存中存储的都是二进制数据(0和1),数据的意义由程序定义
  • 指针:存储其他内存单元地址的特殊变量

内存访问优化

1
2
3
4
5
6
7
8
// 内存访问的局部性原理
int array[100][100];
// 按行访问(高效) vs 按列访问(低效)
for(int i = 0; i < 100; i++) {
    for(int j = 0; j < 100; j++) {
        array[i][j] = i + j;  // 空间局部性好
    }
}

寄存器文件

  • 数量有限的快速存储单元
  • 可以直接通过汇编指令访问
  • 比内存访问速度快几个数量级

算术逻辑单元(ALU)

  • 纯组合逻辑电路,无状态
  • 执行算术和逻辑运算
  • 类似纯函数:输入确定,输出确定

程序计数器(PC)

  • 特殊的寄存器,指向当前执行的指令地址
  • 控制程序的执行流程

控制单元

  • 指挥数据通路协调工作
  • 解释指令并生成控制信号
需要重点理解的内容

内存:取快递的快递箱,可以往里面放也可以往外拿;存储的最小单元;里面是一大堆01,所有的意义都是人赋予它的

指针:存储着另一个存储单元的地址

可以把memory想象成一个巨大的一维数组

为什么把四个放在一行?—否则效率太低,一次拿一批;地址总线和数据总线

寄存器的数量是有限的,于是我们可以指名道姓地指定用哪个寄存器—汇编语言

有了寄存器的内容,就要计算—ALU(算数和逻辑单元)

ALU是一种纯组合电路,可以理解成纯函数(没有任何状态的函数,任何时候我们输入a和b,都会输出c),属于组合逻辑电路

而有状态的就叫时序逻辑单元,是有状态的

PC也是一个寄存器,指向了当前运行的指令的位置,负责告诉我们程序运行到了哪里

程序的指令也认为是一种数据

CPU只能操作Datapath,谁在指挥呢?—Control

2. 程序执行全过程

2.1 从源代码到可执行文件

编译流程

C源代码 → 预处理器 → 编译器 → 汇编器 → 链接器 → 可执行文件

C语言代码怎么变成01?—纯软件过程,比如gcc

1
2
3
4
5
# 分步编译过程
gcc -E hello.c -o hello.i    # 预处理
gcc -S hello.i -o hello.s    # 编译
gcc -c hello.s -o hello.o    # 汇编
gcc hello.o -o hello         # 链接

image-20250929105043818

2.2 进程内存布局

image-20250929105036808

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
高地址
┌─────────────┐
│   内核空间   │
├─────────────┤
│   栈(stack)  │ ← 向下生长
│      ↓      │
│      ↑      │
│   堆(heap)   │ ← 向上生长
├─────────────┤
│   BSS段     │ ← 未初始化全局变量
├─────────────┤
│   数据段     │ ← 已初始化全局变量
├─────────────┤
│   代码段     │ ← 只读的程序指令
└─────────────┘
低地址

各段详细说明

代码段(.text)

  • 存储机器指令
  • 只读属性,防止程序意外修改指令
  • 在内存中通常有执行权限

数据段(.data)

  • 已初始化的全局变量和静态变量
  • 程序加载时即具有初始值

BSS段(.bss)

  • 未初始化的全局变量和静态变量
  • 程序加载时自动初始化为0
  • “Better Save Space” - 节省磁盘空间

堆(heap)

  • 动态内存分配区域
  • 手动管理(malloc/free, new/delete)
  • 向上生长

栈(stack)

  • 自动管理局部变量和函数调用信息
  • 向下生长
  • 每个函数调用创建栈帧

3. 堆与栈的深入理解

image-20250929103227387

3.1 栈(Stack)机制

如果没有栈也没有堆,内存就是一块平坦的线性空间,但是内存应该是一个高度动态的东西

如果想要实现函数调用,我们就不得不承认栈是最适合函数调用的机制!

只有当前的栈是active的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
栈帧结构

高地址
┌─────────────┐
│  参数n       │
├─────────────┤
│  ...        │
├─────────────┤
│  参数1       │
├─────────────┤
│  返回地址     │
├─────────────┤
│  保存的基址   │ ← EBP/RBP
├─────────────┤
│  局部变量1    │
├─────────────┤
│  ...        │
├─────────────┤
│  局部变量n    │ ← ESP/RSP
└─────────────┘
低地址

栈的核心特性

  • 后进先出(LIFO)的数据结构
  • 自动管理:编译器负责分配和释放
  • 仅当前栈帧处于活跃状态
  • 快速分配和释放

函数调用时会在堆内开辟一块内存,这块内存不会随着函数调用的结束而摧毁

但是堆并不知道什么时候会不需要自己,因此需要手动管理,这也就是堆容易造成内存泄漏的原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdlib.h>

void heapExample() {
    // 在堆上分配内存
    int* array = (int*)malloc(100 * sizeof(int));
    
    if (array != NULL) {
        // 使用堆内存
        for (int i = 0; i < 100; i++) {
            array[i] = i;
        }
        
        // 必须手动释放
        free(array);
        array = NULL;  // 避免悬空指针
    }
}

堆的核心特性

  • 动态内存分配
  • 手动管理,生命周期由程序员控制
  • 分配相对较慢,需要寻找合适的内存块
  • 容易产生内存泄漏和碎片
特性栈(Stack)堆(Heap)
管理方式自动手动
分配速度快(O(1))慢(需要搜索)
生命周期函数作用域直到显式释放
大小限制较小(通常MB级)较大(受系统内存限制)
碎片问题
安全性相对安全容易内存泄漏

4. 进程隔离与虚拟内存

每个进程只能玩自己的游戏,而操作系统是上帝视角,是游戏规则的制定者

4.1 虚拟内存的意义

解决的问题

  • 进程间内存隔离
  • 物理内存有限性
  • 内存访问保护

虚拟内存的优势

1
2
3
// 每个进程都有独立的地址空间
// 进程A看到的地址0x1000 ≠ 进程B看到的地址0x1000
// 操作系统通过页表实现地址转换

虚拟内存布局

text

1
2
3
4
5
6
7
8
9
10
11
每个进程的视角:
0x00000000 ┌─────────────┐
           │   代码段     │
           ├─────────────┤
           │   数据段     │
           ├─────────────┤
           │     堆      │
           │     ↑       │
           │     ↓       │
           │     栈      │
0x7fffffff └─────────────┘

4.2 地址转换机制

页表的作用

  • 虚拟地址 → 物理地址的映射
  • 内存访问权限控制
  • 实现内存共享和写时复制

5. RISC-V架构简介

https://eseo-tech.github.io/emulsiV/

5.1 RISC-V设计哲学

  • 精简指令集(RISC)架构
  • 模块化设计:基础指令集 + 标准扩展
  • 开源架构,无授权费用

5.2 主要特性

# RISC-V汇编示例
# 寄存器命名:x0-x31,其中:
# x0: 零寄存器(恒为0)
# x1: 返回地址(ra)
# x2: 栈指针(sp)
# x8: 帧指针(fp)

add x5, x6, x7    # x5 = x6 + x7
ld x10, 0(x11)    # 从内存加载
sd x12, 8(x13)    # 存储到内存

6.ELF:Executable and ​Linkable Format

6.1 链接与加载过程

符号(Symbol)概念

  • 函数名、全局变量等对外接口
  • 链接器通过符号解析解决依赖关系
  • 局部变量不是符号(作用域限制)

如何编译?

链接:把多个文件组合起来—合并什么?

合并依赖!

A需要B,B需要C

SYMBOL:对外的接口、全局变量、数据都属于symbol,symbol就是让外界能用的你提供的东西

在C语言中,局部变量不是symbol,而函数和全局变量是,因为他们是“稳定可靠”的

image-20250929105101696

symbol是一种中间状态!人—编译器—CPU

image-20250929100429437

ELF(可执行、可链接的格式)文件主要段

.text段:可执行代码 .data段:已初始化数据 .bss段:未初始化数据(节省磁盘空间)

better save space —未初始化的全局变量:我只需要存储有哪些全局变量,不需要给它赋值,节省了很多空间

.rodata段:只读数据 .strtab段:字符串表,存放字符串字面值 .symtab段:符号表

6.2 段(Sections)与节(Segments)

关键区别

  • 段(Sections):链接视图,供链接器使用,only relevant at runtime
  • 节(Segments):执行视图,供加载器使用,only relevant at link time

把sections理解成库,把segments理解成执行视图,会真的被加载到内存中

image-20250929105127606

6.3 加载过程详解

三步加载过程

  1. 解析头部
    • 读取ELF头验证文件格式
    • 检查架构兼容性
  2. 内存映射
    • 根据程序头表创建内存映射
    • 设置适当的权限(读/写/执行)
  3. 执行启动
    • 设置入口点
    • 传递环境变量和参数
    • 开始执行程序

示例加载代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 简化的加载器逻辑
void load_elf(Elf32_Ehdr *ehdr) {
    // 1. 验证ELF魔数
    if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
        ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
        ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
        ehdr->e_ident[EI_MAG3] != ELFMAG3) {
        return; // 无效的ELF文件
    }
    
    // 2. 遍历程序头表并建立映射
    Elf32_Phdr *phdr = (Elf32_Phdr *)((char *)ehdr + ehdr->e_phoff);
    for (int i = 0; i < ehdr->e_phnum; i++) {
        if (phdr[i].p_type == PT_LOAD) {
            // 映射到内存
            map_segment(&phdr[i]);
        }
    }
    
    // 3. 跳转到入口点
    jump_to_entry(ehdr->e_entry);
}

7. 实际应用与调试技巧

7.1 内存问题调试

常见内存错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1. 栈溢出
void stack_overflow() {
    int huge_array[1000000];  // 可能栈溢出
}

// 2. 内存泄漏
void memory_leak() {
    char *buffer = malloc(1024);
    // 忘记调用 free(buffer);
}

// 3. 悬空指针
void dangling_pointer() {
    char *ptr = malloc(100);
    free(ptr);
    // ptr现在成为悬空指针
    // strcpy(ptr, "dangerous");  // 未定义行为
}

调试工具

  • gdb:调试器
  • valgrind:内存检查工具
  • strace:系统调用跟踪

7.2 性能优化建议

  1. 利用局部性原理
  2. 减少动态内存分配
  3. 合理使用栈和堆
  4. 注意缓存友好性
This post is licensed under CC BY 4.0 by the author.

Trending Tags