2022春哈工大ICS大作业——程序人生-hello‘sP2P
计算机系统(初发布于2022/5/15,修改于2022/5/20)
大作业
题 目 程序人生-Hello’s P2P
专 业
学 号
班 级
学 生 沈业力
指 导 教 师 史先俊
计算机科学与技术学院
2022年5月
第1章 概述................................................... - 4 -
1.1 Hello简介............................................ - 4 -
1.2 环境与工具........................................... - 4 -
1.3 中间结果............................................... - 4 -
1.4 本章小结............................................... - 4 -
第2章 预处理............................................... - 5 -
2.1 预处理的概念与作用........................... - 5 -
2.2在Ubuntu下预处理的命令................ - 5 -
2.3 Hello的预处理结果解析.................... - 5 -
2.4 本章小结............................................... - 5 -
第3章 编译................................................... - 6 -
3.1 编译的概念与作用............................... - 6 -
3.2 在Ubuntu下编译的命令.................... - 6 -
3.3 Hello的编译结果解析........................ - 6 -
3.4 本章小结............................................... - 6 -
第4章 汇编................................................... - 7 -
4.1 汇编的概念与作用............................... - 7 -
4.2 在Ubuntu下汇编的命令.................... - 7 -
4.3 可重定位目标elf格式........................ - 7 -
4.4 Hello.o的结果解析............................. - 7 -
4.5 本章小结............................................... - 7 -
第5章 链接................................................... - 8 -
5.1 链接的概念与作用............................... - 8 -
5.2 在Ubuntu下链接的命令.................... - 8 -
5.3 可执行目标文件hello的格式........... - 8 -
5.4 hello的虚拟地址空间......................... - 8 -
5.5 链接的重定位过程分析....................... - 8 -
5.6 hello的执行流程................................. - 8 -
5.7 Hello的动态链接分析........................ - 8 -
5.8 本章小结............................................... - 9 -
第6章 hello进程管理.......................... - 10 -
6.1 进程的概念与作用............................. - 10 -
6.2 简述壳Shell-bash的作用与处理流程.. - 10 -
6.3 Hello的fork进程创建过程............ - 10 -
6.4 Hello的execve过程........................ - 10 -
6.5 Hello的进程执行.............................. - 10 -
6.6 hello的异常与信号处理................... - 10 -
6.7本章小结.............................................. - 10 -
第7章 hello的存储管理...................... - 11 -
7.1 hello的存储器地址空间................... - 11 -
7.2 Intel逻辑地址到线性地址的变换-段式管理............................................................ - 11 -
7.3 Hello的线性地址到物理地址的变换-页式管理........................................................ - 11 -
7.4 TLB与四级页表支持下的VA到PA的变换................................................................ - 11 -
7.6 hello进程fork时的内存映射......... - 11 -
7.7 hello进程execve时的内存映射..... - 11 -
7.8 缺页故障与缺页中断处理................. - 11 -
7.9动态存储分配管理.............................. - 11 -
7.10本章小结............................................ - 12 -
第8章 hello的IO管理....................... - 13 -
8.1 Linux的IO设备管理方法................. - 13 -
8.2 简述Unix IO接口及其函数.............. - 13 -
8.3 printf的实现分析.............................. - 13 -
8.4 getchar的实现分析.......................... - 13 -
8.5本章小结.............................................. - 13 -
结论............................................................... - 14 -
附件............................................................... - 15 -
参考文献....................................................... - 16 -
第1章 概述
1.1 Hello简介
1.2 环境与工具
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
硬件:Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz 2.30 GHz
软件:Windows 10,VMware 15.5.7,Ubuntu 20.04
调试工具:Visual Studio 2022 community 64-bit;
gedit,gcc,notepad++,readelf, objdump, hexedit, edb
1.3 中间结果
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
文件名 |
作用 |
hello.i |
hello.c预处理后得到的文件 |
hello.s |
编译后的汇编语言文件 |
hello.o |
汇编后得到的可重定位目标文件 |
hello.elf |
用readelf读取hello.o得到的ELF格式信息 |
Dhello.s |
反汇编hello.o得到的反汇编文件 |
helloout.elf |
由hello可执行文件生成的.elf文件 |
Dhelloout.s |
反汇编hello可执行文件得到的反汇编文件 |
hello |
最终链接得到的可执行文件 |
1.4 本章小结
本文首先根据自白介绍了P2P,020模式,列出了大作业所用到的硬件软件环境,以及相关调试工具,最后列出来过程中的中间文件
第2章 预处理
2.1 预处理的概念与作用
3、预编译程序将根据有关的文件,将那些不必要的代码(如条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等)过滤掉;
4、识别与替换特殊符号。例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。
2.2在Ubuntu下预处理的命令
2.3 Hello的预处理结果解析
2.4 本章小结
本章主要介绍了预处理的概念及作用、并结合Ubuntu系统下hello.c文件实际预处理之后得到的hello.i程序对预处理结果进行了解析。
第3章 编译
3.1 编译的概念与作用
编译过程就是利用编译器(ccl)把预处理完的.i文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的.s汇编代码文件。本作业中指的就是hello.i生成hello.s的过程。
1、扫描(词法分析)。将源代码程序输入扫描器,将源代码的字符序列分割成一系列记号。例array[index] = (index + 4) * (2 + 6);
3、语义分析。由语义分析器完成,指示判断是否合法,并不判断对错。又分静态语义:隐含浮点型到整形的转换,会报warning;动态语义:在运行时才能确定:例1除以3 。
4、源代码优化(中间语言生成)。中间代码(语言)使得编译器分为前端和后端,前端产生与机器(或环境)无关的中间代码,编译器的后端将中间代码转换为目标机器代码,目的:一个前端对多个后端,适应不同平台。
5、代码生成,目标代码优化。编译器后端主要包括:代码生成器:依赖于目标机器,依赖目标机器的不同字长,寄存器,数据类型等。目标代码优化器:选择合适的寻址方式,左移右移代替乘除,删除多余指令[2]。
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
命令:gcc -m64 -no-pie -fno-PIC -S hello.i -o hello.s
3.3 Hello的编译结果解析
常量在汇编代码中以立即数的形式存在。以if (argc!=4)为例,4是一个常量,在hello.s中对应的汇编语句为:
在这里4前面有$,以立即数形式存在,保存在.text中,作为指令的一部分。同样的道理,下面的for循环:
printf("Hello %s %s\n",argv[1],argv[2]);
和exit(1)中的8,1,2,3,0也是作为汇编代码的一部分存在于.text中,对应汇编代码为:
除了数字常量,还有字符串常量,printf()、scanf()中的字符串就被存储在.rodata节中作为本地常量存在。我们可以看见,在hello.s文件开头有这么一段代码:
.LC0代表local constant 0,第0本地常量,.string代表字符串,\XXX为UTF-8编码,一个汉字对应三个字节。
全局变量都在头文件,hello.s并没有全局变量。也没有静态变量。
唯一的局部变量i(4字节int型)在运行时保存在栈中。Hello.c第11行:int i
赋值操作,hello.c第17行:for(i=0;i<8;i++)
3.3.2 操作
hello.s中只有对应于hello.c中局部变量i的赋值操作:
hello.s的赋值语句:movl $0, -4(%rbp)。
此外,局部变量赋值用mov指令完成,具体的指令选择要根据变量数据类型大小来决定。用b,w,l,q分别代表1,2,4,8字节。
加法——自加操作符++,在for循环中每一次循环结束i使用自加操作符++进行加一操作,对应汇编语言为:
初次之外,还有寄存器值得加减操作,整个hello.s的算数操作如下:
printf("用法: Hello 学号 姓名 秒数!\n");
检查argc是否不等于4,设置条件码,一旦不相等就跳转到指定地址。
int main(int argc, char *argv[]) {…}
在argv数组中,argv[0]指向输入程序的路径和名称,argv[1],argv[2],argv[3]分别表示三个字符串,在我们的要求中分别对应学号,姓名,休眠秒数。
参数传递:传入参数argc和argv[],分别用寄存器%rdi和%rsi存储。
函数返回:通过movl $0, %eax将%eax设置为0,返回0,对应return 0.
函数调用:均由main函数调用。第一个printf在argc!=4时调用,第二个printf在每次for循环调用,汇编代码都是call puts@PLT。
函数调用:由main函数调用,当argc!=4时调用,call exit@PLT为调用的汇编命令。
参数传递:call atoi@PLT以及movl %eax, %edi将atoi函数的返回结果作为参数传入sleep函数,也即是将%eax中的值复制给%edi。
函数调用:main函数调用,每次for循环调用,通过汇编语句call sleep@PLT调用。
函数调用:main函数调用,每次for循环调用,通过汇编语句call atoi@PLT调用。
函数调用:main函数调用,在main函数返回前调用,call getchar@PLT调用。
3.4 本章小结
第4章 汇编
4.1 汇编的概念与作用
作用:编译器根据根据汇编指令表和机器指令表一一进行翻译,把编译阶段生成的汇编文件转化成机器代码,生成目标文件(.o)[2].
4.2 在Ubuntu下汇编的命令
命令:gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o
4.3 可重定位目标elf格式
分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析[3]。
首先用readelf -a hello.o > hello.elf指令生成hello.o的elf格式:
除了以下的五个结构,hello.elf其实还包括了程序头,节组等信息,主要的五部分如下:
4.3.1 ELF头(ELF header)
4.3.2 节头部表
不同节的位置和大小是由节头部表描述的,其中目标文件每个节都有一个固定大小的条目。
可以直接看hello.elf也可以使用readelf命令,这里使用readelf命令:
4.3.3 重定位节之.rela.text
一个.text 节中位置的列表,包含.text 节中需要进行重定位的信息,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。
4.3.4 重定位节之.rela.eh_frame
这个section同.rel.text一样属于重定位信息的section,只不过它包含的是eh_frame的重定位信息,而eh_frame生成描述如何unwind 堆栈的表。
4.3.5 符号表(.symtab)
.symtab是一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。
4.4 Hello.o的结果解析
objdump -d -r hello.o 分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。
使用objdump -d -r hello.o > Dhello.s分析hello.o的反汇编,并于第3章的hello.s进行对照分析。
1、数制。在hello.s中数都是十进制,而反汇编Dhello.s中都是十六进制
2、分支跳转。在hello.s中,跳转指令的目标地址直接记为段名称,如.L2,.L3等。而在反汇编得到的Dhello.s中,跳转的目标为相对偏移的地址,也即间接地址。
4.5 本章小结
第5章 链接
5.1 链接的概念与作用
注意:这儿的链接是指从 hello.o 到hello生成过程。
概念:链接是指将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载到内存并执行。链接可以执行于编译、加载、运行时。
5.2 在Ubuntu下链接的命令
5.3 可执行目标文件hello的格式
分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息[3]。
首先用readelf -a hello > helloout.elf生成hello可执行文件的elf格式helloout.elf
与前一部分ELF头基本一致,但类型发生改变,程序头大小和节头数量增加,并且获得了入口地址。
与hello.elf相比,其在链接之后的内容更加丰富详细。记录了其各段的基本信息,包括各段的起始地址,大小等信息,这些信息如下:
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 00000000004002e0 000002e0
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.propert NOTE 0000000000400300 00000300
0000000000000020 0000000000000000 A 0 0 8
[ 3] .note.ABI-tag NOTE 0000000000400320 00000320
0000000000000020 0000000000000000 A 0 0 4
[ 4] .hash HASH 0000000000400340 00000340
0000000000000038 0000000000000004 A 6 0 8
[ 5] .gnu.hash GNU_HASH 0000000000400378 00000378
000000000000001c 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 0000000000400398 00000398
00000000000000d8 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 0000000000400470 00000470
000000000000005c 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 00000000004004cc 000004cc
0000000000000012 0000000000000002 A 6 0 2
[ 9] .gnu.version_r VERNEED 00000000004004e0 000004e0
0000000000000020 0000000000000000 A 7 1 8
[10] .rela.dyn RELA 0000000000400500 00000500
0000000000000030 0000000000000018 A 6 0 8
[11] .rela.plt RELA 0000000000400530 00000530
0000000000000090 0000000000000018 AI 6 23 8
[12] .init PROGBITS 0000000000401000 00001000
000000000000001b 0000000000000000 AX 0 0 4
[13] .plt PROGBITS 0000000000401020 00001020
0000000000000070 0000000000000010 AX 0 0 16
[14] .plt.sec PROGBITS 0000000000401090 00001090
0000000000000060 0000000000000010 AX 0 0 16
[15] .text PROGBITS 00000000004010f0 000010f0
00000000000001f5 0000000000000000 AX 0 0 16
[16] .fini PROGBITS 00000000004012e8 000012e8
000000000000000d 0000000000000000 AX 0 0 4
[17] .rodata PROGBITS 0000000000402000 00002000
000000000000003b 0000000000000000 A 0 0 8
[18] .eh_frame PROGBITS 0000000000402040 00002040
0000000000000100 0000000000000000 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000403e00 00002e00
0000000000000008 0000000000000008 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000403e08 00002e08
0000000000000008 0000000000000008 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000403e10 00002e10
00000000000001e0 0000000000000010 WA 7 0 8
[22] .got PROGBITS 0000000000403ff0 00002ff0
0000000000000010 0000000000000008 WA 0 0 8
[23] .got.plt PROGBITS 0000000000404000 00003000
0000000000000048 0000000000000008 WA 0 0 8
[24] .data PROGBITS 0000000000404048 00003048
0000000000000010 0000000000000000 WA 0 0 8
[25] .bss NOBITS 0000000000404058 00003058
0000000000000008 0000000000000000 WA 0 0 1
[26] .comment PROGBITS 0000000000000000 00003058
000000000000002b 0000000000000001 MS 0 0 1
[27] .symtab SYMTAB 0000000000000000 00003088
0000000000000630 0000000000000018 28 43 8
[28] .strtab STRTAB 0000000000000000 000036b8
0000000000000216 0000000000000000 0 0 1
[29] .shstrtab STRTAB 0000000000000000 000038ce
00000000000000fe 0000000000000000 0 0 1
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)
5.4 hello的虚拟地址空间
使用edb加载hello, Data Dump 窗口可以查看加载到虚拟地址中的 hello 程序。
Data dump一开始显示是0x401000,对照一下节头部表,发现是.init段:
我们知道,main函数正是由init段里的启动代码所调用,因此这个对照关系是正确的。
进程的虚拟内存地址由0x400000开始,切换到0x400000:
可以发现,0x400000开始所储存的内容正和ELF头的magic字节序列一致:
字符串常量储存在rodata段,查看helloout.elf可以得知,.rodata段开始于0x402000,data dump中0x402000如下:
打开edb的symbolsviewer,与helloout.elf对比:
可以发现二者是完全一一对应的。说明了各段的虚拟地址与节头部表的对应关系。
5.5 链接的重定位过程分析
objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
利用objdump -d -r hello > Dhelloout.s生成反汇编文件
2、增加了一些节,增加了.init,.plt,第一点多出来的系统函数就是在这两个节中。
以下面的重定位条目为例,说明hello是如何对hello.o重定位:
CSAPP上讲的是R_X86_64_PC32,说的是利用与PC的相对地址计算重定位,这里利用的是R_X86_64_PLT32,其实计算过程是一样的。
refaddr=ADDR(s)+r.offset=0x4011f5
*refaddr=(unsigned)(ADDR(r.symbol)+r.addend-refaddr)=(unsigned)(-0x169)
在运行时,call指令调用PC值为0x4011fb,PC+0xfffffe95截断后得到0x401090,可以发现,正是hello运行时call的地址。
2、重定位节的符号引用,依靠重定位条目选择合适方式计算(R_X86_64_PC32或R_X86_64_32)
5.6 hello的执行流程
使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。
子程序名 |
地址(16进制) |
ld-2.31.so!_dl_start |
0x7f92fcd0e100 |
ld-2.31.so!_dl_init |
0x7f92fcd1edf0 |
hello!_start |
0x4010f0 |
libc-2.31.so!__libc_start_main |
0x7f92fcb27fc0 |
hello!printf@plt(调用8次) |
0x4010a0 |
hello!atoi@plt(调用8次) |
0x4010c0 |
hello!sleep@plt(调用8次) |
0x4010e0 |
hello!getchar@plt |
0x4010b0 |
libc-2.31.so!exit |
0x7f92fcb4aa70 |
5.7 Hello的动态链接分析
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。
5.8 本章小结
第6章 hello进程管理
6.1 进程的概念与作用
1、一个独立的逻辑控制流,他提供一个假象,好像我们的程序独占使用处理器;
2、一个私有的地址空间,他提供一个假象,好像我们的程序独占地使用内存系统。
6.2 简述壳Shell-bash的作用与处理流程
Shell是信号处理的代表,负责各进程创建与程序加载运行及前后台控制,作业调用,信号发送与管理等。
6.3 Hello的fork进程创建过程
在目标文件夹打开shell,输入./hello 120L020318 syl 1,带参数执行可执行文件:
父进程也负责子进程的回收。如果子进程结束时父进程仍然存在,那么将有父进程进行回收;反之则有init进行回收,一切进程都是init的子进程。
6.4 Hello的execve过程
execve函数在当前进程的上下文中加载并运行一个新程序(hello),原型为:
int execve(const char *filename, const char *argv[], const char *envp[])
execve加载并运行可执行文件filename(hello),且带参数列表argv和环境变量列表envp。与fork不同,execve只有在找不到hello时才会返回。
在execve加载了hello之后,它利用启动代码设置栈,并将控制传递给新程序的主函数,该主函数有如下的原型:
int main(int argc , char *argv[] , char *envp[]);
6.5 Hello的进程执行
结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。
6.6 hello的异常与信号处理
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。
打印8次信息,期间sleep时间由我们决定,以输入任意字符作为结束。
这是一个异步中断,来自于运行期间不断敲入的回车(这里敲了3下),于是程序启动处理程序(实验四的parseline有关),完成后正常执行下一条指令,因此正常结束,中断处理如下:
乱按的字符串留到缓冲区,程序做出的反应是直接打印在屏幕上。同时如果期间输入回车,那么这之前到上一次输出信息的输入将会被视为命令行(与shell解析命令有关,实验四的eval函数)。
输入ctrl+z后我们可以发现不在输出信息,而是输出hello进程的状态与ID
输入jobs与ps确认只是被挂起,然后输入fg 1(1是jid)使其恢复,可以发现,他接着输出未完成的输出:
进程收到 SIGINT 信号,结束 hello。与ctrl+z形成对比:在ps中查询不到其PID,在job中也没有显示,可以看出ctrl+c让hello彻底结束。
输入pstree可以以树方式查看父子进程关系,可以看到hello进程也在其中:
6.7本章小结
第7章 hello的存储管理
7.1 hello的存储器地址空间
虚拟地址是程序运行在保护模式下,这样程序访问存储器所使用的逻辑地址称为虚拟地址。我们用edb执行hello时data dump所显示的地址就是虚拟地址,虚拟地址一般与进程的虚拟空间挂钩。
7.2 Intel逻辑地址到线性地址的变换-段式管理
逻辑地址实际是由 48 位组成的,前 16 位包括“段选择符”后 32 位“段内偏移量”。前16位组成如下图:
整体来讲,转换过程就是利用索引在GDT或者LDT中找到段描述符得到段基址,用段基址加上逻辑地址的段内偏移即可得到,如下图:
补充说明下GDT以及LDT。GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。二者都是不可见的。详细功能如下:
7.3 Hello的线性地址到物理地址的变换-页式管理
1、由逻辑地址得到的线性地址一共 32 位。前 10 位是页目录索引,中间 10 位是页表索引,最后 12 位是业内偏移量
2、由 CR3 寄存器得到「页目录基地址」,再得到「页目录项」
4、由「页表基地址」得到「页表项」,最后得到物理地址[6]。
我们csapp中一级页表的虚拟地址向物理地址转换也极为类似,因为虚拟地址空间一般也是线性地址空间:
7.4 TLB与四级页表支持下的VA到PA的变换
7.3中阐述的一般针对一级页表,事实上,一级页表会造成极大的浪费,因此实际使用多级页表。同时TLB快表的存在也进一步加速了VA向PA的变换,整体过程如书上P577 core i7地址翻译:
7.5 三级Cache支持下的物理内存访问
经过7.4,我们已经将VA转换为PA,可以通过PA对内存进行访问。Intel core i7的三级cache结构如下:
(2) 若未找到相匹配的行或有效位为0,则L1未命中,继续在L2中进行类似过程的查找。若仍未命中,还要在L3高速缓存中进行查找。三级Cache均未命中则需访问主存或硬盘获取数据。
下图展示了cache如何和虚拟内存结合起来。主要思路是地址翻译发生在cache查找之前[1]。
7.6 hello进程fork时的内存映射
7.7 hello进程execve时的内存映射
execve函数在当前进程中加载并运行包含在可执行文件hello中的程序,用hello替代了当前bash中的程序。
1、删除已存在的用户区域。删除当前进程hello虚拟地址的用户部分中的已存在的区域结构。
3、映射共享区域。若hello程序与共享对象或目标(如标准C库libc.so)链接,则将这些对象动态链接到hello程序,然后再映射到用户虚拟地址空间中的共享区域内。
4、设置程序计数器(PC)。exceve做的最后一件事是设置当前进程的上下文中的程序计数器,是指指向代码区域的入口点。
下一次调度这个进程时,他将从这个入口点开始执行。Linux将根据需要换入代码和数据页面[1]。加载器映射用户地址空间区域图示如下(hello地位等同于a.out):
7.8 缺页故障与缺页中断处理
页面命中完全由硬件处理,处理缺页则需要硬件和操作系统内核协作完成:
4. PTE中的有效位是0,所以MMU出发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序。
5. 缺页处理程序确认出物理内存中的牺牲页,如果这个页已经被修改了,则把它换到磁盘。
7. 缺页处理程序返回到原来的进程,再次执行导致缺页的命令。CPU将引起缺页的虚拟地址重新发送给MMU。因为虚拟页面已经换存在物理内存中,所以就会命中[1]。
7.9动态存储分配管理
堆中的空闲块通过头部中的大小字段隐含地连接,分配器通过遍历堆中所有的块,从而间接遍历整个空闲块的集合。
在每个空闲块中,都包含一个前驱(pred)与后继(succ)指针,从而减少了搜索与适配的时间。
维护多个空闲链表,其中,每个链表的块具有相同的大小。将所有可能的块大小分成一些等价类,从而进行分离存储。
7.10本章小结
第8章 hello的IO管理
8.1 Linux的IO设备管理方法
8.2 简述Unix IO接口及其函数
3、改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0。这个文件位置是从文件开头起始的字节偏移量。应用程序能够通过执行seek操作,显式地设置文件的当前位置为k。
1、int open(char *filename, int flags, mode_t mode)
Open函数将filename转换为一个文件描述符,并且返回描述符数字,且总是在进程中当前没有打开的最小描述符。Flags参数指明了进程打算如何访问这个文件;mode参数指定了新文件的访问权限位。
Close函数用于关闭一个打开的函数,成功返回0;关闭一个已关闭的描述符会出错返回-1.参数fd是文件描述符。
3、ssize_t read(int fd, void *buf,size_t n)
4、ssize_t write(int fd,const void *buf,size_t n)
Write函数从内存位置buf复制最多n字节到描述符fd的当前文件位置[1]。
5、off_t lseek(int fildes, off_t offset, int whence)
8.3 printf的实现分析
int printf(const char *fmt, ...)
va_list arg = (va_list)((char*)(&fmt) + 4);
很显然,我们需要一种手段,来让函数体可以知道具体调用时参数的个数。这一条语句给我们指明了答案(va_list是一个字符指针):
va_list arg = (va_list)((char*)(&fmt) + 4)
接近十年前应该是32位计算机,所以指针大小是4,所以 (char*)(&fmt) + 4) 表示的是...中的第一个参数的地址。
int vsprintf(char *buf, const char *fmt, va_list args)
itoa(tmp, *((int*)p_next_arg));
传输完对应参数后,以int INT_VECTOR_SYS_CALL结束,那么玄机肯定藏在这之中,因为这之前都只是做着传参的工作。看一看实现:
call [sys_call_table + eax * 4]
mov [esi + EAXREG - P_STACKBASE], eax
8.4 getchar的实现分析
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ASCII码,直到接受到回车键才返回。
8.5本章小结
结论
Hello.c进化成可执行文件hello经历了层层关卡:
- hello.c经过预编译,对#处理得到hello.i文本文件
- hello.i经过编译,得到汇编代码hello.s汇编文件
- hello.s经过汇编,得到二进制可重定位目标文件hello.o
- hello.o经过静态和动态链接,生成了可执行文件hello
而hello的实际执行离不开操作系统的调度以及硬件的配合:
1、shell bash调用fork函数,生成子进程;这个子进程调用execve函数在当前进程的上下文中加载并运行新程序hello
2、hello的变化过程中,会有各种地址,但最终我们真正期待的是PA物理地址,可执行文件中VA需要通过TLB以及页表机制进行转换;而PA的访问也需要内存层次结构的支持才得以快速运行。
3、hello再运行时会调用一些函数,比如printf函数,这些函数与linux I/O的设备模拟化密切相关
4、hello最终被shell父进程回收,内核会收回为其创建的所有信息,hello完成了他的使命。
CSAPP是计算机书籍中极为经典的著作,介绍了计算机系统的基本概念,包括最底层的内存中的数据表示、流水线指令的构成、虚拟存储器、编译系统、动态加载库,以及用户应用等。书中提供了大量实际操作,可以帮助读者更好地理解程序执行的方式,改进程序的执行效率。此书以程序员的视角全面讲解了计算机系统,深入浅出地介绍了处理器、编译器、操作系统和网络环境。内容很多,同时也为今后408课程的进一步学习打下基础。
What is a programmer?互联网行业大力发展的今天,许多人误将码农当作真正的programmer,殊不知,一个合格的程序员不能仅仅盯着算法以及库是怎么实现的,更应该着眼于计算机底层。所有技术的革新首先是基于硬件条件与软件底层,我们不应眼高手低,只关注上层而忽略底层。HITer应该要有这个觉悟。
(结论0分,缺失 -1分,根据内容酌情加分)
附件
文件名 |
作用 |
hello.i |
hello.c预处理后得到的文件 |
hello.s |
编译后的汇编语言文件 |
hello.o |
汇编后得到的可重定位目标文件 |
hello.elf |
用readelf读取hello.o得到的ELF格式信息 |
Dhello.s |
反汇编hello.o得到的反汇编文件 |
helloout.elf |
由hello可执行文件生成的.elf文件 |
Dhelloout.s |
反汇编hello可执行文件得到的反汇编文件 |
hello |
最终链接得到的可执行文件 |
参考文献
[1] 《深入理解计算机系统》 Randal E.Bryant David R.O’Hallaron 机械工业出版社
[2]《gcc--编译的四大过程及作用》https://blog.csdn.net/shiyongraow/article/details/81454995
[3] 《深入理解ELF文件》https://blog.51cto.com/u_12444109/3026869
[4] 《段页式访存——逻辑地址到线性地址的转换》https://www.jianshu.com/p/fd2611cc808e
[5]《C语言lseek()函数:移动文件的读写位置》http://c.biancheng.net/cpp/html/236.html
[6] 《段页式访存——线性地址到物理地址的转换》https://www.cnblogs.com/pipci/p/12404023.html
[7] 《深入了解GOT,PLT和动态链接》https://www.cnblogs.com/pannengzhi/p/2018-04-09-about-got-plt.html
[8] 《printf 函数实现的深入剖析》https://www.cnblogs.com/pianist/p/3315801.html
2022春哈工大ICS大作业——程序人生-hello‘sP2P相关推荐
- 【2022】哈工大计算机系统大作业——程序人生Hello’s P2P
2022哈工大计算机系统大作业--程序人生Hello's P2P 摘要 第1章 概述 1.1 Hello简介 1.2 环境与工具 1.3 中间结果 1.4 本章小结 第2章 预处理 2.1 预处理的概 ...
- 哈工大计算机系统大作业 程序人生-Hello’s P2P 2022
2022哈工大计算机系统大作业 目录 摘 要 第1章 概述 1.1 Hello简介 1.2 环境与工具 1.3 中间结果 1.4 本章小结 第2章 预处理 2.1 预处理的概念与作用 2.2在Ubun ...
- 哈工大计算机系统大作业 程序人生-Hello‘s P2P
哈工大计算机系统大作业 摘要 第1章 概述 1.1 Hello简介 1.2 环境与工具 1.3 中间结果 1.4 本章小结 第2章 预处理 2.1 预处理的概念与作用 2.2 在Ubuntu下预处理的 ...
- 2022春哈工大计算机系统大作业——hello的程序人生
计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算学部 学 号 班 级 学 生 指 导 教 师 计算机科学与技术学院 2021年5月 摘 ...
- 2022哈工大计算机系统大作业——程序人生
计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 人工智能(未来技术) 学 号 120L020301 班 级 2036011 学 生 张思远 ...
- 哈工大csapp大作业程序人生
大作业 题 目 程序人生-Hello's P2P 专 业 计算机科学与技术 学 号 2021111719 班 级 2103101 学 生 杨济荣 指 导 教 ...
- 哈工大 计算机系统大作业 程序人生-Hello’s P2P From Program to Process
计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算学部 学 号 120L020512 班 级 2003004 学 生 黄鹏程 指 导 ...
- 哈工大计算机系统大作业-程序人生-Hello’s P2P
计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算机科学与技术 学 号 2021110802 班 级 21w0312 学 生 黄键树 ...
- 哈工大计算机系统大作业 程序人生-Hello‘s P2P 020
计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 计算机科学与技术 学 号 2021112808 班 级 2103103 学 生 陶丽娜 指 导 教 师 刘宏伟 摘 要 本文详细介 ...
最新文章
- [Linux学习]Linux下进程通讯之共享内存
- 移动端页面输入法挡住input输入框的解决方法
- 电信联通三联手 “事实婚姻”还是临时取暖?
- dos拼接字符串以及截取字符串
- WebService基于SoapHeader实现安全认证[webservice][.net][安全][soapheader]
- 启明云端分享|SSD201_自动升级固件与烧录MAC地址
- 10万人的大场馆如何“画座位”?
- 怎么使图表居中显示_【Excel技巧】制作柱形图图表完美呈现百分比,提升您的报表颜值...
- HDU 5869 Different GCD Subarray Query 树状数组 + 一些数学背景
- 工作217:重置逻辑
- 10款Flash和Javascript网页音乐播放器
- POJ-3421 X-factor Chains---求因子+递推 或 素因子+组合数学
- Navicat Premium 12快捷键
- SSL协议之数据加密过程详解
- 《数据结构》C语言版 (清华严蔚敏考研版) 全书知识梳理
- FishC《零基础学习python》笔记-- 第014讲、15讲、16讲:字符串:各种奇葩的内置方法、格式化;序列
- sas不能安装独立的java_sas安装问题java platform standard edition runtime environment
- 服务器在外国那么登录网站算,国外服务器网站好吗?好在哪?
- 实时股票行情接口api有哪些?
- turf.js字典——查询turf库的所有方法及用途