计算机系统
大作业
题目 | 程序人生-Hello’s P2P |
---|---|
专业 | 计算学部 |
学号 | 120L021403 |
班级 | 2003011 |
学生 | 冯新航 |
指导教师 | 郑贵滨 |
计算机科学与技术学院
2022年5月
摘 要
计算机系统是人类创造出的工业奇迹之一,其构造之精妙,功能之强大,令人赞叹。要全面描述计算机系统的方方面面实际上是非常困难的,因此本文从简单的 hello
程序入手,讲述计算机系统的一些基本概念、功能与原理。我们将讲述 hello
是如何从源码编译为可执行文件,并经由 shell
执行的过程,剖析一些概念的细节和实现原理。
关键词:Linux C语言 计算机系统 编译 内存管理 进程
文章目录
第 1 章 概述
1.1 Hello简介
根据Hello的自白,利用计算机系统的术语,简述Hello
的P2P,020的整个过程。
hello
程序在 Linux 下将经历以下步骤:
- 由文本编辑器编写成
hello.c
源文件 - 由编译预处理器将其预处理成
hello.i
文件 - 由汇编器将其翻译成汇编语言文件
hello.s
- 由编译器将其编译成可重定向目标文件
hello.o
- 由链接器
ld
将hello.o
与系统的其他目标文件链接起来,形成最终的hello
可执行文件 - 在
shell
中输入相应命令后,shell
为其fork
一个子进程,经由execve
函数,使得子进程代表用户执行hello
程序 - 此过程中,该函数将
hello
可执行文件映射到虚拟内存,而后载入物理内存,进入main
函数执行 - 调度器调度该进程执行控制流,直到
main
函数返回 shell
回收该进程,内核删除相关数据结构
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
硬件环境
- CPU: Intel Core i7-10875H @ 16x 2.304GHz
- GPU: NVIDIA GeForce RTX 2060
- RAM: 40GB DDR4 3200
软件环境
- 操作系统:Windows 11 Home China、Ubuntu 20.04 (on the Windows Subsystem for Linux)
开发工具
- Visual Studio Code
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
hello.c
源代码文本hello.i
编译预处理文件hello.s
汇编文本文件hello.o
可重定位目标文件hello
可执行文件
1.4 本章小结
本章讲述了 hello
程序如何从源代码文本转换为可执行文件的过程,同时列出了实验环境等相关信息。
第 2 章 预处理
2.1 预处理的概念与作用
-
概念:
编译预处理器是针对源代码中的编译预处理指令进行的一类操作,如引入头文件、条件编译等
-
作用:
处理
#include
指令,将#include
后的文件原封不动地拷贝到源码对应位置处理条件编译指令
#ifdef
等,保留满足条件的源码代码替换由
#define
定义的常量等
2.2 在Ubuntu下预处理的命令
应截图,展示预处理过程!
命令:
gcc -E hello.c -o hello.i
截图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tIpRjdSD-1653009846945)(https://s1.ax1x.com/2022/05/20/OqqpZt.png)]
2.3 Hello的预处理结果解析
编译预处理器按照C语言标准规则修改和替换源码中某些位置的代码,我们可以在得到的 hello.i
底部找到原来的部分,而在顶端是由 #include
引入的头文件,它被原封不动地拷贝到对应位置。
2.4 本章小结
本章讲述了编译预处理器和编译预处理指令的相关概念及其作用,本质上是修改源文件的文本内容,为编译器将其编译成汇编语言做准备。
第 3 章 编译
3.1 编译的概念与作用
编译器将编译预处理文件 hello.i
编译成汇编文件 hello.s
,这个文件仍然是一个文本文件,内容是 hello
源文件的等价汇编结果。
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
编译命令:
gcc -S -g hello.c -o hello.s
应截图,展示编译过程!
3.3 Hello的编译结果解析
此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要 hello.s
中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析。
3.3.1 汇编文件头
.file "hello.c"
.text
.Ltext0:
.section .rodata
.align 8
.LC0:
.string "\347\224\250\346\263\225: Hello \345\255\246\345\217\267 \345\247\223\345\220\215 \347\247\222\346\225\260\357\274\201"
.LC1:
.string "Hello %s %s\n"
.text
.globl main
.type main, @function
各部分内容说明:
.file
声明源文件.text
代码段.section.rodata
数据段.align
地址对齐方式.string
声明字符串globl
声明全局符号.type
声明符号类型
3.3.2 数据
-
字符串
main
函数中存在对printf
的调用,格式控制字符串的地址作为printf
的参数被传入,如对于:printf("用法: Hello 学号 姓名 秒数!\n");
的汇编为:
leaq .LC0(%rip), %rdi call puts@PLT
-
局部变量
main
函数中有局部变量i
作为循环变量,由语句:cmpl $7, -4(%rbp)
可知该变量存储在栈上,位置为
%rpb-4
。 -
argc
与argv
参数argc
与argv
都是main
的参数,在main
函数开头有movl %edi, -20(%rbp) movq %rsi, -32(%rbp)
可知这些变量都存储在栈上。
-
立即数
立即数是直接硬编码在汇编代码中的。
3.3.2 函数
-
函数调用、参数传递与返回
以
sleep(atoi(argv[3]))
调用为例,调用sleep
函数需传入调用atoi
的返回值,在调用atoi
时,先访问argv[3]
将其值保存到%rdi
:movq %rax, %rdi call atoi@PLT
在
atoi
中以ret
返回,返回值保存在%rax
中,再将返回值作为sleep
函数参数传入:movl %eax, %edi call sleep@PLT
3.3.3 赋值
赋值语句对应 x86
汇编中的 mov
指令,后缀表示操作的字节数,如:
movq (%rax), %rax
将 %rax
作为内存地址,取地址的值(四字)传入 %rax
其他后缀,有:
movb
单字movw
双字movl
四字movq
八字
3.3.4 算术操作
四则运算在 x86
有指令直接实现,如:
addl $1, -4(%rbp)
对 %rbp-4
的内存引用值加一,即 i++
其他指令的使用方法如图所示:
3.3.5 关系与条件转移
以 hello.c
的第一个判断语句为例:
if (argc != 4)
{
printf("用法: Hello 学号 姓名 秒数!\n");
exit(1);
}
汇编如下:
cmpl $4, -20(%rbp)
je .L2
.loc 1 16 3
leaq .LC0(%rip), %rdi
call puts@PLT
.loc 1 17 3
movl $1, %edi
call exit@PLT
因为 %rbp-20
就是 argc
的地址,将其与 4
比较,cmpl
会修改条件码寄存器,je
指令根据条件码寄存器的值决定是否转跳,此处可知,只有相等时转跳,不相等时进入 if
代码块内。
对 i < 8
,同理:
cmpl $7, -4(%rbp)
jle .L4
3.3.6 类型转换
机器级的类型转换,如基本类型 int
float
double
short
等是通过二进制表示的截断与扩展实现的。
字符串与数字间的类型转换则利用 atoi
这样的函数实现。
3.4 本章小结
本章对 hello.c
与 hello.i
,即源码到汇编的对应关系做出解释,讲述了 x86
的基本汇编命令的作用。
第 4 章 汇编
4.1 汇编的概念与作用
注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。
汇编器将汇编语言文本翻译成机器语言,产生二进制文件,并将这些文件打包成可重定向目标文件,将各类信息存储在文件头中。
4.2 在Ubuntu下汇编的命令
命令:
gcc -c -g hello.s -o hello.o
4.3 可重定位目标 elf
格式
分析 hello.o
的ELF格式,用 readelf
等列出其各节的基本信息,特别是重定位项目分析。
编译命令:
gcc -c hello.s -o hello.o
ELF
头
命令:
readelf -h hello.o
结果:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 1240 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 14
Section header string table index: 13
ELF
头描述了该可重定向目标文件的一些信息,可以帮助链接器链接文件,其中包括了 ELF
头的大小、目标文件类型、各节的大小和偏移等信息。
在本例中,可以看见这个可重定向目标文件有 14
个节。
节分布
命令:
readelf -S hello.o
结果:
There are 14 section headers, starting at offset 0x4d8:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
0000000000000092 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 00000388
00000000000000c0 0000000000000018 I 11 1 8
[ 3] .data PROGBITS 0000000000000000 000000d2
0000000000000000 0000000000000000 WA 0 0 1
[ 4] .bss NOBITS 0000000000000000 000000d2
0000000000000000 0000000000000000 WA 0 0 1
[ 5] .rodata PROGBITS 0000000000000000 000000d8
0000000000000033 0000000000000000 A 0 0 8
[ 6] .comment PROGBITS 0000000000000000 0000010b
000000000000002a 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 00000135
0000000000000000 0000000000000000 0 0 1
[ 8] .note.gnu.propert NOTE 0000000000000000 00000138
0000000000000020 0000000000000000 A 0 0 8
[ 9] .eh_frame PROGBITS 0000000000000000 00000158
0000000000000038 0000000000000000 A 0 0 8
[10] .rela.eh_frame RELA 0000000000000000 00000448
0000000000000018 0000000000000018 I 11 9 8
[11] .symtab SYMTAB 0000000000000000 00000190
00000000000001b0 0000000000000018 12 10 8
[12] .strtab STRTAB 0000000000000000 00000340
0000000000000048 0000000000000000 0 0 1
[13] .shstrtab STRTAB 0000000000000000 00000460
0000000000000074 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
此处可以看到各节的类型、大小、位置等信息,还可以看出各节的可执行或可读情况。
符号表
命令:
readelf -s hello.o
结果
Symbol table '.symtab' contains 18 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 9
9: 000