HIT-ICS2022大作业(程序人生-Hello’s P2P)

2023-11-19

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业      计算机科学与技术

学     号                           

班     级                           

学       生                        

指 导 教 师                          

计算机科学与技术学院

2022年5月

摘  要

一个简单的Hello程序,其生命周期的整个P2P与020过程需要计算机系统各个部分的完美配合。本文旨在陪伴Hello程序走过它的一生,依次分析了Hello程序从源文件经过预处理、编译、汇编、链接到可执行文件的过程、进程管理过程、储存管理过程,同时也回顾了本学期在ICS课程上的所学内容。

第1章是关于hello程序的总体概述,并列举了编写本文的软硬件环境与生成的中间文件。第2至5章分别详细地介绍了hello的整个预处理、编译、汇编、链接过程,其中着重解析了汇编的结果、目标文件中的ELF格式以及重定位过程。第6章介绍了hello的进程管理,基于进程这个重要概念讲述了hello如何在得以在计算机上执行,其中包括shell工作原理、信号处理技术。第7章介绍了hello的储存管理,分析了hello的代码与数据如何在计算机上存储,其中包括虚拟内存、Cache等内容,以及快表、多级页表等加速访问的技术。

    本文通过阐述一个具体程序在计算机中运行的全过程,深入体会到计算机系统的精妙设计,以一个直观具体的方式感受计算机系统为一个程序的良好运行所做的一切工作。

关键词:计算机系统;预处理;编译;汇编;链接;进程;储存                         

目  录

第1章 概述... - 5 -

1.1 Hello简介... - 5 -

1.2 环境与工具... - 5 -

1.3 中间结果... - 5 -

1.4 本章小结... - 6 -

第2章 预处理... - 7 -

2.1 预处理的概念与作用... - 7 -

2.2在Ubuntu下预处理的命令... - 7 -

2.3 Hello的预处理结果解析... - 8 -

2.4 本章小结... - 10 -

第3章 编译... - 11 -

3.1 编译的概念与作用... - 11 -

3.2 在Ubuntu下编译的命令... - 11 -

3.3 Hello的编译结果解析... - 12 -

3.3.1 数据类型处理操作:... - 13 -

3.3.2 操作分析:... - 14 -

3.4 本章小结... - 19 -

第4章 汇编... - 20 -

4.1 汇编的概念与作用... - 20 -

4.2 在Ubuntu下汇编的命令... - 20 -

4.3 可重定位目标elf格式... - 20 -

4.3.1 ELF头... - 21 -

4.3.2 节头部表... - 21 -

4.3.3 重定位条目... - 22 -

4.3.4 符号表... - 23 -

4.4 Hello.o的结果解析... - 23 -

4.4.1        机器语言的构成,与汇编语言的映射关系... - 25 -

4.4.2        机器语言与汇编语言的操作数的不同点... - 25 -

4.4.3反汇编器使用重定位条目替换全局变量和函数... - 25 -

4.5 本章小结... - 27 -

第5章 链接... - 28 -

5.1 链接的概念与作用... - 28 -

5.2 在Ubuntu下链接的命令... - 28 -

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

5.4 hello的虚拟地址空间... - 30 -

5.5 链接的重定位过程分析... - 33 -

5.5.1 重定位节和符号定义... - 34 -

5.5.2 重定位节中的符号引用... - 34 -

5.6 hello的执行流程... - 35 -

5.7 Hello的动态链接分析... - 36 -

5.8 本章小结... - 37 -

第6章 hello进程管理... - 38 -

6.1 进程的概念与作用... - 38 -

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

6.3 Hello的fork进程创建过程... - 38 -

6.4 Hello的execve过程... - 39 -

6.5 Hello的进程执行... - 39 -

6.5.1 上下文切换... - 39 -

6.5.2 调度... - 39 -

6.5.3 用户模式与内核模式... - 39 -

6.6 hello的异常与信号处理... - 40 -

6.6.1异常种类... - 40 -

6.6.2 信号种类... - 41 -

6.6.3 hello执行过程中命令示例分析... - 41 -

6.7本章小结... - 46 -

第7章 hello的存储管理... - 47 -

7.1 hello的存储器地址空间... - 47 -

7.1.1逻辑地址:... - 47 -

7.1.2线性地址:... - 47 -

7.1.3虚拟地址:... - 47 -

7.1.4物理地址:... - 47 -

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

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

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

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

7.6 hello进程fork时的内存映射... - 52 -

7.7 hello进程execve时的内存映射... - 53 -

7.8 缺页故障与缺页中断处理... - 54 -

7.9动态存储分配管理... - 55 -

7.10本章小结... - 56 -

第8章 hello的IO管理... - 57 -

8.1 Linux的IO设备管理方法... - 57 -

8.2 简述Unix IO接口及其函数... - 57 -

8.3 printf的实现分析... - 57 -

8.4 getchar的实现分析... - 57 -

8.5本章小结... - 57 -

结论... - 58 -

附件... - 59 -

参考文献... - 60 -

第1章 概述

1.1 Hello简介

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

Hello的P2P(From Program to Process)过程:Hello程序的生命周期是从一个源程序(或者说源文件)开始的,即程序员通过编辑器创建并保存的文本文件,文件名是hello.c(Program),接着源程序文件经过预处理、编译、汇编和链接生成可执行目标文件。用户在Bash内输入运行这个可执行目标文件的命令以及相关参数,OS为它创建一个新的进程(Process) (fork函数),这就是Hello的P2P过程。

Hello的020(From Zero-0 to Zero-0)过程:Hello程序是程序员从无(Zero)到有创建并保存的,再经过编译器驱动程序一系列过程生成可执行目标文件,执行时OS为它创建新进程,再通过execve函数在进程上下文中加载并运行Hello程序,OS为此进程提供一个独立的页表,映射到物理内存上,CPU执行一条条机器指令,以完成程序的功能。在程序执行结束后,bash将之前创建的子进程(运行Hello的进程)回收,内核会从系统中删除它的所有痕迹(Zero)。

1.2 环境与工具

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

硬件环境:AMD Ryzen 7 5800H with Radeon Graphics 3.20 GHz ;16G RAM ;

512GHD Disk

软件环境:Windows 11 家庭中文版 ;Ubuntu-19.04

开发工具:Visual Studio 2022 ;CodeBlocks 64位 ;gedit+gcc ;GDB;EDB

1.3 中间结果

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

  1. hello.c:hello的源代码;
  2. hello.i:预处理得到的文本文件,用来分析预处理器所做的工作;
  3. hello.s:编译得到的文本文件,用来分析汇编语言格式;
  4. hello.o:汇编得到的可重定位目标文件,包含重定位条目,指导链接器进行工作;
  5. hello.out:链接得到的可执行文件,可直接执行;

1.4 本章小结

       本章介绍了hello的P2P过程和020过程,强调了hello的一生从源文件开始,最终成长为一个进程,并且hello对于计算机而言,干干净净地出生,最终运行完毕也消失地无影无踪。此外,还介绍了hello成长过程中留下的中间文件,以及Hello使用的硬软件环境,开发与调试工具。

第2章 预处理

2.1 预处理的概念与作用

  1. 预处理的概念:

程序设计领域中,预处理一般是指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器(preprocessor) 对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。这个过程并不对程序的源代码进行解析,但它把源代码分割或处理成为特定的单位——(用C/C++的术语来说是)预处理记号(preprocessing token)用来支持语言特性(如C/C++的宏调用)。

  1. 预处理的作用:

预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。

主要操作有:

1)将源文件中以“#include”格式包含的文件复制到编译的源文件中。

2)用实际值替换用“#define”定义的字符串。

3)根据“#if”后面的条件决定需要编译的代码。

4)删除源代码中的注释语句。

2.2在Ubuntu下预处理的命令

在终端中输入命令对hello.c进行预处理,生成同一目录下的hello.i文件。

  1. cpp hello.c -o hello.i

预处理操作示例与生成的hello.i文件

2.3 Hello的预处理结果解析

       大作业的源程序如下:

大作业源程序

预处理结果如下:

预处理结果(一)

预处理结果(二)

    通过对预处理结果与源文件的比较,我们可以看到,预处理器读取系统头文件stdio.h、unistd.h、stdlib.h的内容,并把它们直接插入程序文本中。此外,源程序中的注释也被清除。

     

2.4 本章小结

       预处理过程是Hello出生后的人生走过的第一步,虽然文件属性没有发生本质的改变,但从hello.c到hello.i的成长,Hello已经为锐变成汇编程序做好了准备。

第3章 编译

3.1 编译的概念与作用

  1. 编译的概念

编译指编译器(cc1)将文本文件hello.i翻译成文本文件hello.s的过程,hello.s包含一个汇编语言程序。

  1. 编译的作用

检查源程序有无语法错误。在预处理文件转化为汇编文件的过程中,会经过一系列的词法分析、语法分析、语义分析,存在语法错误则会给出提示信息,使得编译不通过。

对源程序进行优化。在预处理文件转化为汇编文件的过程中,会进行一系列的优化,生成与源程序等价的程序,但运行起来效率更高。

文本语言的转换。在预处理文件转化为汇编文件的过程中,代码会被从高级编程语言转换为适合机器理解的汇编语言。

提高程序员的工作效率。程序员可以使用更易于人理解的高级语言,编写蕴含更复杂、更高级逻辑的代码,而不是用实现难度更大的汇编语言编写。

3.2 在Ubuntu下编译的命令

在终端中输入命令对hello.i进行编译,生成同一目录下的ASCII汇编语言文件hello.s。

  1. gcc -S hello.i -o hello.s

编译操作示例及生成的hello.s文件

编译结果

3.3 Hello的编译结果解析

3.3.1 数据类型处理操作:

3.3.1.1 int型数据argc

由以上两个语句可推知%edi保存着main函数的第一个参数argc,因为argc必须为4,并被拷贝至栈中。

3.3.1.2字符串指针数组char *argv[]

字符指针首地址被保存在%rsi中,并拷贝到栈中。

3.3.1.3循环局部变量i

i被赋初值0

i++操作

比较循环条件i<=8

3.3.1.4字符串

由.string可知,程序运行时保存着两个字符串,分别对应着源代码中蓝框所示的字符串。

字符串指针通过加载有效地址的方式传入%rdi,作为printf的参数。

    3.3.2 操作分析:

3.3.2.1 函数调用

             

Call +函数名

3.3.2.2 条件控制实现条件分支

若argc不等于4,则不会执行.L2的操作,直接输出本不应该输出的printf函数格式提示信息,并exit(1)。

条件分支

3.3.2.3 条件测试与跳转实现循环操作

i被赋初值0

i++操作

比较循环条件i<=8

直到i>8循环退出

通过用条件测试和跳转组合的组合实现循环的效果。

3.3.2.4 各类赋值操作

1)简单的数据传送指令

2)加载有效地址

程序中出现示例

3.3.2.5 函数参数传递

       在一个函数调用前,需要在对应的寄存器中准备好函数要传入的各个参数,例如在调用printf前,修改好了%rdi、%rsi、%rdx的值。%rdi为表示输出格式的字符串,%rsi、%rdx分别是两个char型指针。

函数传参示例

3.3.2.6 ++操作

       利用addl操作实现i++。

3.3.2.7 关系判断操作

       Cmpl比较两个操作数的大小,并按大小设置条件码寄存器对应的值,jle语句根据条件码寄存器的值判断是否跳转。

Cmpl与jle常前后组合实现功能

3.3.2.8 开辟栈空间操作

因为在x86-64中,栈是向下增长的,将%rsp的值减小32,开辟了栈的储存空间。

栈操作图示

3.4 本章小结

通过将hello.i编译成hello.s,Hello程序已经成长为程序员可读、又符合机器具体操作方式的汇编程序。

为了更好地展示长大后的Hello与幼时的联系,本章重点介绍了C语言中各类数据和操作是如何在汇编语言中得到实现的,同时通过例子直观地分析了编译器编译程序的具体行为。

第4章 汇编

4.1 汇编的概念与作用

       (1)汇编的概念:

    汇编是指汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中。

(2)汇编的作用:

将汇编代码翻译成机器能直接执行的机器语言指令,打包后的可重定位目标程序可被直接复制到内存并执行。

4.2 在Ubuntu下汇编的命令

       在终端中输入命令对hello.s进行汇编,生成在同一目录下的可重定位目标程序hello.o。

  1. as hello.s -o hello.o

4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。

生成hello.o的ELF格式

4.3.1 ELF

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

ELF头

    4.3.2 节头部表

不同节的位置和大小是由节头部表描述的,其中目标文件中的每个节都有一个固定大小的条目。

节头部表

4.3.3 重定位条目

.rela.text节是一个.text节中位置的列表。当链接器把这个目标文件和其他文件组合时,需要修改这些位置。一般而言,任何调用外部函数或者应用全局变量的指令都需要修改。

“偏移量”是需要被修改的引用的节偏移。

“信息”包含了symbol和type,symbol标识被修改引用应该指向的符号。Type告知链接器如何修改新的引用。

“加数”是一个有符号常数,一些类型的重定位要使用它对被修改引用的值做偏移调整。

重定位条目

4.3.4 符号表

每个重定位目标模块都有一个符号表,它包含m定义和引用符号的信息。

Num是字符串表中的字节偏移,指向符号的以Null结尾的字符串名字。

Value是符号的地址。

Size是目标的大小。

Type要么是数据,要么是函数。

Bind表示符号是本地的还是全局的。

符号表

4.4 Hello.o的结果解析

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

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

在终端输入objdump命令,可以得到hello.o的反汇编。

  1. objdump -d -r hello.o

Hello.o的反汇编结果

4.4.1  机器语言的构成,与汇编语言的映射关系

      机器语言是有一串16进制的数字构成的,从某个给定位置开始,可以将字节唯一地解码成机器指令。因此,给定一个机器指令的序列,可以唯一地解码成相应的汇编代码序列。

4.4.2  机器语言与汇编语言的操作数的不同点

      在hello.s中(左图)操作数是以10进制表示的,但在hello.o的反汇编中是以16进制表示的。

操作数对比分析

4.4.3反汇编器使用重定位条目替换全局变量和函数

通过对反汇编语句的观察,反汇编器使用了重定位条目替换了跳转的Lable,即<函数名+偏移量>表示跳转地址,因此2f<main+0x2f>指图中2f处的指令。

Label被替换

当进行函数调用时,由于未重定位,函数的具体地址不确定,因此e8(call指令的机器码)后面的地址全为0,而call后接着的是下一条指令的地址。这条指令接着的是需要调用函数的重定位信息,便于在链接时进行函数地址的确定。

调用函数时的反汇编形式

此外,通过查看hello.o的机器代码表示,可以发现跳转语句的跳转地址全都用0替代,因为此时还未进行重定位操作,程序运行时跳转的真正地址要在链接后经过重定位才能确定。

4.5 本章小结

       通过汇编,Hello程序离真正可执行又迈出了人生中重要的一步。Hello程序已经被转换为了机器代码表示,对于编写它的程序员而言,已经变得面目全非,但机器悦纳此时长成这个样子的Hello。Hello调用的外部函数,全局变量都已经用于自己的重定位条目,在重定位的时候根据条目,就可以把为0的地址改成最终在内存中的地址。

5章 链接

5.1 链接的概念与作用

(1)链接的概念:

       链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存中并执行。在现代的系统中,链接是由叫做链接器的程序自动执行的。

(2)链接的作用:链接器进行链接,它们在软件开发中扮演着一个关键的角色,使得分离编译成为可能。我们不用将一个大型的应用程序组织为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当我们改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其它文件。

5.2 在Ubuntu下链接的命令

       在终端输入命令,可得到在同一目录下的可执行文件hello

  1. ld -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 /usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o hello.o -lc /usr/lib/gcc/x86_64-linux-gnu/9/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -z relro -o hello.out

链接操作及结果

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

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

在命令行中输入如下语句,查看打印出的hello.out的ELF格式。

  1. readelf -a hello.out

可执行目标文件的格式类似于重定位目标文件的格式。

各节的位置和大小是由节头部表描述的,其中目标文件每个节都有一个固定大小的条目。

以下是节头部表的信息:

节头部表

5.4 hello的虚拟地址空间

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

使用EDB加载hello,可在Data Dump区域查看hello的虚拟地址空间范围。

首先程序头部表描述了可执行文件连续的片被映射到连续的内存段的映射关系。

以下是程序头部表的信息,蓝圈中的信息为虚拟内存每个段的起始地址。

程序头部表

首先查看可执行文件的连续的片被映射到连续的内存段的信息,例如查看在虚拟内存空间0x400040处PHDR的内容。

PHDR的信息

      接着,在Memory Regions中查看0x400000到0x401000是一块只读的区域,为只读代码段。0x401000到0x402000是读写段。中间有部分区域是共享库的内存映射区域,在往高地址方向是用户创建的栈,最后是内核内存。

Memory Regions

      5.3中节头部表说明.text节的起始位置为0x4010f0,可直接在edb中查看信息。

Edb使用界面

.text在虚拟内存中的起始处

5.5 链接的重定位过程分析

objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。

结合hello.o的重定位项目,分析hello中对其怎么重定位的。

一旦链接器完成符号解析这一步,链接器已经知道它的输入目标模块中的代码节和数据节的确切大小,可以开始重定位步骤。

重定位主要由两步组成:重定位节和符号定义,下面以hello为例,分别说明两个过程。正在上传…

hello.o与hello.out中main函数部分的对比图

通过上图,hello.out的反汇编中main函数的每行语句都拥有在内存中的实际地址,callq函数的指令e8后面也不是全为0的占位符了,jump指令后面也不是相对于main函数的偏移量o.out经过了重定位的过程。

5.5.1 重定位节和符号定义

      在这一步中,链接器将所有相同类型的节合并为同一类型的新的聚合节。链接器将运行时内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号。当这一步完成时,程序中的每条指令和全局变量都有唯一地运行时内存地址了。

5.5.2 重定位节中的符号引用

      通过第四章的分析,汇编器生成一个目标模块时,它并不知道数据和代码最终将放在内存中的什么位置。它也不知道这个模块引用的任何外部定义的函数或者全局变量的位置。所以,无论何时汇编器遇到对最终位置的目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。代码的重定位条目放在.rel.data中,已初始化的数据的重定位条目放在.rel.data中。

链接器在修改新的引用的时候,需要依据ELF重定位条目这个数据结构。下面主要介绍两种重定位的类型:

  1. 重定位PC相对引用:

Call指令后面跟着的是对跳转目标的PC相对引用占位符。其具体值为目标地址与程序计数器(%rip)的差值。

在Hello.out的反汇编中,地址4011f6处的调用了call指令,此时程序计数器的值为4011fb,目标地址为401090,可见call指令后面跟着的是后者与前者的差值。

  1. 重定位绝对引用:

Call指令后面跟着的是对跳转目标绝对引用的占位符。

5.6 hello的执行流程

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

在下图所示页面中分析程序的执行流程:

DEBUG [Analyzer] adding: hello!_init <0x0000000000401000>

DEBUG [Analyzer] adding: hello!puts@plt <0x0000000000401030>

DEBUG [Analyzer] adding: hello!printf@plt <0x0000000000401040>

DEBUG [Analyzer] adding: hello!getchar@plt <0x0000000000401050>

DEBUG [Analyzer] adding: hello!atoi@plt <0x0000000000401060>

DEBUG [Analyzer] adding: hello!exit@plt <0x0000000000401070>

DEBUG [Analyzer] adding: hello!sleep@plt <0x0000000000401080>

DEBUG [Analyzer] adding: hello!_start <0x0000000000401090>

DEBUG[Analyzer] adding: hello!_dl_relocate_static_pie <0x00000000004010c0>

DEBUG [Analyzer] adding: hello!main <0x00000000004010c1>

DEBUG [Analyzer] adding: hello!__libc_csu_init <0x0000000000401150>

DEBUG [Analyzer] adding: hello!__libc_csu_fini <0x00000000004011b0>

DEBUG [Analyzer] adding: hello!_fini <0x00000000004011b4>

5.7 Hello的动态链接分析

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

动态链接比静态链接灵活,但牺牲了性能,据统计ELF程序在静态链接下比动态库快大约1%~5%。

主要原因是,动态链接下对于全局和静态数据的访问都要进行复杂的GOT定位,然后间接寻址,对于模块间的调用也要先定位GOT,然后进行间接跳转。

另外,动态链接的链接过程是在运行时完成的,动态链接器会寻找并转载所需要的对象,然后进行符号查找地址重定位等工作。

延迟绑定实现:

    因为很多函数可能在程序执行完时都不会被用到,比如错误处理函数或一些用户很少用到的功能模块等,那么一开始就把所有函数都链接好实际是一种浪费,因此ELF采用了一种延迟绑定(Lazy Binding),就是在当函数第一次被用到时才进行绑定(符号查找,重定位等),如果没有用到则不进行绑定。

ELF使用PLT(Procedure Linkage Table)的方法来实现,使用一些很精妙的指令序列来完成。

下图显示了.got.plt开始于0x404008,结束于0x404048。

.got.plt

在加载时,动态链接器会重定位GOT中的每个条目,使它包含正确的绝对地址,而PLT中的每个函数负责调用不同函数。那么,通过观察edb,便可发现dl_init后.got.plt节发生的变化。

在edb中点击一下run,hello开始动态链接过程,在前后两图的比较中可知,.got.plt节在动态链接前后发生了变化。

5.8 本章小结

       本章介绍了hello经过链接变成可执行文件的操作,链接中最重要的两步是符号解析与重定位,此时hello终于成长为一个可以直接在shell中运行的程序,迈出了成长中的关键一步,其代码段与数据段都已在虚拟内存中分配好了位置,并可以在执行中再与动态库进行链接。

6章 hello进程管理

6.1 进程的概念与作用

(1)进程的概念:

      进程的经典定义是一个执行中的程序的实例。

(2)进程的作用:

      进程概念提供给应用程序两个关键抽象:

1)一个独立的逻辑控制流,好像我们的程序独占地使用处理器。

2)一个私有的地址空间,好像我们的程序独占地使用内存系统。

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

(1)壳Shell-bash的作用:shell执行一系列的读/求值步骤,然后终止。读步骤读取来自用户的一个命令行。求值步骤解析命令行,并代表用户运行程序。

(2)壳Shell-bash的处理流程:shell打印一个命令提示符,等待用户在stdin上输入命令行,然后对这个命令求值。Shell首先解析以空格分隔的命令行参数,并构造最终会传递给execve的argv向量。第一个参数被假设为要么是一个内置的shell命令名,马上就会解释这个命令,要么是一个可执行目标文件,会在一个新的子进程的上下文中加载并运行这个文件,具体操作是调用folk函数创建新进程,在子进程中读取参数,调用execve函数执行指定程序,如果用户没有要求后台运行,那么使用waitpid函数等待作业终止,如果用户要求在后台运行程序,那么shell返回到循环的顶部,等待下一个命令。

6.3 Hello的fork进程创建过程

在命令行输入./hello后,然后shell程序判断出不是其内置命令,通过调用fork函数创建一个新的运行的子进程。

子进程得到与父进程用户级虚拟地址空间相同的一份副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程任何打开文件描述符相同的副本,这就意味着父进程调用fork时,子进程可以读写父进程打开的任何文件。父进程和新创建的子进程之间最大的区别在于它们有不同的PID。

6.4 Hello的execve过程

在folk函数创建的子进程中,execve函数读取之前划分的字符串,加载并运行可执行目标文件hello,且带参数列表argv和环境变量列表envp,argv[0]是可执行目标文件的名字。在execve加载了hello之后,它调用第七章描述的启动代码。启动代码设置栈,并将控制转移给新程序的主函数。Hello主函数开始执行。

6.5 Hello的进程执行

6.5.1 上下文切换

操作系统内核使用一种成为上下文切换的较高层形式的异常控制流来实现多任务。内核为每个进程维持一个上下文,fork函数创建的新进程也是如此。上下文就是内核重新启动一个被抢占的进程所需的状态。它由一些对象组成,这些对象包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种数据结构,比如描述地址空间的页表、包含有关当前进程信息的进程表,以及包含进程已打开文件的信息的文件表。

上下文切换的具体步骤:1)保存当前进程的上下文2)恢复某个先前被抢占了的进程被保存的上下文 3)将控制传递给这个新恢复的进程

一个进程执行它的控制流的一部分的每一时间段叫做时间片。

6.5.2 调度

在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程。这种决策叫做调度,是由内核中一个称为调度器的代码处理的。当内核选择了一个新的进程开始运行时,我们说内核调度了这个进程。在内核调度了一个新的进程运行后,它就抢占当前进程,并使用一种称为上下文切换的机制来将控制转移到新的进程。

6.5.3 用户模式与内核模式

运行hello程序代码的进程初始时是在用户模式中的。进程从用户模式变为内核模式的唯一方法是通过诸如中断、故障或者陷入系统调用这样的异常。Hello等待某个事件而发生阻塞,内核可以选择执行上下文切换。下面展示的是一个read调用需要访问磁盘时,上下文切换的剖析,在切换的过程中,内核分别代表A与B在内核模式下执行了指令。

6.6 hello的异常与信号处理

6.6.1异常种类

      hello在执行过程中会出现中断(当用户按Ctrl-C时),陷阱exit(1)产生的系统调用)。

6.6.2 信号种类

按Ctrl-C会收到产生的SIGINT信号、按Ctrl-Z会收到产生的SIGTSTP信号。

6.6.3 hello执行过程中命令示例分析

(1)程序正常运行示例:

(2)Ctrl-Z

输入Ctrl-Z会导致内核发送一个SIGTSTP信号到前台进程组的每个信号。默认情况下,结果是挂起前台作业,此时hello被挂起了。

   

(3)输入ps展示各个进程的信息,可以看到各个进程的PID以及名字。

(4)输入jobs,显示当前shell环境中已启动的作业状态。

(5)输入pstree,可以在树形结构中显示程序和进程之间的关系。

(6)输入fg,将挂起的指令转移到前台执行,发送信号SIGCONT给被挂起的进程使之继续执行。

(7)输入kill命令,将挂起的hello进程杀死。再输入ps命令,可以看到进程已经结束。

(8)输入Ctrl-C

导致内核发送一个SIGINT信号到前台进程组中的每个进程。在默认情况下,结果是终止前台作业。再输入ps,发现没有进程hello,可知hello已经被回收了。

(9)在hello执行的过程中不停乱按

可以观察到在hello执行的过程中不停乱按键盘不会影响当前进程的执行,但输入的内容被存放至stdin中,当hello执行完,shell会依次解析之前输入的命令。可知乱按键盘不会向前台进程组发送会影响进程执行的信号(除非恰好同时按Ctrl-C与Ctrl-Z)

6.7本章小结

在本章中,重点介绍了hello程序其实是在Linux的壳shell上运行,shell解析命令行,并为hello创建新进程,并在这个新进程中加载和运行hello。进程提供给hello两个关键抽象:一个独立的逻辑控制流、一个私有的地址空间,让hello尽情驰骋了,hello的上下文信息也被妥善的保管好,以便hello每次出场都准备有序。

接着,详细地剖析了hello在执行过程中如何通过异常、信号与操作系统、硬件进行交互,hello可以随时被键盘的输入挂起或者中断,受到了许多支配,但hello也有着许多权利,如想进行系统调用时,陷阱机制为hello铺平了路,syscall为hello提供了丰富的系统调用选择,hello的父进程也早就制定好一套完成的回收机制,在hello终止后为它收尸。

7章 hello的存储管理

7.1 hello的存储器地址空间

结合hello说明逻辑地址、线性地址、虚拟地址、物理地址的概念。

7.1.1逻辑地址:

      逻辑地址空间:[段地址:偏移地址]。hello执行程序反汇编后的内存地址是逻辑地址。逻辑地址经过段式管理可生成线性地址。

7.1.2线性地址:

      在Linux中,线性地址等于虚拟地址。

7.1.3虚拟地址:

      CPU通过生成一个虚拟地址来访问主存,这个虚拟地址在被送到内存之前先转换成适当的物理地址。

7.1.4物理地址:

      计算机系统的主存被组成一个由M个连续的字节大小的单元组成的数组,每字节都有一个唯一的物理地址。物理地址描述了数据在内存中真正的存放位置。

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

被选中的段描述符先被送至描述符cache,每次从描述符cache中取32位段基址,与32位段内偏移量(有效地址)相加得到线性地址。下图展示了如何从逻辑地址变换成线性地址。

段选择符结构如下图所示:

当TI=0,选择全局描述符表(GDT),TI=1,选择局部描述符表(LDT)。

首地址和索引共同选择段描述符,生成段基地址,再与段内偏移量相加得到线性地址。

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

       存放在物理内存中的一个叫页表的数据结构,将虚拟页映射到物理页。每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表。

下图展示了一个页表的基本组织结构。页表就是一个页表条目的数组。虚拟地址空间中的每个页在页表中一个固定偏移量处都有一个PTE。每个PTE有一个有效位和一个n位地址字段。如果设置了有效位,那么地址字段就表示DRAM中相应的物理页的起始位置,这个物理页缓存了该虚拟页。如果没有设置有效位,那么一个空地址表示这个虚拟页还未被分配。否则,这个地址就指向该虚拟页在磁盘上的起始位置。

一个虚拟地址由虚拟页号(VPN)和虚拟页偏移量(VPO)组成。MMU在PTE中的物理页号(PPN)与虚拟页偏移量(VPO)组合成物理地址。因为一个虚拟页面的大小等于一个虚拟页面的大小,所以PPO和VPO相等。

      下图展示的是页面命中时,CPU硬件执行的步骤:

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

       MMU中包括了一个关于PTE的小的缓存,称为快表(TLB)。TLB是一个小的、虚拟寻址的缓存,其中每一行都保存着一个由单个PTE组成的块。如果TLB有T=2^t个组,那么TLB索引是由VPN的t个最低位组成的,而TLB标记是由VPN中剩余的位组成的。下图是TLB命中和不命中的操作图。

用来压缩页表的常用方法是使用层次结构的页表。这种方法从两个方面减少了内存要求。第一,如果一级页表中的一个PTE是空的,那么相应的二级页表就根本不存在,这是一种巨大的潜在节约,因为对于一个典型的程序,4GB地址空间的大部分都是未分配的。第二,只有一级页表才需要总是在主存中,虚拟内存系统可以在需要时创建、页面调入或调出二级页表。下图描述了使用k级页表层次结构的地址翻译。

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

Intel Core i7采用三级Cache,我们以此型号的处理器为例。L1、L2和L3高速缓存是物理寻址的,块大小为64字节。L1和L2是8路组相联的,L3是16路组相联的。三级缓存采用同一个物理地址来寻址。

Intel Core i7的三级Cache都采用组相联高速缓存,它必须检查多个行的标记位和有效位,以确定所请求的字是否在集合中。组中的任何一行可以包含任何映射到这个组的内存块。

首先根据s位组索引位进行组选择,再根据标记位在组中检查有效位和标记位是否匹配。只有当某一行的有效位被设置并且标记位匹配地址中的标记位,再根据块偏移选择起始字节。

由于组中的任何一行都可以包含任何映射到这个组中的内存块,有着几种常用的行替换策略,其中一些运用了局部性原理:如最不常使用策略、最近最少使用策略。

7.6 hello进程fork时的内存映射

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

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

下图是私有写时对象其中一个进程进行写之后的操作。

Shell为即将要运行hello的进程创建虚拟内存,创建了当前进程的mm_struct、区域结构和页表的原样副本。它将两个进程中的每个页面都标记位只读,并将两个进程中的每个区域结构都标记位私有的写时复制。

hello可读写父进程打开的任何文件,共享代码和数据段、堆、共享库以及用户栈。

7.7 hello进程execve时的内存映射

运行在当前进程中的程序执行了execve调用后,加载并运行程序有如下几个步骤。

加载并执行hello时,当前进程删除之前的已存在的区域结构,即父进程的一份副本。

       加载时为hello映射私有区域,为hello的代码、数据、bss和栈区域创建新的区域结构。栈区域和堆区域是请求二进制零的,映射到匿名文件,长度为0,bss区域是请求二进制零的

       加载操作接着为hello映射共享区域。

       最后设置程序计数器,指向hello代码区域的入口点。

下图描述了加载器是如何映射用户地址地址空间的区域的:

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

下图描述了缺页时的操作:

      值得注意的几点:

1)缺页中断处理后,页面被换入主存,然后要重新执行导致缺页的步骤。

2)选择牺牲页,若这个牺牲页被修改过,则要将它换出到磁盘。

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之类的高级语言就依赖垃圾收集来释放已分配的块。

malloc函数返回一个指针,指向大小为至少size字节的内存块,这个块会为可能包含在这个块内的任何数据对象类型做对齐。实际中,对齐依赖于编译代码在32位模式(gee -m32)还是64位模式(默认的)中运行。在32位模式中,malloc 返回的块的地址总是8的倍数。在64位模式中,该地址总是16的倍数。

7.10本章小结

在本章中,重点分析了hello在运行时代码、数据的储存方式。现代系统提供的的一种对主存的抽象概念——虚拟内存让hello感觉到它在独享整台计算机的内存资源,TLB加速对虚拟地址的翻译、多级页表显神通,大大减少了对主存资源的消耗,虚拟内存是计算机科学中最为重要的概念之一,使得每个可执行文件的内存映像使用相同的格式,hello也是如此,它的代码段总是从虚拟地址0x400000开始。虚拟内存也简化了hello的加载,当hello第一次需要引用一个虚拟内存时,才将虚拟页换入主存。

三级Cache的储存器结构,使得其成本与层次结构底层最便宜的存储设备相当,但是却以接近于层次结构顶部的储存设备的高速率向程序提供数据。

8章 hello的IO管理

8.1 Linux的IO设备管理方法

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

设备的模型化:文件

设备管理:unix io接口

8.2 简述Unix IO接口及其函数

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

8.3 printf的实现分析

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

[转]printf 函数实现的深入剖析 - Pianistx - 博客园

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

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

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

8.4 getchar的实现分析

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

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

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

8.5本章小结

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

(第81分)

结论

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

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

  1. 首先程序员编写名称为hello.c的文本文件;
  2. 预处理器将hello.c进行预处理,生成名为hello.i的文本文件,将#include后面的内容插入hello.c文件中,进行宏替换、注释的清除;
  3. hello.i经过编译生成文本文件hello.s,高级语言已经被转化为汇编语言;
  4. hello.s经过汇编生成可重定位文件hello.o,汇编语言已经转换为二进制的机器代码,并且为每条指令附上了地址;
  5. hello.o通过链接,最终生成了可执行文件hello。Hello的代码段、数据段拥有运行时的虚拟地址,各个hello.o文件中需要重定位的符号引用、函数调用等也重定位完毕;

hello的执行过程:

  1. 在Linux Shell中输入./hello,在后面跟上需要的三个参数以执行hello;
  2. 子进程与执行:shell进程调用fork函数创建一个用来运行hello程序的子进程, 在子进程中通过调用execve函数来加载并运行hello。在execve加载了hello之后,它调用第七章描述的启动代码。启动代码设置栈,并将控制转移给新程序的主函数。Hello主函数开始执行。
  3. 指令执行时通过上下文切换的机制,为hello进程分配时间片,hello进程在分配的时间片中执行;
  4. 运行时动态申请内存:printf会调用malloc向动态内存分配器申请堆中的内存。
  5. hello访问内存时采用的地址是虚拟内存,MMU通过多级页表、TLB将虚拟地址映射成物理地址,虚拟页只有第一次使用时才被换入内存。
  6. 程序运行结束后,shell父进程会回收子进程,此时hello结束了它的一生。
  7. Hello在运行时信号机制也发挥着作用。输入Ctrl-C、Ctrl-Z,hello都会接受相应的信号,从而采取不同的行为。

计算机系统的设计与实现凝聚着人类智慧的精华,其中也体现了许多朴素但非常实用的思想,如通过多级页表技术层层检索而节约空间、重定位算法结合程序计数器的PC相对寻址,简单直观而有效。简单的Hello程序在计算机系统上运行短短几十秒,牵扯到了计算机系统的各个部分,我也从中体会到了计算机硬件与软件相结合的精妙,以及Linux Shell、GCC的功能之强大之处。

当然,书本上的内容展现的也许已经不是现在的主流技术了,如ARM架构的CPU摒弃了段式管理机制,计算机系统的各项技术更新迭代速度非常之快,但这本书所学的内容是计算机系统的思想根基。

附件

  1. hello.c:hello的源代码;
  2. hello.i:预处理得到的文本文件,用来分析预处理器所做的工作;
  3. hello.s:编译得到的文本文件,用来分析汇编语言格式;
  4. hello.o:汇编得到的可重定位目标文件,包含重定位条目,指导链接器进行工作;
  5. hello.out:链接得到的可执行文件,可直接执行;

参考文献

[1]  Randal E.Bryant, Dazke R.O’Hallaron 深入理解计算机系统. 北京:机械工业出版社,2016.7

[2]  http://docs.huihoo.com/c/linux-c-programming/ C汇编Linux手册

[3]  http://nicephil.blinkenshell.org/my_book/index.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

HIT-ICS2022大作业(程序人生-Hello’s P2P) 的相关文章

  • redis性能测试

    redis性能测试 redis提供了一个性能测试工具redis benchmark 可以使用redis benchmark命令来了解redis的性能 redis benchmark q c 50 q 表示简化输出结果 c 50 表示有五十个
  • Android 的非阻塞 IO

    我目前正在尝试评估一个项目是否可以在Android上实现 我认为一个主要问题是 由于它是 P2P 客户端 因此我们必须在运行时保持大量连接打开 现在连接不会传输大量数据 它更像是一个消息系统 因此如果我们时不时地读取一条 64 字节的消息
  • 银行测试要求高吗?从业人员来为你解答!

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读1 5k次 点赞69次 收藏10次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 低代码是行业毒瘤?我不这么认为

    低代码是行业毒瘤 我不这么认为 1 什么是低代码 2 低代码的优缺点 3 你认为低代码会替代传统编程吗 4 如何入门低代码 5 常见的低代码平台 1 什么是低代码 低代码是一种可视化的应用开发方法 它允许用户通过较少的代码 以较快的速度来交
  • 程序员的养生之道

    程序员的养生之道 1 对程序员的初次印象 2 我的养生之道 2 1 规律作息 2 2 合理饮食 2 3 健康饮食 2 4 增强锻炼 2 5 心态平和 2 6 生活习惯
  • 外包干了5个月,技术退步太明显了。。。。。

    先说一下自己的情况 本科生生 18年通过校招进入武汉某软件公司 干了差不多4年的功能测试 今年国庆 感觉自己不能够在这样下去了 长时间呆在一个舒适的环境会让一个人堕落 而我已经在一个企业干了5个月的功能测试 已经让我变得不思进取 谈了2年的
  • MySQL安装

    MySQL安装 MySQL在MAC下安装 下载 brew install mysql mysql server 在support files下 启动服务 mysql server start windows启动命令 net start my
  • 从一个程序员的角度看东方甄选“小作文”事件

    最近东方甄选 小作文 风波愈演愈烈 开始小编和观众吵架 后面东方小孙本来想要平息风波 而 摔手机 和泄漏董宇辉薪资待遇有激起更大的风波 导致东方甄选粉丝每天都几万 几十万的下降 作为一个消费者 开始是不太能理解东方甄选的这些骚操作 东方甄选
  • 达芬奇18.6DaVinci ResolveStudio(Win/Mac)激活版

    DaVinci Resolve Studio 18是一款业界领先的视频后期制作软件 它集成了剪辑 调色 视觉特效 动态图形和音频后期制作等功能 为用户提供了完整的创作解决方案 该软件不仅适用于电影 电视和网页内容的制作 还广泛应用于广告 纪
  • Android、NSD/DNS-SD:NsdManager 不可靠的发现和 IP 解析

    在过去的几周里 Android 的 NSD 实现让我抓狂 从用户的角度来看 会出现以下问题 设备以完全不确定的方式发现彼此 如果我启动我的NsdManager基于应用程序 如果只涉及两个设备 它或多或少可以工作 如果第三个设备加入 它很少会
  • WebRTC:匹配最近的同行

    给定一个公共 IP 地址 对等点 A 和许多其他公共 IP 地址 IPv4 和 IPv6 地址的混合 列表 将对等点 A 的 IP 地址匹配的最简单方法是什么 n最近的对等点 而无需让对等点手动相互 ping 通以进行延迟基准测试 我认为使
  • C 语言文件读取全指南:打开、读取、逐行输出

    C 语言中的文件读取 要从文件读取 可以使用 r 模式 FILE fptr 以读取模式打开文件 fptr fopen filename txt r 这将使 filename txt 打开以进行读取 在 C 中读取文件需要一点工作 坚持住 我
  • C++ 中 const 和 constexpr 关键字解析:常量、函数和指针

    很多 C 的初学者看到 const 这个关键字的第一反应都是一头雾水 主要是因为 const 可 以出现在很多的位置 以及后面加入的 constexpr 更是常常感到困惑 今天就为大家一一解释出现它们的含义和以及作用 const 关键字 c
  • 《Spring 测试指南》:JPA、MockMvc 和 @SpringBootTest 详解

    测试 Spring 提供了一组测试工具 可以轻松地测试 Spring 应用程序的各个组件 包括控制器 服务 存储库和其他组件 它具有丰富的测试注释 实用程序类和其他功能 以帮助进行单元测试 集成测试等 JPA 测试 Spring JPA J
  • 深入了解 Python MongoDB 操作:排序、删除、更新、结果限制全面解析

    Python MongoDB 排序 对结果进行排序 使用 sort 方法对结果进行升序或降序排序 sort 方法接受一个参数用于 字段名 一个参数用于 方向 升序是默认方向 示例 按名称按字母顺序对结果进行排序 import pymongo
  • 深入了解 Python MongoDB 查询:find 和 find_one 方法完全解析

    在 MongoDB 中 我们使用 find 和 find one 方法来在集合中查找数据 就像在MySQL数据库中使用 SELECT 语句来在表中查找数据一样 查找单个文档 要从MongoDB的集合中选择数据 我们可以使用 find one
  • C# Break 和 Continue 语句以及数组详解

    C Break 它被用于 跳出 switch 语句 break 语句也可用于跳出循环 以下示例在 i 等于 4 时跳出循环 示例 for int i 0 i lt 10 i if i 4 break Console WriteLine i
  • 种子中的 DHT

    我正在编写一个 P2P 实现 我希望将其去中心化 然而我在掌握如何做时遇到了一些困难DHT https en wikipedia org wiki Distributed hash table在像 BitTorrent 这样的协议中是有效的
  • WCF 是否支持点对点实现?

    我正在尝试在 LAN 内实现点对点消息传递和文件共享实用程序 那么 WCF 支持 p2p 吗 有人尝试过通过 WCF 进行文件共享吗 是的 它确实 请参见如何在对等网络中设计状态共享 http msdn microsoft com en u
  • 为什么我在使用 WifiP2pManager 时总是显示 BUSY?

    我正在尝试使用 Wi Fi Direct 连接两个 Android 设备 在我的 HTC 手机 One SV 上它似乎可以工作 但在我的第二台设备 LG Optimus 4xhd 上它不起作用 在我的 onResume 函数中 我启动以下线

随机推荐

  • ChatGLM-6b本地安装手把手教学

    什么是ChatGLM 6B ChatGLM 6B 是一个开源的 支持中英双语的对话语言模型 基于 General Language Model GLM 架构 具有 62 亿参数 结合模型量化技术 用户可以在消费级的显卡上进行本地部署 INT
  • 关于windows update 无法更新,以及.NET4.0安装失败

    故障 打开 Windows Update 出现红色盾牌图标 点击 检查更新 出现 Windows Update 当前无法检查更新 因为未运行服务 您可能需要重新启动计算机 查看 Windows Update 服务 正常 查看 Backgro
  • Linux环境下安装notepad++

    Notepad 在linux下名字为Notepadqq Centos下安装方法 sudo wget O etc yum repos d sea devel repo http sea fedorapeople org sea devel r
  • 爬虫(一):Python网络数据采集(爬虫)概述

    专栏介绍 结合自身经验和内部资料总结的Python教程 每天3 5章 最短1个月就能全方位的完成Python的学习并进行实战开发 学完了定能成为大佬 加油吧 卷起来 全部文章请访问专栏 Python全栈教程 0基础 文章目录 专栏介绍 什么
  • hack the box—Lame

    扫描 还是老方法nmap fscan得到开放的端口和服务 nmap sV sC sT v T4 10 10 10 3 看到开了445 先来波ms17 010 发现失败 这里还开个21 并且可以知道版本号 直接搜索ftp漏洞 msf正好有对应
  • Qiskit API架构介绍(一)

    API是一组类 函数和数据结构 用于与设备和模拟器进行接口 并运行实验 Qiskit实验概述 Qobj中的实验序列定义了运行在后端backend上的量子操作 单个Qobj定义了一批要并发运行的实验 即 Qobj中每个实验按列出的顺序运行一个
  • ttl一会255一会64_Ping TTL 的值越小越好?不对!

    我们在使用Ping命令的时候 通常关注的是 时间 这个值 忽略 TTL 这个值 但是细心的人会发现 TTL的值不是每次Ping都一样 也不是Ping每个域名都一样 这是什么原因呢 TTL 又是什么意思呢 可能不少人认为 TTL 的值越小越好
  • java8 sum_lambda – Java 8流由3个字段组合并按sum和count聚合产生单行输出

    前提 class Product public String name public String category public String type public int id todo implement equals toStri
  • 智能合约-Solidity官方文档(1)

    写在前面 HiBlock区块链社区成立了翻译小组 以太坊中文社区 翻译区块链相关的技术文档及资料 本文为solidity官方文档翻译的第一部分 智能合约概述 特发布出来邀请solidity爱好者 开发者做公开的审校 您可以添加微信baoba
  • CSDN城市开发者联盟、C友会期待你的加入

    文章目录 课前小差 chatGPT CSDN中的持续学习 23年原力计划 C友会 CDC 如何关联本地的开发者 写在最后 课前小差 哈喽 大家好 我是几何心凉 这是一份全新的专栏 唯一得到CSDN王总的授权 来对于我们每周四的绿萝时间 直达
  • matlab 改变图片的长宽,Matlab怎么调整图片的大小,使它成为特定的长宽

    满意答案 whymhm 推荐于 2017 12 15 采纳率 53 等级 6 已帮助 2557人 一般而言 只需对目标图像进行图形句柄对象和坐标轴句柄对象进行操作即可 MATLAB中分别用gcf和gca表示 如 set gcf unit c
  • 转载:Beginning WF 4.0翻译——第一章(创建一个简单的工作流)续二

    关于工作流设计器 即使是很简单的工作流设计 你可能都很难去观看整个工作流图形 幸运的是 设计器有一些非常有用的工具区帮助我们在一个大的工作流上去工作 在设计器的右上角 点击Collapse 收缩 链接 工作流图会如图Figure1 20所示
  • C语言编写图形界面

    文章目录 环境 配置环境 使用库 基础概念 句柄 程序的入口 创建窗口 定义窗口类 注册窗口类 创建窗口 完整代码 运行效果 环境 使用的是VSCode MinGW 配置环境 VSCode写C语言的环境就不讲了 就说一下本篇文章编译的条件吧
  • 复习Ajax

    ajax简介 ajax全称为Asynchronous JavaScript And XML 就是异步的js和XML ajax不是一个新的编程语言 而是一种将现有的技术组合在一起使用的新方式 ajax特点 通过ajax可以在浏览器中向服务器发
  • ubuntu CMake中的set指令详解

    遇到一个CmakeList txt里面出现的一行 SET WORKSPACE DIR ENV HOME workspace 不知道这个 ENV HOME 到底指的什么路径 查阅了一些资料 也没能理解 只知道是cmake文件独有的路径赋值方式
  • mysql中or详细使用方式(Mysql之and和or混合使用) MES

    mysql中or详细使用方式 Mysql之and和or混合使用 在mysql中 经常会遇到这样的情况 在写条件语句where时 可能会同时有多个条件的 或 或者 与 但经常会达不到效果 经百度 本人发现一个where语句中同时出现条件的 与
  • 两台电脑通过网线共享文件

    参考博客 https blog csdn net qq 38161654 article details 80865241 谢谢 1 用一根网线把两台电脑连接起来 2 关闭两台电脑的防火墙 具体操作如下 Windows Defender 防
  • 华为OD机试 Java 实现【扑克牌大小】【牛客练习题 HJ88】,附详细解题思路

    一 题目描述 扑克牌游戏大家应该都比较熟悉了 一副牌由54张组成 含3 A 2各4张 小王1张 大王1张 牌面从小到大用如下字符和字符串表示 其中 小写joker表示小王 大写JOKER表示大王 3 4 5 6 7 8 9 10 J Q K
  • pytorch 卸载_windows安装cuda和cudnn以及pytorch+卸载

    查看cuda版本号 1 首先需要进入pytorch官网查看一下需要安装的pytorch版本适配的cuda版本号 网址如下所示 PyTorch pytorch org 如图所示 官网默认显示最新版本的PyTorch 点击下面的链接 可以安装一
  • HIT-ICS2022大作业(程序人生-Hello’s P2P)

    计算机系统 大作业 题 目 程序人生 Hello s P2P 专 业 计算机科学与技术 学 号 班 级 学 生 指 导 教 师 计算机科学与技术学院 2022年5月 摘 要 一个简单的Hello程序 其生命周期的整个P2P与020过程需要计