计算机系统

大作业

题     目 程序人生-Hello’s P2P

专       业 计算机

学     号 120L021909

班     级 2003006

学       生 邢俊文  

指 导 教 师 吴锐  

计算机科学与技术学院

2021年5月

摘  要

本论文通过详细分析一个hello程序在Linux系统下的从代码到执行结束的整个过程,从预处理、编译、汇编、链接、进程管理、存储管理和IO管理这几个方面,结合CSAPP课本相关章节,分析hello过程出现的各种操作以及现象,从而更深入地理解计算机系统。

关键词:Hello;预处理;编译;汇编;链接;进程管理;存储管理;IO管理

(摘要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的整个过程。

P2P:

编写代码得到hello.c程序,在linux下,调用C预处理器ccl得到hello.i,然后调用汇编器as得到可重定位目标文件hello.o,然后通过链接器ld得到可执行目标文件hello。在shell中键入./hello启动程序,系统为hello fork子进程,hello就变成了进程。

O2O:

OS的进程管理调用fork函数产生子进程,调用execve函数,并进行虚拟内存映射,并为运行的hello分配时间片以执行取指译码流水线等操作;OS的存储管理以及MMU解决VA到PA的转换,cache、TLB、页表等加速访问过程,IO管理与信号处理综合软硬件对信号等进行处理;程序结束时,shell回收hello进程,内核将其所有痕迹从系统中清除,操作系统恢复shell进程的上下文,并将控制权传回shell,shell进程等待下一个输入。

1.2 环境与工具

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

硬件工具:

X64 CPU;2GHz;2G RAM;256GHD Disk 以上

软件工具:

Windows10 64 位;Vmware14.1.3;Ubuntu 18.04.1 LTS 64 位

开发与调试工具:

gcc,edb,readelf,gedit,objdump

1.3 中间结果

列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。

hello.i

预处理得到的文件

hello.s

编译得到的文件

hello.o

汇编得到的文件

hello.asm

hello.o反汇编得到的文本文件

hello.elf

hello.o的elf文件

hello

链接得到的可执行目标文件

hello.asm2

hello反汇编得到文本文件

5.3helloelf.txt

可执行hello的elf格式,作用是重定位过程分析

1.4 本章小结

本章介绍了hello.c的P2P和O2O,然后介绍了本次实验的环境和工具,最后展示的所有的中间文件。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理的概念:

所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能, 它由预处理程序负责完成。当对一个源文件进行编译时, 系统自动引用预处理程序对源程序中的预处理部分作处理, 处理完毕自动进入对源程序的编译。

预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。比如hello.c中第一行的#include <stdio.h>命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中。结果就得到了另一个C程序,通常是以.i作为文件扩展名。

预处理的作用:

预处理指令包括条件编译、源文件包含、宏替换、行控制、抛错、杂注和空指令。

  1. 条件编译

条件编译的功能是根据条件有选择的保留或者放弃源文件中的内容。常见的条件包含#if、#ifdef、#ifndef指令开始,以#endif结束。用#undef定义的标识符取消定义。

  1. 源文件包含

源文件包含指令的功能是搜索指定的文件,并将它的内容包含进来,放在当前所在的位置,源文件包含有两种,包含系统文件和用户自定义文件。

#include <stdio.h>

让C编译器去系统目录下查找相关文件

#include”test.c”

  1. 宏替换

宏的作用是把一个标识符指定为其他一些成为替换列表的预处理记号,当这些标识符出现在后面的文本中时,将用对应的预处理记号把它替换掉,宏的本质是替换。

宏的定义分为两种方式:有参和无参。

无参数的宏(对象式宏定义)

#difine MAX 100

有参数的宏(函数式定义)

#define MAX(a,b) ((a)>(b)?(a):(b))

  1. 行控制

行控制指令以”#”和”line”引导,后面是行号和可选的字面串。它用于改变预定义宏”_LINE_”的值,如果后面的字面串存在,则改变”_FILE_”的值。

  1. 抛错

抛错指令是以”#”和”error”引导,抛错指令用于在预处理期间发出一个诊断信息,在停止转换。抛错是人为的动作。

  1. 杂注

杂注指令用于向C实现传递额外的信息(编译选项),对程序的某些方面进行控制。

杂注指令以”#”开始,跟着”pragma”,后面是其他预处理记号,即所谓的选项。下面这个杂注用于指示C实现将结构成员对齐到字节边界。

# pragma pack(1)

  1. 空指令

空指令只有一个”#”,自称一行,空指令的使用没有效果。

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

hello.i

2.3 Hello的预处理结果解析

查看hello.i文件,可以看到hello.i的篇幅较hello.c有了大幅度增加,有3000+行。这是因为在预处理的过程中,实现了条件编译、源文件包含、宏替换等工作。

而前面的3000行就是.c文件中包含的头文件,这里体现的就是预处理器根据以字符#开头的命令,修改原始的C程序。最后则是程序主体。

2.4 本章小结

本章主要介绍了预处理的概念和作用、Ubuntu下预处理的指令和Hello的预处理解析,分析了预处理的具体过程。预处理主要由预处理器完成,进行文件包含、条件编译、宏替换等工作。预处理也是之后所有操作的基础。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译的概念:

编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序.该程序包含函数main的定义,这个过程称为编译.

编译就是把高级语言变成计算机可以识别的2进制语言,计算机只认识1和0。

编译程序把一个源程序翻译成目标程序的工作过程分为四个阶段:词法分析;语法分析;语义检查和中间代码生成;代码优化。

编译的作用:

一、词法分析

词法分析是对由字符组成的单词进行处理,从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。执行词法分析的程序称为词法分析程序或扫描器。

二、语法分析

编译程序的语法分析器以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,如表达式、赋值、循环等,最后看是否构成一个符合要求的程序,按该语言使用的语法规则分析检查每条语句是否有正确的逻辑结构,程序是最终的一个语法单位。编译程序的语法规则可用上下文无关文法来刻画。

三、中间代码

中间代码是源程序的一种内部表示,或称中间语言。中间代码的作用是可使编译程序的结构在逻辑上更为简单明确,特别是可使目标代码的优化比较容易实现中间代码,即为中间语言程序,中间语言的复杂性介于源程序语言和机器语言之间。中间语言有多种形式,常见的有逆波兰记号、四元式、三元式和树。

四、代码优化

代码优化是指对程序进行多种等价变换,使得从变换后的程序出发,能生成更有效的目标代码。所谓等价,是指不改变程序的运行结果。所谓有效,主要指目标代码运行时间较短,以及占用的存储空间较小。这种变换称为优化。

有两类优化:一类是对语法分析后的中间代码进行优化,它不依赖于具体的计算机;另一类是在生成目标代码时进行的,它在很大程度上依赖于具体的计算机。对于前一类优化,根据它所涉及的程序范围可分为局部优化、循环优化和全局优化三个不同的级别。

3.2 在Ubuntu下编译的命令

编译的命令:gcc -S hello.i -o hello.s

gcc -S hello.i -o hello.s

3.3 Hello的编译结果解析

此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析

3.3.1hello.s文件内容

.file

源文件

.text

代码段

.globl

全局变量

.data

数据段

.align

存放地址的对齐方式

.type

声明是函数类型还是对象类型

.size

大小

.long

long类型

.section.rodata

rodata节

.string

string类型

3.3.2数据

hello.c中数据包括:整型、数组、字符

3.3.2.1整型

int argc

main函数的第一个参数,指明argv[]数组中的数量。

保存在%edi(第一个参数)中并传递给%rbp-20。

接下来与4比较,说明参数的个数是4个

如下图3.2.2.1.1。

图3.2.2.1.1

int i

局部变量保存在栈中,i保存在了-4(%rbp)中,如图3.2.2.1.2

图3.2.2.1.2

3.3.2.2数组

char *argv[]

main函数的第二个参数,是指针数组,保存着终端输入的命令行。

argv[0]

可执行目标文件的名字

argv[1]

命令行参数“学号”

argv[2]

命令行参数“姓名”

argv[3]

命令行参数“秒数”

保存在%rsi中,并被传递给%rbp-32。

如图3.2.2.2.1。

图3.2.2.2.1

3.3.2.3字符

“Usage: Hello 学号 姓名!\n”

“Hello %s %s\n”

在.rodata节中,声明两个string类型的字符串。如下图3.2.2.3.1。

图3.2.2.3.1

3.3.4赋值

i=0

通过立即数赋值,把0赋给%rbp-4,如图3.2.3.1

图3.2.3.2

i++

通过add指令,把%rbp-4加一,如图3.2.3.2

图3.2.3.2

3.3.5算术操作

i++

如3.3.3中的i++部分

3.3.6关系操作

i<0

计算-4(%rbp)-7,设置条件码,随后jle根据这些条件码,进行跳转。如图3.3.6.1

图3.3.6.1

argv!=3

计算-20(%rbp)-3,设置条件码,随后用je根据这些条件码,进行跳转。如图3.3.6.2

图3.3.6.2

3.3.7数组/指针/结构操作

如3.3.2.2

3.3.8控制转移

计算-20(%rbp)-4,设置条件码,随后用je根据这些条件码,跳转到.L2段,如图3.3.8.1。

计算-4(%rbp)-7,设置条件码,随后jle根据这些条件码,跳转到.L4段,如图3.3.8.2

图3.3.8.2

3.3.9函数操作

3.3.9.1main函数

第一个参数是argv,指明argv[]数组中非空指针的数量。由%edi保存并传递给%rbp-20。

第二个参数是argc[],argv[]是一个指针数组,每个指针都指向一个参数字符串。argv[0]是可执行目标文件的名字,argv[1]、argv[2] 、argv[3]指向命令行参数“学号”、“姓名” 、“秒数”。

返回值在%eax中,设置为0。

3.3.9.2printf函数

用call调用

printf函数被优化成为puts函数,首先将%rdi赋值给字符串“Usage:Hello 学号 姓名 秒数!\n”的首地址,然后调用puts函数,如图3.3.9.2.1。

图3.3.9.2.1

设置%rsi为argv[1],%rdx为argv[2],然后调用printf函数,如图3.3.9.2.2。

图3.3.9.2.2

3.3.9.3sleep函数

用call调用

参数是%edi,如图3.3.9.3.1

图3.3.9.3.1

3.3.9.4getchar函数

用call调用

如图3.3.9.4.1

图3.3.9.4.1

3.3.9.5exit函数

用call调用

参数是%edi,如图3.3.9.3.1

图3.3.9.3.1

3.4 本章小结

本章介绍了编译的概念和作用,Ubuntu下编译的指令,最后对hello.s文件中的数据(整型、字符、数组)和操作(赋值、类型转换、算术、数组、函数)进行了分析。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

汇编的概念:

汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中,这个过程就叫做汇编。hello.o文件是一个二进制文件,它包含的17个字节是函数main的指令编码。如果我们在文本编辑器中打开hello.o文件,将看到一堆乱码。

汇编的作用:

将高级语言转化为机器可直接识别执行的代码文件。

4.2 在Ubuntu下汇编的命令

汇编命令:gcc -c hello.s -o hello.o

gcc -c hello.s -o hello.o

4.3 可重定位目标elf格式

ELF格式的可执行目标文件的各类信息:

ELF头:字大小、字节序、文件类型、机器类型、节头表位置、条目大小、数量等。

程序头表:页面大小、虚拟地址内存段、段大小

.text:已编译程序的代码

.rodata:只读数据:printf的格式串、开关语句跳转表……

.data:已初始化全局和静态变量

.bss:未初始化静态变量、初始化为0的全局和静态变量

.symtab:函数和全局/静态变量名,节名称和位置

.rel.txt:可重定位代码

.rel.data:可重定位数据

.debug:调试符号表:符号调试的信息

节头表:每个节的在文件中的偏移量、大小等

4.3.1读取hello.o

readelf -a hello.o > hello.elf,用文本文件输出信息。

readelf -a hello.o > hello.elf

4.3.2ELF头

ELF头以一个16字节的序列开始,这个序列描述了系统的字的大小和字节顺序。剩下的部分包括帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小,程序头的大小、目标文件的类型、机器类型,节头部表的文件偏移,节头部表中条目的大小和数量,如下图。

ELF头

4.3.3节头部表

包括节名称、类型、地址、偏移量、大小等。

节头部表

4.3.4重定位节

包括偏移量、信息、类型、符号值、符名称和加数。

.rela.text中包含8条重定位信息,分别对应第一个字符串、puts函数、exit函数、第二个字符串、printf函数、sleepsecs、sleep函数、getchar函数。

针对重定位分析,以第一个字符串为例:

r.offset = 0x1c,r.sympol=.rodata,r.type=R_X86_X64_PC32,r.addend=-4(如下图所示)

首先,连接器计算处引用的运行时的地址

refaddr = ADDR(s) +r.offset = ADDR(s) + 0x18。

然后,更新该引用,使得它在运行时指向真正的内容

*refptr = (unsigned)(ADDR(r.sympol) + r.addend - refaddr) = (unsigned) (ADDR(r.sympol) + (-4) - refaddr)

最后,在得到的可执行目标文件中,我们便可以得到正确的引用地址,即完成对第一个重定位条目的重定位计算。

重定位节

4.3.5符号表

Name:字符中表中的字节偏移,指向符号的以null结尾的字符串名字。

Value:距定义目标的节的起始位置的偏移。对可执行目标文件来说,该值是一个据对运行时的地址。

Size:目标的大小。

Type:数据或者函数。

Bind:本地或者全局字段。

符号表

4.4 Hello.o的结果解析

objdump -d -r hello.o  分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。

说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

objdump –d -r hello.o > hello.asm:

hello.asm

4.4.1对照分析

4.4.1.1分支转移

hello.s:

使用段名称进行跳转,如图4.4.1.1.1

图4.4.1.1.1

hello.o:

使用地址进行跳转,如图4.4.1.1.2

图4.4.1.1.2

4.4.1.2函数调用

hello.s:

call+函数名称,如图4.4.1.2.1

图4.4.1.2.1

hello.o:

call+下一条指令,因为此时地址还不确定,所以call指令将相对地址设置为0,然后在.rela.text节为其添加重定位条目,等待下一步链接,如图4.4.1.2.2。

图4.4.1.2.2

4.4.1.3全局变量

hello.s:

段地址+%rip,如图4.4.1.3.1

图4.4.1.3.1

hello.o:

0+%rip,也需要重定位,在.rela.text节为其添加重定位条目,如图4.4.1.3.2

图4.4.1.3.2

4.4.2机器语言构成

4.4.2.1机器语言构成

机器语言是二进制机器指令的集合。基本格式:操作码字段和地址码字段,其中操作码指明指令的功能,地址码指明操作数或者操作数的地址。

4.4.2.2与汇编语言的映射关系

一一对应,如图4.4.2.2.1

图4.4.2.2.1

4.4.2.3操作数

对于立即数,反汇编是十六进制,hello.s是十进制。

对于寄存器,二者相同。

对于内存引用,反汇编基址加偏移量寻址,hello.s是伪指令。

4.5 本章小结

本章对汇编的概念和作用、Ubuntu下汇编的命令进行了介绍。还对可重定位目标elf格式进行了重点介绍,还对hello.o的反汇编文件和hello.s进行了对比。

(第41分)

第5章 链接

5.1 链接的概念与作用

链接的概念:

链接是将各种代码和数据片段收集并组合为单一文件的过程,这个文件可以被加载(复制)到内存并执行。

链接的作用:

链接可以执行于编译时,也就是源代码被翻译成机器代码时;也可以执行于加载时,即程序被加载器加载到内存并执行时;甚至执行于运行时,也就是由应用程序来执行。

链接使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立的修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其他文件。

5.2 在Ubuntu下链接的命令

以下格式自行编排,编辑时删除

使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件

输入命令: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

链接生成hello可执行目标文件

5.3 可执行目标文件hello的格式

分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。

输入命令readelf -a hello

5.3.1ELF头

 ELF头

5.3.2节头部表,声明各节的大小、地址、偏移量等。

节头部表

5.3.3程序头

程序头表

5.3.4段节

段节

5.3.5Dynamic section

 Dynamic section

5.3.6重定位节

重定位节

5.3.7符号表

符号表

5.3.8others

others

5.4 hello的虚拟地址空间

使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。

举例.interp

在节头部表信息如下:

在edb中:

该节的位置,大小都和节头部表一致,其他类推.

5.5 链接的重定位过程分析

输入命令:objdump -d -r hello > hello.asm2

5.5.1分析hello与hello.o的不同

5.5.1.1hello.asm2比hello.asm多了很多文件节,如图5.5.1.1.1

5.5.1.1.1

5.5.1.2hello.asm是相对偏移地址,hello.asm2是虚拟地址,如图5.5.1.2.1和图5.5.1.2.2

图5.5.1.2.1

图5.5.1.2.2

5.5.1.3hello.asm2增加了很多共享库函数,如图5.5.1.3.1

图5.5.1.3.1

5.5.2链接的重定位过程说明

重定位分为两步:

1.重定位节和符号定义。分为两步:

链接器将所有相同类型的节合并为同一类型的新的聚合节。

链接器将运行时内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋

给输入模块定义的每个符号。

上面两步完成后,程序中的每条指令和全局变量都有唯一的运行时内存地址了。

2.重定位节中的符号引用。

链接器修改代码节和数据节中对每个符号的引用,是他们指向正确的运行时地址。

链接器依赖于可重定位目标模块中的重定位条目。

5.6 hello的执行流程

使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。

5.7 Hello的动态链接分析

分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。

5.8 本章小结

该章介绍了链接的概念和作用、在Ubuntu下链接的命令行,对helllo的elf格式进行了分析,对hello和hello.o的反汇编文件进行了对比,对hello的执行流程和动态链接进行了分析。

(第51分)

第6章 hello进程管理

6.1 进程的概念与作用

进程的概念:

进程就是一个执行中程序的实例.每次用户通过向shell 输入一个可执行目标文件的名字,运行程序时, shell 就会创建一个新的进程.

进程的作用:

在现代系统上运行一个程序时,我们会得到一个假象,我们的程序好像是系统中当前运行的唯一程序 一样,我们的程序好像是独占的使用处理器和内存,处理器好像是无间断的一条接一条地执行我们程序中的指令,我们程序中的代码和数据好像是系统内存中唯一的对象。

每次用户通过向shell 输入一个可执行目标文件的名字,运行程序时, shell 就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进程,并且在这个新进程的上下文中运行它们自己的代码或其他应用程序。

进程提供给应用程序两个关键抽象:一个独立的逻辑控制流;一个私有的地址空间。

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

shell-bash的作用:

Shell是用户与操作系统之间完成交互式操作的一个接口程序,它为用户提供简化了的操作。而NU组织发现sh是比较好用的又进一步开发Borne Again Shell,简称bash,它是Linux系统中默认的shell程序。

shell-bash的处理流程:

1、解析输入的命令行

2、如果是内置命令行,解释命令

3、如果是可执行文件,在一个新的紫禁城的上下文中execve并运行文件。

4、运行过程中,shell处理异常。

5、运行终止后,shell回收子进程。

6.3 Hello的fork进程创建过程

fork函数:父进程调用创建子进程。

新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚

拟地址空间相同的一份副本,包括代码和数据段、堆、共享库及用户栈。子进程

还获得与父进程任何打开文件描述符相同的副本,这就意味着当父进程调用fork时,子进程可以读写父进程打开的任何文件,父进程和创建的子进程最大的区别在于它们有不同的 PID。fork 函数被调用一次会返回两次,一次是在父进程中,一次是在新创建的子进程中。在父进程中,fork返回子进程的 PID,在子进程中fork返回0

6.4 Hello的execve过程

fork创建完子进程后,execve函数在子进程中加载hello,调用启动代码,启动代码设置栈,并将控制传递给新程序的主函数main,当main开始执行时,用户栈的组织结构如图6.4.1所示。让我们从栈底往栈顶看,首先是参数和环境字符串,然后是以null结尾的指针数组,其中每个指针都指向栈中的一个环境变量字符串。全局变量environ指向这些指针中的第一个envp[0]。之后是以null结尾的argv[]数组,其中每个元素都指向栈中的一个参数字符串。栈顶是系统启动函数libc_start_main的栈帧。

图6.4.1

hello子进程通过execve系统调用加载器。

加载器删除子进程所有的虚拟地址段,并创建一组新的代码、数据、堆段。

通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件中的内容。最后加载器跳到_start地址,它最终调用hello的main 函数。

6.5 Hello的进程执行

结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。

进程上下文信息:内核为每个进程维持一个上下文。上下文包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等。

进程调度的过程(如图6.5.1):

开始时,运行hello程序时处于用户模式,直到调用sleep让hello进入内核模式,内核模式中的陷阱处理程序让调用进程休眠。

进程休眠时间很长,内核进行hello进程到进程B的上下文切换,切换的前一部分,内核代表进程 hello 在内核模式下执行指令,然后在后半部分,代表进程 B

执行指令。在切换之后,内核代表进程 B 在用户模式下执行指令。之后,进程B在用户模式下运行,直到hello进程休眠结束,执行一个从进程B到进程hello的上下文切换,将控制返回给进程 hello 中紧随系统调用 sleep 之后的那条指令。进程 hello 继续运行,直到下一次异常发生,其他 9 次调用 sleep 以此类推。直到进程终止被 shell回收。

6.6 hello的异常与信号处理

hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。

程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。

程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

异常的分类:中断、陷阱、故障、终止。

hello执行过程中会出现陷阱和终止。

随便按键盘,回车。

键入Ctrl+C,内核会发送一个 SIGINT 信号给前台进程 组中的每个进程,终止前台作业。然后 shell 会回收该进程。

键入 Ctrl+Z,会发送一个 SIGTSTP 信号到前台进程组中的每个进程,挂起前台作业,即挂起进程 hello。使用 ps 命令,发现进程还在。

使用 jobs 命令查看前台进程

使用 pstree 查看 hello 进程所在位置

使用 fg 命令,让 hello 程序在前台运行

使用 kill 命令杀死 hello进程,shell 回收该进程

6.7本章小结

在本章中,介绍了进程的定义和作用,介绍了Shell的处理流程,介绍了shell如何调用fork创建新进程,如何调用execve执行hello,分析了hello的进程执行,还研究了hello的异常与信号处理.

异常控制流发生在计算机系统的各个层次.在硬件层,硬件检测到的事件会触发控制突然转移到异常处理程序.在操作系统层,内核通过上下文切换将控制从一个用户进程转移到另一个用户进程.在应用层,一个进程可以发送信号到另一个进程,而接收者会将控制突然转移到它的一个信号处理程序.一个程序可以通过回避通常的栈规则,并执行到其他函数中任意位置的非本地跳转来对错误做出反应.

(第61分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:

程序代码经过编译后出现在 汇编程序中地址.逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址.

线性地址:

是逻辑地址到物理地址变换之间的中间层。程序hello的代码产生逻辑地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址能再变换产生一个物理地址。若没有,那么线性地址就是物理地址。

虚拟地址:

就是hello的逻辑地址。

物理地址:

目前CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终地址。如果启用了分页基址,那么线性地址会使用页目录和页表中的项变换成物理地址。如果没有启用分页机制,那么线性地址就是物理地址。

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

实模式下:逻辑地址CS:EA到物理地址CS*16+EA

保护模式下:以段描述符作为下表,到GDT/LDT表查表获得段地址,段地址+偏移地址=线性地址。

段选择符各字段含义

15 14

32

10

索引

TI

RPL

高13位-8K个索引用来确定当前使用的段描述符在描述符表中的位置

TI=0,选择全局描述符表(GDT)

TI=1,选择局部描述符表(LDT)

RPL=00,为第0级,位于最高级的内核态,

RPL=11,为第3级,位于最低级的用户态,第0级高于第3级。

被选中的段描述符先被送至描述符cache,每次从描述符cache中取32位段基址,与32位段内偏移量(有效地址)相加得到线性地址

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

假设系统仅用一个单独的页表进行翻译。CPU 中的一个控制寄存器,页表基址寄存器指向当前页表。48 位的虚拟地址包括12 位的虚拟页偏移量 VPO和36 位的虚拟页号 VPN。MMU利用 VPN 选择适当的页表条目 PTE。例如,VPN0 选择 PTE0,VPN1 选择 PTE1,以此类推。将页表条目中物理页号 PPN(40 位)和虚拟地址中的 VPO(12 位)串联起来,就得到相应的物理地址(52 位)。如图7.3.1

图7.3.1

实际上,我们采取四级页表结构。如图7.3.2。48 位虚拟地址被翻译成了 52 位物理地址。每一个页表有 512 项,每一个页表项有 8个字节,每个页表都是 4KB 大小。一级页表每个页表项映射 512GB 区域,一级页表总计映射 256TB区域;二级页表有 512个,每个页表映射 512GB 区域;三级页表有 256K个,每

个页表映射 1GB 区域;四级页表有 128MB 个,每个页表映射 2MB 区域。四级页表每个页表项映射 4KB 区域

图7.3.2

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

TLB是一个小的、虚拟寻址的缓存,其中每一行都保留着一个由单个 PTE 组成的块。在 Core i7 中,TLB 索引有 4 位,TLB 标记有 32 位。故 TLB 有 16 组,并且每组 4个条目。

VA到PA转换(如图7.4.1):

1、CPU产生一个48位的虚拟地址

2、MMU从虚拟地址得到36位的VPN,并检查PTE。TLE从VPN中抽取TLB索引和TLB标记,TLB索引选择一个组,TLB标记匹配标记位,找到匹配。命中将PPN返回给MMU.

3、如果TLB不命中,MMU需要从主存中得到PTE。36位VPN被划分4个片,每个片被作用到一个页表的偏移量。CR3控制寄存器指向第一级页表L1 PTE的起始位置。VPN1提供一个指向L1PTE的偏移量,这个PTE包含L2页表的基地址。VPN2提供一个L2 PTE的偏移量,以此类推,直到找到L4中的PTE。

4、如果得到PTE是无效的,就产生一个缺页,内核调入页面,再次运行刚才的指令。

5、最后MMU得到PPN和VPO,形成物理地址。

图7.4.1

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

1、得到物理地址VA后,首先使用物理地址的CI进行组索引,对8路的块分别匹配 CT进行标志位匹配。如果匹配成功且块的valid标志位为1,则命中hit。然后根据数据偏移量 CO取出数据并返回。

2、若没有相匹配或者标志位为0,就miss。那么cache向下一级cache,这里向二级cache或者三级cache中查找,然后写入cache

3、再更新cache的之后,判断是否有空闲块。如果有空闲块,就写入,没有就驱逐(LRU策略)。

7.6 hello进程fork时的内存映射

当fork被当前进程调用时,内核为新进程创建数据结构,分配PID,为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构(vm_area_struct)和页表的原样副本。它将两个进程中的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。

当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面。因此,也就为每个进程保持了私有地址空间的抽象概念。

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 缺页故障与缺页中断处理

缺页时的操作

第1步:处理器生成一个虚拟地址,并把它传送给MMU.

第2步: MMU生成PTE地址,并从高速缓存/主存请求得到它.

第3步:高速缓存/主存向MMU返回PTE.

第4步:PTE中的有效位是零,所以MMU触发了一次异常,传递CPU中的控制到操作系统内核中的缺页异常处理程序.

第5步:缺页处理程序确定出物理内存中的牺牲页,如果这个页面已经被修改了,则把它换出到磁盘.

第6步:缺页处理程序页面调人新的页面,并更新内存中的PTE.

第7步:缺页处理程序返回到原来的进程,再次执行导致缺页的指令,CPU将地址重新发送给MMU.因为虚拟页面现在已经缓存在物理内存中,所以会命中,主存将所请求字返回给处理器.

7.9动态存储分配管理

Printf会调用malloc,请简述动态内存管理的基本方法与策略。

动态内存分配器维护着一个进程的虚拟内存区域,称为堆.系统之间细节不同,但是不失通用性,假设堆是一个请求二进制零的区域,它紧接在未初始化的数据区域后开始,并向上生长(向更高的地址) .对于每个进程,内核维护着一个变量brk, 它指向堆的顶部.

分配器将堆视为一组不同大小的块的集合来维护.每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的.已分配的块显式地保留为供应用程序使用.空闲块可用来分配.空闲块保持空闲,直到它显式地被应用所分配.一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的.

分配器有两种基本风格.  两种风格都要求应用显式地分配块.它们的不同之处在于由哪个实体来负责释放已分配的块

显式分配器(explicit allocator):

要求应用显式地释放任何已分配的块.例如,C标准库提供一种叫做malloc程序包的显式分配器.C程序通过调用malloc函数来分配一个块,并通过调用free函数来释放一个块.C++中的new和delete操作符与C中的malloc和free相当.

隐式分配器(implicit allocator):

要求分配器检测一个已分配块何时不再被程序所使用,那么就释放这个块.隐式分配器也叫做垃圾收集器(garbage collector),而自动释放未使用的已分配的块的过程叫做垃圾收集( garbage collection).例如,诸如Lisp、ML以及Java之类的高级语言就依赖垃圾收集来释放已分配的块.

隐式空闲链表:

一个块是由一个字的头部,有效载荷,以及可能的一些额外的填充组成的.头部编码了这个块的大小,以及这个块是已分配的还是空闲的.如果我们强加一个双字的对齐约束条件,那么块大小就总是8的倍数,且块大小的最低3位总是零.因此,我们只需要内存大小的29个高位,释放剩余的3位来编码其他信息.在这种情况中,我们用其中的最低位(已分配位)来指明这个块是已分配的还是空闲的.

带边界标签的隐式空闲链表:

在隐式空闲链表堆块的基础上,在每个块的结尾处添加一个脚部(footer),边界标记),其中脚部就是头部的一个副本.如果每个块包括这样一个脚部,那么分配器就可以通过检查它的脚部,判断前面一个块的起始位置和状态,这个脚部总是在距当前块开始位置一个字的距离,这样就允许在常数时间内进行对前面块的合并.

显示空闲链表

一种更好的方法是将空闲块组织为某种形式的显式数据结构.因为根据定义,程序不需要一个空闲块的主体,所以实现这个数据结构的指针可以存放在这些空闲块的主体里面.例如,堆可以组织成一一个双向空闲链表,在每个空闲块中,都包含一个pred(前驱)和succ(后继)指针.

7.10本章小结

本章主要介绍了hello的存储器地址空间、intel的段式管理、页式管理,TLB与四级页表支持下的VA到PA的变换、三级cache支持下物理内存访问, hello进程fork时的内存映射、execve时的内存映射、缺页故障与缺页中断处理、动态存储分配管理等内容.

不难看出作为一个程序员,我们需要理解存储器层次结构,因为它对应用程序的性能有着巨大的影响.如果我们的程序需要的数据是存储在CPU 寄存器中的,那么在指令的执行期间,在0个周期内就能访问到它们.如果存储在高速缓存中,需要4~7 5 个周期.如果存储在主存中,需要上百个周期.而如果存储在磁盘上,需要大约几千万个周期.

熟悉这一章节的内容,可以帮助我们以后编写一些对高速缓存友好的代码,或者说运行速度更快的代码,十分重要.

(第7 2分)

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件

设备管理:unix io接口

所有的I/ O 设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当作对相应文件的读和写来执行.这种将设备优雅地映射为文件的方式,允许Linux 内核引出一个简单、低级的应用接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行,这就是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.进程是通过调用open 函数来打开一个已存在的文件或者创建一个新文件的:int open(char *filename, int flags, mode_t mode);

open 函数将filename 转换为一个文件描述符,并且返回描述符数字.返回的描述符总是在进程中当前没有打开的最小描述符.flags 参数指明了进程打算如何访问这个文件,mode 参数指定了新文件的访问权限位.返回:若成功则为新文件描述符,若出错为-1.

2.进程通过调用close 函数关闭一个打开的文件.int close(int fd);

返回:若成功则为0, 若出错则为-1.

3.应用程序是通过分别调用read 和write 函数来执行输入和输出的.

ssize_t read(int fd, void *buf, size_t n);

read 函数从描述符为fd 的当前文件位置复制最多n 个字节到内存位置buf .返回值-1表示一个错误,而返回值0 表示EOF.否则,返回值表示的是实际传送的字节数量.

返回:若成功则为读的字节数,若EOF 则为0, 若出错为-1.

ssize_t write(int fd, const void *buf, size_t n);

write 函数从内存位置buf 复制至多n 个字节到描述符fd 的当前文件位置.图10-3 展示了一个程序使用read 和write 调用一次一个字节地从标准输入复制到标准输出.返回:若成功则为写的字节数,若出错则为-1.

8.3 printf的实现分析

从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等.

字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。

显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。

getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

本章通过介绍Linux的IO设备管理方法,简述Unix IO接口及其函数,对printf和getchar的实现的具体分析,了解了Unix I/O 在系统中是一个什么样的存在,I/O 是系统操作不可或缺的一部分.有时你除了使用Unix I/O 以外别无选择.在某些重要的情况中,使用高级I/O 函数不太可能,或者不太合适.

(第81分)

结论

用计算机系统的语言,逐条总结hello所经历的过程。

你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。

hello所经历的过程:

编写阶段,通过文本编辑器写出hello.c.

hello.c经过预处理,变成hello.i文件,预处理器将hello.c include的外部的库取出合并到hello.i文件中.

hello.i经过编译,变成hello.s汇编文件,

hello.s经过汇编,汇编程序翻译成机器语言指令,变成hello.o可重定位目标文件。

hello.o经过链接,,将hello.o与可重定位目标文件和动态链接库链接成为可执行目标程序hello.

运行:在shell中输入./hello 并跟上三个参数120L021909 xjw 1

运行程序:shell进程调用fork创建子进程,shell调用execve,execve调用启动加载器,加映射虚拟内存,创建新的内存区域,并创建一组新的代码、数据、堆和栈段.程序开始运行.

执行指令:CPU为其分配时间片,在一个时间片中,hello有对CPU的控制权,顺序执行自己的代码

访问内存:MMU将程序中使用的虚拟内存地址通过页表映射成物理地址.

动态申请内存:printf会调用malloc向动态内存分配器申请堆中的内存.

信号:如果运行途中键入ctr-c则停止,如果运行途中键入ctr-z则挂起.

结束:shell父进程回收子进程.

感悟:

学习了计算机系统,让我对程序是如何一步步从无到有再到执行有了一个大概的映像,加深了我对计算机系统的理解。作为一名程序员,这门课也让我了解了在程序调试, 性能提升, 程序移植和健壮性等方面的知识; 建立扎实的计算机系统概念, 为后续的课程打下坚实基础。

同时,也让我对前人的智慧感到惊叹,如此难以理解的东西,他们发现出来,并用于造福人类,这让我感到十分的敬仰。

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

附件

hello.i

预处理得到的文件

hello.s

编译得到的文件

hello.o

汇编得到的文件

hello.asm

hello.o反汇编得到的文本文件

hello.elf

hello.o的elf文件

hello

链接得到的可执行目标文件

hello.asm2

hello反汇编得到文本文件

5.3helloelf.txt

可执行hello的elf格式,作用是重定位过程分析

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

参考文献

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

[1]  深入了解计算机系统(第三版)2016 Bryant,R.E. 机械工业出版社

[2]  深藏功名丿小志 程序预处理阶段,在做什么?[电子文献]:  404 - 知乎

[3]  陈英 陈朔鹰.编译原理.清华大学出版社,2009年

[4]  逻辑地址到线性地址的转换https://blog.csdn.net/xuwq2015/article/details/48572421

(参考文献0分,缺失 -1分)

(参考文献0分,缺失 -1分)

HIT深入理解计算机系统大作业相关推荐

  1. HIT 深入理解计算机系统 大作业 程序人生-Hello’s P2P

    HIT 深入理解计算机系统 大作业 程序人生-Hello's P2P 本论文旨在研究 hello 在 linux 系统下的整个生命周期.结合 CSAPP 课本, 通过 gcc 等工具进行实验,从而将课 ...

  2. 2021春深入理解计算机系统大作业---hello的一生

    计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算学部 学    号 120L021725 班    级 2003006 学       生 杨楠 指 导 ...

  3. 2021春深入理解计算机系统大作业——程序人生

    计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算学部 学    号 1190200608 班    级 1903004 学       生 琚晓龙 指 导 ...

  4. 【程序人生-Hello‘s P2P】哈尔滨工业大学深入理解计算机系统大作业

    计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 xxxx 学 号 2021xxxx 班 级 210xxxx 学 生 xx 指 导 教 师 xxx 计算机科学与技术学院 2023年 ...

  5. HIT CS:APP 计算机系统大作业 《程序人生-Hello’s P2P》

    HIT CS:APP 计算机系统大作业 程序人生-Hello's P2P Hello的自白 我是Hello,我是每一个程序猿¤的初恋(羞羞--) l却在短短几分钟后惨遭每个菜鸟的无情抛弃(呜呜--), ...

  6. 2022计算机系统大作业————hit

    -/************************************************************************************************** ...

  7. HIT计算机系统大作业2022

    计算机系统 大作业 计算机科学与技术学院 2021年5月 摘 要 学习计算机系统后,从底层对一个简单的Hello.c如何一步步编译,链接,加载,执行等进行浅层的描述. 关键词:计算机系统,底层. 目 ...

  8. HIT 计算机系统大作业 Hello程序人生P2P

    计算机系统 大作业 题 目 程序人生-Hello's P2P 专 业 人工智能(未来技术模块) 学 号 7203610725 班 级 2036014 学 生 黄鸿睿 指 导 教 师 刘宏伟 计算机科学 ...

  9. HIT计算机系统大作业

    计算机系统 大作业 题     目 程序人生-Hello's P2P 专       业 计算学部 学 号 120L022305 班       级 2003008 学       生 XXX 指 导 ...

最新文章

  1. 区块链的安全软肋是什么?
  2. linux网络配置命令 ifconfig 、route 、ip 、ip route
  3. Kerberos 协议和 KDC 实现 Apache Kerby
  4. 重装Windows10系统(Win10系统)
  5. 解读netty3.9的数据处理流程(一)
  6. chart控件做实时曲线显示_Spark+Kafka+WebSocket+eCharts实时分析-完全记录(5)
  7. java/php/net/python房产信息管理系统设计
  8. vsco使用教程_vsco cam怎么用?vsco cam安卓版使用教程详解
  9. ug齿条插件_NX9.0齿轮齿条运动仿真—齿轮工具箱巧用及渐开线制作
  10. docker pxc mysql_docker安装pxc集群的详细教程
  11. 盒图(boxplot)
  12. 中国远程医疗行业发展前景预测和投资规划分析报告2022-2027年
  13. KVM/QEMU虚拟机申请和释放内存的步骤
  14. 西瓜书笔记16-2:逆强化学习
  15. 当计算机遇上经济学:如何量化你的投资并获得第一桶金
  16. YOLO train.txt创建
  17. 0-1型整数规划—MATLAB数学建模
  18. 登月源码开源登顶GitHub No.1!接而又被中国程序员“玩坏”了
  19. 绝地求生为何显示服务器上限,绝地求生服务器为啥不给力?官方解释:人太多了...
  20. vivado DocNav软件打不开PDF文档的解决办法

热门文章

  1. 斯坦福SCI写作公开课目录
  2. 关于SCI论文发表的五不准原则是
  3. 迪信通机器人_迪信通要做机器人:玩票还是另有深意?
  4. 关于EasyRecovery工具数据恢复的原理
  5. AWS 云从业者认证(AWS Certified Cloud Practitioner)
  6. 单基因gsea_零代码5分+的单基因综合分析
  7. vm无法连接虚拟设备sata01_无法连接虚拟设备sata0:1怎样解决?
  8. 苹果2014WWDC亮点之个人浅见
  9. 【笔记】SQL查询语句的基本使用方法
  10. 影视动画设计有些SCI期刊推荐? - 易智编译EaseEditing