HITICS大作业 程序人生-Hello’s P2P

程序人生-Hello’s P2P

摘 要

在本篇文章之中,我们将跟随着程序hello的脚步,畅游计算机系统的世界。我们将从刚刚出生成为C语言代码的hello开始,看着他经过预处理、编译、汇编、链接的P2P过程,渐渐的从一个文本文件变成一个可执行的目标程序。我们也将了解hello从它开始运行起,在计算机的系统之中发生的种种变化,直到hello这个程序运行结束从系统之中消失。

关键词:计算机系统 ;程序的生成;进程控制;存储管理

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

第1章 概述

1.1 Hello简介

Hello的P2P就是hello从一个高级程序语言的文本文件开始经历了预处理、编译、汇编、链接等阶段的处理之后,变成一个可以运行的应用程序的过程。
Hello的O2O就是hello这个程序在shell中fork、execve成为进程,载入内存,然后开始执行hello的代码,在这个过程中系统为其分配时间片来执行逻辑控制流,最终在hello执行完毕后,通过父进程进行hello的回收,并清除hello在内核所使用的所有资源的过程。

1.2 环境与工具

硬件环境:Inter Core i5 7200U CPU;2G RAM;128G SSD;500G HDD
软件环境:Vmware;Ubuntu 16.04 LTS 64位;Windows 7 64位
使用工具:gcc;edb;codeblocks;objdump;readelf等

1.3 中间结果

hello.i 经过预处理后的hello
hello.s 经过编译后的汇编语言文件
hello.o 经过汇编后的可重定位目标文件
hello.out 经过连接后生成的课执行目标文件
asm1.txt hello.o经过objdump反汇编后的输出
asm2.txt hello.out经过objdump反汇编后的输出
elf1.txt hello.o使用readelf读取出的elf的输出
elf2.txt hello.out使用readelf读取出的elf的输出

1.4 本章小结

在本章之中我们大致了解了hello的P2P和O2O的过程,并对在实验的过程之中对hello进行处理所使用的软硬件环境以及工具和所生成的中间结果进行了一定的了解。

	(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理是根据以字符#开头的命令,来修改源程序。
预处理是对头文件以及宏定义等的处理
其具体处理的内容如下:
1).将源文件中用#include形式声明的文件复制到新的程序中。比如hello.c第6-8行中的#include<stdio.h> 等命令告诉预处理器读取系统头文件stdio.h unistd.h stdlib.h 的内容,并把它直接插入到程序文本中。
2).用实际值替换用#define定义的字符串
3).根据#if后面的条件决定需要编译的代码

2.2在Ubuntu下预处理的命令

cpp hello.c > hello.i
在这里插入图片描述在这里插入图片描述

2.3 Hello的预处理结果解析

查看hello.i以后我们发现hello.c的代码的前端插入了具体的头文件的内容
当我们把hello.i文件用codeblocks打开之后发现,hello.i的代码增长到了3119行,在这之中我们找到main函数的位置:
在这里插入图片描述

2.4 本章小结

本章完成了对hello的预处理,在hello的预处理阶段我们对C程序的代码中的以字符#开头的命令进行处理,处理的方式是对把这些命令所代表的具体内容直接插入C程序代码。

				(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译阶段,编译器将文本文件hello.i翻译成文本文件hello.s,其中翻译后的hello.s文件中保存的是与该程序对应的汇编语言代码
编译的主要作用:1.扫描(词法分析),2.语法分析,3.语义分析,4.源代码优化(中间语言生成),5.代码生成,目标代码优化。
1)将源代码程序输入扫描器,将源代码的字符序列分割成一系列记号。
2)基于词法分析得到的一系列记号,生成语法树。
3)由语义分析器完成,指示判断是否合法,并不判断对错。又分静态语义:隐含浮点型到整形的转换,会报warning。
4)中间代码(语言)使得编译器分为前端和后端,前端产生与机器(或环境)无关的中间代码,编译器的后端将中间代码转换为目标机器代码,目的:一个前端对多个后端,适应不同平台。
5)编译器后端主要包括:代码生成器:依赖于目标机器,依赖目标机器的不同字长,寄存器,数据类型等
目标代码优化器:选择合适的寻址方式,左移右移代替乘除,删除多余指令

3.2 在Ubuntu下编译的命令

gcc –S hello.c –o hello.s
在这里插入图片描述在这里插入图片描述

3.3 Hello的编译结果解析

3.3.1全局变量

int sleepsecs=2.5;
“Usage: Hello 学号 姓名!\n”
“Hello %s %s\n”
这些储存在全局变量区,在hello.s文件中我们在使用全局变量的时候就要用$地址(%rip)使用

3.3.2 局部变量

int i
局部变量存放在栈中
此处为-4(%rbp)

3.3.3 参数传递与函数调用

因为是64位的系统,所以说在函数调用时的参数传递使用rsi和rdi两个寄存器,函数调用在汇编程序hello.s中被翻译成调用语句call

3.3.4 条件控制

在汇编程序hello.s中if语句被翻译成条件跳转语句
此处为:在这里插入图片描述

3.3.5循环

在汇编程序hello.s中for循环被翻译成条件跳转语句
在这里插入图片描述
L4部分为循环体,在循环体结束时add语句对局部变量i加一,在这之后把i的值和9进行比较,若i<=9则返回L4处再次执行循环体

3.3.6类型转换

我们发现在hello中还存在着类型转换int sleepsecs=2.5就是一种隐式的类型转换,他把浮点型的数2.5赋值给了整型的变量sleepsecs,所以说会发生类型的转换,而在进行编译的时候程序改变数值和位模式的原则是:值会向零舍入,所以说最终类型转换后的sleepsecs的值为2
在这里插入图片描述

3.4 本章小结

本章完成了对hello的编译过程,在hello的编译过程中,编译器将经历过预处理后的C语言文件的语句按照一定的类型与方式进行翻译,并最终生成汇编代码的文件hello.s

					(第3章2分)

第4章 汇编

4.1 汇编的概念与作用

汇编阶段是汇编器(as)将hello.s翻译成机器指令,并把这些指令打包成可重定位目标程序的格式,并将结果保存在hello.o中

4.2 在Ubuntu下汇编的命令

as hello.s –o hello.o
在这里插入图片描述在这里插入图片描述

4.3 可重定位目标elf格式

ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: REL (可重定位文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x0
程序头起点: 0 (bytes into file)
Start of section headers: 1144 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 0 (字节)
Number of program headers: 0
节头大小: 64 (字节)
节头数量: 13
字符串表索引节头: 12

节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0 ] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1 ] .text PROGBITS 0000000000000000 00000040
0000000000000081 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 00000338
00000000000000c0 0000000000000018 I 10 1 8
[ 3] .data PROGBITS 0000000000000000 000000c4
0000000000000004 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 000000c8
0000000000000000 0000000000000000 WA 0 0 1
[ 5] .rodata PROGBITS 0000000000000000 000000c8
000000000000002b 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 000000f3
0000000000000025 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 00000118
0000000000000000 0000000000000000 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 00000118
0000000000000038 0000000000000000 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 000003f8
0000000000000018 0000000000000018 I 10 8 8
[10] .symtab SYMTAB 0000000000000000 00000150
0000000000000198 0000000000000018 11 9 8
[11] .strtab STRTAB 0000000000000000 000002e8
000000000000004d 0000000000000000 0 0 1
[12] .shstrtab STRTAB 0000000000000000 00000410
0000000000000061 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)

There are no section groups in this file.

本文件中没有程序头。

There is no dynamic section in this file.

重定位节 ‘.rela.text’ at offset 0x338 contains 8 entries:
偏移量 信息 类型 符号值 符号名称 + 加数
000000000018 000500000002 R_X86_64_PC32 0000000000000000 .rodata - 4
00000000001d 000c00000004 R_X86_64_PLT32 0000000000000000 puts - 4
000000000027 000d00000004 R_X86_64_PLT32 0000000000000000 exit - 4
000000000050 000500000002 R_X86_64_PC32 0000000000000000 .rodata + 1a
00000000005a 000e00000004 R_X86_64_PLT32 0000000000000000 printf - 4
000000000060 000900000002 R_X86_64_PC32 0000000000000000 sleepsecs - 4
000000000067 000f00000004 R_X86_64_PLT32 0000000000000000 sleep - 4
000000000076 001000000004 R_X86_64_PLT32 0000000000000000 getchar - 4

重定位节 ‘.rela.eh_frame’ at offset 0x3f8 contains 1 entry:
偏移量 信息 类型 符号值 符号名称 + 加数
000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table ‘.symtab’ contains 17 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 6
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 sleepsecs
10: 0000000000000000 129 FUNC GLOBAL DEFAULT 1 main
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND GLOBAL_OFFSET_TABLE
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND exit
14: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND sleep
16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND getchar

No version information found in this file.

4.4 Hello.o的结果解析

  1. 在反汇编代码中地址的偏移量是用十六进制数表示的,而在hello.s中则是直接采用的十进制的数。
  2. 在反汇编代码中函数调用时采用的是相对地址的寻址方法,而在hello.s中则是call 函数名,因为hello.c中调用的函数都是共享库中的函数,最终需要通过动态链接器才能确定函数的运行时执行地址,在汇编成为机器语言的时候,对于这些不确定地址的函数调用,所以说要采用相对寻址的方法,在接下来的链接的重定位中才能确定实际地址
    3.在hello.s中进行分支转移的条件跳转时,采用了跳转表的方法,而在反汇编代码中则是使用了相对寻址的方法,因为跳转表是在hello.s中可以使代码更易读,而在汇编后就不存在这种表示形式了
  3. 全局变量访问:在hello.s文件中,访问全局变量时,使用段名称+%rip,在反汇编代码中0+%rip,因为rodata中数据地址也是在运行时确定,故访问也需要重定位。所以在汇编成为机器语言时,将操作数设置为全0并添加重定位条目。

4.5 本章小结

本章完成了hello的汇编过程,在汇编阶段中我们将编译阶段生成的汇编语言hello.s文件翻译成可重定位目标文件hello.o,在这个过程中,hello.s中的十进制地址偏移量以十六进制存储,而因为程序的地址不确定所以说机器语言在进行跳转的时候,会以相对寻址的方式进行。

				(第4章1分)

第5章 链接

5.1 链接的概念与作用

链接阶段是程序将分别在不同的目标文件中编译或汇编的代码收集到一个可直接执行的文件中,从而生成可执行目标文件hello的过程。在链接之后的程序之中除了原有的代码中的函数以外,程序还链接目标程序和用于标准库函数的代码,以及链接目标程序和由计算机的操作系统提供的资源(例如,存储分配程序及输入与输出设备)

5.2 在Ubuntu下链接的命令

ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o
在这里插入图片描述在这里插入图片描述5.3 可执行目标文件hello的格式
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: EXEC (可执行文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x400500
程序头起点: 64 (bytes into file)
Start of section headers: 5920 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 56 (字节)
Number of program headers: 8
节头大小: 64 (字节)
节头数量: 25
字符串表索引节头: 24

节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400200 00000200
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 000000000040021c 0000021c
0000000000000020 0000000000000000 A 0 0 4
[ 3] .hash HASH 0000000000400240 00000240
0000000000000034 0000000000000004 A 5 0 8
[ 4] .gnu.hash GNU_HASH 0000000000400278 00000278
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000400298 00000298
00000000000000c0 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400358 00000358
0000000000000057 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 00000000004003b0 000003b0
0000000000000010 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 00000000004003c0 000003c0
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 00000000004003e0 000003e0
0000000000000030 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000400410 00000410
0000000000000078 0000000000000018 AI 5 19 8
[11] .init PROGBITS 0000000000400488 00000488
0000000000000017 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 00000000004004a0 000004a0
0000000000000060 0000000000000010 AX 0 0 16
[13] .text PROGBITS 0000000000400500 00000500
0000000000000132 0000000000000000 AX 0 0 16
[14] .fini PROGBITS 0000000000400634 00000634
0000000000000009 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 0000000000400640 00000640
000000000000002f 0000000000000000 A 0 0 4
[16] .eh_frame PROGBITS 0000000000400670 00000670
00000000000000fc 0000000000000000 A 0 0 8
[17] .dynamic DYNAMIC 0000000000600e50 00000e50
00000000000001a0 0000000000000010 WA 6 0 8
[18] .got PROGBITS 0000000000600ff0 00000ff0
0000000000000010 0000000000000008 WA 0 0 8
[19] .got.plt PROGBITS 0000000000601000 00001000
0000000000000040 0000000000000008 WA 0 0 8
[20] .data PROGBITS 0000000000601040 00001040
0000000000000008 0000000000000000 WA 0 0 4
[21] .comment PROGBITS 0000000000000000 00001048
0000000000000024 0000000000000001 MS 0 0 1
[22] .symtab SYMTAB 0000000000000000 00001070
0000000000000498 0000000000000018 23 28 8
[23] .strtab STRTAB 0000000000000000 00001508
0000000000000150 0000000000000000 0 0 1
[24] .shstrtab STRTAB 0000000000000000 00001658
00000000000000c5 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)

There are no section groups in this file.

程序头:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001c0 0x00000000000001c0 R 0x8
INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000076c 0x000000000000076c R E 0x200000
LOAD 0x0000000000000e50 0x0000000000600e50 0x0000000000600e50
0x00000000000001f8 0x00000000000001f8 RW 0x200000
DYNAMIC 0x0000000000000e50 0x0000000000600e50 0x0000000000600e50
0x00000000000001a0 0x00000000000001a0 RW 0x8
NOTE 0x000000000000021c 0x000000000040021c 0x000000000040021c
0x0000000000000020 0x0000000000000020 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000000e50 0x0000000000600e50 0x0000000000600e50
0x00000000000001b0 0x00000000000001b0 R 0x1

Section to Segment mapping:
段节…
00
01 .interp
02 .interp .note.ABI-tag .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame
03 .dynamic .got .got.plt .data
04 .dynamic
05 .note.ABI-tag
06
07 .dynamic .got

Dynamic section at offset 0xe50 contains 21 entries:
标记 类型 名称/值
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
0x000000000000000c (INIT) 0x400488
0x000000000000000d (FINI) 0x400634
0x0000000000000004 (HASH) 0x400240
0x000000006ffffef5 (GNU_HASH) 0x400278
0x0000000000000005 (STRTAB) 0x400358
0x0000000000000006 (SYMTAB) 0x400298
0x000000000000000a (STRSZ) 87 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x601000
0x0000000000000002 (PLTRELSZ) 120 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x400410
0x0000000000000007 (RELA) 0x4003e0
0x0000000000000008 (RELASZ) 48 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x4003c0
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x4003b0
0x0000000000000000 (NULL) 0x0

重定位节 ‘.rela.dyn’ at offset 0x3e0 contains 2 entries:
偏移量 信息 类型 符号值 符号名称 + 加数
000000600ff0 000300000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
000000600ff8 000500000006 R_X86_64_GLOB_DAT 0000000000000000 gmon_start + 0

重定位节 ‘.rela.plt’ at offset 0x410 contains 5 entries:
偏移量 信息 类型 符号值 符号名称 + 加数
000000601018 000100000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0
000000601020 000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000601028 000400000007 R_X86_64_JUMP_SLO 0000000000000000 getchar@GLIBC_2.2.5 + 0
000000601030 000600000007 R_X86_64_JUMP_SLO 0000000000000000 exit@GLIBC_2.2.5 + 0
000000601038 000700000007 R_X86_64_JUMP_SLO 0000000000000000 sleep@GLIBC_2.2.5 + 0

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table ‘.dynsym’ contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getchar@GLIBC_2.2.5 (2)
5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.2.5 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.2.5 (2)

Symbol table ‘.symtab’ contains 49 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000400200 0 SECTION LOCAL DEFAULT 1
2: 000000000040021c 0 SECTION LOCAL DEFAULT 2
3: 0000000000400240 0 SECTION LOCAL DEFAULT 3
4: 0000000000400278 0 SECTION LOCAL DEFAULT 4
5: 0000000000400298 0 SECTION LOCAL DEFAULT 5
6: 0000000000400358 0 SECTION LOCAL DEFAULT 6
7: 00000000004003b0 0 SECTION LOCAL DEFAULT 7
8: 00000000004003c0 0 SECTION LOCAL DEFAULT 8
9: 00000000004003e0 0 SECTION LOCAL DEFAULT 9
10: 0000000000400410 0 SECTION LOCAL DEFAULT 10
11: 0000000000400488 0 SECTION LOCAL DEFAULT 11
12: 00000000004004a0 0 SECTION LOCAL DEFAULT 12
13: 0000000000400500 0 SECTION LOCAL DEFAULT 13
14: 0000000000400634 0 SECTION LOCAL DEFAULT 14
15: 0000000000400640 0 SECTION LOCAL DEFAULT 15
16: 0000000000400670 0 SECTION LOCAL DEFAULT 16
17: 0000000000600e50 0 SECTION LOCAL DEFAULT 17
18: 0000000000600ff0 0 SECTION LOCAL DEFAULT 18
19: 0000000000601000 0 SECTION LOCAL DEFAULT 19
20: 0000000000601040 0 SECTION LOCAL DEFAULT 20
21: 0000000000000000 0 SECTION LOCAL DEFAULT 21
22: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
23: 0000000000000000 0 FILE LOCAL DEFAULT ABS
24: 0000000000600e50 0 NOTYPE LOCAL DEFAULT 17 __init_array_end
25: 0000000000600e50 0 OBJECT LOCAL DEFAULT 17 _DYNAMIC
26: 0000000000600e50 0 NOTYPE LOCAL DEFAULT 17 __init_array_start
27: 0000000000601000 0 OBJECT LOCAL DEFAULT 19 GLOBAL_OFFSET_TABLE
28: 0000000000400630 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
29: 0000000000601040 0 NOTYPE WEAK DEFAULT 20 data_start
30: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
31: 0000000000601044 4 OBJECT GLOBAL DEFAULT 20 sleepsecs
32: 0000000000601048 0 NOTYPE GLOBAL DEFAULT 20 _edata
33: 0000000000400634 0 FUNC GLOBAL DEFAULT 14 _fini
34: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2.5
35: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _libc_start_main@@GLIBC
36: 0000000000601040 0 NOTYPE GLOBAL DEFAULT 20 __data_start
37: 0000000000000000 0 FUNC GLOBAL DEFAULT UND getchar@@GLIBC_2.2.5
38: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
39: 0000000000400640 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
40: 00000000004005c0 101 FUNC GLOBAL DEFAULT 13 __libc_csu_init
41: 0000000000601048 0 NOTYPE GLOBAL DEFAULT 20 _end
42: 0000000000400530 2 FUNC GLOBAL HIDDEN 13 _dl_relocate_static_pie
43: 0000000000400500 43 FUNC GLOBAL DEFAULT 13 _start
44: 0000000000601048 0 NOTYPE GLOBAL DEFAULT 20 __bss_start
45: 0000000000400532 129 FUNC GLOBAL DEFAULT 13 main
46: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@@GLIBC_2.2.5
47: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@@GLIBC_2.2.5
48: 0000000000400488 0 FUNC GLOBAL DEFAULT 11 _init

Histogram for bucket list length (total of 3 buckets):
Length Number % of total Coverage
0 0 ( 0.0%)
1 0 ( 0.0%) 0.0%
2 2 ( 66.7%) 57.1%
3 1 ( 33.3%) 100.0%

Version symbols section ‘.gnu.version’ contains 8 entries:
地址: 00000000004003b0 Offset: 0x0003b0 Link: 5 (.dynsym)
000: 0 (本地) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5)
004: 2 (GLIBC_2.2.5) 0 (本地) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5)

Version needs section ‘.gnu.version_r’ contains 1 entry:
地址:0x00000000004003c0 Offset: 0x0003c0 Link: 6 (.dynstr)
000000: Version: 1 文件:libc.so.6 计数:1
0x0010: Name: GLIBC_2.2.5 标志:无 版本:2

Displaying notes found in: .note.ABI-tag
所有者 Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.2.0
5.4 hello的虚拟地址空间
代码段.text的虚拟地址空间代码段.text的虚拟地址空间
在这里插入图片描述只读数据段.rodata的虚拟地址空间

5.5 链接的重定位过程分析

  1. 相较于hello.o来说,hello中的地址不再是使用相对寻址方式的相对地址,而是在内存中的实际地址
  2. 在hello中除了在hello.o中已经有的main函数的机器语言的代码之外,还有在main函数中所使用到的位于其他文件上的其他函数的机器语言的代码,是加以整合后的文件

5.6 hello的执行流程

ld-2.27.so!_dl_start
ld-2.27.so!_dl_init
hello!_start
libc-2.27.so!__libc_start_main
libc-2.27.so!__cxa_atexit
hello!__libc_csu_init
libc-2.27.so!_setjmp
hello!_main
if内容(
hello!puts@plt
hello!exit@plt
)
hello!printf@plt
hello!sleep@plt
hello!getchar@plt
ld-2.27.so!_dl_runtime_resolve_xsave
ld-2.27.so!_exit

5.7 Hello的动态链接分析

在dl_init执行之前:
在这里插入图片描述在dl_init执行之后:
在这里插入图片描述

5.8 本章小结

经历过本章的链接阶段的处理之后,hello终于在整合了他所调用的其他文件中的函数等,从C语言的文本文件变成了可以执行的程序。

				(第5章1分)

第6章 hello进程管理

6.1 进程的概念与作用

进程是一个执行中的程序的实例,每一个进程都有它自己的地址空间,一般情况下,包括文本区域、数据区域、和堆栈。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储区着活动过程调用的指令和本地变量。

6.2 简述壳Shell-bash的作用与处理流程

Shell字面理解就是个“壳”,是操作系统(内核)与用户之间的桥梁,充当命令解释器的作用,将用户输入的命令翻译给系统执行。Linux中的shell提供一些内建命令(shell命令)供用户使用,可以用这些命令编写shell脚本来完成复杂重复性的工作。

6.3 Hello的fork进程创建过程

在命令行中输入 ./hello 1170300219 liuxuanlin 并回车,即可创建hello的进程。系统会首先利用fork创建一个该程序的子进程,并是之与父进程可以做到并发的运行

6.4 Hello的execve过程

在进行了fork之后,系统会使用加载器execve函数来在进程的上下文中加载进程hello。加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零,通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件中的内容,这样hello就完成了execve的过程

6.5 Hello的进程执行

在hello的调度过程中,当调用sleep之前,如果hello程序不被抢占则顺序执行,假如发生被抢占的情况,则进行上下文切换。而在hello运行到sleep的时候,
因为sleep的关系,hello发生上下文的切换,从用户态切换到内核态,直到经过sleepsecs=2.5秒后,解除程序的休眠再次发生上下文切换,从内核态重新恢复到用户态,其大概的过程如下图所示,在循环执行的几次之中,hello的进程都会发生上述的过程
在这里插入图片描述

6.6 hello的异常与信号处理

6.6.1正常运行

在这里插入图片描述

6.6.2 输入ctrl-z

在这里插入图片描述

6.6.3 输入ctrl-c

在这里插入图片描述

6.6.4 乱按

在这里插入图片描述

6.7本章小结

在本章之中hello开始了运行,我们通过fork,execve创建了hello进程,分析了hello进程的调用与上下文的切换,并了解了hello进程对异常和信号的处理。

				(第6章1分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

物理地址:加载到内存地址寄存器中的地址,内存单元的真正地址。
逻辑地址:CPU所生成的地址。逻辑地址是内部和编程使用的。
线性地址或也叫虚拟地址:跟逻辑地址类似,它也是一个不真实的地址,假设逻辑地址是相应的硬件平台段式管理转换前地址的话,那么线性地址则相应了硬件页式内存的转换前地址。

7.2 Intel逻辑地址到线性地址的变换-段式管理

段式管理,是指把一个程序分成若干个段进行存储,每个段都是一个逻辑实体,它的产生是与程序的模块化直接有关的。段式管理是通过段表进行的,它包括段号或段名、段起点、装入位、段的长度等。此外还需要主存占用区域表、主存可用区域表。
线性地址是由逻辑地址加上隐含的DS 数据段的基地址而得到

7.3 Hello的线性地址到物理地址的变换-页式管理

页式存储管理就是把内存物理空间划分成大小相等的若干区域,一个区域称为一块。把逻辑地址空间划分为大小相等的若干页,页大小与块大小相等
在不考虑多级页表的情况下,线性地址由页号和页内地址构成,而在线性地址转化为物理地址的时候,我们需要先通过页号找到页的起始地址,再加上页内地址就可以得到物理地址

7.4 TLB与四级页表支持下的VA到PA的变换

在这里插入图片描述
从图中我们可以看出在四级页表的支持下要得到物理地址,我们需要从第一个页表开始逐级读取页表中的内容,直到我们经过4级的页表后在加上页内地址就可以得到物理地址

7.5 三级Cache支持下的物理内存访问

首先在物理地址中读取出组索引CI位,通过组索引我们就可以得到加载该地址到cache中的具体位置,并可以根据行中的有效位判断cache中是否有该地址的内容。若不命中则再向下一级的cache中进行寻找,查询到数据后则会进行数据的放置。

7.6 hello进程fork时的内存映射

当fork函数被shell进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID,为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。

7.7 hello进程execve时的内存映射

execve在当前进程中加载并运行包含在可执行目标文件hello中的程序,用hello程序有效地替代了当前程序。加载并运行hello需要以下几个步骤:
1.删除已存在的用户区域,删除当前进程虚拟地址的用户部分中的已存在的区域结构。
2.映射私有区域,为新程序的代码、数据、bss和栈区域创建新的区域结构,所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为hello文件中的.text和.data区,bss区域是请求二进制零的,映射到匿名文件,其大小包含在hello中,栈和堆地址也是请求二进制零的,初始长度为零。
3.映射共享区域, hello程序与共享对象libc.so链接,libc.so是动态链接到这个程序中的,然后再映射到用户虚拟地址空间中的共享区域内。
4.设置程序计数器(PC),execve做的最后一件事情就是设置当前进程上下文的程序计数器,使之指向代码区域的入口点。

7.8 缺页故障与缺页中断处理

在执行一条指令时,如果发现他所需要访问的页没有在内存中(即有效位为0),那么停止该指令的执行,并产生一个页不存在的异常,这个异常就是缺页中断。
缺页中断处理一般流程:
1.硬件陷入内核,在堆栈中保存程序计数器,大多数当前指令的各种状态信息保存在特殊的cpu寄存器中。
2.启动一个汇编例程保存通用寄存器和其他易丢失信息,以免被操作系统破坏。
3.当操作系统发现缺页中断时,尝试发现需要哪个虚拟页面。通常一个硬件寄存器包含了这些信息,如果没有的话操作系统必须检索程序计数器,取出当前指令,分析当前指令正在做什么。
4.一旦知道了发生缺页中断的虚拟地址,操作系统会检查地址是否有效,并检查读写是否与保护权限一致,不过不一致,则向进程发一个信号或者杀死该进程。如果是有效地址并且没有保护错误发生则系统检查是否有空闲页框。如果没有,则执行页面置换算法淘汰页面。
5.如果选择的页框脏了,则将该页写回磁盘,并发生一次上下文切换,挂起产生缺页中断的进程让其他进程运行直到写入磁盘结束。且回写的页框必须标记为忙,以免其他原因被其他进程占用。
6.一旦页框干净后,操作系统查找所需页面在磁盘上的地址,通过磁盘操作将其装入,当页面被装入后,产生缺页中断的进程仍然被挂起,并且如果有其他可运行的用户进程,则选择另一用户进程运行。
7.当磁盘中断发生时,表明该页已经被装入,页表已经更新可以反映他的位置,页框也标记位正常状态。
8.恢复发生缺页中断指令以前的状态,程序计数器重新指向这条指令。
9.调度引发缺页中断的进程,操作系统返回调用他的汇编例程
10.该例程恢复寄存器和其他状态信息,返回到用户空间继续执行,就好像缺页中断没有发生过。

7.9动态存储分配管理

实现动态内存分配往往要考虑以下四个问题:
(1)空闲块组织:我们如何记录空闲块?
(2)选择:我们如何选择一个合适的空闲块来作为一个新分配的块?
(3)分割:在我们选了一个空闲块分配出我们需要的大小之后,我们如何处理这个空闲块中的剩余部分?
(4)合并:我们如何处理一个刚刚被释放的块?
malloc的空闲链表机制:这是对问题(1)的解答。有两种方法:显式空闲链表、隐式空闲链表。
(1)显式空闲链表:用一个链表将可用的内存块连接为一个长长的列表,称为空闲链表。将堆组织成双向空闲链表,每一个空闲块结点都包含一个祖先指针和一个后继指针。链表中的每个结点记录一个连续的、未分配的内存小块。结点的结构很简单,只需要记录可用内存小块的首地址和大小即可。
(2)隐式空闲链表:隐式空闲链表由N个块组成,一个块由头部、有效载荷(只包括已分配的块),以及可能的一些额外的填充(为了保证内存字节对齐而需要的填充)和尾部组成。头部大小为4个字节,在强加双字对齐的约束之后,块大小总是8的倍数,所以用高29位来表示存储块大小,剩余3位中利用最低位来表示块是否已分配(1表示已分配,0表示空闲);尾部和头部的内容一样,加入尾部是为了分配器可以判断出一个块的起始位置和状态。整个堆空间就是一个隐式空闲链表,从低地址向高地址生长,第一个和最后一个8字节标记为已分配,以确定堆的大小。

空闲链表如何从中选择分配内存块?这是对问题(2)的解答。有下面四种选择方法。
(1)首次适应法(First Fit):链表按块地址排序。选择第一个满足要求的空闲块。特点:低地址碎片多,高地址碎片少。
(2)最佳适应法(Best Fit):链表按空闲块大小排序。选择满足要求的,且大小最小的空闲块。特点:费时间,并且会出现很小的碎片。
(3)最坏适应法(Worst Fit):链表按空闲块大小排序。选择最大的空闲块。特点:碎片少,容易缺乏大块。
(4)循环首次适应法(Next Fit):链表按块地址排序。从上次分配位置开始找到第一个满足要求的空闲块。特点:碎片分布的又多又均匀。

如何处理被选空闲块中的剩余部分?这是对问题(3)的解答。一般来讲,是要把剩余的部分再插入回到空闲链表中去的。要注意一个空闲块分割成两个块时,需要腾出若干字节作为块的头部尾部等部分。

如何合并被释放的块?这是对问题(4)的解答。有两种方法:立即合并、推迟合并。对于隐式空闲链表,合并的具体过程是,
(1)前后块都已分配:直接释放当前块即可;
(2)前块分配、后块空闲:和后块合并;
(3)前块空闲、后块分配:和前块合并;
(4)前后块都已空闲:和前后块合并;

glibc对malloc的实现
目前最新版本为2.18,glibc源码目录/glibc-2.18/malloc中可以看到。在glibc的malloc的实现中, 分配虚存有两种系统调用可用: brk()和mmap(), 如果要分配大块内存, glibc会使用mmap()去分配内存,这种内存靠近栈。
基于UNIX 的系统有两个可映射到附加内存中的基本系统调用: brk: brk() 是一个非常简单的系统调用。还记得系统中断点吗?该位置是进程映射的内存边界。 brk()只是简单地将这个位置向前或者向后移动,就可以向进程添加内存或者从进程取走内存。sbrk()是以增量的方式增加扩大内存。
mmap: mmap(),或者说是“内存映像”,类似于 brk(),但是更为灵活。首先,它可以映射任何位置的内存,而不单单只局限于进程。其次,它不仅可以将虚拟地址映射到物理的
RAM 或者 swap,它还可以将它们映射到文件和文件位置,这样,读写内存将对文件中的数据进行读写。不过在这里,我们只关心 mmap向进程添加被映射的内存的能力。
在glibc的malloc的实现有一个优化:

  1. 当malloc()一块很小的内存是, glib
    c调用brk(), 只需要在heap中移动一下指针, 即可获得可用虚存, 这样分配得到的地址较小.
  2. 当malloc()一块较大内存时, glibc调用mmap(), 需要在内核中重新分配vma结构等, 他会在靠近栈的地方分配虚存, 这样返回的地址大.

7.10本章小结

在本章的过程之中我们了解了hello的存储器地址空间、intel的段式管理、hello的页式管理,以intel Core7在的四级页表的条件下介绍了VA到PA的变换、物理内存访问,还介绍了hello进程fork时的内存映射、execve时的内存映射、缺页故障与缺页中断处理、动态存储分配管理等方面的内容。

		  	(第7章 2分)

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件
设备管理:unix io接口
操作系统对IO设备的管理主要分为三部分:逻辑IO,设备驱动程序,中断服务程序
设备驱动程序:完成了对不同设备的各种各样的控制,对应用层提供接口。
中断服务程序:当设备结束的时候,向cpu发出中断信号。

8.2 简述Unix IO接口及其函数

Unix I/O接口统一操作:
1).打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件,内核记录有关这个打开文件的所有信息。
2).Shell创建的每个进程都有三个打开的文件:标准输入,标准输出,标准错误。
3) .改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0,这个文件位置是从文件开头起始的字节偏移量,应用程序能够通过执行seek,显式地将改变当前文件位置k。
4).读写文件:一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n,给定一个大小为m字节的而文件,当k>=m时,触发EOF。类似一个写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。
5).关闭文件,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中去。

Unix I/O函数:
1).int open(char* filename,int flags,mode_t mode) ,进程通过调用open函数来打开一个存在的文件或是创建一个新文件的。open函数将filename转换为一个文件描述符,并且返回描述符数字,返回的描述符总是在进程中当前没有打开的最小描述符,flags参数指明了进程打算如何访问这个文件,mode参数指定了新文件的访问权限位。
2).int close(fd),fd是需要关闭的文件的描述符,close返回操作结果。
3).ssize_t read(int fd,void *buf,size_t n),read函数从描述符为fd的当前文件位置赋值最多n个字节到内存位置buf。返回值-1表示一个错误,0表示EOF,否则返回值表示的是实际传送的字节数量。
4).ssize_t wirte(int fd,const void *buf,size_t n),write函数从内存位置buf复制至多n个字节到描述符为fd的当前文件位置

8.3 printf的实现分析

研究printf的实现,首先来看看printf函数的函数体
int printf(const char *fmt, …)
{
int i;
char buf[256];

va_list arg = (va_list)((char*)(&fmt) + 4);
i = vsprintf(buf, fmt, arg);
write(buf, i);

return i;
}
首先用vsprintf进行格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。

然后在write函数中,将栈中参数放入寄存器,ecx是字符个数,ebx存放第一个字符地址,int INT_VECTOR_SYS_CALLA代表通过系统调用syscall

syscall将字符串中的字节从寄存器中通过总线复制到显卡的显存中,显存中存储的是字符的ASCII码。字符显示驱动子程序将通过ASCII码在字模库中找到点阵信息将点阵信息存储到vram中。显示芯片会按照一定的刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)

最终就在屏幕上出现了我们所需要的字符串

8.4 getchar的实现分析

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

在本章的过程之中我们了解到在hello中所调用的printf和getchar函数,实际上是对系统的Unix I/O的封装,而真正调用的是write和read这样的系统调用函数。

		(第8章1分)

结论

Hello的P2P和O2O的过程中要经历以下阶段
1.程序的编写 将hello文本文件用C语言的代码编写好
2. 程序的预处理 将hello中的#开头的语句进行处理,并整合到文件hello.i中
3. 程序的编译 将hello.i文件翻译成汇编语言文件hello.s
4. 程序的汇编 将hello.s翻译成可重定位目标文件hello.o
5. 程序的链接 将hello.o与动态共享库进行链接整合成可执行目标文件hello.out
6. 程序的运行 在终端中输入./hello 1170300219 liuxuanlin以运行程序
7. 进程的创建 利用fork函数创建hello的子进程
8. 进程的加载 利用execve函数进行hello的加载
9. 时间片的分配 hello在分配的时间片中享有CPU资源,顺序执行自己的控制逻辑流
10. 内存的访问 hello将虚拟地址通过段式管理和页式管理获得物理地址,并进行访问
11. cache的内存使用 hello通过cache访问内存
12. 动态内存分配 hello在printf中调用malloc进行动态内存申请
13. 进程控制 hello调用sleep函数是进程挂起一段时间
14. 进程终止 hello通过父进程回收进程并结束运行

(结论0分,缺失 -1分,根据内容酌情加分)

附件

列出所有的中间产物的文件名,并予以说明起作用。
hello.i 经过预处理后的hello
hello.s 经过编译后的汇编语言文件
hello.o 经过汇编后的可重定位目标文件
hello.out 经过连接后生成的课执行目标文件
asm1.txt hello.o经过objdump反汇编后的输出
asm2.txt hello.out经过objdump反汇编后的输出
elf1.txt hello.o使用readelf读取出的elf的输出
elf2.txt hello.out使用readelf读取出的elf的输出

(附件0分,缺失 -1分)

参考文献

[1]深入理解计算机系统 兰德尔 E.布莱恩特 大卫 R.奥哈拉伦 机械工业出版社
[2] gcc–编译的四大过程及作用
https://blog.csdn.net/shiyongraow/article/details/81454995
[3] 程序的链接
https://blog.csdn.net/weixin_42749767/article/details/83901326
[4] 内存管理笔记(分页,分段,逻辑地址,物理地址与地址转换方式)
https://www.cnblogs.com/felixfang/p/3420462.html
[5] linux内核缺页中断处理
https://blog.csdn.net/a7980718/article/details/80895302
[6]页式存储管理(不连续存储)
https://blog.csdn.net/shuxnhs/article/details/80785451
[7] linux下逻辑地址-线性地址-物理地址图解
https://blog.csdn.net/l_nan/article/details/51188290
[8] 进程的挂起、阻塞和睡眠
https://www.cnblogs.com/ck1020/p/6669661.html

(参考文献0分,缺失 -1分)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值