哈工大ICS大作业-Hello的一生

摘 要

本文从hello.c源代码开始,分两部分介绍hello程序的在计算机系统中的整个生命周期。

首先,利用gcc和gdb分析hello.c在预处理、编译、汇编、链接这每个阶段的变化。当编译完成,生成可执行文件hello后,本文又从操作系统进程的概念出发,分析hello的进程管理、内存管理以及Unix I/O的交互。

关键词: 编译原理;操作系统;进程;虚拟内存;深入理解计算机系统

第1章 概述

1.1 Hello简介

​ hello程序的生命周期是从一个高级C语言程序开始的。为了在系统上运行hello.c程序,每条C语句都必须被其他程序转化为一系列的低级机器语言指令。这样的转化成为编译,最后得以作为进程在计算机系统中运行,也就是P2P过程。

hello的P2P过程是GCC调用cpp(预处理器)/cc1(编译器)/as(汇编器)/ld(连接器),将C语言源文件预处理、编译、汇编、链接,最终生成可执行文件保存在磁盘中。

​ hello的020是Hello可执行目标程序从运行到最后被回收的过程。在Shell中运行该程序时,Shell调用fork函数创建子进程,创建完毕后,操作系统内核提供的execve函数会创建虚拟内存的映射,即mmp,然后开始加载物理内存,进入到main函数当中执行相关的代码,打印出信息。在进程中,TLB、4级页表、3级Cache,Pagefile等等设计会加快程序的运行。程序运行完成后,Shell回收子进程,操作系统内核删除相关数据结构,释放其占据的资源。至此,hello的一生就结束了。

1.2 环境与工具

1.2.1硬件环境

CPU:Intel® Core™ i7-10875H CPU @ 2.30GHz 2.30 GHz

RAM:16.0 GB

1.2.2操作系统

​ Windows 11 21H2、Windows Subsystem for Linux2(Ubuntu 20.04 LTS)

1.2.3开发工具

​ Vscode、vim、gcc、gdb、edb

1.3 中间结果

为编写本论文,生成的中间结果文件以及它们的作用如下:

文件 作用
hello.c 源代码
hello.i 预处理后的代码
hello.s 汇编代码
hello.o 可重定位目标文件
hello.o.elf.txt hello.o的ELF
hello.o.s hello.o反汇编后的代码
hello 链接后的可执行目标文件
hello.elf.txt hello的ELF
obj_hello.s hello的反汇编代码

表格 1 中间结果文件及作用

1.4 本章小结

本章大致介绍了hello的P2P和020过程,描述了使用的环境与工具,并列出了生成的中间结果文件以及它们的作用。

第2章 预处理

2.1 预处理的概念与作用

2.1.1 什么是预处理

在编译和链接hello.c之前,需要对源文件进行一些文本方面的操作,比如文本替换、文件包含、删除部分代码等,这个过程叫做预处理,由预处理程序完成。

2.1.1 预处理的作用

预处理根据以字符#开头的命令,修改原始的C程序。比如我们初学C语言时记忆最深刻的代码:

#include<stdio.h>

它就利用了预处理:引用头文件。它告诉预处理器读取系统头文件stdio.h的内容,预处理器把它插入程序文本中。ANSI标准定义的c语言预处理指令有以下这些:
在这里插入图片描述
图 2 预处理指令

2.2在Ubuntu下预处理的命令

Linux下使用gcc预处理的命令为:

gcc –E hello.c –o hello.i

-E 表示只激活预处理过程

2.3 Hello的预处理结果解析

2.3.1生成文件对比

​ 使用vscode打开文件,hello.c源程序只有24行,如下:

​ 打开生成的hello.i文件,发现有3060行,并且我们原始的main代码部分被放在最后

图 4 hello.i文件

2.3.2预处理后的文件解析

  1. 外部库文件

​ 首先,开始部分有一系列外部库.h文件路径

图 5 hello.i外部库

  1. 数据类型名称替换

​ 接下来是一堆typedef,前面是我们编写代码时使用的标准数据类型,而后面的那些别名就是上述讲到的引入的头文件中使用的类型定义

图 6 hello.i类型别名

​ 比如,我们随便打开一个头文件,它也有类似的行为:

图 7 types.h示例

  1. 内部函数声明

​ 中间部分是很多内部函数的声明,包括系统内核提供的接口的封装:

图 8 hello.i内部函数声明

  1. 主体代码

​ 而一直到最后,才是我们写的main函数代码部分

在这里插入图片描述

图 9 hello.i主体代码部分

2.4 本章小结

​ 本章介绍了hello.c的预处理过程,并分析了预处理的结果文件hello.i。

​ 从程序员的角度来说,利用宏定义指令可以让我们轻松写出可读性更好的代码,利用条件编译指令可以让我们更加方便快捷的调试代码。

​ 从hello程序的角度来说,hello.c是残缺的,不完整的,预处理阶段使它健全了四肢,得以最终运行在操作系统的上下文中。

第3章 编译

3.1 编译的概念与作用

3.1.1什么是编译

​ 汇编语言是对硬件的抽象,而C语言又是对汇编语言的抽象,C语言对人友好,但对机器并不友好。编译阶段正是把完整的代码hello.i翻译成对应的汇编语言程序hello.s

3.2 在Ubuntu下编译的命令

Linux下使用gcc编译的命令为:

gcc -S hello.i -o hello.s

-S表示只激活到编译过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M0EMRFi7-1653021981416)(D:/Img/clip_image024-1653021216714164.jpg)]

图 10 编译命令

3.3 Hello的编译结果解析

hello.i编译生成了对应的汇编代码,在这一节中,我将对C语言中数据类型及各式操作如何编译到汇编代码中逐个解析

3.3.1常量

  1. 字符型常量

在这里插入图片描述

图 11 字符型常量位置

printf中打印了一个字符串,这个字符串常量存在.LC0中:

在这里插入图片描述

图 12 hello.s中的字符型常量

  1. 其它常量

​ 还有一些其它常量直接在汇编代码中以立即数的身份出现,例如这段代码,有一个整型常量4

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dqWkDBme-1653021981417)(D:/Img/clip_image029-1653021216714166.jpg)]

图 13 整型常量位置

它对应的汇编代码如下:

在这里插入图片描述

图 14 整型常量在汇编中的形式

cmpl比较argc与4是否相等,如果不相等,才把上述的字符串常量加载到寄存器中

3.3.2变量与运算

  1. 局部变量

​ 局部变量存储在寄存器或者栈中。

hello.c中有一个局部变量:

图 15 局部变量位置

i是在一个for循环语句中作为循环变量,这段代码如下:

图 16 局部变量在hello.s中的形式

可以看到,i存储在栈中。

  1. 算数操作

​ 同时在上述的for循环中,局部变量i的值每次加1,这个运算就由addl指令来完成:
在这里插入图片描述

图 17 addl指令

3.3.3数组/指针操作

main函数的参数中,有一个字符串数组:

在这里插入图片描述

图 18 main函数参数

其中,argc是输入的参数的个数,也就是字符串数组argv中的元素个数。

​ 根据下面这行代码:

在这里插入图片描述

图 19 sleep参数

​ 找到其对应的汇编代码为: 在这里插入图片描述

图 20 sleep汇编代码

​ %rdi是第一个参数寄存器,也就是存储函数atoi调用的参数,所以栈中%rbp-8指向的栈空间存的内容就是argv[3]的地址

​ 同理,观察以下代码:

在这里插入图片描述

图 21 汇编代码

经过分析,分别找到argv[1]的地址存放在%rbp-16指向的栈空间中,argv[2]的地址存放在%rbp-24存放的栈空间中。

3.3.4控制转移

​ 同样还是以上述那段for循环的代码为例,循环变量i从0开始,每次循环都要加1,并在循环开始判断i<8,对应的汇编代码就是用cmpl指令,判断i是否小于等于7,如果是,则继续执行循环体中的内容,如果不是则跳出循环。

3.3.5函数调用与返回

main函数

​ 传入参数为argc,和argv,为系统调用,且参数从Shell中传入,返回值设置为0

  1. printf函数

​ 通过设置寄存器%rdi和%rsi的值来传入参数并调用

在这里插入图片描述

图 22 printf函数调用

  1. exit函数

​ 通过设置寄存器%rdi和%rsi的值来传入参数并调用 在这里插入图片描述

图 23 exit函数调用

  1. atoi函数

将一个字符串的首地址赋给%rdi调用,函数返回这个字符串转成的整数值,存放在字符串%eax

在这里插入图片描述

图 24 atoi函数调用

3.4 本章小结

本章介绍了从hello.i文件编译成hello.s文件的过程,以及原始的.c文件中各部分变量、常量、控制转移以及函数调用在汇编语言中是什么样子。

接下来,只需要将hello.s稍加改造(汇

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Deconx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值