哈工大csapp大作业 程序人生-Hello’s P2P

伤心客  金牌会员 | 昨天 06:30 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 521|帖子 521|积分 1563






盘算机体系


大作业



题     目  程序人生-Hello’s P2P 
专       业   人工智能范畴      
学     号    2021112571      
班     级    21WL026         
学       生     邹轩崎           
指 导 教 师      吴锐            






盘算机科学与技能学院

2023年5月

摘  要

本文从hello.c文件入手,在linux体系上,从团体上介绍了hello程序P2P和020的过程。阐述了hello代码颠末怎样的过程最终又是怎样在盘算机上运行,以及hello历程创建直到回收的全部过程,通过对这些过程的分析,我们能更好的相识盘算机体系的底层布局,并且进一步相识整个程序运行的的生命周期

关键词:hello world;CSAPP;程序的生命周期; P2P,O2O                              


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









目  录


第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.5 三级Cache支持下的物理内存访问................................ - 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简介

根据Hello的自白,利用盘算机体系的术语,简述Hello的P2P,020的整个过程。

Hello的P2P,020的整个过程是指Hello程序从一个C语言源文件到一个可实行的历程,以及从一个运行中的历程到一个结束的状态的过程。具体来说,可以分为以下几个步调:

预处置惩罚:预处置惩罚器(cpp)根据以#开头的下令,修改原始的C程序,比方将#include指令替换为对应的头文件内容,将#define指令替换为对应的宏定义,删除注释等。预处置惩罚后天生一个修改后的C程序,通常以.i为扩展名。
编译:编译器(cc1)将预处置惩罚后的C程序翻译为汇编语言程序,进行词法分析、语法分析、语义分析和优化等操作。编译后天生一个汇编语言程序,通常以.s为扩展名。
汇编:汇编器(as)将汇编语言程序翻译为机器语言指令,并打包成可重定位目标程序,利用ELF格式存储。汇编后天生一个可重定位目标程序,通常以.o为扩展名。
链接:链接器(ld)将可重定位目标程序和其他库函数或静态库合并成一个可实行目标程序,解决符号引用和重定位题目。链接后天生一个可实行目标程序,通常以.out或无扩展名为文件名。
加载:当在shell中输入可实行目标程序的文件名时,shell会创建一个子历程,并调用execve体系调用来加载可实行目标程序到内存中,并映射到虚拟地址空间。加载后,可实行目标程序就酿成了一个历程,并开始实行。
运行:历程在运行时,会通过CPU、内存、I/O等体系资源来完成其功能。CPU会按照时间片轮转的方式给历程分配实行时间,并进行取指、译码、实行等流水线操作。内存管理器会通过缓存和页表来管理历程的虚拟内存和物理内存之间的映射关系。I/O体系会根据历程的指令进行输入输出操作,比方打印字符串到屏幕上等。
结束:当历程实行完毕或遇到非常时,会向操作体系发送终止信号,并开释其占用的资源。操作体系会回收历程的内存空间和其他资源,并将其从体系中清除。这时,历程就从运行状态酿成了结束状态。

1.2 环境与工具

列出你为编写本论文,折腾Hello的整个过程中,利用的软硬件环境,以及开发与调试工具。
1.2.1 硬件环境

X64 CPU;2GHz;2G RAM;256GHD Disk 1.2.2

 软件环境

Windows10 64位; Vmware 11Ubuntu 16.04 LTS 64
1.2.3 开发工具

WINDOWS:clion; vi/vim/gedit+gcc

1.3 中心结果

列出你为编写本论文,天生的中心结果文件的名字,文件的作用等。
hello.c         hello程序的源代码
hello.i          源代码颠末预处置惩罚后的结果
hello.s         编译后天生的汇编程序
hello.o         汇编后天生的可重定位文件
hello            链接之后天生的可实行文件
a.out            添加相关编译参数的可实行文件
1.4 本章小结

本章根据hello自白介绍了hello的p2p,020的过程,说明白开发环境与利用的工具,以及实行中心的各种文件。

(第1章0.5分)




第2章 预处置惩罚

2.1 预处置惩罚的概念与作用

概念:预处置惩罚器cpp根据以字符#开头的下令(宏定义、条件编译),修改原始的C程序,将引用的所有库展开合并成为一个完整的文本文件。

作用:对原有的C程序进行预处置惩罚之后,原程序就不包罗预处置惩罚的部分了。比方我们引用的<stdio.c>该库之中的内容直接到场到代码之中,我们利用宏定义#define定义的语句也直接被替换掉,还有根据#if决定是否处置惩罚之后的代码。

颠末如许的处置惩罚,程序会变得更加方便阅读、修改、移植与调试,也有利于模块化的程序筹划。

2.2在Ubuntu下预处置惩罚的下令

Ubuntu下预处置惩罚的下令是:gcc -E hello.c  -o hello.i
正在上传…重新上传取消
正在上传…重新上传取消

2.3 Hello的预处置惩罚结果分析

由hello.c天生了hello.i文件
hello.i文件是预处置惩罚后天生的修改后的C程序,它包罗了源程序中的所有代码,以及被#include指令替换的头文件内容。它还删除了注释,展开了宏定义,添加了一些行号和文件名信息等。





正在上传…重新上传取消正在上传…重新上传取消


2.4 本章小结

本章介绍了预处置惩罚的概念与作用,介绍了在Linux中预处置惩罚下令的利用方法,以及对hello.c预处置惩罚结果进行相识析。

(第2章0.5分)


第3章 编译

3.1 编译的概念与作用

概念:编译是指将高级语言程序程序翻译成汇编语言文本程序,编译器把预处置惩罚后的文本文件hello.i文件进行一系列语法分析及优化后天生相应的汇编语言文件hello.s文件的过程。
编译的作用:
编译可以或许将不同的高级语言转化为通用的低级语言指令,即汇编语言
3.2 在Ubuntu下编译的下令

在Ubuntu下编译的下令为:gcc -S hello.i -o hello.s
正在上传…重新上传取消正在上传…重新上传取消
正在上传…重新上传取消
3.3 Hello的编译结果分析


正在上传…重新上传取消

3.3.1数据
(1)整型变量
正在上传…重新上传取消

由汇编程序的内容可知,argc作为main函数的第一个参数是一个int整型变量,在实行时先被存储在寄存器%edi中,然后又通过movl指令被存入-20(%rbp)。而根据.L2所示,函数内部的局部变量i被存储在-4(%rbp)(即用户栈)中,初始化为0,占用4B大小,它没有标识符,也不需要被声明。

(2)字符串
正在上传…重新上传取消

在hello.s中,字符串的信息都被放在了.rodata节中。如下图:LC0为默认输出,第一个字符串.LC0包罗汉字,每个汉字被编码为三个字节,LC1为argc=4时的输出
(3)指针数组

正在上传…重新上传取消
argv[]数组存放我们在下令行中输入的字符串。hello.s的argv的首地址被存放在寄存器%rsi中,厥后被存放在栈中,在L4中可以看到-32(%rbp)中的首地址被再次调用,如上图所示
3.3.2赋值与操作
(1)赋值
汇编程序的赋值通过mov语句实现,如下图对i赋值为零。
正在上传…重新上传取消
(2)算术盘算
算术盘算一样寻常通过add,sub,imul,inc语句实现,而移位操作可以通过SAL、SAL等语句实现(在hello中没有体现),如下图实行i++,
正在上传…重新上传取消
(3)关系操作
main函数中需要判定argc是否为4,而argc作为第一个参数应该存放在%edi中,且结合前面的分析可知它又被存入-20(%rbp)中,如下图,程序判定argc是否为4,若相等,就跳转到.L2。
正在上传…重新上传取消
(4)数组/指针/布局操作
argv[0]指向输入程序的路径和名称,argv[1]和argv[2]分别指向我们从shell终端输入的字符串。如下图所示,通过语句addq $16,%rax以及addq $8,%rax分别得到argv[1]和argv[2]。
正在上传…重新上传取消
3.3.4控制转移
在main函数中存在if(argc!=4)如许的语句,而在汇编程序中它通过控制转移实现,如下图
正在上传…重新上传取消
3.3.5函数调用
函数通过跳转到特定代码实行待定函数之后再返返来实现功能。函数一样寻常是在栈中实现的,函数调用可分为如下过程:
1,传递参数给被调用者
在64位栈布局中按照:%rdi,%rsi,%rdx,%rcx,%r8,%r9的次序传递参数,从第七个参数开始放在调用者栈布局中。
2,调用函数
call指令会将返回地址压入栈中,并且将rip的值指向所调用函数的地址,等函数实行完之后调用ret规复栈帧。
3,函数进行操作
函数在自己栈帧内进行操作,返回值存入rax中。
4,函数返回
函数返回时,假如有返回值,则先将返回值存在%rax中,再返回调用函数。
hello.c文件中调用的函数有:main()、printf()、exit()、sleep()、atoi()、getchar()。
(1)main函数
参数传递:第一个参数是argc(int型),第二个参数是argv[](char *型),分别存放在寄存器%rdi和%rsi中;
调用:main函数被体系函数__libc_start_main调用,call指令将main函数的地址分配给%rip,随后调用main函数。
函数操作中的栈维护:main函数利用栈指针,同时利用栈帧%rbp来记录利用环境。如图main函数在进入时先将rsp减去32形成一个栈空间布局,然后开始进行各种操作。
正在上传…重新上传取消
返回:可以看到main函数的尾部将0压入到eax中,然后调用了leav平衡栈帧,调用ret返回退出。
正在上传…重新上传取消
(2):printf函数
参数传递:call puts时只传入了字符串参数首地址;for循环中call printf时传入了 argv[1]和argc[2]的地址;
正在上传…重新上传取消
调用:第一个printf()由call puts@PLT调用,第二个printf()由call printf@PLT调用;
返回:从printf中返回。
(3):exit函数
参数传递:将1传给了%edi,完成参数传递。
正在上传…重新上传取消
调用:通过call exit@PLT函数,进行函数调用。
返回:从exit返回。
(4):sleep函数
正在上传…重新上传取消
参数传递:将atoi的返回值%eax通过%rdi传递给sleep函数
调用:调用了sleep函数,将控制传送。
返回:从sleep中返回。
(5):atoi函数
参数传递:将argv[3](字符串)通过%rdi传递给atoi函数。
调用:通过call atoi@PLT函数,进行函数调用。
返回:从atoi中返回。
正在上传…重新上传取消
(6):getchar函数
参数传递:无;
正在上传…重新上传取消
调用:call getchar@PLT调用getchar;
返回:从getchar中返回
3.4 本章小结

本章介绍了编译器怎样用汇编语言实现对各种数据范例和操作的处置惩罚,以及hello中各种函数在汇编程序中的实现。
(第32分)


第4章 汇编

4.1 汇编的概念与作用


概念:汇编是指as将汇编程序翻译成机器语言,把这些机器语言指令打包成可重定位目标程序的格式,并将结果保存在.o目标文件中。
作用:将汇编语言的程序翻译天生机器语言(二进制文件)。
4.2 在Ubuntu下汇编的下令

gcc -c hello.s -o hello.o

正在上传…重新上传取消
正在上传…重新上传取消
4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特殊是重定位项目分析。
ELF格式:如图所示:
正在上传…重新上传取消
用readelf等列出其各节的基本信息:如图所示:
正在上传…重新上传取消
正在上传…重新上传取消
4.3.1:ELF头
ELF头部以一个16字节的序列开始,形貌天生该文件的体系的字的大小和字节次序。剩下的部分包罗帮助链接器分析语法和解释目标文件的信息,其中包罗ELF头大小、目标文件的范例、及其范例、节头部表的文件偏移,以及节头部表中条目的大小和数量,如下图所示。
正在上传…重新上传取消

4.3.2:节头部表
节头部表形貌了不同节的位置和大小,其中目标文件中每个节都有一个固定大小的条目。具体的形貌包括节的名称、范例、地址和偏移量等,如下图所示:

正在上传…重新上传取消
4.3.3:重定位条目

当汇编器天生一个目标模块是,它并不知道数据和代码最终将放在内存中的什么位置,它也不知道这个模块引用的任何外部定义的函数或者全局变量的位置。以是,无论何时汇编器遇到对最终位置未知的目标引用,它就会天生一个重定位条目,告诉链接器在将目标文件合并成可实行目标文件时怎样修改这个引用。代码的重定位条目放在.rel.text中,已初始化数据的重定位条目放在.rel.data中。
正在上传…重新上传取消


4.3.4:符号表
.symtab是一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。

正在上传…重新上传取消


4.4 Hello.o的结果分析

如图:
正在上传…重新上传取消
和hello.s对比,可以发现以下几点:
(1)操作数:
hello.s中的操作数时十进制,hello.o反汇编代码中的操作数是十六进制。
(2)分支转移:
跳转语句之后,hello.s中是.L2和.L3等段名称,而反汇编代码中跳转指令之后是相对偏移的地址。
(3)函数调用:
hello.s中,call指令之后直接是函数名称,而反汇编代码中call指令之后是函数的相对偏移地址。
(4)指令:
汇编中mov、push、sub等指令都有表示操作数大小的后缀,反汇编得到的代码中则没有。
4.5 本章小结

本章介绍了汇编的概念和作用,ELF的格式,并且比较了反汇编程序和汇编程序的异同。
(第41分)


第5章 链接

5.1 链接的概念与作用

概念:链接是将各种代码和数据片段网络并组合成一个文件的过程,这个文件可被加载到内存实行。链接可以实行于编译时、加载时、运行时。
作用:把预编译好了的多少目标文件合并成为一个可实行目标文件。使得分离编译成为可能,不用将一个大型的应用程序构造为一个巨大的源文件,而是可以把它分解为可独立修改和编译的模块。
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的格式

利用指令readelf -a hello > hello1.elf天生hello的ELF格式文件,如下图所示
正在上传…重新上传取消
5.3.1.ELF头:
如下图所示,ELF头形貌文件的总体格式,还包括程序的入口点。
正在上传…重新上传取消
5.3.2.节头:
Section Headers对hello中所有的节信息进行了声明,其中包括大小Size以及在程序中的偏移量Offset,因此根据Section Headers中的信息我们就可以用HexEdit定位各个节所占的区间(起始位置,大小)。其中Address是程序被载入到虚拟地址的起始地址。如下图所示
正在上传…重新上传取消
5.3.3.程序头:
如下图所示,Program Headers一共有8个段
(1)PHDR包罗程序头表本身
(2)INTERP:只包罗了一个section,在这个节中,包罗了动态链接过程中所利用的解释器路径和名称。
(3)两个LOAD:第一个是代码段,第二个是数据段。在程序运行时需要映射到虚拟空间地址。
(4)DYNAMIC:保存了由动态链接器利用的信息。
(5)NOTE: 保存了辅助信息。
(6)GNU_STACK:堆栈段。
(7)GNU_RELRO:在重定位之后哪些内存区域需要设置只读。
正在上传…重新上传取消
5.3.4.段节:
正在上传…重新上传取消
5.3.5.重定位节:
正在上传…重新上传取消
5.4 hello的虚拟地址空间

利用edb加载hello,如图所示:
正在上传…重新上传取消
可根据上述的程序头来查看各段虚拟空间的映射。
5.5 链接的重定位过程分析

5.5.1hello与hello.o的反汇编代码比较:
运用指令objdump -d -r hello > hello-asm.txt得到hello的反汇编文件。
正在上传…重新上传取消
得到如下几方面的不同:

  • 链接后函数数量增加。多出了puts@plt,printf@plt,getchar@plt,exit@plt,sleep@plt等函数的代码。
  • 函数调用指令call的参数发生变革。
  • 跳转指令参数发生变革。
5.6 hello的实行流程


正在上传…重新上传取消

5.7 Hello的动态链接分析

在调用共享库函数时,编译器没有办法预测这个函数的运行时地址,因为定义它的共享模块在运行时可以加载到恣意位置。因而,我们为该引用天生一条重定位记录,然后动态链接器在程序加载的时候再分析它。
为避免运行时修改调用模块的代码段,链接器接纳耽误绑定的计谋。动态链接器利用过程链接表PLT+全局偏移量表GOT实现函数的动态链接,GOT中存放函数目标地址,PLT利用GOT中地址跳转到目标函数。
在节头部表中我们可以看到,.got.plt的起始地址为0x404000。
正在上传…重新上传取消
在edb中我们找到该位置:
正在上传…重新上传取消
可以发现在dl_init未实行时,0x404008往后的16个字节均为0,在dl_init实行后如下图:
正在上传…重新上传取消
这里多了两个地址,分别为0x7ff339ccc190和0x7ff339af5cc0,这里保存的即动态链接后,函数的最终地址。
5.8 本章小结

本章结合实行中的hello可实行程序依此介绍了链接的概念及作用以及下令,并对hello的elf格式进行了具体的分析对比。以及hello的虚拟地址空间知识,并通过反汇编hello文件,将其与hello.o反汇编文件对比,具体相识了重定位过程,遍历了整个hello的实行过程,整理了过程中的子函数,在最后对hello进行了动态链接分析,对链接有了更深的明白。

第6章 hello历程管理

6.1 历程的概念与作用

概念:历程就是一个实行中程序的实例.每次用户通过向shell 输入一个可实行目标文件的名字,运行程序时, shell 就会创建一个新的历程。
作用:历程为用户提供以下假象:我们的程序好像是体系中当前运行的唯一程序一样,我们的程序好像是独占的利用处置惩罚器和内存,处置惩罚器好像是无间断的实行我们程序中的指令,我们程序中的代码和数据好像是体系内存中唯一的对象。

6.2 简述壳Shell-bash的作用与处置惩罚流程

shell是一个应用程序,他在操作体系中提供了一个用户与体系内核进行交互的界面。他的处置惩罚过程一样寻常是如许的:从终端读入输入的下令,将输入字符串切分得到所有的参数,假如是内置下令则立刻实行,否则调用相应的程序为其分配子历程并运行,shell应该担当键盘输入信号,并对这些信号进行相应处置惩罚。
6.3 Hello的fork历程创建过程

Shell(父历程)通过fork 函数创建一个新的运行的子历程.新的子历程险些但不完全与父历程雷同.子历程得到与父历程用户级虚拟地址空间雷同的(但是独立的)一份副本,包括代码和数据段、堆、共享库以及用户栈.子历程历程还得到与父历程任何打开文件形貌符雷同的副本,这就意味着当父历程调用fork 时,子历程可以读写父历程中打开的任何文件。

6.4 Hello的execve过程

execve 函数加载并运行可实行目标文件filename, 且带参数列表argv 和环境变量列表envp .只有当出现错误时,比方找不到filename, execve 才会返回到调用程序.以是,与fork 一次调用返回两次不同, execve 调用一次并从不返回。在hello中,execve调用驻留在内存中的被称为启动加载器的操作体系代码来实行程序,加载器删除子历程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段.新的栈和堆段被初始化为零,通过将虚拟地址空间中的页映射到可实行文件的页大小的片,新的代码和数据段被初始化为可实行文件中的内容.最后加载器设置PC指向_start地址,_start最终调用main函数.除了一些头部信息,在加载过程中没有任何从磁盘到内存的数据复制.直到CPU引用一个被映射的虚拟页时才会进行复制,这时,操作体系利用它的页面调理机制自动将页面从磁盘传送到内存。
6.5 Hello的历程实行

在hello历程实行过程中利用上下文切换的方法,如图所示
正在上传…重新上传取消
hello在用户模式下运行,当hello调用sleep后进入内核模式,内核处置惩罚休眠主动开释当前历程,并将hello历程从运行队列中移出到场等候队列,定时器开始计时,内核进行上下文切换将当前历程的控制权交给其他历程,当定时器到时时发送一个中断信号,此时进入内核状态实行中断处置惩罚,将hello历程从等候队列中移出重新到场到运行队列,成为就绪状态,hello历程继续进行自己的控制逻辑流。
6.6 hello的非常与信号处置惩罚

非常可分为四类分别是中断,陷阱,故障和终止
(1)中断处置惩罚:中断是异步发生的,是来自处置惩罚器外部的 I/O 设备的信号的结果。硬件中断不是由任何一条专门的指令造成的,从这个意义上来说它是异步的。硬件中断的非常处置惩罚程序经常称为中断处置惩罚程序。
(2)陷阱处置惩罚:陷阱是故意的非常,是实行一条指令后的结果。就像中断处置惩罚程序一样,陷阱处置惩罚程序将控制返回到下一条指令。陷阱最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做体系调用。
(3)故障处置惩罚:故障由错误环境引起,它可能可以或许被故障处置惩罚程序修正。当故障发生时,处置惩罚器将控制转移给故障处置惩罚程序。假如故障处置惩罚程序可以或许修正这个错误,它就将控制返回给引起故障的指令,从而重新实行它,否则,处置惩罚程序返回到内核中的 abort 例程,abort 例程会终止引起故障的应用程序。
(4)终止处置惩罚:终止是不可规复的致命错误造成的结果,通常是一些硬件错误,比如 DRAM 或者 SRAM 位被损坏时发生的奇偶错误。终止处置惩罚程序不会将控制返回给应用程序。

hello历程面对的各种环境:
(1)正常实行。如下图所示,为hello程序正常运行的结果,接着输入下令ps后实行,程序背景并没有hello历程正在实行了,说明历程正常结束,已经被回收了。
./hello 2021112571 邹轩崎 1
正在上传…重新上传取消
(2)不绝乱按:结果是程序运行环境和前面的雷同,不同之处在于shell将我们刚刚乱输入的字符除了第一个回车按下之前的字符当做getchar的输入之外,其余都当做新的shell下令,在hello历程结束被回收之后,将会在下令行中尝试解释这些下令。中心没有任何对于历程产生影响的信号被产生。
正在上传…重新上传取消
(3)运行CTRL+Z:运行中按CTRL+Z之后,将会发送一个SIGTSTP信号给shell。然后shell将转发给当前实行的前台历程组,使hello历程挂起。此时,我们输入ps下令,查看当前存在的历程,如图所示
正在上传…重新上传取消
输入jobs下令:
正在上传…重新上传取消
输入pstree下令:以树状图显示历程间的关系
正在上传…重新上传取消
利用fg指令完成剩下的实行。
正在上传…重新上传取消
利用kill下令:运行hello程序,将其挂起一次,利用kill函数杀死它。
正在上传…重新上传取消
CTRL+C下令:在hello程序运行时输入CTRL+C会导致内核发送一个SIGINT信号到前台历程组的每个历程。默认环境下,结果是终止前台作业。
正在上传…重新上传取消
6.7本章小结

本章介绍了历程的概念和作用,shell的基本原理,shell怎样调用fork和execve历程hello,hello历程在实行时会遇到的各种环境。
(第61分)


第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:程序代码颠末编译后出现在 汇编程序中地址.逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址.
线性地址&虚拟地址:逻辑地址颠末段机制后转化为线性地址(虚拟地址),是逻辑地址到物理地址变换之间的中心层.在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址.是一个32位无符号整数,可以用来表示高达4GB的地址,也就是,高达4294967296个内存单元.线性地址通常用十六进制数字表示,值得范围从0x00000000到0xfffffff)程序代码会产生逻辑地址,通过逻辑地址变换就可以天生一个线性地址.假如启用了分页机制,那么线性地址可以再颠末变换以产生一个物理地址.假如没有启用分页机制,那么线性地址直接就是物理地址.
物理地址:CPU地址总线传来的地址,由硬件电路控制(现在这些硬件是可编程的了)其具体含义.物理地址中很大一部分是留给内存条中的内存的,但也常被映射到其他存储器上(如显存、BIOS等).在没有利用虚拟存储器的机器上,虚拟地址被直接送到内存总线上,使具有雷同地址的物理存储器被读写;而在利用了虚拟存储器的环境下,虚拟地址不是被直接送到内存地址总线上,而是送到存储器管理单元MMU,把虚拟地址映射为物理地址.
在hello程序中,他就表示了这个程序运行时的一条确切的指令在内存地址上的具体哪一块进行实行。
7.2 Intel逻辑地址到线性地址的变换-段式管理

一个逻辑地址由两部份构成,段标识符、段内偏移量。段标识符是由一个16位长的字段构成,称为段选择符。其中前13位是一个索引号。背面3位包罗一些硬件细节。
索引号,是“段形貌符(segment descriptor)”,段形貌符具体地址形貌了一个段。如许,很多个段形貌符,就组了一个数组,叫“段形貌符表”,如许,可以通过段标识符的前13位,直接在段形貌符表中找到一个具体的段形貌符,这个形貌符就形貌了一个段,由8个字节构成。
Base字段:它形貌了一个段的开始位置的线性地址。 Intel筹划的本意是,一些全局的段形貌符,就放在“全局段形貌符表(GDT)”中,一些局部的,比方每个历程自己的,就放在所谓的“局部段形貌符表(LDT)”中。
当段选择符中的T1字段=0,表示用GDT;若为1,表示用LDT。 GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中
起首,给定一个完整的逻辑地址[段选择符:段内偏移地址],
(1)看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。
(2)拿出段选择符中前13位,可以在这个数组中,查找到对应的段形貌符,如许,它了Base,即基地址就知道了。
(3)把Base + offset,就是要转换的线性地址了。 还是挺简单的,对于软件来讲,原则上就需要把硬件转换所需的信息准备好,就可以让硬件来完成这个转换了。
7.3 Hello的线性地址到物理地址的变换-页式管理

起首Linux体系有自己的虚拟内存体系,其虚拟内存构造情势如下图,Linux将虚拟内存构造成一些段的聚集,段之外的虚拟内存不存在因此不需要记录。内核为hello历程维护一个段的任务布局即图中的task_struct,其中条目mm指向一个mm_struct,它形貌了虚拟内存的当前状态,pgd指向第一级页表的基地址(结合一个历程一串页表),mmap指向一个vm_area_struct的链表,一个链表条目对应一个段,以是链表相连指出了hello历程虚拟内存中的所有段。
7.4 TLB与四级页表支持下的VA到PA的变换

TLB:
TLB支持下的VA到PA的变换可以或许利用局部性原理,加速地址翻译速度。
TLB即翻译后备缓冲器,是一个具有如下性子的缓存:
1. 分页内存管理单元中一个小的具有高相联度的聚集。
2. 实现虚拟页号到物理页号的映射。
3. 页数很小的页表可以完全存放在TLB中。
假如缓存在TLB中的话,那么TLB可以直接将VPN映射到PTE,以上步调都是在CPU内部的MMU单元完成的,因而速度较快。
四级页表:
四级页表支持下的VA到PA的变换可以或许利用多级页表,降低内存占用。
假如我们的页面大小4KB,48位地址空间,8字节的PTE,那么我们的页表占用的空间至少应该是512GB,这显然不实际。我们发现实在有很多页我们并没有用上,于是可以不放入PTE内里,而接纳多级页表的处置惩罚思路,如下图:
正在上传…重新上传取消
由CR3寄存器指向L1的PT,然后由VPN1作为索引,进行探求。找到PTE以后,以PTE条目内里的Base作为基址,再以VPN2作为索引,重复上述操作,直到找到L4的PT内里的PTE。以这个作为PPN,并上PPO,作为虚拟地址。

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

缓存的出现是为了缓解存储设备和CPU之间巨大的速度差异。处置惩罚器对内存数据的访问,一样寻常是通过cache进行。具体过程为:
通过地址分析出缓存的索引和偏移,对缓存进行访问,匹配标记查找是否含有相关的字,假如掷中,则将数据发送给CPU,假如没有掷中,则访问下一级缓存,取出这个字,存入高一级缓存,返回数据给CPU。
在物理内存访问的过程中,这个过程被实行为:
对于给定的物理地址PA,将PA分割成CT CI CO。其中CI是组号,定位了应该出现的组,CO是offset定位了偏移量,CT是tag即标识的tag。
起首对本层的Cache进行探求(从L1开始)。硬件会定位到CI的组索引,然后对组内里的每一行进行比较tag。假如tag雷同且有用,那么找到了。
假如找到,就直接返回数据。假如找不到,就会到L2内里进行探求,重复上述操作直到找到为止。
7.6 hello历程fork时的内存映射

当fork 函数被shell调用时,内核为hello历程创建各种数据布局,并分配给它一个唯一的PID。为了给hello历程创建虚拟内存,它创建了hello历程的mm_struct 、区域布局和页表的原样副本。它将两个历程中的每个页面都标记为只读,并将两个历程中的每个区域布局都标记为私有的写时复制。

当fork在hello历程中返回时,hello历程现在的虚拟内存刚好和调用fork 时存在的虚拟内存雷同。当这两个历程中的任一个厥后进行写操作时,写时复制机制就会创建新页面,也就为每个历程保持了私有地址空间的抽象概念。
正在上传…重新上传取消
7.7 hello历程execve时的内存映射

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

缺页:引用虚拟内存中的字,不在物理内存中,DRAM 缓存不掷中。
缺页时的实行流程如下:
起首,处置惩罚器天生一个虚拟地址,并将其传送给MMU。MMU天生PTE地址(PTEA),并从高速缓存/主存请求得到PTE。高速缓存/主存向MMU返回PTE,PTE的有用位为零, 因此 MMU 触发缺页非常。
缺页处置惩罚程序确定物理内存中的牺牲页 (若页面被修改,则换出到磁盘),缺页处置惩罚程序调入新的页面,并更新内存中的PTE。缺页处置惩罚程序返回到原来历程,再次实行导致缺页的指令。
7.9动态存储分配管理

动态内存管理的基本方法与计谋:
动态内存分配器维护着一个历程的虚拟内存区域,称为堆。假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长。对于每个历程,内核维护着一个变量brk,它指向堆的顶部。
分配器将堆视为一组大小不同的块的聚集来维护。每个块就是一个连续的虚拟内存片,要么是以分配的,要么是空闲的。已分配的块显式的保存为供应用程序利用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被开释,这种开释要么是应用程序显式实行的,要么是内存分配器自身隐式实行的。
分配器有两种基本风格,两种风格都要求应用显式的分配快。它们的不同之处在于由哪个实体来负责开释已分配的快。
显式分配器:要求应用显式地开释任何已分配的块。比方C标准库提供一种叫malloc程序包的显式分配器。C程序通过调用malloc函数来分配一个块,并通过调用free函数来开释一个块。
隐式分配器:另一方面,要求分配器检测一个已分配块何时不再被程序所利用,那么就开释这个块。隐式分配器也叫垃圾网络器,而自动开释未利用的已分配块的过程叫做垃圾网络。
7.10本章小结

本章介绍了hello程序的存储管理,包括各类地址空间的转换,段式管理和页式管理等,介绍了VA到PA的转换,物理内存访问。以及调用fork函数和execve函数时的内存映射,发生缺页故障后的处置惩罚方法和动态储存的分配管理方法。
(第7 2分)


第8章 hello的IO管理

8.1 Linux的IO设备管理方法

文件是Linux管理的基本思想,所有的IO设备都被抽象为文件,所有的输入输出操作都作为对文件的操作。这种将设备映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O。这使得输入和输出都能以一种同一且同等的方式的来实行。
8.2 简述Unix IO接口及其函数

Unix I/O接口:
1.打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访间一个I/O 设备。内核返回一个小的非负整数,叫做形貌符,它在后续对此文件的所有操作中标识这个文件。内核记录有关这个打开文件的所有信息。应用程序只需记住这个形貌符。
2.Linux shell 创建的每个历程开始时都有三个打开的文件:标准输入(形貌符为0) 、标准输出(形貌符为1) 和标准错误(形貌符为2) 。头文件< unistd.h> 定义了常量STDIN_FILENO 、STOOUT_FILENO 和STDERR_FILENO, 它们可用来代替显式的形貌符值。
3.改变当前的文件位置。对于每个打开的文件,内核保持着一个文件位置k, 初始为0。这个文件位置是从文件开头起始的字节偏移量。应用程序可以或许通过实行seek 操作,显式地设置文件的当前位置为K 。
4.读写文件。一个读操作就是从文件复制n>0 个字节到内存,从当前文件位置k 开始,然后将k增加到k+n 。给定一个大小为m字节的文件,当k~m 时实行读操作会触发一个称为end-of-file(EOF) 的条件,应用程序能检测到这个条件。在文件结尾处并没有明确的“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 write(int fd,const void *buf,size_t n),write函数从内存位置buf复制至多n个字节到形貌符为fd的当前文件位置。
8.3 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;

}

在形参列表中的...表示传递参数个数不确定,va_list的定义为 typedef char *va_list是一个字符型指针,(char*)(&fmt) + 4) 表示的是...中的第一个参数。
此后,程序调用vsprintf函数,vsprintf的作用就是格式化。它担当确定输特别式的格式字符串fmt。用格式字符串对个数变革的参数进行格式化,产生格式化输出,并返回字符串长度。
之后,程序调用write函数,将缓冲区中的前i个字符输出到屏幕上,我们对write进行反汇编:

     mov eax, _NR_write

     mov ebx, [esp + 4]

     mov ecx, [esp + 8]

     int INT_VECTOR_SYS_CALL

Write将参数传如寄存器,然后进入一个陷阱-体系调用。sys_call将串内里的字节,从寄存器内里通过总线,复制到显卡显存内里,存放ASCII码。字符显示驱动子程序将通过ASCII码在字模库中找到点阵信息将点阵信息存储到vram中。显示芯片会按照一定的革新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。最后我们的打印字符串就显示在了屏幕上。
8.4 getchar的实现分析

起首看getchar()函数的函数体:
int getchar(void)

{

    static char buf[BUFSIZ];

    static char* bb=buf;

    static int n=0;

    if(n==0)

    {

        n=read(0,buf,BUFSIZ);

        bb=buf;

    }

    return(–n>=0)?(unsigned char)*bb++:EOF;

}

当用户键入回车之后,getchar从stdio流中每次读入一个字符。getchar函数的返回值是用户输入的第一个字符的ascii码,如出错返回-1,且将用户输入的字符显示到屏幕。假如用户在按回车之前输入了不止一个字符,其他字符会保存在键盘缓存区中,等候后续getchar调用读取。也就是说,后续的getchar调用不会等候用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等候用户按键。
异步非常-键盘中断的处置惩罚:键盘中断处置惩罚子程序。担当按键扫描码转成ascii码,保存到体系的键盘缓冲区。
getchar等调用read体系函数,通过体系调用读取按键ascii码,直到担当到回车键才返回。
8.5本章小结

本章介绍了Linux的I/O设备的基本概念和管理方法,以及Unix I/O接口及其相关函数,并且分析了printf函数和getchar函数的工作过程。
(第81分)

结论

- hello程序是一个用C语言编写的简单程序,它的源代码是一个文本文件,包罗了一条#include <stdio.h>指令和一个main函数,main函数中调用了printf函数来输出"Hello World\\n"字符串。

-为了在盘算机体系上运行hello程序,它必须颠末预处置惩罚、编译、汇编和链接的四个阶段,将源代码转换为可实行目标文件,即一系列的机器语言指令和相关数据。

-预处置惩罚阶段:预处置惩罚器cpp根据以#开头的指令,修改原始的C程序,将stdio.h文件中的内容插入到源代码中,天生一个新的C程序文件hello.i。

- 编译阶段:编译器ccl将hello.i文件翻译成汇编语言文件hello.s,它包罗了一条条用文本格式形貌的低级机器语言指令。

- 汇编阶段:汇编器as将hello.s文件翻译成机器语言指令,并打包成一种叫做可重定位目标程序的格式,保存在二进制文件hello.o中。

- 链接阶段:链接器ld将hello.o文件和printf.o文件(包罗了printf函数的实现)合并在一起,解决了它们之间的引用关系,天生了一个可实行目标文件hello。

- 运行阶段:当用户在终端输入./hello下令时,外壳程序shell通过体系调用将控制权交给操作体系,操作体系创建一个新的历程来实行hello程序,并将其代码和数据从磁盘复制到主存中¹²。然后处置惩罚器开始实行hello程序的main函数中的机器语言指令,这些指令将"Hello World\\n"字符串从主存复制到寄存器文件,再从寄存器文件复制到显示设备(通过I/O总线和控制器),最终显示在屏幕上¹²。当程序实行完毕后,操作体系回收历程资源,并将控制权返回给shell²。


- 我对盘算机体系的筹划与实现有以下几点深切感悟:

    - 盘算机体系是由硬件和软件构成的复杂而有趣的体系,它们共同协作来运行各种应用程序。作为程序员,我们需要相识盘算机体系的基本原理和构成部分,以及它们怎样影响程序的正确性和性能。

    - 盘算机体系中所有的信息都是由位+上下文构成的。不同范例的数据(如整数、浮点数、字符串、指令等)都是由一串位表示的,只有根据上下文才能正确地解释它们。因此,我们需要相识不同数据范例的机器表示方式,以及它们与真实值之间可能存在的差异和限定。

    - 盘算机体系中有许多抽象层次,每个层次都提供了一些基本功能和服务,并隐藏了底层实现细节。这些抽象层次使得我们可以更容易地明白和筹划复杂的体系,并提高了体系的可移植性和效率。比方,在编译过程中,C语言是一种高级语言抽象,它提供了比汇编语言更方便和强盛的编程工具;在运行过程中,历程是一种虚拟处置惩罚器抽象,它提供了每个程序独占处置惩罚器和主存的假象。


- 我对盘算机体系有以下几点创新理念:

    - 利用人工智能技能来优化编译过程,使得编译器可以或许根据不同场景和需求自动选择最合适的优化计谋和参数,提高程序的运行效率和质量。

    - 利用区块链技能来包管盘算机体系的安全性和可靠性,使得体系中的数据和操作都可以或许被加密、验证和记录,防止窜改、伪造和攻击。

    - 利用云盘算技能来扩展盘算机体系的功能和服务,使得用户可以随时随地地访问和利用各种盘算资源和应用程序,无需担心硬件配置、软件更新和数据备份等题目。


附件

hello: 链接之后的可实行目标文件

hello.c: 源文件

hello.elf: hello的ELF格式

hello.i: 预处置惩罚产生的文本文件

hello.o: 汇编产生的可重定位目标文件

hello.objdmp: hello的反汇编代码

hello.s: 编译产生的汇编文件

hello.txt: hello.o的ELF格式
(附件0分,缺失 -1分)


参考文献

为完资本次大作业你翻阅的书籍与网站等

[1]  BryantO’Hallaron, D.R.R.E.,. (2019). 深入明白盘算机体系(第三版). 北京: 机器工业出版社.

[2]  https://blog.csdn.net/alanwalker1/article/details/103848576

[3]  百度安全验证

[4]  exit(0)在c语言中是什么意思-C#.Net教程-PHP中文网



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

伤心客

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表