哈工大计算机系统大作业——程序人生

摘  要

一个简单的hello,却有着不平凡的人生。本文以hello为例,讲述了Linux下从C文件到可执行文件,从程序到进程,最后到程序循环结束的整个过程。其中我们将介绍关于预处理、编译、汇编、链接、进程管理、存储管理的各种细节。这些都有赖于计算机系统硬件和软件的共同支持,在所有系统部件的协调工作下,一个简单的应用程序也显示出非凡高效的结构和流程。

关键词:计算机系统;编译;内存管理;汇编;                    

第1章 概述

1.1 Hello简介

Hello程序将从hello.c文件,经历编译,创建进程,进程在时间片上运行等环节,最终完成自己的使命。我们首先将简要介绍Hello程序所经历的几个环节的基本情况。

从代码到可执行文件:hello.c文件经过gcc编译器预处理以后成为hello.i文件,经过编译和汇编得到一个孤立的可执行程序hello.o,这个程序尚未建立和系统中其他共享库和代码段之间的联系。最后我们显示调用链接指令,得到一个完整的,每一行代码都有明确定义的行为的一个可执行文件hello.out。

从可执行文件到进程:万事具备只欠东风。我们通过在bash中调用hello.out脚本,使得bash将其fork出的一个子进程的虚拟内存空间设置成hello.out文件中描述的内容。这些虚拟内存空间表示为指向磁盘某一位置的VA,并被MMU交换到物理内存中,使得CPU可以快速访问。至此“P2P”的过程就完成了。

进程从运行到停止:内核不断地给hello.out进程分配时间片,在时间片中CPU执行hello.out的只读代码区域包含的指令,直到进程收到中断/停止信号,或者进程结束自然退出。退出后hello.out进程对应的内存片段不断成为其他进程的牺牲片段,直到hello.out程序的内容彻底离开内存,完成了“O2O”的循环。

1.2 环境与工具

软件环境:

操作系统:macOS Monterey 12.3 64位

Bash环境:zsh

终端程序:iTerm2

C编译器:clang/clang++

代码编辑器:Visual Studio Code

P.S. 第三章编译过程在一台CentOS服务器上完成,系统信息:

Linux version 4.18.0-193.28.1.el8_2.x86_64 

(gcc version 8.3.1 20191121 (Red Hat 8.3.1-5) (GCC))

硬件环境:

       MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)

处理器:2 GHz 四核Intel Core i5

内存和储存器:16 GB 3733 MHz LPDDR4X,500GB闪存

1.3 中间结果

包括的文件如下:

  1. hello.c:hello源文件
  2. hello.i:预处理文件
  3. hello.s:hello编译出的汇编代码
  4. hello.o:未链接的汇编结果
  5. hello_elf.txt:hello.o的ELF格式
  6. hello_elf_final.txt:hello的ELF格式
  7. hello_reverse.txt:hello.o的反汇编结果
  8. hello_rev_final.txt:hello的反汇编结果

1.4 本章小结

本章描述了hello程序完成O2O过程的三个主要环节和完成本次大作业使用的软硬件环境。

第2章 预处理

2.1 预处理的概念与作用

预处理从一个c文件产生一个新的c文件(以.i结尾)。其不对代码中的具体函数和语句进行处理,预处理处理的是c文件中的“宏”,即以#开头的语句,如#define#include#if等。同时预处理也会删除所有的注释

例如,对于#include<filename>,预处理程序会用filename中的代码代替c文件中的#include语句。相当于将filename文件的内容插入到c文件中。而对于#define A B,则会把c文件中的所有B文本替换成A。

2.2在Ubuntu下预处理的命令

在Ubuntu和macOS下,都可以使用gcc -E input_file.c来实现预处理。

 

2.3 Hello的预处理结果解析

查看hello.i文件,可以看到其包含两千余行。其中的内容可以分为三类:

  1. hello.c文件内容。这部分内容中的宏都被适当的处理了,由于原hello.c文件中没有宏,因此这部分和原文件中内容一致。这些处理后的内容位于i文件的末尾。

 

  1. #include宏明确包含的文件。#include文件中的内容会被插入到c文件的对应位置,如图:hello.i中关于printf的声明语句实际上就来自stdio.h文件。

  1. 关于行号的注释:可以看到i文件中包含很多以#开头的语句,这些语句包含了关于被include的文件的相关信息,有助于debugger显示有用的追踪信息。

 

2.4 本章小结

这一章展示了编译器预处理的过程,预处理文件实际上也是合法的c文件。预处理只是对c语言中的宏进行了处理,新的i文件是一个不包含宏的文件。方便后续编译器将c语言代码转化成为汇编语言。

第3章 编译

3.1 编译的概念与作用

编译指编译器接受c语言文件,产生对应的汇编语言文件。汇编语言只需要汇编和链接就可以得到可以执行的二进制文件。汇编包含不同的优化等级,包括-Og,-O1,-O2,-O3等等。不同编译选项会产生不同的汇编语言文件,更高的编译优化等级相应的执行得也更快。

3.2 在Ubuntu下编译的命令

在CentOS下使用命令gcc -S -Og -fno-asynchronous-unwind-tables hello.i -o hello.s来进行编译。使用-fno-asynchronous-unwind-tables的目的是减少不必要的仅对编译器可见的语句(Call Frame Information)

3.3 Hello的编译结果解析

执行编译指令后,我们得到了hello.s汇编语言文件。对该文件进行简单的注释:(见这一节最末尾)首先我们对几个重要的方面进行分析

3.3.1表示变量

       hello.c中包含常数变量(e.g. "用法: Hello 学号 姓名 秒数!\n")和局部变量(e.g. int i)。它们在汇编中的表示是不一样的。

       对于常数变量,在这里表现为字符串产量,hello.c中的字符串常量被放在text段中,并标注为只读状态(rodata)。

       对于局部变量(main开头定义的i),汇编语言则使用寄存器%ebx代表其值。

 

3.3.2获得函数参数

       main函数含有两个参数int argc, char *argv[],这些元素保存在函数栈中,大致按照以下方式:

       因此汇编代码中,对各个参数的访问如下表,假定运行命令为 ./hello 潘文博 7203610819 3

 

C变量

汇编访问方式

具体值

int argc

%edi

4

char **argv

%rbp

栈顶地址

char *arg[0]

(%rbp)

./hello

char *arg[1]

8(%rbp)

“潘文博”

char *arg[2]

16(%rbp)

“7203610819”

char *arg[3]

24(%rbp)

“3”

3.3.3处理if语句

       hello.c中的if语句由汇编文件的第17行到第26行表达:

       <

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值