程序人生 Hello’s P2P -2024ICS SPRING
摘 要
本文通过hello小程序的一生,对我们所学的CSAPP课程进行全面的梳理与回顾。对hello程序的整个生命周期进行了系统的分析,从hello.c源程序开始,经过预处理、编译、汇编、链接、创建进程、加载hello…最后结束进程并由父进程进行回收这一系列分析,对于hello在计算机系统的一生有深入的理解。
关键词:编译;汇编;链接;预处理;hello的进程管理;p2p;020
目 录
第1章 概述............................................................................................................. - 4 -
1.1 Hello简介...................................................................................................... - 4 -
1.2 环境与工具..................................................................................................... - 4 -
1.3 中间结果......................................................................................................... - 4 -
1.4 本章小结......................................................................................................... - 4 -
第2章 预处理......................................................................................................... - 5 -
2.1 预处理的概念与作用..................................................................................... - 5 -
2.2在Ubuntu下预处理的命令.......................................................................... - 5 -
2.3 Hello的预处理结果解析.............................................................................. - 5 -
2.4 本章小结......................................................................................................... - 5 -
第3章 编译............................................................................................................. - 6 -
3.1 编译的概念与作用......................................................................................... - 6 -
3.2 在Ubuntu下编译的命令............................................................................. - 6 -
3.3 Hello的编译结果解析.................................................................................. - 6 -
3.4 本章小结......................................................................................................... - 6 -
第4章 汇编............................................................................................................. - 7 -
4.1 汇编的概念与作用......................................................................................... - 7 -
4.2 在Ubuntu下汇编的命令............................................................................. - 7 -
4.3 可重定位目标elf格式................................................................................. - 7 -
4.4 Hello.o的结果解析...................................................................................... - 7 -
4.5 本章小结......................................................................................................... - 7 -
第5章 链接............................................................................................................. - 8 -
5.1 链接的概念与作用......................................................................................... - 8 -
5.2 在Ubuntu下链接的命令............................................................................. - 8 -
5.3 可执行目标文件hello的格式.................................................................... - 8 -
5.4 hello的虚拟地址空间.................................................................................. - 8 -
5.5 链接的重定位过程分析................................................................................. - 8 -
5.6 hello的执行流程.......................................................................................... - 8 -
5.7 Hello的动态链接分析.................................................................................. - 8 -
5.8 本章小结......................................................................................................... - 9 -
第6章 hello进程管理................................................................................... - 10 -
6.1 进程的概念与作用....................................................................................... - 10 -
6.2 简述壳Shell-bash的作用与处理流程..................................................... - 10 -
6.3 Hello的fork进程创建过程..................................................................... - 10 -
6.4 Hello的execve过程................................................................................. - 10 -
6.5 Hello的进程执行........................................................................................ - 10 -
6.6 hello的异常与信号处理............................................................................ - 10 -
6.7本章小结....................................................................................................... - 10 -
第7章 hello的存储管理................................................................................ - 11 -
7.1 hello的存储器地址空间............................................................................ - 11 -
7.2 Intel逻辑地址到线性地址的变换-段式管理............................................ - 11 -
7.3 Hello的线性地址到物理地址的变换-页式管理....................................... - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换............................................. - 11 -
7.5 三级Cache支持下的物理内存访问.......................................................... - 11 -
7.6 hello进程fork时的内存映射.................................................................. - 11 -
7.7 hello进程execve时的内存映射.............................................................. - 11 -
7.8 缺页故障与缺页中断处理........................................................................... - 11 -
7.9动态存储分配管理....................................................................................... - 11 -
7.10本章小结..................................................................................................... - 12 -
第8章 hello的IO管理................................................................................. - 13 -
8.1 Linux的IO设备管理方法.......................................................................... - 13 -
8.2 简述Unix IO接口及其函数....................................................................... - 13 -
8.3 printf的实现分析........................................................................................ - 13 -
8.4 getchar的实现分析.................................................................................... - 13 -
8.5本章小结....................................................................................................... - 13 -
结论......................................................................................................................... - 14 -
附件......................................................................................................................... - 15 -
参考文献................................................................................................................. - 16 -
第1章 概述
1.1 Hello简介
P2P:From Program to Program。
图1 从源文件到可执行文件
a.编写代码,得到源程序hello.c,这就是Program。
b.预处理器cpp读取头文件内容,直接插入到程序文本,得到中间文件hello.i。
c.编译器gcc得到翻译成汇编语言文件hello.s。
d.汇编器as得到可重定位目标文件hello.o。
e.链接器ld得到可执行目标文件hello。
f.最后在shell键入启动此程序,shell调用fork函数为其产生子进程,hello便成为了进程Process。完成了P2P。
O2O:From Zero-0 to Zero-0。
OS的进程管理调用fork函数产生子进程,调用execve函数,并进行虚拟内存映射(mmp),并为运行的hello分配时间片以执行取指译码流水线等操作。OS的存储管理以及MMU解决VA到PA的转换,cache、TLB、页表等加速访问过程,IO管理与信号处理综合软硬件对信号等进行处理;程序结束时,shell回收hello进程,内核将其所有痕迹从系统中清除。至此,从0开始编写的hello.c经历了它短暂的一生,又回到了最初的起点
1.2 环境与工具
CPU i7-12700H,16G RAM,1T SSD;
Win11,VMware,Ubuntu20.04
1.3 中间结果
hello.i :预处理
hello.s :所得汇编代码
hello.o :所得的可重定位目标执行文件
hello.out :链接后所得二进制文件
hello :可执行程序
1.4 本章小结
对hello做了基础的介绍,简介了P2P,O2O;介绍了作业环境,利用的工具和中间结果的文件名称。
第2章 预处理
2.1 预处理的概念与作用
概念:预处理(或称预编译)是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理指令指示在程序正式编译前就由编译器进行的操作,可放在程序中任何位置。
作用:
a.将源文件中以”include”格式包含的文件复制到编译的源文件中。
b.用实际值替换用“#define”定义的字符串。
c.根据“#if”后面的条件决定需要编译的代码。
d.合理使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。
2.2在Ubuntu下预处理的命令
图2 预处理命令
2.3 Hello的预处理结果解析
由图3可以看出,预处理对原文件中的宏进行了宏展开,文件最开头的三个#include消失,一段代码代替了三个#include。头文件中的内容被包含进该文件中。例如声明函数、定义结构体、定义变量、定义宏等内容,最后hello.c文件变为hello.i文件。而且hello.c的主体部分还存在于hello.i中。
图3 hello.i部分文件内容
2.4 本章小结
本章说明了预处理的概念以及作用,命令以及hello.c文件的预处理结果hello.i文本文件解析。详细了解了预处理的内涵,知道了预处理这一阶段一共完成若干事情:头文件的展开、宏替换、去掉注释、条件编译。同时截图展示了预处理的过程和结果。
第3章 编译
3.1 编译的概念与作用
概念:编译过程是整个程序构建的核心部分,将源代码由文本形式转换成机器语言,编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件。也就是将hello.i翻译成汇编语言文件hello.s的过程。
作用: 将高级语言程序转化为机器可直接识别处理执行的的机器码的中间步骤,为后面的汇编做好准备。具体来说有以下几部分作用:词法分析,对输入的字符串进行分析和分割,形成所使用的源程序语言所允许的记号,同时标注不规范记号,产生错误提示信息;语法分析,分析词法分析得到的记号序列,并按一定规则识别并生成中间表示形式,以及符号表。同时将不符合语法规则的记号识别出其位置并产生错误提示语句;语义分析,即静态语法检查,分析语法分析过程中产生的中间表示形式和符号表,以检查源程序的语义是否与源语言的静态语义属性相符合;代码优化,将中间表示形式进行分析并转换为功能等价但是运行时间更短或占用资源更少的等价中间代码。
3.2 在Ubuntu下编译的命令
命令:gcc hello.i -S -o hello.s
图4 在Ubuntu下编译命令
3.3 Hello的编译结果解析
3.3.1 数据
a.常量
图5 hello.s中的常量
由图5,常量是两个字符串,这两个字符串都是printf函数的参数。即
用法: Hello 学号 姓名 手机号 秒数!
Hello %s %s %s\n
b.变量
main函数里声明了一个局部变量i,在编译器编译生成汇编语言后,i被存储在了-4(%rbp)的位置,如下图所示:
图6 局部变量i
main函数的参数argc和argv,其中argv为指针数组。同时由寄存器规则可知,argc存储在了寄存器%edi(第一个参数)中,argv存储在了寄存器%rsi(第二个参数)中,每个地址是八位。
图7 参数argc和指针数组argv
3.3.2 赋值
hello.c程序中的赋值操作为i=0,具体在汇编代码中的实现形式为movl $0, -4(%rbp),其中l后缀表明i是双字类型的整数数据,即占4个字节的整数数据(int型)。
图8 赋值i=0
3.3.3 类型转换
atoi(argv[4]),该语句实现了将字符串类型转换为整数类型。
3.3.4 算术操作
i++操作以及计算格式串地址操作。
图9 计算格式串地址
图10 i++指令
3.3.5 关系操作
argc!=5,该语句用于判断argc的值是否与5相等,该语句被编译为cmpl $5, -20(%rbp)。比较完成后会设置条件码,通过条件码判断跳转位置。
图11 比较argc!=5
i
p n = read(0, buf, BUFSIZ);/p p bb = buf;/p p }/p p return(–n >= 0)?(unsigned char) *bb++ : EOF;}
getchar函数调用read函数,将整个缓冲区都读到buf里,并将缓冲区的长度赋值给n。返回时返回buf的第一个元素,除非n