【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

06-02 1172阅读

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业   计算机与电子通信类    

学     号        2023112915      

班     级        23L0505          

学       生        杨昕彦           

指 导 教 师         刘宏伟            

计算机科学与技术学院

2024年5月

摘  要

本文详细描述了“Hello”程序从源代码到可执行文件的全生命周期,涵盖了预处理、编译、汇编、链接、运行及回收的各个阶段。通过分析每个阶段的具体操作和工具,深入探讨了计算机系统的工作原理。文章首先介绍了预处理阶段如何展开宏和头文件,生成中间文件hello.i;接着阐述了编译阶段将hello.i转换为汇编代码hello.s的过程;随后讨论了汇编阶段将hello.s汇编成目标文件hello.o的步骤;最后讲解了链接阶段如何将hello.o与库文件合并生成可执行文件hello。在运行阶段,文章详细描述了Shell如何通过fork和execve创建并执行hello进程,并探讨了虚拟地址到物理地址的转换机制。此外,文章还分析了printf和getchar等函数的实现原理,揭示了Linux I/O设备管理的核心思想。通过这一系列步骤,文章展示了计算机系统从源代码到硬件执行的复杂流程,强调了理论与实践结合的重要性,并总结了学习计算机系统的深刻体会。

关键词:Hello;CSAPP;P2P;Linux; VM;I/O;Shell;Cache;Ubuntu;进程;

目  录

目录

第1章 概述

1.1 Hello简介

1.1.1 P2P

1.1.2 020

1.2 环境与工具

1.2.1 硬件环境

1.2.2 软件环境

1.2.3 开发工具

1.3 中间结果

1.4 本章小结

第2章 预处理

2.1 预处理的概念与作用

2.1.1 预处理的概念

2.1.2 预处理的作用

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

2.4 本章小结

第3章 编译

3.1 编译的概念与作用

3.1.1 编译的概念

3.1.2 编译的作用

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

3.3.1 数据

3.3.1.1 常量

3.3.1.2 变量(全局/局部/静态)

3.3.1.2 表达式

3.3.1.4 类型

3.3.1.5 宏

3.3.2 赋值

3.3.3 算术操作

3.3.4 关系操作

3.3.5 数组/指针/结构操作

3.3.6 控制转移

3.3.7函数操作

3.3.7.1参数传递(地址/值)

3.3.7.2函数调用

3.3.7.3函数返回

3.4 本章小结

第4章 汇编

4.1 汇编的概念与作用

4.1.1 汇编的概念

4.1.2 汇编的作用

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

    4.3.1文件头

    4.3.2程序头表

    4.3.2节表

4.4 Hello.o的结果解析

4.4.1 hello.o反汇编与hello.s的比较

4.4.2 机器语言的构成

4.4.3 与汇编语言的映射关系

4.5 本章小结

第5章 链接

5.1 链接的概念与作用

5.1.1 链接的概念

5.1.2 链接的作用

5.2 在Ubuntu下链接的命令

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

    5.3.1 ELF头信息

    5.3.2节头

    5.3.3程序头

    5.3.4符号表

    5.3.5重定位节

5.4 hello的虚拟地址空间

5.5 链接的重定位过程分析

5.6 hello的执行流程

5.7 Hello的动态链接分析

5.8 本章小结

第6章 hello进程管理

6.1 进程的概念与作用

6.1.1 进程的概念

6.1.2 进程的作用

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

6.3 Hello的fork进程创建过程

6.4 Hello的execve过程

6.5 Hello的进程执行

6.6 hello的异常与信号处理

6.6.1 异常的类型

6.6.2 异常的处理方式

6.7本章小结

第7章 hello的存储管理

7.1 hello的存储器地址空间

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

    7.2.1段描述与段选择符

   7.2.2逻辑地址到线性地址的转换过程

7.2.3段寄存器与描述符缓存

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

7.3.1虚拟内存的组织结构

7.3.2页表与地址映射

7.3.2缺页异常处理

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

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

7.6 hello进程fork时的内存映射

7.6.1 fork()时的内存映射

7.6.2 写时复制(COW)具体过程

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.1 P2P

P2P,即“从程序到进程”(From Program to Process),指的就是将人可读的源程序转变为操作系统可调度执行的进程的全过程。以hello为例,其生命周期始于一份用 C 语言编写、易于理解的源代码文件 hello.c,随后由 GCC 编译驱动程序接管,将其翻译成可执行文件 hello。整个翻译过程分为四个阶段:首先,预处理器 cpp 对 hello.c 进行预处理,生成含有展开宏和头文件内容的中间文件 hello.i;接着,编译器前端 cc1 将 hello.i 转换为汇编代码 hello.s;然后,汇编器 as 将 hello.s 汇编成目标文件 hello.o;最后,链接器 ld 将 hello.o 与所引用的标准库或其他库文件进行链接,输出最终的可执行文件 hello。生成可执行文件后,当在 Shell 中运行时,系统首先通过 fork() 创建一个子进程,再通过 execve() 将 hello 的代码和数据加载到该子进程的内存空间中,至此,一个新的进程便诞生了。

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

图1 编译系统

1.1.2 020

020,即From Zero-0 to Zero-0,描述了程序从加载到结束的完整生命周期。当用户在终端输入 ./hello 时,Shell 先调用 fork() 创建子进程,子进程再通过 execve() 将可执行文件映入进程虚拟地址空间,并借助 mmap 将必要的代码和数据从存储介质载入物理内存,随后交由 CPU 调度执行。CPU 为该进程分配时间片,依次进行取指、译码、执行等流水线操作;在此过程中,内存管理单元通过多级页表和 TLB 进行地址转换,并利用 L1、L2、L3 缓存加速数据访问;I/O 子系统则根据程序指令完成外部设备的读写输出。待程序执行结束后,子进程将进入僵尸状态,由父进程调用 wait() 或类似机制回收,内核随即释放其虚拟内存空间并清除相关进程表项,从而完成程序从zero到zero的过程。

1.2 环境与工具

1.2.1 硬件环境

X64 CPU;2GHz;2G RAM;256GHD Disk 以上

1.2.2 软件环境

Windows7/10 64位以上;VirtualBox/Vmware 11以上;Ubuntu 16.04 LTS 64位/优麒麟 64位 以上

1.2.3 开发工具

Visual Studio 2010 64位以上;CodeBlocks 64位;vi/vim/gedit+gcc;edb

1.3 中间结果

文件的名字

文件的作用

hello.c

源程序文件

hello.i

hello.c通过预处理器cpp预处理后的文本文件

hello.s

hello.i通过编译器ccl编译后的汇编程序

hello.o

hello.s通过汇编器as汇编后的文件

hello

hello.o通过链接器ld链接后的可执行文件

1.4 本章小结

本章首先以 “Hello” 程序为例,阐述了从可读的 C 语言源代码到操作系统可调度进程的全流程:源文件通过 cpp、cc1、as 和 ld 四个阶段依次生成可执行文件,再由 Shell 调用 fork() 与 execve() 在内存中创建并运行进程;随后通过 mmap、多级页表、TLB 及 L1/L2/L3 缓存进行内存管理,并由 CPU 按时间片执行取指—译码—执行流水线,I/O 子系统负责外设读写;进程结束后由父进程 wait() 回收,内核释放资源,完成从“零”到“零”的闭环。此外,本章还介绍了硬件(X64、2 GHz、2 GB RAM、256 GB 硬盘及以上)、软件(Windows 7/10 64 位或 Ubuntu 16.04 LTS/优麒麟)、虚拟化(VirtualBox/VMware 11+)与开发工具(Visual Studio、Code::Blocks、GCC、edb 等)环境,并列出了 hello.c→hello.i→hello.s→hello.o→hello 及相关 ELF 和反汇编文件的中间产物及其作用。

第2章 预处理

2.1 预处理的概念与作用

2.1.1 预处理的概念

预处理是由预处理器(cpp)负责完成的源代码准备阶段,它会识别以 # 开头的指令并对 .c 文件进行改写与扩展。主要操作包括:

宏定义替换(define):将代码中出现的宏标识符替换为对应的文本或表达式;

文件包含(include):将被包含文件的内容嵌入到当前源文件中;

条件编译(ifdef、if 等):根据条件决定是否保留或丢弃特定代码段。

完成预处理后,生成的中间结果仍是一份合法的 C 语言源代码,通常以 .i 为扩展名,供后续编译阶段使用。

2.1.2 预处理的作用

预处理阶段通过解析以 # 开头的指令,对源代码进行初步转换和组织,为后续的编译、汇编和链接打下基础。其主要功能包括以下三方面:

  1. 宏定义替换

    允许程序员使用 define 定义符号常量或代码片断,在预处理时将这些宏标识符替换为对应的文本或表达式,从而减少重复代码、提升可维护性和可读性。

  2. 文件包含

    通过 include 指令将头文件或其他源文件的内容插入到当前文件中,这不仅能集中管理函数声明、数据结构和宏定义,还能实现模块化编程。

  3. 条件编译

    使用 if、ifdef、ifndef 等指令,根据编译环境或自定义宏的定义情况选择性地保留或剔除代码片段,以便同一份源代码在不同平台、不同配置下能够灵活编译。

总体而言,预处理使得程序在逻辑上更简洁、有条理,并增强了可移植性与调试效率,是现代 C/C++ 开发流程中不可或缺的第一步。

2.2在Ubuntu下预处理的命令

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

图2 cpp hello.c >hello.i

2.3 Hello的预处理结果解析

预处理阶段生成中间文件 hello.i,此时预处理器已根据源代码中 #include 指令在进入 main 函数之前,按顺序读取并展开了系统头文件 stdio.h、unistd.h 和 stdlib.h 的全部内容;若这些头文件内部仍包含以 # 开头的指令,预处理器同样会继续处理直至展开完毕。所有注释被剔除,宏定义在预处理完成后不再保留,最终得到一份标准且完整的 C 源代码,可直接供后续的编译阶段使用。

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

图3 预处理结果

2.4 本章小结

本节首先阐述了预处理在 C 语言编译流程中的概念与作用,然后在 Ubuntu 环境下使用gcc -E 命令对 hello.c 进行了预处理,生成了中间文件 hello.i。通过查看 hello.i,可以直观地看到:所有以 include 引入的系统头文件(如 stdio.h、unistd.h、stdlib.h)已按顺序展开插入;注释被清除;宏定义亦已展开且在结果文件中不再保留,从而形成一份可直接送入编译器的标准 C 源代码,有助于更深入地理解预处理阶段的核心功能与工作原理。

第3章 编译

3.1 编译的概念与作用

3.1.1 编译的概念

编译阶段由编译器(cc1)负责,将预处理后生成的 hello.i 文件作为输入,翻译成目标平台对应的汇编代码,并输出为 hello.s。该过程首先进行词法和语法分析,验证代码的正确性;接着在语义分析中检查类型与作用域;随后生成中间表示(IR),并对其进行必要的优化;最后将经过优化的 IR 转换为具体的汇编指令,形成包含 main 函数定义及所有函数实现的汇编程序。整个流程不仅完成了从高级语言到汇编语言的转换,还为后续的汇编和链接阶段提供了可读且高效的汇编源码。

3.1.2 编译的作用

编译阶段由编译器(cc1)接管,将预处理生成的 .i 文件翻译成目标平台对应的汇编代码(.s 文件),其主要功能包括以下几个方面:

  1. 语法与语义检查

    编译器首先对源代码进行词法分析和语法分析,确保程序符合语言规范;随后进行语义分析,检查类型一致性与作用域正确性,为生成正确的低级代码奠定基础。

  2. 中间表示与优化

    在分析通过后,代码会被转换为中间表示(IR),编译器可在此阶段对其进行多种优化,如常量折叠、循环展开等,以提升最终生成代码的执行效率。

  3. 汇编生成

    优化后的 IR 会被映射为具体的汇编指令,形成包含 main 函数定义及所有程序逻辑的汇编程序,便于后续的汇编和链接环节处理。

总体而言,编译不仅完成了将人类可读的高级语言转换为机器可执行的低级指令,还通过一系列检查和优化手段,保证了生成代码的正确性与高效性,同时为后续汇编和链接阶段提供了结构清晰、性能优良的汇编源文件。  

3.2 在Ubuntu下编译的命令

在Ubuntu中,hello.i文件进行编译的操作命令为:gcc -S hello.i -o hello.s

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

图4 编译命令

3.3 Hello的编译结果解析

3.3.1 数据

3.3.1.1 常量

字符串常量,位于只读数据段(.rodata)

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

图5 字符串常量

3.3.1.2 变量(全局/局部/静态)

1)无全局与静态变量。

全局变量在汇编语言中通常存储在数据段(Data Segment)中。数据段是程序的一部分,用于存储全局变量和静态变量。全局变量在程序的整个生命周期内都存在,而不仅仅在特定的函数调用期间。需要注意的是,全局变量的修改和访问是在整个程序执行期间有效的,因此它可以被程序中的任何函数访问和修改。这使得全局变量在需要在不同部分之间共享数据时非常有用。由于hello.c中不含全局变量,这里就不详细描述了。

2)局部变量

局部变量通常使用栈指针(%rsp)和基址指针(%rbp)进行访问。在此段汇编代码中,可以看到通过基址指针(%rbp)来访问局部变量。

-20(%rbp):该偏移量用于在栈帧中存放来自 main 函数的第一个参数 argc 的值,对应汇编指令 movl %edi, -20(%rbp)。

-32(%rbp):该偏移量用于在栈帧中存放来自 main 函数的第二个参数 argv 的地址,对应汇编指令 movq %rsi, -32(%rbp)。

-4(%rbp):存储局部变量i,用于在循环中计数。汇编代码中通过movl $0, -4(%rbp)初始化该变量。

通过addl $1, -4(%rbp)递增该变量。

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

图6 局部变量

函数调用结束后的清理: 在函数返回前,栈帧会被清理。这包括将栈指针恢复到原始的位置,以及弹出保存的帧指针值。这个过程确保了栈的一致性。

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

图7函数调用结束后的清理

3.3.1.2 表达式

.c中的表达式argc!=5,在.s文件中表示为:

【HITCSAPP 哈工大计算机系统期末大作业】 程序人生-Hello’s P2P

图8 .c中的表达式argc!=5在.s文件

i

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码