哈尔滨工业大学计算机系统大作业——程序人生-Hello’s P2P

目录

摘要

第1章 概述

1.1 Hello简介

1.1.1 P2P:From Program to Process

1.1.2 020:From Zero-0 to Zero-0

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.1.2 赋值

3.1.3 类型转换

3.1.4 算术操作

3.1.5 关系操作及控制转移

3.1.6 函数操作

3.4 本章小结

第4章 汇编

4.1 汇编的概念与作用

4.1.1 概念

4.1.2 作用

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

4.4 Hello.o的结果解析

4.5 本章小结

第5章 链接

5.1 链接的概念与作用

5.1.1 概念

5.1.2 作用

5.2 在Ubuntu下链接的命令

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

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.2.1 作用

6.2.2 处理流程

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.2.1 Unix I/O接口

8.2.2 Unix I/O函数

8.3 printf的实现分析

8.4 getchar的实现分析

8.5本章小结

结论

附件

参考文献

摘要

       本文围绕hello的“程序人生”,对hello“From Program to process”和“From Zero-0 to Zero-0”两个过程展开分析,hello.c从预处理、编译、汇编、链接、fork一系列操作从程序变成进程,再通过调用execve函数加载进程、对控制流的管理、内存空间的分配、异常的处理、对I/O设备的调用、shell父进程回收等一系列操作从无到有再到无。回顾hello的一生,便是一次深入理解计算机系统的历程。

关键词:程序;进程;存储;I/O;程序人生

第1章 概述

1.1 Hello简介

1.1.1 P2P:From Program to Process

       在Unix系统上,GCC编译器驱动程序读取源程序文件hello.c,并把它翻译成一个可执行目标文件hello,这个过程可分为四个阶段——预处理器(cpp)将源程序hello.c修改成hello.i文本文件,编译器(ccl)将hello.i翻译成汇编程序hello.s,汇编器(as)将hello.s翻译成机器语言指令,并将这些指令打包成可重定位目标程序hello.o,连接器(ld)将hello.o与库函数相链接生成可执行目标程序hello。执行该程序时,操作系统调用fork创建一个子进程,此时hello.c就从program变成了一个process。

1.1.2 020:From Zero-0 to Zero-0

       execve函数加载并运行可执行目标文件hello,操作系统为其分配虚拟内存空间,在物理内存与虚拟内存之间建立映射。执行过程中,虚拟内存为进程提供独立的空间,数据从磁盘传输到CPU中,TLB、分级页表等保障了数据的高效访问,I/O管理与信号处理共同实现了hello的输入输出。程序运行结束后,shell父进程负责回收hello进程,对应的虚拟空间以及相关数据结构被释放,hello进程便经历了从无到有再到无的过程。

1.2 环境与工具

1.2.1 硬件环境

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

1.2.2 软件环境

       Windows10 64位以上;VirtualBox 11以上;Ubuntu 16.04 LTS 64位

1.2.3 开发工具

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

1.3 中间结果

       hello.c:源程序文件

       hello.i:hello.c预处理后的源程序文件

       hello.s:hello.i编译后的汇编程序

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

       elf.txt:hello.o的ELF文件

       hello_o2s.s:hello.o的反汇编代码

       hello:hello.o链接后的可执行目标文件

       hello_elf.txt:hello的ELF文件

       hello_objdump.s:hello的反汇编代码

1.4 本章小结

       本章简述了hello的“一生”,即P2P和020,并简单列出此次大作业所需的环境和工具,以及hello.c所生成的中间结果文件。

第2章 预处理

2.1 预处理的概念与作用

2.1.1 概念

       在C语言中,预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。

2.1.2 作用

       1.宏替换:将宏名替换为字符串或数值;

       2.文件包含:预处理器读取头文件中的内容,并直接插入到程序文本中;

       3.条件编译:根据条件编译指令决定需要编译的代码;

       4.删除注释。

2.2在Ubuntu下预处理的命令

图1-在Ubuntu下的预处理过程

2.3 Hello的预处理结果解析

       打开预处理后的hello.i文件,发现由原来的23行扩展到3060行。其中头文件中的内容被直接插入到文本文件中,出现大量声明函数、定义结构体、定义变量、定义宏等内容,.c文件中的注释也被删除,但main函数没有改变。

图2-hello.i文件部分代码行截图

2.4 本章小结

       本章介绍了预处理的概念和作用,并在Ubuntu中进行预处理,并分析了预处理前后源文件的差别。

第3章 编译

3.1 编译的概念与作用

3.1.1 概念

       编译器(ccl)将文本文件hello.c翻译成文本文件hello.s,它包含一个汇编语言程序。

3.1.2 作用

       通过以下五个阶段把人们熟悉的高级语言语言换成计算机能解读、运行的低级语言——词法分析,语法分析,语义检查和中间代码生成,代码优化,目标代码生成。

       1.词法分析:对由字符组成的单词进行处理,从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。

       2.语法分析:以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,如表达式、赋值、循环等,最后看是否构成一个符合要求的程序,按该语言使用的语法规则分析检查每条语句是否有正确的逻辑结构,程序是最终的一个语法单位。

       3. 语义检查和中间代码生成:由语义分析器完成,指示判断是否合法,并不判断对错。中间代码的作用是可使编译程序的结构在逻辑上更为简单明确,特别是可使目标代码的优化比较容易实现中间代码。

       4.代码优化:对程序进行多种等价变换,使得从变换后的程序出发,能生成更有效的目标代码。

       5. 目标代码生成:把语法分析后或优化后的中间代码变换成目标代码。

3.2 在Ubuntu下编译的命令

图3-在Ubuntu下的编译过程

3.3 Hello的编译结果解析

3.3.1 数据及数组

       在hello.i的main函数中,常量包括printf函数中打印的两个字符串常量和if条件、for循环里的数字常量。其中,如图4所示,字符串常量被存储在./rotate段,如图5所示,数字常量被存储在.text段中,且作为立即数出现。

图4-hello.s字符串常量相关代码段

图5-hello.i(左)与hello.s数字常量对比

       代码中并没有全局变量,局部变量分别是函数参数argc和argv,以及for循环中的i;其中argc表示参数argv的个数,分析图6、图7可知,argc、数组argv以及i均存储在栈中,argc地址为-20(%rbp); argv首地址为-32 (%rbp),每个参数加8(对应图7绿色方框),即argv[k]=-(32+8k)(%rbp),所以数组取值的操作离不开首地址偏移量;i地址为-4(%rbp)。

图6-局部变量argc
图7- hello.i(左)与hello.s部分代码对照

3.1.2 赋值

       如图7左上及右边红色方框所示,左边的hello.i中,需对i进行赋值,即将0赋给i,在汇编代码中对应第一行“movl $0, -4(%rbp)”。

3.1.3 类型转换

       如图7蓝色方框所示,hello.i中类型转换体现在“atoi(argv[3])”上,即将字符串参数转换为整型参数,需调用atoi函数,在汇编代码中对应为“call atoi”。

3.1.4 算术操作

       如图7右边红色方框,for循环中每次循环i都要自加1,在汇编代码中则由“addl $7, -4(&rbp)”实现。

3.1.5 关系操作及控制转移

       如图6及图7右边红色方框所示,if条件中需要用到“!=”和“<”这样的关系操作,if-else或者是否跳出for循环则是控制转移,而在汇编代码中,关系操作往往与控制转移(跳转指令)一起出现,即“cmp”后会跟上“jXX”,跳不跳转则需看cmp设置的条件码,或者是直接跳转指令“jmp”,例如图6中,首先将立即数4与 -20(%rbp)对应值(即argc)进行对比,若等于,即“je”就代表相等则跳转至.L2。

3.1.6 函数操作

       hello.i中一共涉及5个函数调用,即main、printf、exit、sleep、getchar,在汇编语言中,寄存器一般都有特定用途,例如%rax存储返回值,%rdi存储第一个参数,%rsi存储第二个参数等。其次,若有多个参数,则将参数存储在栈中,如图8所示,调用函数前都有mov指令,设置不同的参数,最典型的就是调用atoi和sleep函数前都将寄存器%rax中的值传送到储存第一个参数的寄存器%rdi中,而像getchar这样没有参数的函数,则无需执行以上操作。

图8-hello.s中函数调用参数传递

       再如图9所示,C语言中main函数返回0,那么汇编代码中则对应为“movl $0, %eax”,“leave”,“ret”,即将0传送到%eax,然后使用leave恢复调用者栈帧,清理被调用者栈帧,最后使用ret指令返回。

图9- hello.s中函数返回值

3.4 本章小结

       本章围绕编译操作展开,首先介绍了编译的概念和作用,之后在Linux中将hello.i编译成hello.s,并对编译后的hello.s进行分析。

第4章 汇编

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值