[HITICS] 哈工大2019秋CSAPP大作业-程序人生-Hello’s P2P

本大作业Github地址

计算机系统大作业

题   目 程序人生-Hello’s P2P

学   号 1183710113

学   生 许健

指 导 教 师 史先俊

计算机科学与技术学院

2019年12月

摘 要

对于每个程序员来说,Hello World是一个开始,本论文目的在于利用gcc、edb等工具,结合CSAPP教材,研究hello程序在Linux系统下的整个生命周期,从而达到融会贯通所学知识的效果。

关键词: CSAPP;HIT;大作业;hello

(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)


文章目录


第1章 概述

1.1 Hello简介

P2P:程序员用键盘输入hello.c文件,一个hello的C语言文件诞生,然后经过预处理器、汇编器、编译器、链接器的一系列处理,hello可执行文件诞生了,在shell中键入启动命令后,shell为其fork,产生子进程,于是hello便从Program变成了Process。

020:shell调用execve函数在新的子进程中加载并运行hello,在hello运行的过程中,还需要CPU为hello分配内存、时间片,使得hello看似独享CPU资源。系统的进程管理帮助hello切换上下文、shell的信号处理程序使得hello在运行过程中可以处理各种信号,当程序员主动地按下Ctrl+Z或者hello运行到return 0时,hello所在进程将被杀死,shell会回收它的僵死进程,内核删除相关数据结构。

1.2 环境与工具

硬件环境:Inter® Core™ i5-7300HQ CPU;2.5GHz;8G RAM;128G SSD+1T HDD

软件环境:Windows 10 64位;Vmware 15;Ubuntu 19.04 64位

开发与调试工具:gcc;edb; readelf;objdump;gedit;hexedit;

1.3 中间结果

hello.c——原文件

hello.i——预处理之后文本文件

hello.s——编译之后的汇编文件

hello.o——汇编之后的可重定位目标执行

hello——链接之后的可执行目标文件

hello.elf——hello.o的elf格式,用来看hello.o的各节信息

hello.ob——hello.o的反汇编文件,用来看汇编器翻译后的汇编代码

hello1.ob——hello的反汇编文件,用来看链接器链接后的汇编代码

1.4 本章小结

本章主要简单介绍了hello的P2P,020过程,列出了本次实验信息:环境、中间结果。

(第1章0.5分)


第2章 预处理

2.1 预处理的概念与作用

预处理的概念:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。将所引用的所有库展开,处理所有的条件编译,并执行所有的宏定义,得到另一个通常是以.i作为文件扩展名的C程序。

预处理的作用:

  1. 将c程序中所有#include声明的头文件复制到新的程序中。比如hello.c中第6~8行的#include <stdio.h>、#include <unistd.h>、#include <stdlib.h>命令告诉预处理器读取系统头文件stdio.h、unistd.h、stdlib.h的内容,并把它直接插人程序文本中;

  2. 条件编译。根据条件#if决定是否处理之后的代码;

  3. 执行宏替换。用实际值替换用#define定义的字符串。

2.2在Ubuntu下预处理的命令

命令:cpp hello.c > hello.i
在这里插入图片描述图2.1 对hello.c进行预处理

2.3 Hello的预处理结果解析

使用Text Editor打开hello.i,发现原来的helloc.c已经被拓展成了3042行,前面的内容是hello.c的三个#include指令包含的头文件的代码,先寻找main函数,main函数从第3029行开始,如下图。

在这里插入图片描述图2.2 hello.i中的main函数

再看之前的头文件的处理,以第一条#include指令为例,cpp到默认的环境变量下搜索stdio.h头文件,打开/usr/include/stdio.h,发现其中仍有#include指令,于是再去搜索包含的头文件,直到最后的文件中没有#include指令,并把所有文件中的所有#define和#ifdef指令进行处理,执行宏替换和通过条件确定是否处理定义的指令。如图是对stdio.h包含文件的展开。

在这里插入图片描述
图2.3 #include<stdio.h>包含文件展开

2.4 本章小结

本章主要介绍了预处理的概念及作用,并结合hello.c处理后的hello.i对处理过程进行分析。

(第2章0.5分)


第3章 编译

3.1 编译的概念与作用

编译的概念:编译器将文本文件hello.i翻译成另一个文本文件hello.s,它包含一个汇编语言程序。

编译的作用:将字符串转化成内部的表示结构,然后得到一系列记号,生成语法树,最后将语法树转化为目标代码。

3.2 在Ubuntu下编译的命令

命令:gcc -S hello.i -o hello.s

在这里插入图片描述
图3.1 hello.i编译生成hello.s

3.3 Hello的编译结果解析

3.3.1 数据

  1. 字符串

程序中用到的字符串有:“用法: Hello 学号 姓名 秒数!\n”和“Hello %s %s\n”。编译器一般将字符串存放在.rodata节,这两个个字符串在hello.s中的存储如下图,可以看到第一个字符串中的汉字被编码成UTF-8格式,一个汉字占三个字节,每个字节用\分隔。第二个字符串中的两个%s为用户在终端运行hello时输入的两个参数。

在这里插入图片描述
图3.2 hello.s存储的两个字符串

  1. 整数

hello.c中的整型变量有argc和i。

其中argc是从终端传入的参数个数,也是main函数的第一个参数,所以由寄存器%edi进行保存。由图3.3的21行可知,argc又被存入了栈中-20(%rbp)的位置。

在这里插入图片描述
图3.3 argc被保存在栈中

i则是局部变量,用来控制循环次数的计数器,编译器会将局部变量保存在寄存器或者栈中,由图3.4的30行看出hello.s将i存储在栈中-4(%rbp)的位置。

在这里插入图片描述
图3.4 .L2中的i的位置

  1. 数组

hello.c中数组是main函数的第二个参数,char *argv[],是字符指针数组,由于是第二个参数因而被保存在寄存器%rsi中,由图3.5的第22行可知它随后又被保存在了栈中-32(%rbp)的位置。

在这里插入图片描述
图3.5 argv被保存在栈中

在访问argv[]所指向的内容时,每次先获得数组的起始地址,如图3.6的第33、36、43行,然后通过加8*i来访问之后的字符指针,如图3.6中的第34、37、44,原因是每个字符指针所占的空间大小围为8个字节。然后通过获得的字符指针寻找字符串,如图3.6中的第35、38、45行。

在这里插入图片描述
图3.6 访问argv数组元素

3.3.2 赋值

hello.c中的赋值操作只有i=0这一条,这条语句在汇编中用mov指令实现,由于int占4个字节,所以以‘l’作为后缀。如图3.7中的第30行。

在这里插入图片描述
图3.7 给i赋值

3.3.3 类型转换

程序中涉及的类型转换只有一处,如图3.8所示的第19行,使用atoi函数将命令行的第三个字符串参数转换成了整型。

在这里插入图片描述
图3.8 hello.c的main函数

3.3.4 算术操作

汇编语言中有如下几种算术操作:

指令 行为 描述
inc D D=D+1 加1
dec D D=D-1 减1
neg D D=-D 取反
add S,G D=D+S D加S
sub S,D D=D-S D减S
imul S,D D=D*S D乘S
imulq S R[%rdx]:R[%rax]=S*R[%rax] 有符号乘法
mulq S R[%rdx]:R[%rax]=S*R[%rax] 无符号乘法
idivq S R[%rdx]=R[%rdx]:R[%rax] mod S R[%rax]=R[%rdx]:R[%rax] div S 有符号除法
divq S R[%rdx]=R[%rdx]:R[%rax] mod S R[%rax]=R[%rdx]:R[%rax] div S 无符号触发
leaq S,D D = &S 加载有效地址

helo.c中的算术操作只有一处,循环变量i的自增运算,在hello.s中处理成如图3.9的形式。

在这里插入图片描述
图3.9 i的自增运算

3.3.5 关系操作

C语言中的关系操作有==、!=、>、<、>=、<=,这些操作在汇编语言中主要依赖于cmp和test指令实现,cmp指令根据两个操作数之差来设置条件码。cmp指令与SUB指令的行为是一样,而test指令的行为与and指令一样,除了它们只设置条件码而不改变目的寄存器的值。

在hello.c中有两处用到了关系操作,分别是图3.8中的第13行的argc!=4和第17行的i<8。这两句在hello.s中被分别处理为图3.10和图3.11的形式。cmp之后设置条件码,为之后的je和jle提供判断依据。

在这里插入图片描述
图3.10 argc!=4在hello.s中的体现
在这里插入图片描述
图3.11 i<8在hello.s中的体现

3.3.6 数组/指针/结构操作

在hello.c中通过下标访问argv数组,在hello.s中访问argv[1]的操作如图3.12所示,第36行是取argv首地址,第37行是通过首地址加8字节找到argv[1]的地址,第38行是通过argv[1]中的内容找到对应的字符串,保存在寄存器%rax中。对argv数组其他元素所指的字符串也同理。

在这里插入图片描述
图3.12 访问argv[1]所指的字符串

3.3.7 控制转移

程序涉及到的控制转移有两处。

第一处是判断argc是否与4相等,在hello.s中如图3.13所示,第23行cmpl比较argc和4设置条件码之后,第24行通过判断条件码ZF位是否为零决定是否跳转到.L2,如果为0,说明argc等于4,代码跳转到.L2继续执行,如果不为0,则执行图中第25行的指令。

在这里插入图片描述
图3.13 对if语句的处理

第二处是判断循环变量i是否满足循环条件i<8。如图3.14所示,在第30行循环变量i被初始化为0,第30行无条件跳转到.L3,进入循环判断,在52行cmpl比较i和7之后设置条件码,然后第53行判断是否满足i<=7的要求,如果满足,跳转到.L4执行循环体,如果不满足,则退出循环,执行第54行的指令。

在这里插入图片描述
图3.14 对for循环的处理

3.3.8 函数操作

函数是一种过程,提供了一种封装代码的方式。P调用Q时有如下行为:

传递控制:开始执行Q的时候,PC必须设置为Q的代码的起始地址,而在返回时要把PC设置为P中调用Q之后一条语句的地址。

传递数据:P能够向Q传递任意个数的参数,Q能够向P返回0或1个值。P向Q传递参数时,64为程序参数存储顺序如下表:

</
第一个 第二个 第三个 第四个 第五个 第六个 第七个及之后
%rdi %rsi %rdx %rcx %r8 %r9 栈中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值