计算机系统 大作业

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业       未来技术(人工智能)                

学     号       2021113694                

班     级        21wl022                    

学       生         陈俊乐          

指 导 教 师          史先俊             

计算机科学与技术学院

2023年4月

摘  要

本论文以一个简单的Hello程序为例,深入探讨了计算机系统的设计与实现,从预处理、编译、汇编、链接、进程管理、存储管理和IO管理等方面,逐一分析了HELLO程序所经历的过程。通过分析HELLO程序在不同阶段的中间结果和实现方法,让我们认识到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的整个过程。

P2PFrom Program to Process

1.预处理:预处理器(cpp)根据以#开头的命令,修改原始的C程序,例如展开#include和#define等语句,得到hello.i修改了的源程序。

2.编译:编译器(ccl)将预处理后的hello.i程序翻译成汇编语言,得到一个hello.s汇编程序。

3.汇编:汇编器(as)将汇编语言翻译成机器语言指令,打包成可重定位目标文件hello.o

4.链接:链接器(ld)将多个可重定位目标文件和库文件合并成一个可执行目标文件(二进制)hello.

5.执行:shell输入执行命令(./hello),为hello程序fork一个进程,最终实现P2P过程。

020From Zero-0 to Zero-0

1.加载:当用户在shell中输入./hello命令时,shell会调用fork()函数创建一个子进程,并调用execve()函数在子进程中加载hello程序,替换当前进程的映像、数据、堆栈和环境变量。当shell在子进程中调用execve(“./hello”, argv, envp)时,它会把子进程的虚拟地址空间清空,并把hello程序的可执行文件加载到新的虚拟地址空间中。execve还会设置好程序计数器(PC)和栈指针(SP),使得子进程可以从hello程序的入口点开始执行

2.执行:当子进程开始执行时,CPU会为它分配时间片,并按照程序计数器指示的地址取出指令并执行。hello程序的主要功能是打印出“Hello, world!”字符串,并等待用户输入一个字符。为了实现这些功能,它会调用一些库函数,如printf()、sleep()、getchar()等。这些函数都需要通过系统调用与操作系统交互,例如请求输出设备、设置定时器、读取输入设备等。系统调用会触发异常控制流,使CPU从用户模式切换到内核模式,并执行相应的内核例程。当内核例程完成后,CPU会返回到用户模式,并继续执行Hello程序。

3.退出:当hello程序执行完毕后,它会返回一个退出码给操作系统,并调用exit()函数终止自己。exit()函数会关闭所有打开的文件描述符,并释放所有动态分配的内存。然后,它会发送一个SIGCHLD信号给父进程(即shell),通知它子进程已经结束。父进程会调用wait()函数来回收子进程的资源,并显示出提示符。至此,hello程序在内存中的所有痕迹都被清除了。

1.2 环境与工具

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

1.2.1 硬件环境

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

1.2.2 软件环境

Windows10 64位;VirtualBox/Vmware 11以上;Ubuntu 20.04 64位;

1.2.3 开发工具

Visual Studio 2019 64位以上;vscode 64位;vi/vim/gedit+gcc

1.2.4 调试工具

        gdb

      

1.3 中间结果

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

hello.i :预处理程序(文本)

hello.s :汇编程序(文本)

hello.o :可重定位目标程序(文本)

hello :链接后所得的可执行二进制文件

1.4 本章小结

本章介绍了hello的p2p,从预处理,编译,汇编,链接形成可执行文件,以及执行结束以后的退出回收过程,同时列出了本次实验的环境及开发调试工具,为后续实验做铺垫。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

编译预处理的概念是指在编译源代码之前,由预处理程序对源代码中的预处理指令(以#开头的命令)进行解释和处理,从而生成一个新的源文件供编译器使用。在这个过程中hello.c程序会被预处理为hello.i,编译预处理的作用主要有以下几个方面:

(1)宏定义:用一个标识符来表示一个字符串,可以是常量、表达式、格式串等,可以提高代码的可读性和可维护性。

(2)文件包含:用来把多个源文件或头文件连接成一个源文件进行编译,可以实现代码的复用和模块化。

(3)条件编译:用来按照不同的条件编译不同的程序段,可以生成不同的目标代码,适应不同的环境和需求。

2.2在Ubuntu下预处理的命令

预处理命令:cpp hello.c > hello.i

图2.2

2.3 Hello的预处理结果解析

在预处理阶段,hello.c中的#include<stdio.h>,#include <unistd.h>,#include <stdlib.h>中的stdio.h,unistd.h,stdlib.h被预处理器读取,并插入到程序的文本中,得到hello.i的文件, hello.i文件会去除hello.c文件的注释。hello.i文件中包含了很多以#开头的行,这些行是预处理器添加的行号和文件名信息,用于编译时产生调试用的行号和错误警告行号,同时hello.i末尾包含hello.c的源程序。

如下图所示:

图2.3.1.hello.i中include的一些头文件

图2.3.2.hello.i末尾包含hello.c源程序

2.4 本章小结

本章介绍了预处理的概念及作用,同时展示了cpp如何将hello.c变成hello.i的命令行执行过程,以及对hello.c预处理文件hello.i文件内容的解析。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译的概念:编译器ccl从预处理文件(.i文件)产生汇编程序(.s文件)的过程,如编译器ccl将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序。

编译的作用:将人类容易理解和编写的高级语言转换成计算机能够直接执行的低级语言,从而实现人机交互和程序运行。编译还可以对源代码进行优化,提高目标代码的执行效率和空间利用率。编译还可以检查源代码的正确性和合法性,发现并报告语法错误和语义错误。

       

3.2 在Ubuntu下编译的命令

命令:gcc -S hello.i -o hello.s

图3.2

3.3 Hello的编译结果解析

3.3.1 hello.s初始部分(hello.s的结构)解析

  •   .file "hello.c" 定义了该汇编代码对应的原始C源文件名为"hello.c"。
  • .text 定义了接下来的指令都属于代码段。
  • .section .rodata 定义了接下来的指令都属于只读数据段(.rodata段)。
  • .align 8 定义了该字符串字面值的内存位置需要进行8字节对齐。
  • .LC0 是一个标签符号(Label Symbol),该字符串字面值的内存位置可以通过这个符号引用。
  • .string 定义了该字符串字面值的内容,是一串用16进制表示的Unicode编码。
  • .LC1 是另一个标签符号,用于引用第二个字符串字面值。
  • .globl main 定义了main函数在全局范围内是可见的(global)。
  • .type main, @function 定义了main函数是一个函数(function)类型的符号。
  • main: 定义了一个标签符号,表示main函数的入口点。

开头部分用于定义程序的不同部分应该被放置在内存的哪些段中。

图3.3.1

3.3.2 数据部分解析

(1) 常量

       ①对于hello.c中if(argc!=4)的常量4,在hello.s表示如下:

     àààà         

       将栈中的argc与4比较,如果等于则跳转到.L2,如果不等于,就继续。

           ②对于exit(1),终止程序进行,1表示程序退出状态码,hello.s表示如下:

将常量1存在%edi寄存器中,调用exit函数。

③对于for(i=0;i<5;i++),i<5等效于i<=4,4这个常量在hello.s表示下:

比较i是否小于等于4,如果小于等于则跳转到.L4。

   ④字符串常量

        printf的字符串常量存在.LC0,.LC1中,在hello.s表示如下:

      

       (2)变量

①全局变量

已初始化的全局变量被存储在.data节中,它的初始化没有汇编指令,而是直接完成的。

           ②局部变量i:将i初始化为0,如下图所示,通过movl指令将局部变量i放在-4(%rbp)位置。

                    

3.3.3赋值操作

汇编语言赋值操作是指把一个数据源的值复制到一个数据目的地。数据源和数据目的地可以是寄存器,内存地址,立即数或者标签。汇编语言中最常用的赋值操作的指令是mov,它的格式是mov <源>, <目的值>,它的含义是把源的值复制到目的值。如下图hello.s中,把0赋值给-4(%rbp)。

3.3.4类型转换

atoi函数的功能是把argv[3]这个字符类型转换成一个整数,并返回这个整数。在hello.s中通过atoi(argv[3])进行类型转换,将字符类型(char)转化为整型(int)。

3.3.5算术操作

对于hello.c中for(i=0;i<5;i++)的i++执行i的自增操作,在hello.s中的.L4 for循环内部的末尾执行-4(%rbp)中的值加1,在hello.s表示如下图所示:

3.3.6关系操作,控制转移操作

(1)hello.c中的if(argc!=4),判断输入的参数是否为4个(关系操作),如果是则跳转到.L2(控制转移操作),在hello.s中如下所示:

(2)hello.c中的for(i=0;i<5;i++),中i<5为关系操作,判断i是否小于5,在hello.s中等效于判断i是否小于等于4(关系操作),如果小于等于4则跳转到.L4(即循环内部)(控制转移操作),hello.s对应部分如下图所示:

      

3.3.7数组/指针/结构操作

(1)hello.c中的int main(int argc,char *argv[])的argv为指针数组,argv[0] 的值为 ./hello,即程序的名称和路径,argv[1],argv[2],argv[3]为输入的学号,姓名,秒数。在hello.s中,-32(%rbp)表示数组的首地址,add $16, %rax(蓝色框)表示argv[2],

add $8, %rax(红色框)表示argv[1],add $24, %rax(黑色框)表示argv[3]。

3.3.8 函数操作

Linux x86-64架构下函数调用主要使用callret指令来实现函数的跳转和返回,使用寄存器或者栈来传递参数和返回值,使用栈来保存局部变量和临时数据。(1)调用函数的指令是call,它会把当前的指令地址压入栈中,然后跳转到函数的起始地址。在hello.s中使用的函数有main函数(全局函数)、puts函数、printf函数、exit函数、sleep函数、atoi函数、getchar函数。main函数的参数为argcargv;两次printf函数的参数分别是两个字符串;exit函数的参数是1sleep函数的参数为atoi(argv[3])

  

 

(2)函数的参数可以通过寄存器或者栈来传递,一般情况下,前六个整数或者指针类型的参数通过寄存器%rdi, %rsi, %rdx, %rcx, %r8, %r9来传递,其他类型的参数或者更多的参数通过栈来传递。

3.4 本章小结

在本章中介绍了编译的概念与作用,并基于hello.s讲述了hello.s的结构,以及在hello.s中的数据,赋值操作,类型转换,关系操作,数组操作,函数操作等等。可以看出,经过ccl编译器,源程序变成了更加低级,底层的汇编程序hello.s。

第4章 汇编

4.1 汇编的概念与作用

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

    汇编的作用:汇编语言可以直接对应机器语言指令集,因此执行速度快,效率高,代码体积小。 汇编语言通常用于系统程序的核心部分,以及与系统硬件频繁打交道的部分,如操作系统、驱动程序、动态链接库等。汇编语言还可以用于软件的加密和解密、计算机病毒的分析和防治,以及程序的调试和错误分析等方面。

4.2 在Ubuntu下汇编的命令

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

图4.2

4.3 可重定位目标elf格式

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

4.3.1典型的ELF可执行目标格式

图4.3.1

4.3.2查看hello.o的文件格式

使用file命令查看hello.o的文件类型,确认它是一个可重定位的ELF文件。

图4.3.2

4.3.3 hello.o ELF头部信息

使用readelf -h命令查看hello.oELF头部信息,了解它的基本属性。如下图所示,ELF头以一个16字节的序列(7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00)开始,这个序列描述了生成该文件的字的大小和字节顺序,ELF头剩下部分包含帮助链接器语法分析和解析目标文件的信息。

图4.3.3

4.3.4节表信息

       节头部表描述了不同节的位置和大小,其中目标文件中每个节都有一个固定大小的条目。具体的描述包括节的名称、类型、地址和偏移量等,如下图所示,在hello.o中有14个节表头,开始的偏移量是0x420。

图4.3.4

4.3.5 重定位信息

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

(2)代码的重定位条目放在.rela.text中,已初始化数据的重定位条目放在.rel_data中。下面是Elf重定位条目。

typedef struct{

long offset;        /* 需要被重定位引用的节偏移 */

long type:32,     /*重定位类型*/

symbol:32;               /*索引符号表 */

long attend;       /*重定位表达的符号常数部分*/

}Elf64_Rela;

(3)使用readelf -r命令查看hello.o的重定位信息,了解它有哪些重定位项,每个重定位项的偏移、类型、符号等。如下图所示,可以看出.rela.text的偏移量为0x2d0,且包含8个条目。图中列出了八个可重定位条目的偏移量,信息,类型,符号值,符号名称+加数。其中R_X86_64_PC32表示32PC相对地址,R_X86_64_PLT32表示32PC相对地址到过程链接表(PLT),符号值:表示符号定义的运行时地址,如果符号未定义,则为0。符号名称:表示符号的名称,如果符号未定义,则为节名或空。加数:表示一个有符号常数,用于调整符号引用的值。.rela.eh_frame包含一个条目。

图4.3.5

4.3.6 符号表

symtab是一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。如下图所示 .symtab包含11个条目。

图4.3.6

4.4 Hello.o的结果解析

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

对比hello.o的反汇编和hello.s,如下图,我们得出一些结论。

图4.4.1 hello.o的反汇编

图4.4.2 hello.s

  1. 操作数表示

hello.o的反汇编的操作数是十六进制,而hello.s的操作数是十进制。

  1. 全局变量访问

在hello.s中,全局变量通过.LC0(%rip)进行访问;在hello.o反汇编文件中,通过0x0(%rip),全局变量需要重定位,需要以0x0进行初始化并添加重定位条目。

hello.o反汇编

hello.s

  1. 函数调用

在hello.s中call <function>PLT(过程链接表(PLT)是一种重定位条目,用于在ELF格式的可执行文件中,实现位置无关的函数调用),在hello.o的反汇编中call后面包含重定位条目的信息。

  1. 分支转移

分支转移: hello.s文件中分支转移是使用段名称进行跳转的,而hello.o反汇编文件中分支转移是通过地址进行跳转的。

hello.s通过段名称.L2跳转

hello.o反汇编文件通过地址跳转

总结:机器语言是机器能直接识别的程序语言或指令代码,用二进制代码表示,每一条指令对应一个操作码和一个或多个操作数。

汇编语言是机器语言的一种符号表示,用助记符代替二进制代码,更容易识别和记忆。每一条汇编指令对应一条机器指令。

汇编语言和机器语言之间的映射关系可以简单理解为一一对应的关系。汇编编译器将汇编指令转换为机器指令,反汇编器将机器指令转换为汇编指令。

4.5 本章小结

本章主要介绍了汇编的概念和作用,以hello.o的ELF格式为例,解释了Elf的结构,hello.o ELF格式的头部信息,节表信息,重定位信息和符号表,最后比较了hello.o的反汇编和hello.s的区别。

(第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.2

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

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

5.3.1 ELF的头信息

              命令:readelf -h hello

如下图所示,ELF头描述文件的总体格式。它还包括程序的入口点,即程序运行时要执行的第一条指令的地址。

图5.3.1

5.3.2 节头部表

命令:readelf -S hello

查看hello Elf格式的节头部分,其包含了文件中出现的各个节的语义(节的名称Name、类型Type、地址Address、偏移量Offset及大小Size等),如下图所示,hello的elf格式有27个节头,而hello.o的elf格式只有14个节头。

图5.3.2

5.3.3 重定位节信息

              命令:readelf -r hello

      使用readelf -r命令查看hello的重定位信息,了解它有哪些重定位项,每个重定位项的偏移、类型、符号等。如下图所示,两个重定位节:'.rela.dyn' '.rela.plt'

'.rela.dyn' 包含了2个重定位项。每个重定位项都包含以下信息:偏移量,信息,类型,符号值:符号在运行时的值,符号名称 + 加数。

'.rela.dyn' 用于动态链接,重定位项中包含 __libc_start_main gmon_start 这两个符号。

'.rela.plt' 包含了6个重定位项,每个项都与一个动态库函数相关联。在链接时,这些函数的地址被动态解析并填充到这些项中。'.rela.plt' 用于调用 putsprintfgetcharatoiexit sleep 等函数。当这些函数被调用时,重定位表中相应的项将被用于解析和填充这些函数的地址。

图5.3.3

5.3.4 符号表

              命令:readelf -s hello

      symtab是一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。如下图所示 .symtab包含26个条目,.dymsym包含9个条目。

图5.3.4

5.4 hello的虚拟地址空间

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

虚拟地址空间从0x401000开始,到0x402000结束,这之间的每一节对于5.3的节头,如0x401000对应.init。

图5.3.5

5.5 链接的重定位过程分析

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

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

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

hello是源代码文件,需要经过编译、汇编、链接等多个步骤才能生成可执行文件。hello.o是经过编译和汇编后生成的目标文件,它还需要链接才能生成可执行文件。

通过比较hello和hello.o的反汇编结果,如下图所示,可以发现它们的不同点:

  1. hello.o包含了许多重定位项目,而hello没有。这是因为hello.o是编译和汇编后的中间文件,其中包含了许多符号(如函数、变量等)的地址,这些地址需要在链接时进行重定位,而hello则是已经经过链接后的可执行文件,其中包含了重定位后的地址,不需要再进行重定位了。
  2. hello.o中的指令地址是相对地址,需要在链接时进行重定位;而hello中的指令地址是绝对地址,已经被重定位好了。
  3. 链接后函数数量增加,在hello的反汇编中,多出了.plt,puts@plt,printf@plt,getchar@plt,exit@plt,sleep@plt等函数的代码。
  4. 在开头hello多了.init,.fini,.plt,.plt.got节,分别是程序初始化执行的代码,程序终止时需要执行的代码,动态链接中的过程连接表,动态链接中的全局偏移表。

图5.5.1 hello的反汇编

图5.5.2 hello.o的反汇编

对于hello.o中的重定位项目,可以通过反汇编结果中的R_X86_64_PC32和R_X86_64_PLT32等标记来进行分析

总之,链接的过程是链接器会根据每个符号的类型(如函数、变量等)和链接方式(静态链接或动态链接)来进行重定位。对于每个需要重定位的地址,连接器会找到对应的符号的地址,然后根据符号的类型和链接方式来进行重定位。

5.6 hello的执行流程

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

子程序名

程序地址(16进制)

hello!_start

4010f0

libc. so.6! L ibc start main

7f164d629dc0

hello!printf@plt

401040

hello!sleep@plt

401080

hello!getchar@plt

401050

hello!exit@plt

401070

图5.6

5.7 Hello的动态链接分析

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

动态链接的基本思想是把程序按照模块拆分为彼此相互独立的部分,在程序运行时才把它们链接在一起形成一个完整的程序,不像静态链接一样把所有程序模块都链接成一个单独的可执行文件。

动态链接使我们在调用一个共享库定义的函数可以在运行时找到函数的地址。但是在调用时编译器没办法预测这个函数(共享库定义)的运行时地址,因为定义它的共享模块可以在运行时加载到任何位置。但是GNU编译系统通过延迟绑定技术来解决这个问题,将过程地址的绑定推迟到第一次调用该过程中。

延迟绑定通过GOT和PLT实现,如果一个目标模块调用定义在共享库中的任何函数,那么他就有自己的GOT和PLT。

根据hello的ELF文件可知,.got.plt的其实位置为0x404000。对比运行前后,发现少量字节被修改,此处修改为动态链接器在解析函数地址时会用到的信息。

图5.7.1 运行前

图5.7.2 运行后

5.8 本章小结

本章介绍了链接的概念与作用,通过链接形成可执行目标文件hello,查看了hello的ELF格式,并与hello.o的ELF格式进行对比,查看了hello的虚拟地址空间,分析了链接的重定位过程和hello的执行过程以及动态链接分析。

(第51分)

6章 hello进程管理

6.1 进程的概念与作用

进程的概念:进程是程序的一次执行过程,它是一个程序及其数据在处理机上顺序执行时所发生的活动。进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单元。

进程的作用:进程的作用与其给应用程序提供的两个关键抽象有关,1.一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器2.一个私有的地址空间,它提供一个假象,好像我们的程序独占地使用内存系统。

引入进程后才使得处理机利用率和内存利用率大大提高。它是资源分配和调度的基本单位,是操作系统结构的基础。

6.1

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

Bash Shell 是一个命令解释器,它在操作系统的最外层,负责用户程序与内核进行交互操作的一种接口,将用户输入的命令翻译给操作系统,并将处理后的结果输出至屏幕。它可以提供命令补全,命令编辑和命令历史等功能。它还包含了很多C Shell和Korn Shell中的优点,有灵活和强大的编辑接口。

壳Shell-bash的处理流程大致如下:

1.当用户输入一个命令后,按下回车键,壳Shell-bash会先检查命令是否为内置命令,如果是,则直接执行该命令。

2.如果不是内置命令,则壳Shell-bash会在环境变量PATH指定的目录中查找该命令对应的可执行文件,如果找到,则执行该文件。

3.如果没有找到,则壳Shell-bash会报错,提示命令不存在或无法执行。

4.在执行命令之前,壳Shell-bash会对命令行进行展开和引号处理,以便正确地解析命令的参数和选项。

5.在执行命令之后,壳Shell-bash会根据命令的返回值设置一个特殊变量$?,表示命令的执行状态,0表示成功,非0表示失败或错误。

6.3 Hello的fork进程创建过程

Hello的fork进程创建过程是指使用fork()函数来创建一个子进程,该子进程会复制父进程的用户级虚拟地址空间,包括代码和数据段、堆、共享库以及用户栈。子进程还会获得与父进程任何打开文件描述符相同的副本。fork()函数调用一次,但返回两次,子进程返回0,父进程返回子进程的PID。父子进程是两个独立的进程,各自拥有一份代码,由操作系统进行调度,不确定谁先执行。父子进程的存储空间是独立的,修改其中一个不会影响另一个。

6.4 Hello的execve过程

int execve(char *filename, char *argv[], char *envp[]);

Hello的execve过程是指使用execve()函数来执行一个可执行文件,该函数会替换当前进程的代码、数据、堆和栈,使其变成新的程序。execve()函数的参数包括需要执行的程序(通常是argv[0]/filename)、参数argv、环境变量envp。execve()函数的过程如下:

1.删除已存在的用户区域(自父进程独立)。

2.映射私有区:为Hello的代码、数据、.bss和栈区域创建新的区域结构,所有这些区域都是私有的、写时才复制的。

3.映射共享区:为Hello所需的共享库创建新的区域结构,这些区域是共享的、只读的。

4.设置用户栈:将argv和envp复制到用户栈中,并设置栈指针寄存器。

5.跳转到程序入口:将程序计数器寄存器设置为Hello的入口地址,并开始执行。

6.5 Hello的进程执行

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

进程调度是操作系统中的一个重要组成部分,其主要作用是决定当前可执行进程的执行顺序和时间。当系统中存在多个进程时,CPU资源就需要在这些进程之间进行切换,以便这些进程都能得到相应的CPU时间,从而实现并发执行。

进程调度的过程可以简单地概括为如下几个步骤:

1.选择可执行进程:从就绪队列中选择一个可执行进程,使其进入运行状态。

2.切换进程上下文:将当前进程的上下文信息(包括程序计数器、寄存器状态、堆栈指针等)保存起来,并将新进程的上下文信息加载到CPU中。

3.执行进程:让新进程开始执行。

4.时间片用完切换:如果当前进程的时间片用完了,则将该进程放回就绪队列中,重新选择可执行进程,并重复上述步骤。

在执行上述步骤时,操作系统会根据进程的优先级、调度算法、进程状态等因素来决定选择哪个进程。

用户态和核心态转换是指进程从用户态切换到内核态或从内核态切换到用户态的过程。当进程需要执行某些只有内核才能执行的操作时,需要进行用户态和核心态之间的切换。在Linux中,可以使用系统调用来实现用户态和内核态之间的切换。当进程需要进行系统调用时,会触发从用户态到内核态的切换。

图6.5

对于给定的hello.c程序,它会输出一段简单的信息,并在每次输出后等待一定的时间,然后再进行下一次输出。当程序运行时,会先进行命令行参数的检查,如果参数不符合要求则输出错误信息并退出。如果参数符合要求,则进入循环体,重复输出hello信息并等待一定的时间,直到输出了5次后程序结束。

在操作系统中运行该程序时,操作系统会为该程序创建一个进程,分配一定的资源(如内存、文件描述符等),并将该进程放入就绪队列中等待执行。当调度程序选择该进程执行时,会切换到该进程的上下文,执行该进程中的指令。在程序执行过程中,如果需要进行系统调用,则会触发用户态和核心态之间的切换。在循环体中,程序会先输出Hello信息,然后使用sleep函数等待一定的时间。当等待时间结束后,程序会继续执行下一次循环,直到循环次数达到5次后程序结束。在程序结束时,操作系统会回收该进程所占用的资源,并将该进程从系统中移除。

6.6 hello的异常与信号处理

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

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

6.6.1 异常分类

异常可以分为四类:中断(interrupt)、陷阱(trap)、故障(fault)和终止(abort)。下图的表对这些类别的属性做了小结。

6.6.2 信号

一个信号就是一条小消息,它通知进程系统中发生了一个某种类型的事件。

在Hello程序执行过程中,可能会接收到如下几种信号:

1. SIGINT信号(中断信号):通常由用户在终端上按下Ctrl+C触发,用于请求进程中断。

2.SIGQUIT信号(退出信号):通常由用户在终端上按下Ctrl+\触发,用于请求进程退出并生成core文件。

3.SIGTSTP信号(暂停信号):通常由用户在终端上按下Ctrl+Z触发,用于请求进程暂停。

6.6.3 处理过程

        1.在执行过程中,按回车键,如下图所示,相邻输出的hello间隔多了的行数为按回车的次数,最后程序正常执行结束,进程会自动退出,并由操作系统回收其占用的系统资源。

图6.6.3.1

       2.在执行过程中,按ctrl+z键,进程停止,此时输入ps命令查看,发现hello进程被挂起。输入pstree命令,以树状图显示进程间的关系,输入jobs,可以知道hello进程已停止。fg重新激活hello进程,kill停止进程,fg无法重新激活进程。

图6.6.3.2停止hello进程

图6.6.3.4

输入pstree命令:以树状图显示进程间的关系

图6.6.3.5激活hello进程

图6.6.3.6 Kill终止进程

3.Ctrl+c终止进程,如下图所示。

图6.6.3.7

4.不断乱按,输入的字符会显示在屏幕上,不影响进程进行。

图6.6.3.8

6.7本章小结

本章介绍了进程的概念与作用,简述壳Shell-bash的作用与处理流程,介绍了fork,execve函数,介绍了hello进程执行过程以及异常与信号处理的过程。

(第61分)

7章 hello的存储管理

7.1 hello的存储器地址空间

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

在操作系统中,程序中的数据和代码需要在内存中执行,而内存中的数据存储方式有多种,其中包括逻辑地址、线性地址、虚拟地址和物理地址。

1.逻辑地址是由程序中的指令生成的地址,也称为“逻辑空间地址”,它是程序在逻辑上的地址,没有实际的物理位置,只是一个虚拟的地址。在hello程序中,printf语句中的字符串“Hello %s %s\n”就是一个逻辑地址,它表示在程序中的某个位置存储的字符串。

2.线性地址是逻辑地址通过段式存储管理方式映射到的地址,也称为“段地址”或“虚拟地址”。在操作系统中,内存被分为多个段,每个段都有一个起始地址,当程序中的逻辑地址需要被执行时,操作系统会将逻辑地址转换成线性地址。在hello程序中,逻辑地址被映射到了相应的段上,并被转换为线性地址。

3.虚拟地址是线性地址通过页式存储管理方式映射到的地址,也称为“页地址”。在操作系统中,线性地址会被划分成多个大小相等的页,每个页都有一个起始地址,当程序需要访问某个线性地址时,操作系统会将线性地址转换成虚拟地址。在hello程序中,线性地址被映射到相应的页上,并被转换为虚拟地址。

4.物理地址是虚拟地址通过内存管理单元(MMU)映射到的地址,也称为“实际地址”。在操作系统中,虚拟地址需要通过MMU映射到实际的物理地址,才能够被处理器访问到。在hello程序中,虚拟地址被映射到了相应的物理地址上,并被处理器访问到,例如打印字符串需要将其输出到控制台,就需要访问物理地址上对应的控制台输出设备。

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

Intel逻辑地址到线性地址的变换-段式管理是指在Intel平台下,使用分段机制将逻辑地址转换为线性地址的过程。逻辑地址是指机器语言指令中出现的内存地址,它由一个16位的段选择符和一个32位的段内偏移量组成。线性地址是指处理器可寻址的内存空间中的地址,它是一个32位或64位的无符号整数。段标识符是一个16位长的字段组成,称为段选择符,其中前13位是一个索引号,可以通过段标识符的前13位,直接在段描述符表中找到一个相应的段描述符。

分段机制将逻辑地址转换为线性地址的步骤如下:

首先给定一个完整的逻辑地址

1.看段选择描述符中的T1字段是0还是1,可以知道当前要转换的是GDT中的段,还是LDT中的段,再根据指定的相应的寄存器,得到其地址和大小,我们就有了一个数组了。

2.拿出段选择符中的前13位,找到相应的段描述符,获得基地址信息

3.把基地址加上偏移量就是要转换的下一个阶段的地址。

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

Hello的线性地址到物理地址的变换-页式管理是指在保护模式下,使用页表机制将Hello程序的线性地址转换为物理地址的过程。

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

页表就是一个页表条目(PTE)数组,虚拟地址空间中的每个页在页表中的一个固定偏移量处都有一个PTE。PTE是由一个有效位和一个n个字段组成的。有效位表明了该虚拟页当前是否被缓存在DRAM中。如果设置了有效位,那么地址字段就表示DRAM中相应的物理页的起始位置。

n位的虚拟地址包含两个部分:一个p位的虚拟页面偏移(VPO),一个n-p位的虚拟页号(VPN)MMU利用VPN选择适当的PTE,根据PTE,我们知道虚拟页的信息,VPO与PPO相同,因为虚拟页大小和物理页大小相同,所需要的偏移量位数也就相同。此时,物理地址就通过物理页号先找到对应的物理页,然后再根据物理页偏移找到具体的字节。如果虚拟页是已缓存的,那直接将页表条目的物理页号和虚拟地址的VPO串联起来就得到一个相应的物理地址。如果虚拟页是未缓存的,会触发一个缺页故障。调用一个缺页处理子程序将磁盘的虚拟页重新加载到内存中,然后再执行这个导致缺页的指令。

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

TLB(Translation Lookaside Buffer)和四级页表都是虚拟地址到物理地址的转换机制。它们的主要作用是将虚拟地址(Virtual Address,VA)转换为物理地址(Physical Address,PA),并提供快速访问的能力。

CPU产生虚拟地址VA,虚拟地址VA传送给MMU, MMU使用VPN高位作为TLBT和TLBI,向TLB中寻找匹配。如果命中,则得到物理地址PA。如果TLB中没有命中,MMU查询页表,CR3确定第一级页表的起始地址,VPN1确定在第一级页表中的偏移量,查询出PTE,以此类推,最终在第四级页表中找到PPN,与VPO组合成物理地址PA,添加到PLT。

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

以下格式自行编排,编辑时删除)在拥有三级缓存 (L1、L2、L3) 的系统中,物理内存访问的过程如下:

当 CPU 需要访问内存时,它会首先检查 L1 缓存中是否有该内存地址的副本。

如果 L1 缓存中没有该地址的副本,则 CPU 会检查 L2 缓存中是否有该地址的副本。

如果 L2 缓存中也没有该地址的副本,则 CPU 会检查 L3 缓存中是否有该地址的副本。

如果 L3 缓存中也没有该地址的副本,则 CPU 会直接访问主存 (物理内存),并将所需数据加载到 L3 缓存中,以便下次访问时可以更快地获取数据。

如果 CPU 需要修改内存中的数据,它会首先在 L1 缓存中进行修改。如果 L1 缓存中没有该地址的副本,则 CPU 会将该地址的副本加载到 L1 缓存中,并在 L1 缓存中进行修改。修改完成后,CPU 会将修改后的数据写回到 L2 缓存和主存中。

图7.5

7.6 hello进程fork时的内存映射

在执行fork系统调用时,操作系统会创建一个新的进程,称为子进程。子进程与父进程共享相同的代码和数据,但它拥有自己独立的内存空间和进程标识符 (PID)。

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

在子进程创建的新虚拟地址空间中,父进程的所有内存映射都会被复制到子进程中,但这些内存映射都是只读的。这样做的目的是为了防止父进程和子进程之间相互干扰,例如在父进程中的某个变量被修改后,子进程也会受到影响。因此,子进程需要将只读内存映射变成可写的内存映射,以便它可以在自己的内存空间中独立修改这些变量。

7.7 hello进程execve时的内存映射

exceve函数加载和执行程序Hello,需要以下几个步骤:

1.删除已存在的用户区域。

2.映射私有区域。为Hello的代码、数据、bss和栈区域创建新的区域结构,所有这些区域都是私有的、写时复制的。

3.映射共享区域。比如Hello程序与标准C库libc.so链接,这些对象都是动态链接到Hello的,然后再用户虚拟地址空间中的共享区域内。

4.设置程序计数器(PC)。exceve做的最后一件事就是设置当前进程的上下文中。

下图为execve(“a.out”,NULL,NULL)加载的映射图

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

在虚拟内存的习惯说法中,DRAM缓存不命中称为缺页(page fault)。图9-6展示了在缺页之前我们的示例页表的状态。CPU引用了VP 3中的一个字,VP 3并未缓存在DRAM中。地址翻译硬件从内存中读取PTE 3,从有效位推断出VP 3未被缓存,并且触发一个缺页异常。缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,在此例中就是存放在PP3中的VP4。如果VP4已经被修改了,那么内核就会将它复制回磁盘。无论哪种情况,内核都会修改VP4的页表条目,反映出VP 4不再缓存在主存中这一事实。

接下来,内核从磁盘复制VP 3到内存中的PP 3,更新PTE3,随后返回。当异常处理程序返回时,它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重发送到地址翻译硬件。但是现在,VP3已经缓存在主存中了,那么页命中也能由地址翻译硬件正常处理了。下图展示了在缺页之后我们的示例页表的状态。

7.9动态存储分配管理

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

动态内存分配是一种程序在运行时动态申请和释放内存的方式。动态内存分配管理的基本方法是通过调用malloc和free函数来实现。

malloc函数是动态分配内存的主要工具,它在内存中分配指定数量的字节,并返回一个指向这段内存的指针。在C语言中,malloc的函数原型为:

void *malloc(size_t size);

其中,size是需要分配的内存字节数。如果内存分配成功,则返回指向该内存块的指针;如果失败,则返回空指针NULL。

free函数用于释放动态分配的内存块。在使用完动态分配的内存块后,应该使用free函数将其释放,以便操作系统能够重新使用这些内存。在C语言中,free的函数原型为:

void free(void *ptr);

其中,ptr是指向需要释放的内存块的指针。释放内存块后,应该将指针设置为NULL,以避免出现野指针的情况。

分配器的具体操作过程以及相应策略:

放置已分配块:当一个应用请求一个k字节的块时,分配器搜索空闲链表。查找一个足够大可以放置所请求的空闲块。执行这种搜索的常见策略包括首次适配、下一次适配和最佳适配等。

分割空闲块:一旦分配器找到了匹配的空闲块,需要决定分配这个空闲块中多少空间。可以选择用整个块,但会造成额外的内部碎片;也可以选择将空闲块分割为两部分,第一部分变成已分配块,剩下的变成新的空闲块。

获取额外的堆内存:如果分配器不能为请求块找到空闲块,分配器通过调用sbrk函数,向内核请求额外的堆内存。分配器将额外的内存转化成一个大的空闲块,将这个块插到空闲链表中,然后被请求的块放在这个新的空闲块中。

合并空闲块:分配器释放一个已分配块时,要合并相邻的空闲块。分配器决定何时执行合并,可以选择立即合并或者推迟合并。合并时需要合并当前块和前面以及后面的空闲块。

7.10本章小结

虚拟地址是计算机系统中的一种抽象地址,它由 CPU 生成,用于访问计算机内存中的数据。在现代操作系统中,每个进程都有自己的虚拟地址空间,这使得每个进程可以看到自己独立的内存地址空间,从而保证了进程之间的内存隔离性和安全性。

本章详细介绍了hello的存储器地址空间,以及地址之间的转换,物理内存访问,内存映射,动态内存分配等等,也让我们看到强大,核心的虚拟内存。

(第7 2分)

8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件

设备管理:unix io接口

Linux的IO设备管理方法是指在Linux系统中进行输入输出操作的过程。它包括文件读写、网络通信、设备驱动等方面。在Linux系统中,IO操作是通过文件描述符来实现的,每个文件描述符都对应一个打开的文件、设备或网络连接。通过文件描述符,程序可以进行读写操作。

Linux的IO设备管理方法还涉及到一些重要的概念和组件,例如:

系统调用接口:是操作系统提供给用户程序使用的一组函数,例如open、close、read、write等。这些函数可以让用户程序通过文件描述符来访问不同类型的IO设备。

通用块层:是Linux内核中负责处理块设备(如磁盘)的一层抽象,它将用户程序发出的IO请求转换为bio(block input/output)结构,并将它们传递给下层的IO调度层。

IO调度层:是Linux内核中负责优化磁盘IO性能的一层组件,它会对通用块层传来的bio进行排序和合并,并且提供了多种IO调度算法,适应不同的场景。

块设备驱动层:是Linux内核中负责与具体的磁盘硬件交互的一层组件,它会将IO调度层传来的bio转换为请求(request)结构,并将它们发送给磁盘控制器。

8.2 简述Unix IO接口及其函数

Unix IO 接口是一组在 Unix 和类 Unix 系统中用于进行输入输出操作的标准接口。这些接口定义了一组函数,可以用于打开、关闭、读取和写入文件、管道、套接字等数据源或目标。

Unix IO 接口包含了多个函数,其中一些常见的函数包括:

1.open():用于打开文件或设备,并返回一个文件描述符,以供后续 IO 操作使用。

2.close():用于关闭已打开的文件描述符。

3.read():用于从文件描述符中读取数据,并将数据存储到指定的缓冲区中。

4.write():用于将指定缓冲区中的数据写入到文件描述符中。

5.lseek():用于移动文件指针到指定的位置,以便读取或写入数据。

6.ioctl():用于执行设备的特定操作,例如配置串口参数等。

7.select():用于监视多个文件描述符,以便在某个文件描述符准备好进行 IO 操作时进行通知。

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;

}

其实现原理是通过调用 vsprintf 函数将格式化字符串 fmt 和可变参数列表 arg 格式化成一个字符串 buf,并将其写入到标准输出中。

vsprintf 函数可以将格式化字符串和可变参数列表转换为一个字符串。在 printf 函数中,该字符串会被写入到标准输出中。但是,写入标准输出的操作需要进行系统调用才能完成。

write 是将buf中的i个元素写到终端的函数。Write内容如下

    write:

     mov eax, _NR_write

     mov ebx, [esp + 4]

     mov ecx, [esp + 8]

     int INT_VECTOR_SYS_CALL

int INT_VECTOR_SYS_CALL表示要通过系统来调用sys_call这个函数,用来显示格式化了的字符串。

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

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

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

8.4 getchar的实现分析

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

在 Unix/Linux 系统中,C 标准库中的 getchar 函数通常被实现为调用系统调用函数 read() 来从标准输入流 stdin 中读取一个字符。read() 函数将会被阻塞,直到有字符输入到 stdin 中才会返回。一般情况下,用户在终端中输入的字符会被发送给内核中的键盘中断处理子程序。

当用户在终端中按下一个键时,键盘控制器会将对应的扫描码(scan code)发送到 CPU。CPU 会触发一个中断,将控制权转交给操作系统内核中的中断处理程序,即键盘中断处理子程序。键盘中断处理子程序会读取扫描码并将其转换成 ASCII 码,然后将 ASCII 码保存到系统的键盘缓冲区中。

getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。下面是getchar函数的简单实现:

#include <stdio.h>

#include <unistd.h>

int getchar() {

    char c;

    int ret = read(STDIN_FILENO, &c, 1);

    if (ret == 1) {

        return (int)c;

    } else {

        return EOF;

    }

}

8.5本章小结

本章详细介绍了hello的IO管理,从Linux的IO设备管理方法说起,介绍了概念和组成部件,接下来简单描述了unix IO接口和函数,以及printf,getchar的实现,让我们更加清楚地了解了函数的低层调用。

(第81分)

结论

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

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

  1. Hello程序被以文本的形式输入到计算机中,成为程序源代码。

  1. Hello程序源代码经过预处理,处理掉预处理指令,生成预处理后的程序源代码。

  1. Hello程序预处理后的源代码被编译器编译,生成汇编代码。

  1. Hello程序汇编代码被汇编器汇编,生成可重定位目标文件。

  1. Hello程序可重定位目标文件被链接器链接,生成可执行文件。

  1. Hello程序被操作系统加载到内存中,成为一个进程,操作系统为该进程分配资源,如CPU时间片、内存空间等。

  1. Hello程序运行过程中,CPU不断地从内存中取出指令,解码并执行,完成程序的各项操作。

  1. Hello程序输出文字信息到显示器,需要通过操作系统的IO管理和显卡驱动程序等硬件操作,将信息传输到显示器上。

  1. Hello程序执行完毕后,操作系统回收该进程所占用的资源,如内存空间等。

我的深切感悟是计算机系统是一个庞大而复杂的生态系统,由各种组件、模块和子系统组成,它们相互协作、相互依存,共同完成计算机系统的各种功能。每个组件和模块都有自己的特殊功能和特点,同时也需要与其他组件和模块进行协同工作,才能发挥出整个系统的最大价值。因此,在设计和实现计算机系统时,需要注重整体性,同时也要注重各个部分的优化和协同工作。

创新理念:面向未来、注重创新、突破传统的设计思路。我们需要不断地挑战自己,尝试新的设计方法和实现方式,寻找创新的点子和思路,以应对未来计算机系统的挑战和需求。同时,我们也需要保持对传统设计思路的尊重和借鉴,吸取前人的经验和教训,不断地完善和优化计算机系统的设计和实现

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

附件

列出所有的中间产物的文件名,并予以说明起作用。

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

hello.c                                    源文件

hello.i                                    预处理得到的文件

hello.s                                    编译得到汇编语言文件

hello.o                                    可重定位目标文件

hello                                       可执行目标文件

        hello_objdump                       hello反汇编代码文件

参考文献

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

[1]https://www.eeeguide.com/linear-logical-address-and-physical-address-in-microprocessor/

[2]  Randal E.Bryant, David O'Hallaron. 深入理解计算机系统[M]. 机械工业出版社.2018.4

[3]  https://blog.csdn.net/forcj/article/details/117967945

[4]  https://www.cnblogs.com/pianist/p/3315801.html

[5]  KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.

[6]  CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值