程序员必备的十大技能(进阶版)之底层计算机原理(三)

简介: 教程来源 http://tmywi.cn/ 本节深入解析程序构建与系统底层机制:涵盖编译四阶段(预处理、编译、汇编、链接)、ELF文件结构及动态链接原理(PLT/GOT);并详解Linux进程实现(task_struct、fork/COW)、上下文切换开销、系统调用流程(syscall)与虚拟内存分页机制(四级页表)。

五、编译与链接

5.1 编译过程详解

┌─────────────────────────────────────────────────────────────────────────────┐
│                           编译过程                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   source.c  ──→  预处理  ──→  编译  ──→  汇编  ──→  链接  ──→  executable │
│                (cpp)      (cc1)     (as)      (ld)                         │
│                                                                             │
│   1. 预处理:                                                                │
│      - 展开宏定义(#define)                                                   │
│      - 包含头文件(#include)                                                  │
│      - 处理条件编译(#ifdef)                                                  │
│                                                                             │
│   2. 编译:                                                                │
│      - 词法分析 → 语法分析 → 语义分析 → 中间代码生成                           │
│      - 优化 → 汇编代码生成                                                   │
│                                                                             │
│   3. 汇编:                                                                │
│      - 将汇编代码转换为目标文件(.o)(机器码)                                  │
│                                                                             │
│   4. 链接:                                                                │
│      - 符号解析:处理外部引用                                                │
│      - 重定位:确定最终的内存地址                                            │
│      - 静态链接(.a) vs 动态链接(.so)                                        │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

5.2 ELF文件格式

/*
 * ELF (Executable and Linkable Format) 文件结构
 * 
 * ┌─────────────────────────────────────┐
 * │           ELF Header                │  ← 文件类型、入口点
 * ├─────────────────────────────────────┤
 * │         Program Headers             │  ← 段信息(用于加载)
 * │  (程序头表,用于运行时加载)          │
 * ├─────────────────────────────────────┤
 * │                                      │
 * │         .text (代码段)               │  ← 可执行指令
 * │                                      │
 * ├─────────────────────────────────────┤
 * │         .data (数据段)               │  ← 已初始化全局变量
 * ├─────────────────────────────────────┤
 * │         .bss (未初始化数据)           │  ← 未初始化全局变量
 * ├─────────────────────────────────────┤
 * │         .rodata (只读数据)           │  ← 字符串常量
 * ├─────────────────────────────────────┤
 * │         Section Headers             │  ← 节信息(用于链接)
 * │  (节头表,用于链接和调试)            │
 * └─────────────────────────────────────┘
 */

// 查看ELF文件信息
// readelf -h a.out          // 查看头部
// readelf -S a.out          // 查看节表
// objdump -d a.out          // 反汇编
// size a.out                // 查看各段大小

5.3 动态链接与PLT/GOT

/*
 * 动态链接过程
 * 
 * 程序调用共享库函数:
 * call printf@plt
 * 
 * PLT(Procedure Linkage Table):过程链接表
 * GOT(Global Offset Table):全局偏移表
 * 
 * 第一次调用:
 * call printf@plt  ──→  .plt  ──→ 跳转到 GOT
 *                     │
 *                     ↓
 *                动态链接器解析printf地址
 *                     │
 *                     ↓
 *                更新GOT条目 ──→ 执行printf
 * 
 * 后续调用:
 * call printf@plt ──→ .plt ──→ GOT(已存地址)──→ 直接执行printf
 */

// 查看动态依赖
ldd a.out
// linux-vdso.so.1 (0x00007ffe5bdfe000)
// libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8a2c800000)
// /lib64/ld-linux-x86-64.so.2 (0x00007f8a2ca2c000)

// 查看GOT表
objdump -R a.out

六、操作系统底层原理

6.1 进程与线程的底层实现

/*
 * Linux进程描述符(task_struct)
 */

struct task_struct {
    volatile long state;              // 进程状态
    void *stack;                      // 内核栈
    unsigned int flags;               // 进程标志
    int prio, static_prio;            // 优先级
    struct mm_struct *mm;             // 内存描述符
    struct files_struct *files;       // 文件描述符表
    struct signal_struct *signal;     // 信号处理
    struct task_struct *parent;       // 父进程
    struct list_head children;        // 子进程列表

    // 调度相关
    int on_rq;
    struct sched_entity se;           // CFS调度实体
    struct sched_rt_entity rt;        // 实时调度实体

    // 时间相关
    cputime_t utime, stime;           // 用户态/内核态CPU时间
    struct timespec start_time;       // 启动时间

    // 命名空间
    struct nsproxy *nsproxy;           // 命名空间代理
};

/*
 * 进程创建(fork系统调用)
 * 
 * fork() → sys_fork() → do_fork() → copy_process()
 * 
 * copy_process()关键步骤:
 * 1. 分配新的task_struct
 * 2. 复制进程内存空间(写时复制,COW)
 * 3. 复制文件描述符
 * 4. 复制信号处理表
 * 5. 设置进程状态为TASK_RUNNABLE
 * 6. 返回子进程PID
 */

// 写时复制(Copy-on-Write)原理
// fork后父子进程共享物理内存页
// 只有当某个进程尝试写入时,才复制该页

6.2 上下文切换成本

/*
 * 上下文切换需要保存和恢复的寄存器
 */

// 内核栈上保存的上下文(简化)
struct context {
    unsigned long r15, r14, r13, r12, rbp, rbx;
    unsigned long r11, r10, r9, r8;
    unsigned long rax, rcx, rdx, rsi, rdi;
    unsigned long rip;        // 指令指针
    unsigned long cs, flags;  // 代码段、标志寄存器
    unsigned long rsp;        // 栈指针
    unsigned long ss;         // 栈段
};

// 上下文切换成本:
// 1. 保存/恢复寄存器:~100-200个时钟周期
// 2. TLB刷新:~几十到几百个时钟周期(取决于TLB大小)
// 3. 缓存失效:L1/L2缓存污染,L3部分保留
// 4. 切换页表:cr3寄存器写入,TLB flush(部分处理器有TLB条目tag)

// 总成本:约1-10微秒(取决于体系结构和负载)

6.3 系统调用原理

/*
 * 系统调用流程(x86-64)
 * 
 * 用户态:
 * 1. 将系统调用号放入rax
 * 2. 将参数放入rdi, rsi, rdx, r10, r8, r9
 * 3. 执行syscall指令
 * 
 * 内核态:
 * 4. CPU切换到内核态(权限级别从3切换到0)
 * 5. 根据rax中的系统调用号查找sys_call_table
 * 6. 执行对应的内核函数
 * 7. 返回值放入rax
 * 8. 执行sysret返回用户态
 */

// 示例:write系统调用
// ssize_t write(int fd, const void *buf, size_t count);

// 汇编实现
write:
    movq $1, %rax           // write系统调用号=1
    movq $1, %rdi           // fd=1 (stdout)
    leaq msg(%rip), %rsi    // buf指针
    movq $13, %rdx          // count=13
    syscall                 // 陷入内核
    ret

msg:
    .string "Hello, World\n"

// 使用strace追踪系统调用
// strace -c ./program     // 统计系统调用次数和耗时
// strace -e trace=file ./program  // 只追踪文件相关调用

6.4 虚拟内存与分页

/*
 * 虚拟内存地址转换(x86-64 四级页表)
 * 
 * 虚拟地址(48位有效):
 * ┌───────┬───────┬───────┬───────┬────────────┐
 * │  PML4 │  PDP  │   PD  │   PT  │    Offset  │
 * │  9位  │  9位  │  9位  │  9位  │    12位    │
 * └───────┴───────┴───────┴───────┴────────────┘
 * 
 * 转换过程:
 * 1. 从CR3寄存器读取PML4页表基址
 * 2. 使用PML4索引找到PDP页表
 * 3. 使用PDP索引找到PD页表
 * 4. 使用PD索引找到PT页表
 * 5. 使用PT索引找到物理页帧号(PFN)
 * 6. 物理地址 = PFN << 12 + Offset
 */

// 查看进程内存映射
cat /proc/<PID>/maps

// 示例输出:
// 00400000-0040b000 r-xp 00000000 08:01 12345   /usr/bin/cat
// 0060a000-0060b000 r--p 0000a000 08:01 12345   /usr/bin/cat
// 0060b000-0060c000 rw-p 0000b000 08:01 12345   /usr/bin/cat
// 7f8a2c000000-7f8a2c200000 rw-p 00000000 00:00 0      [heap]
// 7f8a2ca00000-7f8a2cc00000 r-xp 00000000 08:01 67890   /lib/x86_64-linux-gnu/libc.so.6
// 7ffe5bdfe000-7ffe5be1f000 rw-p 00000000 00:00 0      [stack]
// ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0   [vsyscall]

// 内存映射区域权限:
// r = 读, w = 写, x = 执行, p = 私有, s = 共享

来源:
http://yyvgt.cn/

相关文章
|
4天前
|
存储 缓存 固态存储
程序员必备的十大技能(进阶版)之底层计算机原理(四)
教程来源 oplhc.cn 本文深入解析存储I/O与性能极限:涵盖HDD/SSD物理特性、Linux I/O栈及零拷贝优化;剖析CPU功耗墙、内存墙与Amdahl定律;并结合缓存友好设计、伪共享规避、分支预测优化及SIMD向量化等底层编程实践,助力高性能系统开发。
|
4天前
|
Kubernetes 安全 NoSQL
程序员进阶工程师必备技能之工程化与研发效率建设(四)
教程来源 https://bgnno.cn/ 该CI/CD流水线基于GitHub Actions构建:CI阶段涵盖代码规范检查(Black/Isort/Ruff/Mypy)、单元与集成测试(含PostgreSQL/Redis服务)、Docker镜像构建及Trivy安全扫描;CD阶段支持语义化版本触发部署,采用Kubernetes蓝绿发布策略,含人工审批、健康验证与自动回滚,兼顾安全性与可靠性。
|
4天前
|
安全 程序员
程序员进阶工程师必备技能之代码质量与重构能力(三)
教程来源 https://wkmsa.cn/ 本文系统讲解重构的时机与方法:识别重复代码、过长函数等9种“代码坏味”;详解提取方法、内联方法、移动方法、多态替代条件、空对象等经典手法;并提供六步重构流程与渐进式实战示例,助你安全、高效提升代码质量。
|
4天前
|
架构师 程序员 项目管理
程序员必备的十大技能(进阶版)之架构规划与项目统筹(一)
教程来源 http://fndvx.cn/ 本文系统解析架构规划与项目统筹能力,涵盖架构思维、风格选型、边界划分、非功能设计、技术决策、文档治理、全周期管理等十大维度,助你构建技术与管理兼备的复合能力。
|
4天前
|
NoSQL 程序员 API
程序员进阶工程师必备技能之架构落地与组件封装(三)
教程来源 bhttps://bncne.cn/ 本文系统阐述微服务架构落地的最佳实践:涵盖分阶段实施策略(验证→基建→迭代)、自动化架构治理规则(分层/依赖/API/数据库),以及基于Redis的高可用服务注册发现组件实现,助力企业稳健完成架构升级。
|
4天前
|
程序员 数据库 数据安全/隐私保护
程序员进阶工程师必备技能之架构落地与组件封装(一)
教程来源 https://oplhc.cn/ 本文剖析程序员从“写代码”到“做架构”的关键跃迁,直击3–5年开发者面临的系统僵化困境。揭示架构本质是解决“如何组织”而非“如何实现”,详解组件封装、分层设计(四层/六边形/CQRS)、五大核心原则及落地实践,助你构建可维护、易扩展、高协作的复杂系统。
|
4天前
|
存储 人工智能 运维
让智能无界协作:UModel 正式开源,发起通用语义标准倡议
让数据说同一种语言,让智能无界协作。阿里云正式开源 UModel,并携手信通院、中科院、畅捷通、神州商龙、小鹏汽车、卓驭科技、嘉立创科技等企业伙伴与学术机构共同发起通用语义标准倡议。
|
4天前
|
人工智能 运维 Kubernetes
阿里云正式发布 RCA Benchmark,业界首个面向 Agentic Ops 的根因分析开源基准体系
阿里云联合信通院、中科院软件所/计算机网络信息中心、清华大学、复旦大学、南开大学,正式开源首个面向 Agentic Ops 根因分析评估基准RCA Benchmark。通过构建数据集、评估协议与仿真环境,帮助衡量 AI Agent 故障诊断能力,为行业落地夯实底座。
|
4天前
|
存储 消息中间件 SQL
Java在分布式链路追踪系统(Jaeger)中的实现与集成
微服务架构中,一个用户请求可能跨越多达几十个服务。当出现延迟增加或错误时,难以定位具体哪个服务出问题。
122 5
|
4天前
|
人工智能 自然语言处理 监控
阿里云百炼大模型服务平台如何部署模型?具体流程与模型计费方式和收费标准参考
本文系统介绍了阿里云百炼平台大模型从调优到部署上线的流程。首先需完成数据准备与模型调优,生成自定义模型并获取API Key;随后在模型部署控制台选择模型与计费方式(后付费/预付费),配置推理模式、上下文长度等参数,等待服务状态变为"运行中"即部署成功。平台提供预置吞吐、模型单元、Token用量三种计费模式,分别适用于高并发生产场景、资源隔离需求及效果验证场景,支持按量与包月付费。部署后可通过OpenAI兼容API、Dify、Chatbox等多种方式调用,也可借助Gradio搭建本地Web UI,满足多样化应用需求。