关注

程序人生大作业

计算机系统

大作业

题     目  程序人生-Hellos P2P  

专       业   计算机与电子通信      

学     号    2023111996           

班   级  23L0512               

学       生     王郑杰          

指 导 教 师       史先俊         

计算机科学与技术学院

20255

摘  要

本文以C语言程序hello.c为研究对象,系统性地剖析了程序从源代码到可执行文件的完整生命周期,以及进程从创建到终止的全过程运行机制。研究内容主要包含以下三个层面:

程序构建层面:

详细解析了预处理、编译、汇编和链接四个关键阶段。

深入探讨了程序从高级语言到机器指令的转换过程。

分析了可执行目标文件的ELF格式组织结构。

进程执行层面:

研究了fork、execve等系统调用的实现机制。

探讨了进程创建、程序加载和上下文切换的具体过程。

分析了进程调度和资源管理的实现原理。

内存管理层面:

系统阐述了逻辑地址到物理地址的转换机制。

深入研究了页式内存管理和TLB缓存优化技术。

详细分析了动态内存分配和缺页中断处理流程。

本研究基于Ubuntu操作系统平台,采用GCC工具链进行程序构建,并借助gdb、objdump等调试工具进行实验验证,完整呈现了计算机系统中软件与硬件的协同工作机制。通过理论分析与实验验证相结合的方法,系统性地阐释了现代计算机系统的分层设计原理和实现机制。

关键词:程序生命周期;进程管理;虚拟内存;地址转换;系统调用

目  录

第1章 概述

1.1 Hello简介

1.2 环境与工具

1.3 中间结果

1.4 本章小结

第2章 预处理

2.1 预处理的概念与作用

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

2.4 本章小结

第3章 编译

3.1 编译的概念与作用

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

3.4 本章小结

第4章 汇编

4.1 汇编的概念与作用

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

4.4 Hello.o的结果解析

4.5 本章小结

第5章 链接

5.1 链接的概念与作用

5.2 在Ubuntu下链接的命令

5.3 可执行目标文件hello的格式

5.4 hello的虚拟地址空间

5.5 链接的重定位过程分析

5.6 hello的执行流程

5.7 Hello的动态链接分析

5.8 本章小结

第6章 hello进程管理

6.1 进程的概念与作用

6.2 简述壳Shell-bash的作用与处理流程

6.3 Hello的fork进程创建过程

6.4 Hello的execve过程

6.5 Hello的进程执行

6.6 hello的异常与信号处理

6.7本章小结

第7章 hello的存储管理

7.1 hello的存储器地址空间

7.2 Intel逻辑地址到线性地址的变换-段式管理

7.3 Hello的线性地址到物理地址的变换-页式管理

7.4 TLB与四级页表支持下的VA到PA的变换

7.5 三级Cache支持下的物理内存访问

7.6 hello进程fork时的内存映射

7.7 hello进程execve时的内存映射

7.8 缺页故障与缺页中断处理

7.9动态存储分配管理

7.10本章小结

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

8.2 简述Unix IO接口及其函数

8.3 printf的实现分析

8.4 getchar的实现分析

8.5本章小结

结论

附件

参考文献


第1章 概述

1.1 Hello简介

1.1.1Hello程序的P2P与020过程​

​​ P2P(Program → Process)​​

​​启动:Shell通过fork()+execve()加载程序,内核分配内存、设置页表,动态链接库。

​​执行:从main()开始,循环调用printf(触发write系统调用)和sleep(nanosleep系统调用)。

1.1.2 2. 020(Zero → Zero)​​

​​初始化:进程从零创建,继承stdin/stdout/stderr,参数压栈。

​​运行:CPU时间片轮转,Ctrl-C(SIGINT)直接终止,Ctrl-Z(SIGTSTP)暂停转后台。

​​终止:return 0触发exit(),释放内存、关闭文件描述符,父进程wait()回收僵尸进程。

1.2 环境与工具

CPU:13th Gen Intel(R) Core(TM) i7-13700H   2.40 GHz

内存:16.0 GB (15.3 GB 可用)

操作系统:ubuntu 24.04.15

编译器:gcc

调试器:gdb

VMware虚拟机

内存:4GB

处理器内核数:4

硬盘:20GB

1.3 中间结果

1.3.1源代码文件​​

​​hello.c​​

作用:主程序源码,包含循环打印和信号处理逻辑。

1.3.2编译生成文件​​

​​hello(可执行文件)

作用:通过gcc编译生成,用于运行和调试。

​​hello.o​目标文件,若分步编译)

作用:临时编译产物,含机器码但未链接。

1.3.3 调试与分析文件​​

​​hello.asm(反汇编输出)

生成命令:objdump -d hello > hello.asm

作用:查看程序汇编代码,验证优化效果。

​​hello_strace.log​​(系统调用日志)

生成命令:strace -o hello_strace.log ./hello ...

作用:记录write、sleep等系统调用行为。

​​gdb.txt​​(调试日志)

生成命令:gdb -batch -ex 'break main' -ex 'run' -ex 'disassemble' hello > gdb.txt

作用:保存断点、寄存器、反汇编等调试信息。

1.3.4. 测试输出文件​​

​​hello_output.txt​​

生成命令:./hello 学号 姓名 手机号 秒数 > hello_output.txt

作用:保存程序的标准输出(循环打印结果)。

1.3.5. 进程快照​​

​​ps_aux.log​​

生成命令:ps aux | grep hello > ps_aux.log

作用:记录进程运行时的状态(如PID、CPU占用)。

1.4 本章小结

本章详细介绍了P2P和020的过程,使用的软硬件环境和开发与调试工具,

列出了中间结果文件。


第2章 预处理

2.1 预处理的概念与作用

1. 基本定义

预处理是C/C++编译流程的首要阶段,由预处理器执行源代码的文本转换操作,生成经处理的中间代码。

2. 核心功能

(1)头文件包含

解析#include指令

递归展开头文件内容

(2)宏处理

执行宏定义替换

处理预定义宏(__LINE__等)

(3)条件编译

解析#if/#ifdef等指令

选择性保留代码区块

(4)注释处理

移除单行/多行注释

保留有效代码内容

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

2.3 Hello的预处理结果解析

头文件展开:#include替换为实际文件内容(约2500行)

删除所有注释

保留#line标记源码位置

2.4 本章小结

本章介绍了linux下预处理的命令,并且查看了Ubuntu下预处理后的文件内容。

(第2章0.5分)


第3章 编译

3.1 编译的概念与作用

3.1.1概念

编译是将预处理后的源代码(.i文件)转换为汇编代码(.s文件)的过程,由编译器(如GCC的cc1)完成,主要包括:

词法分析(将代码拆解为标记)

语法分析(构建抽象语法树AST)

语义分析(检查类型、作用域等)

代码优化(如常量折叠、死代码删除)

汇编代码生成(生成目标架构的汇编指令)

3.1.2. 核心作用​​

翻译高级语言:将C代码转为机器相关的汇编指令。

代码优化:在汇编前进行中级优化(如-O1/-Og)。

错误检查:发现语法错误、类型不匹配等问题。

3.2 在Ubuntu下编译的命令

gcc -S hello.c -o hello.s

3.3 Hello的编译结果解析

3.4 本章小结


第4章 汇编

4.1 汇编的概念与作用

4.1.1基本概念​​

汇编是将汇编代码(.s文件)转换为机器码(.o目标文件)的过程,由汇编器(如as)完成。这是代码变为可执行文件的倒数第二步。

4.1.2. 核心作用​​

指令转换:​​

将助记符(如mov、call)转为二进制机器指令

示例:push %rbp → 0x55(x86-64编码)

生成重定位信息:​

标记未确定的地址如外部函数调用)

为链接器提供修正依据

生成目标文件结构:​​

按ELF格式组织代码、数据、符号表等节区(section)

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

命令

ELF文件

ELF头

节头

重定位节

                         符号表

4.4 Hello.o的结果解析

查看反汇编的命令

反汇编文件

有如下几个不同:

1.反汇编代码跳转指令的操作数由段名称变成了确定的地址

2.在hello.s中,函数调用之后跟函数名称,在反汇编程序中,call的目标地址是下一条指令。这是因为hello.c中调用的函数是共享库中的函数,需要通过动态链接器才能确定函数的运行时的执行地址。

3.hello.s里的数是十进制表示,hello.asm里的数是十六进制表示。

4.5 本章小结

汇编是程序构建过程中生成机器码的关键环节,它将人类可读的汇编指令精确转化为二进制目标文件(.o),为后续的链接和内存加载做好准备。这一阶段主要完成四个核心功能:机器指令编码、符号地址解析、节区结构组织以及重定位信息记录,通过专门的汇编器工具实现从助记符到机器码的一一映射。

目标文件采用ELF这一标准化格式,其模块化的设计通过不同的节(section)和段(segment)来分类存储代码、数据和元信息。其中,节头表(Section Header Table)记录了静态链接所需的符号定义和引用关系,而程序头表(Program Header Table)则定义了运行时内存的布局要求,这两个核心数据结构共同确保了可执行文件在不同平台间的可移植性。

以Hello程序的目标文件为例,汇编阶段通过精确计算地址偏移量、转换操作码以及设置重定位标记,实现了函数调用和分支跳转等动态地址引用的后期修正能力。同时,该阶段还保留了丰富的调试信息,包括行号映射和符号表等,为逆向工程和故障诊断提供了必要支持。从参数传递的栈帧操作到循环控制的条件转移,汇编过程严格遵循特定指令集架构的编码规范,确保高级语言逻辑能够被准确无误地转换为底层机器指令。

5链接

5.1 链接的概念与作用

5.1.1概念​​

链接是将多个目标文件(.o)和库文件合并为可执行文件的过程,由链接器(如ld)完成,核心任务是解决代码和数据的地址分配、符号引用和重定位问题。

5.1.2.作用​​

合并代码与数据​​

将分散在多个.o文件中的代码段(.text)、数据段(.data)合并为单一的可执行文件。

解析符号引用​​

匹配未定义的符号(如printf)到其实际定义(如libc.so中的实现)。

地址重定位​​

根据最终内存布局,修正代码中的函数调用和变量引用地址。

生成可执行格式​​

输出符合操作系统要求的可执行文件(如ELF格式),包含程序入口和加载信息。

5.2 在Ubuntu下链接的命令

5.3 可执行目标文件hello的格式

ELF头

                    节头

重定位节

                      符号表

头部表

5.4 hello的虚拟地址空间

    使用gdb/edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。   

5.5 链接的重定位过程分析

以下格式自行编排,编辑时删除

objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。

结合hello.o的重定位项目,分析hello中对其怎么重定位的。

5.6 hello的执行流程

以下格式自行编排,编辑时删除

使用gdb/edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程(主要函数)。请列出其调用与跳转的各个子程序名或程序地址。

5.7 Hello的动态链接分析

   (以下格式自行编排,编辑时删除

分析hello程序的动态链接项目,通过edb/gdb调试,分析在动态链接前后,这些项目的内容变化。要截图标识说明。

5.8 本章小结

通过本次研究,我们系统性地解析了目标文件、静态库与动态库如何通过链接器协同工作,构建完整的可执行程序。这一过程不仅完整呈现了程序从加载到终止的生命周期,更揭示了静态库与动态库在程序运行中的关键作用——它们虽不直接可见,却是确保程序功能完整性的重要基础。研究表明,即便是最简单的"hello"程序,其背后也蕴含着精密的系统级设计。

链接机制作为这一过程的核心枢纽,通过符号解析和地址重定位等关键技术,实现了代码模块与功能库的无缝整合。这种机制不仅为开发者提供了便捷的库函数调用方式,更通过模块化设计显著提升了软件开发效率。从技术实现角度看,链接过程完美体现了计算机系统分层抽象的思想;从工程实践层面说,它促进了代码复用,增强了程序的灵活性和可维护性。因此,深入理解链接机制对于提升编程能力和系统认知具有重要意义。


6hello进程管理

6.1 进程的概念与作用

6.1.1概念​​

进程是程序的一次执行实例,是操作系统进行资源分配和调度的基本单位。每个进程拥有独立的地址空间、文件描述符、寄存器状态等资源。

6.1.2. 作用​​

1.隔离性​​

每个进程独立运行,互不干扰(崩溃不会影响其他进程)。

2.资源分配​​

操作系统通过进程管理CPU、内存、文件等资源。

3.并发执行​​

多个进程可同时运行(通过时间片轮转或多核并行)。

4.任务封装​​

将程序运行所需的所有资源(代码、数据、状态)封装为统一实体

6.2 简述壳Shell-bash的作用与处理流程

 6.2.1作用​​

用户与系统间的桥梁:将用户输入的命令翻译成系统能执行的操作

环境管理:维护变量、工作目录等运行时环境

任务控制:支持前后台作业切换、管道、重定向等操作

6.2.2. 处理流程​​

1.读取命令​​

显示提示符(如$),等待用户输入

2.解析命令​​

拆分命令与参数(如ls -l → 程序ls+参数-l)

3.执行命令​​

内置命令(如cd)直接由Shell处理

外部程序(如gcc)则:

调用fork()创建子进程

子进程通过exec()加载程序

父进程wait()等待子进程结束

4.返回结果​​

输出程序执行结果或错误信息

恢复提示符等待下一条命令

6.3 Hello的fork进程创建过程

6.3.1触发条件​​

在终端输入./hello后,bash会启动该程序

6.3.2. 关键步骤​​

1.bash调用fork()​​

复制自身生成子进程

子进程获得独立PID

2.子进程变身​​

调用execve()加载hello程序

替换为hello的代码和数据

继承bash打开的文件(stdin/stdout)

3.父子分道扬镳​​

父进程(bash)调用wait()等待

子进程(hello)开始执行main()

6.4 Hello的execve过程

6.4.1触发时机​​

当shell的子进程需要运行hello程序时

6.4.2. 执行步骤​​

1.清空重置​​

保留原进程PID

清空当前内存映像(代码/数据/堆栈)

保持已打开的文件描述符(stdin/stdout/stderr)

2.加载程序​​

读取hello的ELF头部

将.text(代码)、.data(数据)段载入内存

设置程序入口地址(_start)

3.构建环境​​

压入命令行参数(argv)

设置环境变量(environ)

初始化堆栈指针

6.5 Hello的进程执行

Hello进程的执行与调度过程​​

6.5.1进程上下文​​

当Hello进程被调度执行时,CPU需要加载其进程上下文:

寄存器状态:包括PC(程序计数器)、SP(栈指针)等

内存映射:页表记录的.text/.data段物理地址

文件描述符表:继承自Shell的stdin/stdout/stderr

信号处理设置:默认对Ctrl-C(SIGINT)执行终止

6.5.2. 时间片调度​​

1.时间片分配​​

内核默认分配​​10ms​​时间片(可调整)

Hello进程在时间片内连续执行

2.调度触发场景​​

主动让出:调用sleep()时主动放弃CPU

时间耗尽:10ms用完后被强制剥夺CPU

高优先级抢占:若有实时进程就绪

6.5.3系统调用流程​​

sleep(3);  ↓

SYSCALL指令触发软中断(0x80)  

进入内核态,执行sys_nanosleep()  

将进程状态设为TASK_INTERRUPTIBLE  

调度其他进程执行  

定时器到期后唤醒进程  

返回用户态继续执行

6.5.4执行过程全景​

[Shell] fork → [Hello进程]

    → execve加载

    → 用户态执行main()

    → 调用printf(内核态写文件)

    → sleep(主动让出CPU)

    → 被唤醒后继续执行

    → exit退出

6.6 hello的异常与信号处理

以下格式自行编排,编辑时删除

 hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。

 程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

6.7本章小结

通过对"hello"程序的系统性研究与实践验证,我们深刻认识到其执行过程展现了操作系统精密的资源管理机制。该程序并非独立运行,而是作为系统进程调度体系中的一个基本单元,与其他进程协同共享CPU和内存资源,并动态适应操作系统的调度策略。在运行期间,"hello"进程需要高效处理各类异步事件,包括异常和信号等关键通信机制,这些事件不仅实现了执行流程的灵活控制,更构建了进程间及系统间的高效交互通道,从而确保整体系统的可靠运行。由此可见,"hello"程序的执行过程实质上是操作系统核心机制的微观呈现:从多任务并发的调度算法到异步事件的处理流程,每个环节都体现了现代操作系统的设计智慧。这一研究不仅揭示了用户程序的运行特征,更深入阐释了操作系统如何通过动态资源分配和协调管理策略,在有限硬件条件下实现高效稳定的系统服务。)


7hello的存储管理

7.1 hello的存储器地址空间

逻辑地址(Logical Address):程序代码中直接使用的地址

线性地址(Linear Address):逻辑地址经分段转换后的结果

虚拟地址(Virtual Address):进程视角的连续内存空间地址

物理地址(Physical Address):实际DRAM内存芯片上的地址

​地址类型​

​作用域​

​转换关系​

​Hello中的示例​

​逻辑地址​

程序视角

段选择符+偏移量

movl $0x8048000, %eax(代码段内偏移)

​线性地址​

内核统一管理

段机制转换后结果

页表转换前的32/64位平坦地址

​虚拟地址​

进程独占视角

通过页表映射到物理内存

printf函数的调用地址0x4004f6

​物理地址​

实际内存芯片位置

由MMU根据页表转换得到

代码被加载到物理内存的0x7f8e1000

7.2 Intel逻辑地址到线性地址的变换-段式管理

逻辑地址 = 段选择符 (16位) : 偏移量 (32/64位)

通过段选择符查找GDT/LDT表项 → 获取段描述符

段描述符包含:段基址 (32位) + 段限长 (20位) + 访问权限

线性地址 = 段基址 + 偏移量

(需检查偏移量 ≤ 段限长,否则触发#GP异常)

7.3 Hello的线性地址到物理地址的变换-页式管理

线性地址(64位,实际用48位)

[63..48] 符号扩展位(全0或全1)

[47..39] → 定位PML4表项 → 获取PDPT基址

[38..30] → 定位PDPT表项 → 获取PDT基址

[29..21] → 定位PDT表项 → 获取PT基址

[20..12] → 定位PT表项 → 获取物理页基址

[11..0]  → 页内偏移量(4KB页)

物理地址 = 物理页基址 + 页内偏移

7.4 TLB与四级页表支持下的VA到PA的变换

CPU生成虚拟地址(VA)

① 优先查询TLB(缓存近期转换结果)

   → 命中:直接获取物理地址(PA)(耗时1-3周期)

   → 未命中:触发页表遍历(Page Walk)

② 四级页表遍历(CR3寄存器提供PML4基址)

   [47:39] → PML4 → PDPT

   [38:30] → PDPT → PDT  

   [29:21] → PDT → PT

   [20:12] → PT → 物理页基址

③ 合并页内偏移[11:0]得到PA

④ 将VA→PA映射插入TLB(供后续加速)

7.5 三级Cache支持下的物理内存访问

​Cache级别​

​容量​

​延迟​

​管理策略​

​L1 Cache​

32KB (I/D分离)

1-3周期

8路组相联

​L2 Cache​

256KB

10-12周期

4路组相联

​L3 Cache​

8-32MB (共享)

30-40周期

16路组相联

CPU执行mov指令访问PA:0x7f8e14f6

① L1 Data Cache查询(未命中)

② L2 Cache查询(未命中)

③ L3 Cache查询(命中)

   → 返回数据(30周期)

④ 若L3未命中:

   - 发起DRAM请求(100+周期)

   - 数据填充L3→L2→L1(缓存行通常64B)

7.6 hello进程fork时的内存映射

阶段​

​父进程​

​子进程​

​fork前​

独立拥有所有内存页

尚未存在

​fork瞬间​

所有页表项标记为COW

共享父进程物理页

​写操作时​

触发页错误→内核复制新页

获得独立物理页

具体映射过程​​

1.代码段(.text)​​

继续共享物理页(所有进程只读)

示例:hello的main()函数代码页

2.数据段(.data/.bss)​​

初始共享,修改时复制

示例:全局变量int count的存储页

3.堆空间​​

malloc分配的堆区同样遵循COW

示例:char *buf = malloc(1024)

4.栈空间​​

每个进程获得独立栈页

示例:局部变量int tmp的存储位置

7.7 hello进程execve时的内存映射

对象​

​行为​

​示例​

​代码段​

旧程序.text被替换为hello的.text

bash代码 → hello的main()代码

​数据段​

旧.data/.bss被替换为hello的初始化数据

bash全局变量 → hello的全局变量

​堆​

完全释放,重新初始化

原malloc空间消失

​栈​

保留结构但清空内容

栈底指针重置,argv/envp压入新栈

7.8 缺页故障与缺页中断处理

缺页故障与缺页中断处理是操作系统内存管理的核心机制之一。当程序访问尚未建立有效映射的虚拟内存页面时,处理器会触发缺页异常(Page Fault),这一过程涉及硬件与操作系统的紧密协作。

以hello程序为例,其执行过程中会经历多种类型的缺页场景:程序启动时首次执行main函数会触发MAJOR缺页,需要从磁盘加载代码段;调用printf等库函数时可能产生MINOR缺页,将已缓存的内存页重新映射;若程序包含全局变量修改,fork后的写操作会引发COW(写时复制)缺页;而参数不足时的argv越界访问则会导致权限缺页。

硬件处理流程始于MMU检测到无效页表项或权限违规,此时处理器会将故障地址存入CR2寄存器,记录访问类型并触发#PF异常。内核的缺页处理程序随后介入,首先进行安全检查,验证访问地址是否属于进程合法空间,再根据缺页类型采取不同策略:对于文件映射页从磁盘读取内容,匿名页则分配清零物理页,COW页需要复制原页并建立新映射。

处理完成后,内核会更新页表项,设置物理页帧号和访问权限标志,并刷新TLB缓存。现代操作系统通过预读机制、透明大页等技术优化缺页处理性能,例如预测性加载后续可能访问的页,使用2MB大页减少TLB未命中等。当hello程序访问非法地址时,内核会检查VMA区域并发送SIGSEGV信号,默认生成核心转储并终止进程,这种精细的异常处理机制既确保了内存访问安全,又通过延迟分配策略优化了内存使用效率。整个缺页处理过程充分展现了操作系统如何平衡性能与安全,在硬件支持下实现高效可靠的虚拟内存管理。

7.9动态存储分配管理

动态存储分配管理是现代操作系统内存管理的核心技术,它通过malloc/free等内存分配函数实现对堆内存的动态管理。

在hello程序的执行过程中,当需要动态分配内存时,标准库的内存分配器会首先检查空闲内存链表,寻找合适大小的内存块;如果现有空闲块无法满足需求,则会通过brk或mmap系统调用向操作系统申请扩展堆空间。

Linux系统采用ptmalloc2分配器管理动态内存,其核心机制包括:通过维护多个空闲链表实现不同大小内存块的高效管理,采用最佳适应算法减少内存碎片,并通过内存合并技术回收释放的块。在hello程序调用malloc时,分配器会根据请求大小选择分配策略——小内存(<128KB)从主分配区获取,大内存则直接使用mmap映射独立内存区域。

内存释放时,free函数会将内存块标记为空闲并尝试与相邻空闲块合并,但实际物理内存可能不会立即返还系统,而是由分配器缓存以便后续重用。这种延迟释放机制虽然提高了分配效率,但也可能导致内存碎片问题。

现代分配器还通过线程本地缓存(tcache)优化多线程环境下的分配性能,每个线程维护独立的内存缓存区,减少锁竞争。在调试场景下,工具如valgrind可以检测hello程序中的内存泄漏和越界访问问题,这些工具通过替换标准内存分配函数并添加追踪信息实现。

动态内存管理的设计需要在分配速度、内存利用率和碎片控制之间取得平衡,不同应用场景可能需要选择不同的分配策略,例如实时系统倾向使用确定性更强的分配算法,而通用系统则更关注整体吞吐量。

7.10本章小结

从hello.c源代码到最终可执行文件的转换过程完美诠释了计算机系统的精妙设计。这个多阶段的编译过程始于预处理阶段,预处理器会处理源文件中的所有宏定义、条件编译指令和头文件包含,生成扩展后的中间文件hello.i。随后编译器接手,对这个中间文件进行词法分析、语法分析和语义分析,生成针对特定处理器架构(如x86-64)的汇编代码文件hello.s。汇编器将这个汇编代码转换为机器指令,同时构建包含代码段、数据段、符号表等信息的可重定位目标文件hello.o。最后,链接器将多个目标文件和必要的库文件(如C标准库)进行合并,解析所有外部引用并完成地址重定位,最终输出可直接执行的ELF格式文件hello。

通过这个完整的编译过程实践,我不仅加深了对程序构建机制的理解,更提升了对计算机系统整体架构的认知水平。增加了在Linux环境下使用gdb、edb等专业工具进行开发和调试的经验,为后续的计算机系统研究和工程实践打下了坚实基础。


第8章 hello的IO管理

8.1 Linux的IO设备管理方法

以下格式自行编排,编辑时删除

设备的模型化:文件

设备管理:unix io接口

8.2 简述Unix IO接口及其函数

以下格式自行编排,编辑时删除

8.3 printf的实现分析

以下格式自行编排,编辑时删除

[转]printf 函数实现的深入剖析 - Pianistx - 博客园

从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.

字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。

显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

以下格式自行编排,编辑时删除

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。

getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

以下格式自行编排,编辑时删除

(第8章 选做 0分)

结论

Hello经历的过程:

预处理:宏展开、头文件包含,生成.i中间代码

​​编译:语法/语义分析,生成目标架构(x86-64)的.s汇编

​​汇编:助记符转机器码,生成含重定位信息的.o目标文件

​​链接:符号解析、地址重定位,合并库生成ELF可执行文件

​​加载:按程序头映射内存,动态链接库,初始化栈/堆

​​执行:指令流水线处理,TLB加速地址转换,缺页中断按需加载

​​终止:资源回收,进程表项清除

​​感悟与创新​:

现代系统通过"延迟绑定+按需加载"平衡效率与资源占用。可探索AI预测预加载​基于历史访问模式预映射代码页)和语义感知内存分配器结合程序语义优化堆布局),将静态编译与动态行为深度结合。


附件

列出所有的中间产物的文件名,并予以说明起作用。

hello.i          预处理生成的文本文件

hello.s          .i文件编译后得到的汇编语言文件

hello.o          .s文件汇编后得到的可重定位目标文件

hello            .o经过链接生成的可执行目标文件

hello.elf         hello.out的elf文件

hello.asm        hello.o的反汇编代码文件

elf.txt           hello.o的elf文件

hello_exe_elf     hello的elf文件

参考文献

[1]  林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.

[2]  辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.

[3]  赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).

[4]  谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.

[5]  KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.

[6]  CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.

  1. 俞甲子, 石凡, & 潘爱民. (2009). 程序员的自我修养:链接、装载与库. 电子工业出版社.
  2. Bryant,R.E.,&O'Hallaron,D.R.(2016).ComputerSystems:A Programmer's Perspective(3rded.).Pearson.

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/guohongze666/article/details/148197322

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--