哈尔滨工业大学盘算机体系大作业程序人生

打印 上一主题 下一主题

主题 1021|帖子 1021|积分 3063

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x





盘算机体系


大作业



题     目  程序人生-Hello’s P2P 
专       业       物联网工程                
学     号        2021111566               
班     级        2137301               
学       生         张昊            
指 导 教 师         吴锐              






盘算机科学与技术学院

2023年5月

摘  要

本文主要讲述了hello.c在编写完成后运行在Linux体系中的生命历程,运用相干工具进行分析程序预处理、编译、汇编、链接等过程在Linux下实现的原理,直到应用程序运行并竣事过程中的相干内容,包括对于进程、存储以及IO等方面的分析。;并先容了shell的内存管理,IO管理,进程管理等相干知识,了解假造内存、异常信号等内容。
关键词:盘算机体系;盘算机体系布局;hello.c;Linux                           








目  录


第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简介

P2P(From Program to Process):编写完成的hello.c文件,首先经过预处理器预处理天生hello.i文件;再经过编译器编译,天生汇编代码文件hello.s;再经过汇编器翻译成一个重定位目的文件hello.o;最后利用链接器将多个可重定位目的文件组合起来,形成一个可执行目的文件hello。


020(From Zero-0 to Zero-0):在执行hello程序之后,shell通过execve进行加载到假造内存中。之后程序载入物理内存,然后进入 main函数执行代码。然后在程序执行竣事后,进程保持停止状态,父进程进行接纳后,shell保持最初状态。
1.2 情况与工具

1.2.1 硬件情况
X64 CPU;2.11GHz;16GRAM;512G SSD
1.2.2 软件情况
Windows10 64位;Virtual Box 6.1;Ubuntu 20.04 LTS 64位
1.2.3 开发工具
CodeBlocks 64位;vi/vim/gedit+gcc;GDB;OBJDUMP;EDB
1.3 中心效果


文件的作用

文件名

预处理后得到的文本文件

hello.i

编译后得到的汇编语言文件

hello.s

汇编后得到的可重定位目的文件

hello.o

readelf读取hello.o得到的ELF格式信息

hello.elf

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

hello.asm

由hello可执行文件天生的.elf文件

hello1.elf

反汇编hello可执行文件得到的反汇编文件

hello1.asm


1.4 本章小结

本章对hello进行了总体的先容,分别对P2P和020的意思进行了表明,并给出了实行进行的软件情况,硬件情况,以及开发/调试工具,最后列出了在实行过程中天生的中心效果文件及其作用。



第2章 预处理

2.1 预处理的概念与作用

预处理的概念:
预处理阶段是编译过程的前置步调,它通过预处理器(cpp)根据以#字符开头的指令,对原始的C程序进行修改。例如,对于#include<stdio.h>,#include 指令要求预处理器读取体系头文件 stdio.h的内容,并将其直接嵌入到程序文本中。这样就天生了另一个C程序,通常以.i为文件扩展名。
预处理的作用:
预处理的作用主要可分为以下三部门:
(1) 宏睁开:预处理程序中的“#define”标识符文本,用现实值(可以是字符 串、代码等)替换用“#define”界说的字符串;
(2) 文件包罗复制:预处理程序中用“#include”格式包罗的文件,将文件的内 容插入到该命令所在的位置并删除原命令,从而把包罗的文件和当前源文 件连接成一个新的源文件,这与复制粘贴类似;
(3) 条件编译处理:根据“#if”和“#endif”、“#ifdef”和“#ifndef”背面的 条件确定必要编译的源代码。
2.2在Ubuntu下预处理的命令

命令:gcc -E -o hello.i  hello.c

2.3 Hello的预处理效果剖析



可以看出,经过编译处理后文件变为3091行,文件巨细大大增长,预处理器删除了源文件当中全部的注释,对源文件中的宏进行了宏睁开,将stdio.h stdlib.h unistd.h当中的内容插入到程序当中,同时也对#define相应的符号进行了替换。
以 stdio.h 的睁开为例:cpp首先移除指令#include <stdio.h>,并根据Ubuntu体系默认的情况变量查找 stdio.h,最终打开路径/usr/include/stdio.h下的stdio.h文件。如果stdio.h文件中存在#define指令,则按照上述流程继续递归睁开,直到所有#define指令都被表明替换掉,因此最终的.i 文件中不会出现#define 的。如果遇到大量的#ifdef #ifndef条件编译指令,cpp会根据条件值进行判断来决定是否执行此中的逻辑。此外,cpp还会进行删除程序中的注释和多余的空白字符等操作,并对一些值进行替换。
2.4 本章小结

本章主要先容了预处理的概念和作用,包括头文件的睁开、宏替换、去掉注释、条件编译等功能,以及Linux下预处理的两个指令。同时,本章还以Ubuntu体系下的hello.c文件为例,展示了预处理后得到的hello.i文件,并对其内容进行了剖析,深入地了解了预处理的过程和细节。

第3章 编译

3.1 编译的概念与作用

编译的概念:
编译过程是指编译器ccl通过词法分析、语法分析、语义分析和优化,将源程序文件 hello.i 转换为汇编程序文件 hello.s。在这个过程中,编译器将源程序中的每条正当指令翻译成等价的低级机器语言指令,并以文本的形式描述在 hello.s 中。       
    编译的作用:
编译的作用是将用高级语言编写的源代码转换为机器代码、字节码或另一种编程语言。编译的过程必要通过词法分析、语法分析、语义分析和优化等步调,检查源代码是否符合语法规则,并将其翻译成目的平台可以大概执行的格式。
3.2 在Ubuntu下编译的命令

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


编译后在同一目录下天生乐一个hello.s文件
3.3 Hello的编译效果剖析

hello.s文件的内容如下:


3.3.1文件说明
.file 指明源文件名
.text 代码段:用来存放程序代码的地区
.section 指示将代码分成若干个段
.rodata 用于维护只读数据
.align 8 地址对齐伪指令,用来指定符号的对齐方式
.string 字符串的存储位置
.global 用来让一个符号对链接器可见,可以供其他链接对象模块利用,告诉编译器后续跟的是一个全局可见的名字
.type 指定是对象范例或者函数范例
3.3.2数据

(1). 常量:
常量例如立即数:

立即数直接出如今代码段之中。

常量例如字符串:

     在.LC0和.LC1中都含有字符串常量
.LC0和.LC1部门是标签的一种,它们提供了一个符号名称,用于引用标签背面出现的字节。在本例中,LC1表示字符串“Hello %s %s\n”。GNU汇编程序依照一种约定,将以“L”开头的标签视为“局部标签”。在本例中,编译器利用它来引用特定目的文件专有的符号。在本例中,它表示一个字符串常量。
(2). 变量


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


  • 局部变量:
局部变量存储在寄存器或栈中。
例如:
在c程序中:

在汇编代码中:

注:局部变量i被存储在栈中-4(%rbp)的位置。

在这里我们可以看到在循环体中,局部变量i被初始化为0,经过比较后,决定进入或跳出循环,在循环体中的代码执行竣事后,加1。
3.3.3 赋值:
(1). 通过mov来实现赋值:
   
在这段代码中,汇编语言通过mov立即数的方式来对i进行赋值操作。
(2). 通过leal来实现赋值:

3.3.4 算术操作:
    汇编语言的算术指令是用于对数据进行简朴的加减乘除运算的指令。常见的算术指令有以下几种:
ADD:将一个字节变量与累加器相加,效果存放在累加器中。如果从第7位产生了进位,进位标志位CF会被置1,否则被清零。
SUB:将一个字节变量与累加器相减,效果存放在累加器中。如果从第7位产生了借位,借位标志位CF会被置1,否则被清零。
MUL:将两个字节变量相乘,效果存放在AX寄存器中。高8位存放在AH中,低8位存放在AL中。如果高8位为0,溢出标志位OF会被清零,否则被置1。
DIV:将一个字或双字变量除以一个字节变量,商和余数分别存放在不同的寄存器中。如果被除数是一个字,那么商存放在AL中,余数存放在AH中;如果被除数是一个双字,那么商存放在AX中,余数存放在DX中。如果除数为0或者商超过8位或16位,会产生除法溢出异常。
hello.s中出现了加,减两种操作
加:

减:

3.3.5 关系操作和控制转移
关系操作与控制转移一般是一起出现的。
例如如下代码:


       关系操作还出如今if语句的汇编之中,如:

它所对应的关系操作与控制转移如下:

为了判断argc是否即是4,首先执行cmpl指令,该指令将立即数4与argc的值进行比较,并根据比较效果设置条件码。然后执行je指令,该指令根据条件码的值决定是否跳转到L2地址。如果条件码表示后一个操作数与前一个操作数相等,则跳转到L2地址,否则继续执行下一条指令。
3.3.6 数组/指针/布局操作:
主函数main的参数中有指针数组char *argv[]。

在argv数组中,argv[0]指向输入程序的名称,argv[1]和argv[2]分别指向两个字符串。
C语言代码中的数组访问有argv[1],argv[2],argv[3],在汇编代码中访问这三个量都利用首地址加偏移量的方式。
如图:



3.3.7 函数操作:
函数的调用利用call指令,该指令会将当前的程序计数器压入栈中,并跳转到函数的起始地址。函数的参数可以通过寄存器或栈来传递,具体取决于函数的调用约定。函数执行完毕后,利用ret指令返回到调用者,该指令会从栈中弹出程序计数器,并将其作为下一条指令的地址。函数的返回值通常生存在%rax寄存器中,除非返回值是浮点数或布局体等特别范例。
hello中出现的函数操作有:
调用puts

puts: 将一个以空字符末端的字符串发送到尺度输出流(通常是屏幕)。该函数会在字符串的末端自动添加一个换行符,使得下一次输出重新的一行开始。
调用exit

exit: 立即停止调用进程。
调用sleep

sleep:实现程序的休眠。
调用getchar

getchar:从缓冲区中读取字符。
3.4 本章小结

本章的目的是探讨编译的原理和实践,以及C语言与汇编语言之间的对应关系。首先,先容了编译的界说和功能,即将文本文件转换为汇编语言程序,为天生气器码做好准备。其次,先容了在Ubuntu情况下利用gcc命令对hello.c源文件进行编译,并天生hello.s汇编文件。然后,对hello.s文件中的各种数据范例和操作进行了具体的分析和表明,展示了编译器如何处理不同的数据和指令。通过本章的学习,加深了对C语言和汇编语言的理解,为后续的链接、加载和运行过程打下了基础。


第4章 汇编

4.1 汇编的概念与作用

汇编的概念:
汇编是一种面向机器的程序设计语言,它用易于理解和记忆的符号或名称来表示机器指令的操作码和操作数。汇编语言与机器语言之间有逐一对应的关系,可以通过汇编器将汇编语言翻译成机器语言,也可以通过反汇编器将机器语言还原成汇编语言。汇编语言的优点是程序执行效率高,缺点是难于编写和调试,且与具体的处理器密切相干。
汇编的作用:
(1)顺应不同的高级语言和编译器,提供通用的输出语言。
(2)便于分析和调试底层的问题,查看反汇编代码和机器状态。
(3)提高程序的执行效率和性能,利用处理器的特别指令和优化技巧。
(4)理解盘算机的工作原理和布局,掌握数据的存储格式和寻址方式。
4.2 在Ubuntu下汇编的命令

命令:gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o


4.3 可重定位目的elf格式

4.3.1命令
在shell中输入readelf -a hello.o > hello.elf 指令获得 hello.o 文件的 ELF 格式:


4.3.2布局分析
(1)ELF头(ELF Header

目的文件的开头是一个 16字节的序列 Magic,它表示了该文件是由哪种体系天生的,以及该体系的字长和字节序。在 Magic 之后,是 ELF 头的其他部门,它提供了一些有助于链接器理解和处理目的文件的信息,例如 ELF头的长度、目的文件的种别、处理器的范例、节头部表在文件中的位置,以及节头部表中每个条目的巨细和个数等相干信息。

(2)节头

描述了.o文件中出现的各个节的意义,包括节的范例、位置、所占空间巨细等信息。
(3)重定位节
重定位节包罗了一些描述了段中引用外部符号的位置的条目,在链接过程中,这些位置的地址必要被修改为精确的值。链接器根据重定位条目的范例,采用相应的盘算方法,得到精确的地址值。
① .rela.text



  •  .rela.eh_frame

(4)符号表(Symbol table
.symtab是一个符号表,它存放在程序中界说和引用的函数和全局变量的信息。

4.4 Hello.o的效果剖析

4.4.1命令
在shell中输入 objdump -d -r hello.o > hello.asm 指令输出hello.o的反汇编文件,并与第3章的hello.s文件进行对照分析。

得到汇编文件如图:


与hello.s文件(见第三章配图)进行对比
经过分析 hello.s 和 hello.o 两个文件中的汇编代码,比较了它们之间的差异和缘故起因。主要有以下几个方面:



  • 操作数的表示:hello.s 中的操作数是十进制的,而 hello.o 的反汇编代码中的操作数是十六进制的,这是为了方便与机器码对应。
  • 全局变量的访问:hello.s 中,利用段名称+%rip 访问 rodata(printf 中的字符串),而 hello.o 中,利用 0+%rip 进行访问。这是因为 rodata 中数据地址在运行时才气确定,因此在汇编时,将操作数设置为全0,并添加重定位条目,在链接时才填入精确的地址。
  • 分支转移:hello.s 中,跳转指令的目的地址直接记为段名称,如 .L2,.L3 等。而 hello.o 中,跳转的目的为具体的地址,在机器码中体现为目的指令地址与当前指令下一条指令的地址之差。这是因为分支转移是相对寻址,必要根据当前指令的位置盘算偏移量。
  • 函数调用:hello.s 中,call 指令后跟着函数名称,而 hello.o 中,call 指令的目的地址是当前指令的下一条指令。这是因为 hello.c 中调用的函数都是共享库中的函数,最终必要通过动态链接器才气确定函数的运行时执行地址。在汇编时,对于这些不确定地址的函数调用,将其 call 指令后的相对地址设置为全0,并添加重定位条目,在链接时才填入精确的地址。

4.5 本章小结


本章先容了汇编的概念与作用,并以一个实例说明了汇编的过程和效果。本章利用 as 汇编器将汇编代码文件 hello.s 转换为机器语言文件 hello.o,并天生了符合 ELF 格式的可重定位目的文件 hello.elf。本章还通过反汇编工具查看了 hello.o 的机器码,并与 hello.s 的汇编代码进行了对比,分析了它们之间的差异和缘故起因。通过本章的学习,加深了对汇编语言、机器语言、ELF 格式和重定位的理解。

第5章 链接

5.1 链接的概念与作用

链接的概念:
链接是一种将多个代码和数据片断组合成一个可执行文件的过程,该文件可以被加载到内存并运行。链接由链接器完成,它负责剖析和重定位各个文件中的符号,例如printf函数。链接可以在不同的时机发生,例如编译时、加载时或运行时,取决于源代码如何被转换为机器代码。
链接的作用:
链接使得分离编译成为大概,这样我们可以将一个大型的应用程序分解为更小、更易管理的模块,每个模块都可以独立地修改和编译。链接还可以将一些常用的函数文件(如printf.o)与我们的模块归并,从而节流源程序空间,淘汰文件的复杂度和巨细,增长容错性,并方便针对性修改。链接还负责将各个模块之间相互引用的部门精确地衔接起来,例如从hello.o到hello的天生过程。
      
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的格式

在Shell中输入命令 readelf -a hello > hello1.elf 天生 hello 程序的 ELF 格式文件,生存为hello1.elf

(1)ELF头(ELF Header)

hello1.elf和hello.elf都是ELF格式的文件,它们的ELF头包罗了一些描述文件属性和布局的信息,例如体系的字长和字节序序列Magic,以及链接器必要的一些元数据。这两个文件的ELF头有一些相同的信息(如Magic,种别等),也有一些不同的信息,例如范例,程序头巨细,节头数目和入口地址。hello1.elf相比于hello.elf,有更多的程序头和节头,并且指定了一个入口地址。

(2)节头

节头记录了每个节的属性、巨细和位置。链接器在归并文件时,会将同名的节归并为一个较大的节,并且根据这个较大的节的位置和巨细重新盘算每个符号的地址。


3)程序头
程序头部门是一个布局数组,描述了体系准备程序执行所需的段或其他信息。

4Dynamic section


5Symbol table
       符号表中生存着定位、重定位程序中符号界说和引用的信息,所有重定位必要引用的符号都在此中声明。



5.4 hello的假造地址空间

   利用edb打开hello,可以查看ELF格式文件的Program Headers,它们描述了链接器运行时必要加载的各个段,并提供了动态链接的信息。每个段都有一些属性,例如假造地址、物理地址、巨细等。hello程序包罗了PHDR,INTERP,LOAD ,DYNAMIC,NOTE ,GNU_STACK,GNU_RELRO等段,如下图所示:

5.5 链接的重定位过程分析

在Shell中利用命令objdump -d -r hello > hello1.asm天生反汇编文件hello1.asm5.6 hello的执行流程

  与第四章中天生的hello.asm文件进行比较,其不同之处如下:



  •  链接后函数数目增长

    链接后的反汇编文件hello2.asm中,多出了.plt,puts@plt,printf@plt,getchar@plt,exit@plt,sleep@plt等函数的代码。缘故起因是动态链接器将共享库中hello.c用到的函数加入可执行文件中。





  •  函数调用指令call的参数发生变化
链接器在链接过程中,处理了重定位条目,将call指令后的字节码修改为目的地址与下一条指令地址的差值,从而使call指令可以大概跳转到精确的代码段,天生了完备的反汇编代码。

整体代码如下:






  •  跳转指令参数发生变化

       在链接过程中,链接器剖析了重定位条目,并盘算相对间隔,修改了对应位置的字节代码为PLT 中相应函数与下条指令的相对地址,从而得到完备的反汇编代码。



整体代码如下:


5.6 hello的执行流程


(1)  载入:_dl_start、_dl_init
(2)  开始执行:_start、_libc_start_main
(3) 执行main:_main、_printf、_exit、_sleep、_getchar、_dl_runtime_resolve_xsave、_dl_fixup、_dl_lookup_symbol_x
(4)  退出:exit

5.7 Hello的动态链接分析

在动态链接阶段之前,首先执行静态链接,天生部门链接的可执行目的文件hello,该文件不包罗共享库中的代码和数据。当加载hello时,动态链接器对共享目的文件中的相干模块进行重定位,并加载共享库,从而天生完全链接的可执行目的文件。动态链接采用耽误加载策略,即只有在函数被调用时才进行符号剖析。函数的动态链接通过全局偏移量表GOT和过程链接表PLT来实现。GOT存储函数的目的地址,PLT为每个全局函数创建一个代理函数,并将对函数的调用转换为对代理函数的调用。


5.8 本章小结

本章先容了链接的概念和作用,以及动态链接的原理。通过利用edb、gdb、objdump等工具,分析了hello程序的假造地址空间、ELF文件格式、重定位和执行过程,深入理解了链接的各个环节。


第6章 hello进程管理

6.1 进程的概念与作用

进程的概念:
进程是盘算机中执行程序和分配资源的基本单元,它反映了程序在某个数据集合上运行活动的实体和状态。进程的概念有两个紧张方面:一是进程具有独立的地址空间,包括文本地区、数据地区和堆栈地区;二是进程是一个动态执行的过程,它由程序计数器、寄存器、情况变量和打开文件描述符等组成4。不同的盘算机布局对进程有不同的界说:在早期面向进程设计的盘算机布局中,进程是程序的基本执行实体;在当代面向线程设计的盘算机布局中,进程是线程的容器。因此,进程既可以从静态角度理解为程序关于某个数据集合上运行活动的实体,也可以从动态角度理解为程序在某个数据集合上运行活动的过程。
进程的作用:
进程提供给应用程序的关键抽象:一个独立的逻辑控制流,如同程序独占处理器;一个私有的地址空间,如同程序独占内存体系
6.2 简述壳Shell-bash的作用与处理流程

shell的作用:
Shell-bash 是一个用C语言编写的交互型应用程序,它是用户和Linux内核之间的接口。它可以接收用户在提示符下输入的命令,表明并传递给内核去执行。它也可以调用其他程序,并在它们之间传递数据和参数。它把一个程序的输出作为另一个程序的输入,从而实现数据流的处理。Shell-bash 本身也可以被其他程序调用,作为体系的用户界面,提供了一种方便的方式访问操作体系内核的服务。shell的处理流程:

  • 判断命令是否是内部命令,即 shell 自带的一些功能,如 cd 和 echo。
  • 如果不是内部命令,就在搜索路径中查找命令对应的可执行文件,这些文件可以是 Linux 体系自带的应用程序,如 ls 和 rm,也可以是用户安装的第三方应用程序,如 xv。
  • 如果在搜索路径中找不到可执行文件,就报错并提示命令不存在或无法执行。
  • 如果找到可执行文件,就将其转换为体系调用,并将其传递给 Linux 内核进行处理。

6.3 Hello的fork进程创建过程

为了运行hello程序,shell在命令行输入./hello命令后,调用fork()函数创建一个子进程。子进程继承了父进程的假造地址空间,包括代码和数据段、堆、共享库和用户栈,以及所有打开的文件描述符。子进程可以读写父进程中打开的任何文件。子进程与父进程的唯一区别是它们有不同的进程标识符(PID)。fork()函数在父进程中返回子进程的PID,在子进程中返回0。当子进程运行竣事时,如果父进程仍旧存在,它会接纳子进程的资源,否则init进程会负责接纳。
6.4 Hello的execve过程

为了运行hello程序,shell在命令行输入./hello命令后,调用fork()函数创建一个子进程。子进程继承了父进程的假造地址空间和文件描述符。然后,子进程调用execve函数,在当前进程的上下文中加载并运行hello程序。execve函数首先删除子进程的用户地区,然后为hello程序的代码、数据、.bss和栈地区创建新的私有地区,这些地区都是写时复制的。接着,execve函数映射hello程序必要的共享地区,比如与尺度C库链接的对象。最后,execve函数设置子进程的程序计数器,使之指向hello程序的入口点。execve函数只有在出现错误时才会返回,否则它会执行hello程序,并将参数和情况变量传递给它。
6.5 Hello的进程执行

代码逻辑控制流:代码逻辑控制流是指程序执行的序次和分支,它决定了程序的功能和行为。代码逻辑控制流可以用控制流图、伪代码或其他方式表示。

时间片:时间片是指操作体系给每个进程分配的一段固定的CPU运行时间,当一个进程用完它的时间片后,操作体系会切换到另一个进程,这样可以实现多使命的并发执行。

用户模式和内核模式:用户模式和内核模式是指处理器的两种运行状态,用户模式下只能执行受限的指令集,不能直接访问硬件资源,内核模式下可以执行任何指令集,可以直接访问硬件资源。操作体系通常在内核模式下运行,用户程序通常在用户模式下运行。当用户程序必要调用操作体系提供的服务时,必要通过体系调用切换到内核模式,并在完成后切换回用户模式。

上下文信息:上下文信息是指进程或线程在某一时刻的运行状态,包括程序计数器、寄存器、栈、内存、文件描述符等。当操作体系必要切换进程或线程时,必要生存当前的上下文信息,并恢复下一个要运行的上下文信息。

运行程序的shell 进程通过fork 体系调用创建一个子进程,该子进程继承了父进程的假造内存空间。然后,子进程执行execve 体系调用,加载可执行文件并替换其假造内存空间。加载器首先扫除原有的内存段,然后为代码、数据、堆和栈分配新的内存段,并将其初始化。代码和数据段的初始化是通过将可执行文件中的页对应到假造地址空间中的页来实现的。堆和栈段的初始化是通过将其内容置零来实现的。加载器完成后,跳转到_start 地址开始执行程序,该地址最终会调用main 函数。在用户态和内核态之间切换的过程称为上下文切换。
6.6 hello的异常与信号处理

异常的种别:

在进程中乱按,进程将输入看成是输入的命令缓存到缓冲区中,在程序执行竣事后读取命令。

 当按下Ctrl+C时,进程会收到SIGINT信号,竣事hello程序,利用ps查看进程,发现进程被彻底竣事。

当按下Ctrl+Z时,进程收到SIGSTP信号 ,hello程序被挂起,当利用ps查看进程时,可以看到进程仍旧存在,通过fg加job号可以恢复执行。

利用kill可以使挂起的进程彻底被杀死


 利用pstree指令可以看到进程树。



6.7本章小结

本章先容了进程、Shell的概念和作用、shell如何调用fork和execve函数、以及通过对hello的创建、加载和停止、进程执行、异常与信号处理。


第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址是指在hello反汇编代码中可见的地址,它由一个段标识符和一个段内偏移量组成,用于定位操作数或指令的位置。
线性地址是逻辑地址向物理地址转换过程中的中心效果,它是通过段机制将逻辑地址转换为一维地址空间的地址。在保护模式下,假造地址也可以用“段:偏移量”的形式表示,此中的段是指段选择器。Hello反汇编的地址即为假造地址。
假造地址是保护模式下程序访问存储器所利用的逻辑地址,它可以通过分页机制映射到不同的物理地址。
物理地址是加载到内存地址寄存器中的地址,它是内存单元的现实地址。CPU通过地址总线寻址时,利用的是物理地址。在前端总线上传输的内存地址也都是物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理

段式存储管理是一种以段为单元进行内存分配和接纳的方法,它根据程序的逻辑布局将程序划分为若干个段,每个段具有一个段名和一个段内偏移量,用于定位程序或数据的位置。每个段在内存中占据一个连续的空间,但不同段之间可以不连续。段式存储管理必要维护一个段表,记录每个段的基址、长度、存取控制等信息。地址变换时,通过查找段表,将逻辑地址转换为物理地址。
段式管理的优点是可以大概实现程序的动态链接、保护和共享。动态链接是指在程序运行过程中,根据必要动态地装入或卸载某些段,实现程序的模块化。保护是指通过设置每个段的存取控制位,防止非法访问或修改。共享是指允很多个进程共同利用某些公共的程序或数据段,节流内存空间。
线性地址是逻辑地址向物理地址转换过程中的中心效果,它是通过将逻辑地址中的段内偏移量加上相应的段基址得到的。线性地址可以看作是一维的假造地址空间,它可以进一步通过分页机制映射到物理地址空间。
7.3 Hello的线性地址到物理地址的变换-页式管理

为了实现线性地址到物理地址的转换,操作体系采用了分页机制,将线性地址空间划分为等长的单元,称为页。由于页表大概占用大量的内存空间,操作体系利用了两级页表布局,即页目录表和页表。页目录表的物理地址生存在cr3寄存器中。
当处理器访问一个线性地址时,它会通过内存管理单元(MMU)天生一个假造地址,并根据假造地址的高10位在页目录表中查找对应的页表地址。然后,它会根据假造地址的中心10位在页表中查找对应的页帧地址。最后,它会将页帧地址和假造地址的低10位相加,得到最终的物理地址。
在这个过程中,处理器必要向高速缓存或主存发出请求,获取页目录表项和页表项。如果这些项已经存在于高速缓存中,那么处理器可以快速地得到物理地址。如果这些项不存在于高速缓存中,那么处理器必要从主存中读取它们,并将它们缓存在高速缓存中,以便下次利用。
7.4 TLB与四级页表支持下的VA到PA的变换

TLB是一个存储单个PTE的块的小型假造地址缓存,每个块对应一个假造页。当MMU接收到假造地址时,它会在TLB中查找匹配的块。如果找到,它会从PTE中获取物理页号,并与假造页偏移量组合成52位的物理地址。
如果TLB未掷中,MMU必要访问四级页表来获取PTE。CR3寄存器指向第一级页表的基址。假造页号的第一部门作为第一级页表的索引,得到一个PTE。如果该PTE有用且权限精确,它会指向第二级页表的基址。同样地,假造页号的第二、第三和第四部门分别作为第二、第三和第四级页表的索引,得到最终的PTE。该PTE包罗物理页号,与假造页偏移量组合成物理地址,并添加到TLB中。
如果在访问任何级别的页表时发现PTE无效或权限错误,MMU会触发缺页中断。
7.5 三级Cache支持下的物理内存访问

采用CT、CI、CO三个字段对物理地址进行分割,并按照以下步调实现cache的访问和替换。首先,根据CI字段在一级cache中定位相应的组,并检查该组中所有标志位为有用的块的CT字段是否与物理地址的CT字段相匹配。如果匹配成功,则表示一级cache掷中,并返回相应的数据块。如果匹配失败,则表示一级cache不掷中,必要继续在二级cache中查找。同样地,根据CI字段在二级cache中定位相应的组,并检查该组中所有标志位为有用的块的CT字段是否与物理地址的CT字段相匹配。如果匹配成功,则表示二级cache掷中,并将该数据块复制到一级cache中,并返回数据块。如果匹配失败,则表示二级cache不掷中,必要继续在三级cache中查找。依此类推,如果三级cache也不掷中,则必要从主存中读取数据块,并将其复制到各级cache中,并返回数据块。如果主存发生缺页中断,则必要从硬盘中读取数据块,并将其复制到主存和各级cache中,并返回数据块。
7.6 hello进程fork时的内存映射

shell执行fork函数时,内核为hello进程分配了唯一的PID,并建立了相应的数据布局和task_struct,同时创建了独立的假造地址空间。为了实现假造内存,内核复制了当前进程的mm_struct、地区布局和页表。它将两个进程共享的页面都设为只读,并将两个进程的地区布局都设为私有的写时复制。这样,fork在新进程返回时,新进程的假造内存与fork调用时的假造内存完全一致。当两个进程中恣意一个进行写操作时,写时复制机制会天生新页面,从而保证了每个进程都拥有私有的地址空间抽象
7.7 hello进程execve时的内存映射

在加载新程序时,操作体系必要对当前进程的用户假造地址空间进行重新映射。首先,它会删除已存在的用户地区布局,开释原来的代码、数据、bss、栈和堆地区所占用的物理内存。然后,它会为新程序的代码、数据、bss和栈地区创建新的用户地区布局,并将它们映射到相应的文件或匿名文件中。这些新的地区都是私有的,并且采用写时复制的策略。接着,它会检查新程序是否与共享对象链接,如果是的话,它会动态地将这些对象映射到用户假造地址空间中的共享地区中。最后,它会设置程序计数器(PC),使其指向新程序的代码地区的入口点。
7.8 缺页故障与缺页中断处理

缺页故障:当CPU访问假造内存中的某个地址,而该地址对应的物理页已经加载在主存中时,称为页掷中。反之,如果主存中没有该物理页,称为缺页。
缺页中断处理:当发生缺页时,CPU会调用缺页异常处理程序,该程序负责从主存中选择一个可替换的物理页,称为牺牲页。如果牺牲页已经被修改过,那么必要将其写回磁盘。然后,从磁盘中读取所需的物理页,并将其放入牺牲页的位置。同时,更新相干的PTE,使其指向新的物理页。最后,恢复引起缺页的指令,并让CPU重新发送假造地址给MMU。

7.9动态存储分配管理

动态内存管理是体系软件的基本组件之一,它负责在堆上为应用程序动态地分配和接纳内存空间。动态内存分配器通过维护堆上的不同巨细的内存块来实现动态内存管理,此中每个内存块都是一段连续的假造地址空间,可以是已经被分配给应用程序的或者是尚未被利用的。当应用程序请求一定巨细的内存时,动态内存分配器必要从空闲块中选择一个符合的块来满足请求,并将其标记为已分配状态。当应用程序开释已分配的内存时,动态内存分配器必要将其恢复为空闲状态,并实行与相邻的空闲块进行归并,以淘汰内存碎片。

为了高效地管理堆上的内存块,动态内存分配器通常采用以下三种策略:

记录空闲块策略:这种策略决定了如何构造和查找空闲块,以便快速地找到符合的空闲块来满足应用程序的请求。常见的记录空闲块策略有显示空闲链表、隐式空闲链表、分离空闲链表和红黑树等。
放置策略:这种策略决定了如何从找到的空闲块中划分出所需巨细的内存给应用程序,并处理剩余部门。常见的放置策略有首次适配、下一次适配和最佳适配等。
归并策略:这种策略决定了何时以及如何将相邻的空闲块进行归并,以避免或淘汰内存碎片。常见的归并策略有立即归并和耽误归并等。
7.10本章小结

本章主要先容了hello 的存储器地址空间、intel 的段式管理、hello 的页式管理, VA 到PA 的变换、物理内存访问,hello进程fork、execve 时的内存映射、缺页故障与缺页中断处理、动态存储分配管理的相干内容,对hello的存储管理有了较为深入的讲解。
 
第8章 hello的IO管理


8.1 Linux的IO装备管理方法

Linux内核采用了一种优雅的装备抽象方式,即将所有的I/O装备(如网络、磁盘和终端)视为文件,并将所有的输入和输出操作同一为对应文件的读写操作。这样,Linux内核就可以提供一个简朴而低级的接口,即Unix I/O,实现了输入和输出操作的一致性和同一性。
8.2 简述Unix IO接口及其函数

接口:
(1)打开文件:程序通过调用open()函数来请求内核分配一个可用的描述符,并将其与指定路径名的文件关联起来。描述符是一个非负整数,可以唯一地标识进程打开的某个文件。内核维护了一个打开文件表,记录了每个打开文件的相干信息,如访问权限、当前位置、引用计数等。程序在后续对该文件的操作中只需利用该描述符即可。
(2)linux shell创建的每个进程都有三个预界说的描述符:尺度输入、尺度输出和尺度错误。它们分别对应着0、1和2这三个常量值,可以在头文件中找到它们的界说。它们分别表示进程从终端读取输入、向终端输出效果和向终端报告错误信息所利用的描述符。
(3)改变当前的文件位置:对于每个打开的文件,内核都会记录其当前位置,即从该文件起始处开始盘算的字节偏移量。程序可以通过调用lseek()函数来显式地改变某个描述符所对应的当前位置,以便在随机访问模式下读写该文件。
(4)读写文件。读操作就是通过调用read()函数来将指定描述符所对应的当前位置开始的多个字节复制到内存缓冲区中。如果在给定巨细范围内执行读操作时遇到了该文件的末端,则会产生一个称为EOF(end-of-file)的条件,并返回现实读取到的字节数。应用程序可以通过检测这个条件来判断是否已经读完了该文件。类似地,写操作就是通过调用write()函数来将内存缓冲区中的多个字节复制到指定描述符所对应的当前位置开始的那些字节上,并更新当前位置。
(5)关闭文件。当程序不再必要访问某个打开的文件时,它可以通过调用close()函数来关照内核开释该描述符,并更新打开文件表中相应条目的信息。作为相应,内核会淘汰该条目的引用计数,并在引用计数为零时将其删除。当进程因为任何缘故起因停止时,内核也会自动关闭该进程打开的所有文件,并开释相干资源。
函数:
(1)open()函数用于打开或创建文件。
(2)close()函数用于关闭一个被打开的的文件。
(3)read()函数用于从文件读取数据。
(4)write()函数用于向文件写入数据。
(5)lseek()函数用于在指定的文件描述符中将将文件指针定位到相应位置。

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是字符指针,而(char*)(&fmt + 4)表示fmt后的第一个参数的地址。
vsprintf函数的返回值是格式化后的字符串的长度,其功能是根据fmt中指定的格式标识符将可变参数列表转换为相应的格式。
write函数是一个体系调用,用于将buf中的i个字节写入到文件描述符handle所指向的文件中,返回值是现实写入的字节数,如果发生错误则返回-1。
write函数在执行时,会通过eax寄存器传递体系调用号_NR_write,通过ebx和ecx寄存器传递参数handle和buf,然后触发中断向量INT_VECTOR_SYS_CALL,进入内核态并调用sys_call函数。为了保护进程的状态,在进入内核态之前会生存当前进程的寄存器和栈信息。
字符显示驱动程序负责将ASCII码转换为对应的字模数据,并将其写入到显存vram中。显存vram是一个二维数组,每个元素存储一个像素点的RGB颜色值。
显示芯片按照一定的刷新频率逐行扫描显存vram,并通过信号线向液晶显示器发送每个像素点的RGB颜色值,从而在屏幕上显示出文字。

8.4 getchar的实现分析

getchar函数是一个尺度库函数,用于从尺度输入流(stdin)中读取一个字符,并返回其ASCII码值。它的原型是:
int getchar(void);
getchar函数的实现分析可以从以下几个方面进行:
getchar函数的内部实现通常会调用read体系调用,向内核请求从文件描述符0(即尺度输入)中读取一个字节的数据,并将其存放在一个缓冲区中。read体系调用的原型是:
ssize_t read(int fd, void *buf, size_t count);
此中,fd是文件描述符,buf是缓冲区指针,count是请求读取的字节数。read体系调用返回现实读取的字节数,或者在出错时返回-1。
当程序调用getchar函数时,如果尺度输入流中没有可用的数据,那么程序就会阻塞等候用户输入。用户输入的字符被键盘驱动程序接收并转换为ASCII码,然后存放在体系的键盘缓冲区中,直到用户按下回车键为止(回车字符也放在缓冲区中)。这个过程涉及到异步异常-键盘中断的处理。当键盘发生中断时,内核会调用键盘中断处理子程序,从键盘控制器中读取按键扫描码,并将其转换为ASCII码,然后生存到体系的键盘缓冲区中。
当用户按下回车键后,getchar函数才开始从尺度输入流中每次读取一个字符,并返回其ASCII码值。如果尺度输入流已经到达文件末端(EOF),那么getchar函数会返回-1。同时,getchar函数还会将用户输入的字符回显到尺度输出流(stdout)上,以便用户看到本身输入的内容。
如果用户在按下回车键之前输入了多个字符,那么这些字符会保留在体系的键盘缓冲区中,等候后续的getchar函数调用读取。也就是说,后续的getchar函数调用不会等候用户输入,而是直接从缓冲区中读取一个字符,并返回其ASCII码值。直到缓冲区中的字符读完为止,才会再次等候用户输入。
8.5 本章小结

本章主要先容了Linux体系中的I/O装备管理和Unix I/O接口,以及它们在实现基本的输入输出功能中的作用。窗体顶端

   
窗体底端

  
(第81分)

结论



  • 编写一个输出“Hello, world!”的hello.c源文件
  • 利用gcc命令对hello.c进行预处理、编译、汇编和链接,天生可执行程序hello
  • 利用./hello命令运行可执行程序hello
  • shell进程调用fork()函数创建一个子进程,并复制父进程的地址空间和状态
  • 子进程调用execve()函数加载可执行程序到假造内存,并覆盖原来的地址空间和状态
  • MMU负责将程序中的假造地址转换为物理地址,并检查地址是否正当和可访问
  • 处理器根据程序中的指令执行逻辑控制流,包括序次执行、条件分支、循环等
  • 程序正常竣事或者出现异常时,调用信号处理函数进行处理,并返回相应的退出码
  • 进程竣事后,操作体系负责开释和接纳其占用的资源,包括内存、文件描述符等

盘算机体系的设计毫无疑问使非常精妙的,实现这整个体系每个环节环环相扣,缺一不可

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


附件

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

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

文件的作用

文件名

预处理后得到的文本文件

hello.i

编译后得到的汇编语言文件

hello.s

汇编后得到的可重定位目的文件

hello.o

readelf读取hello.o得到的ELF格式信息

hello.elf

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

hello.asm

由hello可执行文件天生的.elf文件

hello1.elf

反汇编hello可执行文件得到的反汇编文件

hello1.asm


参考文献

为完本钱次大作业你翻阅的册本与网站等

[1]  (31条消息) 哈工大盘算机体系Lab4.Tiny Shell_哈工大盘算机体系实行4_Castria的博客-CSDN博客
[2]  (31条消息) 哈工大盘算机体系大作业(2022)_何以牵尘的博客-CSDN博客
[3] (31条消息) 哈工大计统大作业_盘算机学院杜鑫_那我就让韶光倒流的博客-CSDN博客
(参考文献0分,缺失 -1分)








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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

天空闲话

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表