ICS大作业--hello程序人生

										摘  要

Hello是每个程序员第一个接触的程序,在本文中利用计算机系统所学的知识,基于Linux平台,通过gcc、objdump、gdb、edb等工具,从c源程序的预处理开始,跟踪分析程序编译、汇编、链接、进程管理、存储管理、I\O管理整个过程,完整的参与hello的程序人生。走过C语言程序生命周期的四季,见证shell命令之春,fork进程之夏,加载执行之秋,回收释放之冬,感悟计算机系统之美。

关键词:计算机系统 汇编 链接 进程 虚拟内存

										**第1章 概述**

1.1 Hello简介
在linux中,hello.c经过cpp的预处理、ccl的编译、as的汇编、ld的链接最终成为可执行目标程序hello,在shell中键入相应命令行参数后,shell解析命令并构建argc,argv[]和envp[]参数列表,shell再调用fork()函数创建一个子进程,子进程获得与父进程完全相同的虚拟存储空间的一个备份。将解析得到的argc,argv[]和envp[]参数列表传给execve()函数作为参数,execve()函数启动加载器,在当前的上下文中开始执行可执行文件hello第一条指令即将控制转移到子进程,于是hello便从Program摇身一变成为Process,这便是P2P的过程。
开始执行程序后,随着虚拟存储的访问程序开始载入物理内存,然后进入 main函数执行目标代码,CPU通过上下文切换为运行的hello分配时间片执行逻辑控制流。当hello进程运行结束后,内核将子进程的退出状态传递给父进程,父进程shell(或init进程)负责回收hello进程,内核删除相关数据结构,以上全部便是020的过程。
1.2 环境与工具
硬件环境:Intel Core i5-8300H x64CPU 2.3GHZ,8G RAM,128G SSD +1T HDD.
软件环境:Ubuntu18.04.1 LTS
开发与调试工具:gdb,gcc,as,ld,edb,readelf,HexEdit
1.3 中间结果
hello.i 预处理之后文本文件
hello.s 编译之后的汇编文件
hello.o 汇编之后的可重定位目标执行
hello 链接之后的可执行目标文件
hello.o.objdump Hello.o的反汇编文件
hello.objdump Hello的反汇编文件

1.4 本章小结
程序有四季,往来知春秋。Hello波澜壮阔的人生画卷即将展开。。。

(第1章0.5分)

第2章 预处理
2.1 预处理的概念与作用
预处理:预处理程序(CPP)对源程序中以字符#开头的命令进行处理。
作用:c预处理程序为cpp(C Preprocessor),主要用于C语言编译器对各种预处理命令进行处理,包括对头文件的包含,宏定义的扩展,条件编译的选择等,例如,对于#include指示的处理结果,就是将相应的.h文件的内容插入到源程序文件中。

2.2在Ubuntu下预处理的命令
预处理命令:
在这里插入图片描述

具体过程:
在这里插入图片描述
在这里插入图片描述
2.3 Hello的预处理结果解析
在这里插入图片描述
Hello.i文件部分截图

解析:预处理主要是对各种预处理命令进行处理,具体包括对头文件的包含,宏定义的扩展,条件编译的选择等。预处理主要是根据#符号进行处理,在hello中,对于#include指示的处理结果,就是将相应的.h文件的内容插入到源程序文件中,上图中的主要是对stdio.h unistd.h stdlib.h的依次展开,之后再对宏定义进行拓展,再进行条件编译的选择,即根据条件值来决定是否执行包含其中的逻辑。
2.4 本章小结
预处理开启程序编译之路,#符谱写hello人生初章。字字句句头文件,点点滴滴宏定义,条件编译一线牵。

(第2章0.5分)

				**第3章 编译**

3.1 编译的概念与作用
编译:编译程序(CCL)对预处理后的源程序进行编译,生成一个汇编语言源程序文件。
作用:主要是将C语言文件翻译为汇编语言文件。C编译器在进行具体的程序翻译之前,会先对源程序进行词法分析和语法分析,然后根据分析的结果进行代码优化和存储分配,最终把C语言源程序翻译为汇编语言程序。

3.2 在Ubuntu下编译的命令
编译命令:
在这里插入图片描述

编译过程:
在这里插入图片描述
在这里插入图片描述

3.3 Hello的编译结果解析
部分编译结果:
在这里插入图片描述

汇编指令:
指令 含义
.file 声明源文件
.text 以下是代码段
.globl 声明一个全局变量
.data 表明在.data节
.section .rodata 以下是.rodata节
.align 声明对指令或者数据的存放地址进行对齐的方式
.type 用来指定是函数类型或是对象类型
.size 声明大小
.long、.string 声明一个long、string类型

结果解析:
3.3.1 整数(int 型)

1.全局变量 sleepsecs:
在这里插入图片描述
由代码可见,首先在.text节中被声明为global变量,之后在.data节中声明对齐方式为4字节对齐,为对象类型,声明大小为4字节,另外设置为long类型其值为2(long类型在linux下与int相同为4B,将int声明为long应该是编译器偏好)。
2.局部变量 i: 在栈中分配内存,只读段和读写数据段均不做声明。如下所示,64位下通过rbp分配栈空间给i
在这里插入图片描述

	3.命令行参数 argc: 由shell解析后构造出,传递给execve作为参数启动加载器,存放在main的栈帧之上。
	4.立即数:在代码段中,运行程序时放入栈或寄存器中。

3.3.2 数组
Argv和envp: 由shell解析后构造出,传递给execve作为参数启动加载器,存放在main的栈帧之上。
在这里插入图片描述

3.3.3 字符串
在这里插入图片描述
Printf函数的命令行格式串,首先声明在.rodata节中,再声明类型为string,分别用.LC0和.LC1指代

3.3.4 赋值
程序中涉及的赋值操作有:
1.int sleepsecs=2.5 :因为sleepsecs是全局变量,所以直接在.data节中将sleepsecs声明为值2的long类型数据。
2.i=0:整型数据的赋值使用mov指令完成,在栈或寄存器中分配存储空间,根据数据的大小不同使用不同后缀

3.3.5 类型转换(隐式)
在这里插入图片描述
隐式类型转换,将float转换为int型。当在double或float向int进行类型转换的时候,程序改变数值和位模式的原则是:值会向零舍入,直接舍掉小数点后的部分。如果溢出则为NAN, 与Intel兼容的微处理器指定位模式[10…000]为整数不确定值,故会产生一个不确定值。

3.3.6 算术操作
在这里插入图片描述
i++为运算操作,在汇编中通过add或lea实现,在程序中为:
在这里插入图片描述

3.3.7 关系操作
在这里插入图片描述
在这里插入图片描述
< , !=为关系操作,在汇编中通过cmp,test,set等实现,例如cmp a,b,通过(b-a)设置标志位,如果结果为负则SF置为1,结果为0则ZF置为1,有符号数进位则CF置为1,无符号数溢出则OF置为1(还要考虑取反的影响),再根据设置好的符号为逻辑运算后确定比较结果。
在这里插入图片描述
在这里插入图片描述

3.3.8 数组/指针/结构操作
在这里插入图片描述
argv数组是shell由命令行参数构造出的,传递给main函数的第二个参数,具体在栈中的内存映像见3.3.2中的图。在汇编中,栈如下:
32(rbp)
28 i
24
20
16
12 argc
8
4
0(rsp) 指向argv

通过rbp在栈中寻找argv的首地址,由于是char *类型,故分别加8加16得到argv[1],argv[2],取内存内容后分别放入rsi和rdx,传给printf函数作为参数。
在这里插入图片描述

3.3.9 控制转移
在这里插入图片描述
在这里插入图片描述
在汇编中通过j进行控制转移,由cmp和j语句构成条件判断语句,在这个程序中,
在这里插入图片描述
在这里插入图片描述
由rbp在栈中分别找到argc和i,用cmp设置符号位后,再根据符号为进行跳转,从而实现控制流的转移。

3.3.10 函数操作
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1.main函数
在这里插入图片描述
Main符号首先声明在.text节中,再声明为global变量,类型为函数类型。两个参数argc和argv[]由shell构造好后,传入execve作为参数,调用加载器,在之前的上下文中运行程序,故main的栈帧上方就是argc和argv[](envp[]),具体在栈中的内存映像见3.3.2中的图。
2.printf函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值