CSAPP大作业程序人生

一给  金牌会员 | 2024-10-30 00:37:40 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 988|帖子 988|积分 2964

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

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

x




盘算机系统


大作业



题     目  程序人生-Hello’s P2P 
专       业   数据科学与大数据技能   
学     号   2021110564            
班     级   2103501               
学       生   崔志阳                
指 导 教 师    史先俊                   






盘算机科学与技能学院

2023年4月

摘  要

    本文主要讲述了hello.c程序从编写完到执行完的进程,从预处理,编译,汇编,链接,再到进程管理,存储管理,IO管理。并对每一过程都加以分析。
关键词:关键词1hello;关键词2预处理;关键词3编译;                           










目  录


第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程序起源是用c语言编写的hello.c源文件开始的。在Linux系统下,将hello.c举行预处理,天生hello.i文件,然后将hello.i文件翻译成汇编语言文件hello.s。然后再将hello.s天生重定位文件hello.o。最后链接完成天生可执行文件hello。下面为表示图(来自CSAPP)。


图1-1

操作系统调用execve后映射假造内存,先删除当前假造地址的数据布局并为hello创建新的地区布局,进入程序入口后载入物理内存,再进入main函数执行代码。代码完成后,父进程回收hello进程,内核删除相关数据布局。
1.2 环境与工具

(1)硬件环境:X64 CPU;2GHz;4GRAM;256Disk

(2)软件环境:Windows10 64位;Vmware 10;Ubuntu 16.04 LTS 64位

(3)利用工具:Codeblocks;Objdump;Gdb;Hexedit

1.3 中间效果

hello.c:程序源代码文件。
hello.i:预处理器对hello.c举行宏替换和头文件展开后天生的中间文件。
hello.s:编译器对hello.i举行编译后天生的汇编代码文件。
hello.o:汇编器将hello.s汇编成目标文件hello.o,其中包罗了呆板码和可重定位信息。
hello:可执行文件,由链接器将目标文件和系统库链接天生,包罗了程序的呆板码和可执行信息。
helloo.elf:hello.o的ELF格式。
hello.elf:hello的ELF格式,分析重定位过程。

1.4 本章小结

本章主要介绍Hello程序P2P,020的整个过程,并且列出了实验环境与工具。


第2章 预处理

2.1 预处理的概念与作用

概念:
程序计划范畴中,预处理一样平常是指在程序源代码被翻译为目标代码的过程中,天生二进制代码之前的过程。
作用:
最常见的预处理是C语言和C++语言。ISO C和ISO C++都规定程序由源代码被翻译分为若干有序的阶段(phase) ,通常前几个阶段由预处理器实现。预处理中会展开以#起始的行,试图表明为预处理指令 (preprocessing directive) ,其中ISO C/C++要求支持的包罗#if/#ifdef/#ifndef/#else/#elif/#endif(条件编译)、#define(宏定义)、#include(源文件包罗)、#line(行控制)、#error(错误指令)、#pragma(和实现相关的杂注)以及单独的#(空指令)。预处理指令一样平常被用来使源代码在差别的执行环境中被方便的修改或者编译。
2.2在Ubuntu下预处理的命令

通过gcc -E -o hello.i hello.c命令实现对hello.c的预处理,并且天生hello.i。

 

 


图2-

2.3 Hello的预处理效果解析

       可以看到hello.i拓展到了三千多行,这是因为将hello.c上面的头文件给全部展开写入到hello.i当中。

 

图2-2

2.4 本章小结

在本章实现了对hello.c的预处理,初步相识hello.i文件中的内容。

第3章 编译

3.1 编译的概念与作用

编译是编译器将预处理文件hello.i翻译汇编语言文件hello.s。
作用首先是查抄程序是否有语法错误,例如warning,error。其次是将其翻译成汇编语言。编译器还能对程序举行优化。
3.2 在Ubuntu下编译的命令

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

 

图3-1

产生了hello.s汇编语言文件。
3.3 Hello的编译效果解析

3.3.1:程序前面的初始部门:
     

                                                图3-2
     
.file    指出源文件名为hello.c
.text    表示代码节
.rodata  表示只读的数据
.align   表示对齐的方式,这里表示8字节对齐
.string   声明字符串
.globle  声明全局变量
.type     声明符号类型
3.3.2 数据部门

 
 
图3-3

这两个字符串放在只读数据中。

 

图3-4

这里是将main函数的两个参数argc和字符指针argv存入rdi,rsi中并且将其压入栈中。
3.3.3赋值操作:

 

图3-5

将循环控制变量i赋值为0。通过movl指令,因为int为32位,所以位l。
3.3.4关系操作:

 

图3-6

此处对应if语句中argc与4的比力操作,通过cmpl对这两个数举行比力,假如相等就跳转。

 

图3-7

此处应该对于for循环中对i与4的比力,假如小于等于就跳转到.L4。
        3.3.5算术操作:

 

图3-8

       此处是对循环控制变量i的加一操作,每次循环过后对应对i++。
       3.3.6转移控制:

 
     

图3-9

此处对应if语句中argc与4的比力操作,通过cmpl对这两个数举行比力,假如相等就跳转到.L2。


图3-10

此处应该对于for循环中对i与4的比力,假如小于等于就跳转到.L4。
3.3.7函数操作:
(1)main函数:
参数传递:main函数的参数是argc和argv字符指针,通过压入栈中举行参数传递。
函数调用:通过利用call内部指令举行函数调用。
局部变量:局部变量i举行循环控制。
(2)printf函数:
参数传递:调用参数argv[1],argv[2]。
函数调用:调用了两次,通过rdx,rdi传入参数。
(3)exit函数:

 

 
图3-11

因为程序中是exit(1),通过将rdi设为1传参调用。
(4)atoi函数:

 

图3-12

通过rdi传承调用。
(5)sleep函数:

 

图3-13

调用方式同atoi函数。
(6)getchar函数:

 

图3-14

无参数通过call调用。
3.3.8类型转换:
atoi函数将字符型argv[3]转换为整型数。
3.4 本章小结

本章主要讲述了编译阶段编译器如何处理各种数据和操作,并通过详细实例hello.s文件解析这个过程,相识各个C语言中的数据和操作在汇编语言中的操作。通过明白这种机制,我们也可以将汇编语言翻译为C语言举行反汇编工作,明白高级语言和汇编语言之间的转化关系。
第4章 汇编

4.1 汇编的概念与作用

概念:汇编过程将把编译得到的汇编程序指令逐条翻译为呆板语言指令,然后把这些指令打包成一种叫做可重定位目标程序(relocatable object program)的格式,并将效果生存在一个二进制文件中。

作用:汇编将汇编指令转换成一条条呆板可以直接读取分析的呆板指令,盘算机只有通过呆板指令才能实现各运算过程。


4.2 在Ubuntu下汇编的命令

gcc-c hello.s -o hello.o


图4-1

通过以上命令产生了hello.o文件。
4.3 可重定位目标elf格式

首先通过readelf -a hello.o >helloo.elf天生helloo.elf。
 

 

图4-2

下面是CSAPP中提供的elf文件内容。

 

图4-3

1.ELF头:
ELF文件的头部包罗了文件类型、目标平台、程序头表偏移量、节头表偏移量等信息。详细来说,ELF头部包罗以下字段:

Magic Number:ELF文件的魔数,用于标识文件类型和版本。

文件类型(File Type):ELF文件的类型,如可执行文件(ET_EXEC)、共享库文件(ET_DYN)等。

目标平台(Machine):ELF文件所要运行的目标平台,如x86、ARM、MIPS等。

版本(Version):ELF文件的版本号。

入口地址(Entry Point):程序执行的入口地址。

程序头表偏移量(Program Header Offset):程序头表在文件中的偏移量。

节头表偏移量(Section Header Offset):节头表在文件中的偏移量。

标志(Flags):ELF文件的标志信息,如是否需要动态链接等。

头部巨细(Header Size):ELF头部的巨细。

程序头表项巨细(Program Header Entry Size):程序头表项的巨细。

程序头表项数量(Program Header Entry Count):程序头表项的数量。

节头表项巨细(Section Header Entry Size):节头表项的巨细。

节头表项数量(Section Header Entry Count):节头表项的数量。

节名称字符串表偏移量(Section Name String Table Offset):存储节名称的字符串表在文件中的偏移量。

 

2.节头部表
节头表(Section Header Table)是ELF文件中的一个数据布局,用于描述差别节(Section)的信息,例如节的名称、巨细、偏移量、属性等


3.重定位节
用于描述程序的重定位信息。在编译和链接过程中,差别模块的代码和数据可能被编译成差别的目标文件,其中每个目标文件都包罗了一些重定位节,用于描述该文件中需要被动态链接器修改的符号引用


图4-6

 
4.符号表
用来存放程序中定义和引用的函数或全局变量的信息。每个可重定位目标模块m都有一个符号表,它包罗m定义和引用的符号的信息。符号表是由汇编器构造的,利用编译器输出到汇编语言.s文件中的符号。

 

4.4 Hello.o的效果解析

       通过objdump -d -r hello.o指令天生了hello.o的反汇编:

 

       观察到其与hello.s基本雷同,有几个地方差别:在分支转移的时间没用.L3,而是详细的地址,访问字符串也是如许。在调用函数时.s用call加函数名,这里时call加地址。
4.5 本章小结

本章介绍了hello 从hello.s 到hello.o 的汇编过程,分析了可重定位文件的布局和各个构成部门,以及它们的内容和功能;还检察了hello.o 的elf 格式,并利用objdump 得到反汇编代码与hello.s 举行比力。在这个过程中,天生了重定位条目,为之后的链接中的重定位过程奠定了底子。

第5章 链接

5.1 链接的概念与作用

链接的概念:链接(Linking)是将多个目标文件(Object File)组合成一个可执行文件(Executable File)或共享库文件(Shared Library)的过程。链接器(Linker)是执行链接操作的程序。
链接的作用:解决符号引用:在程序中,函数和变量通常是在差别的源文件中定义和引用的。链接器的一个主要作用是解决这些符号引用。它会查找每个符号的定义,并将符号引用修改为符号定义的地址或位置,以便程序正确执行。

镌汰重复代码和数据:在多个源文件中可能会包罗雷同的代码和数据。链接器可以将这些雷同的代码和数据归并,镌汰最终可执行文件的巨细,提高可执行文件的运行服从。

天生可执行文件或共享库文件:链接器将多个目标文件组合成一个可执行文件或共享库文件,使得软件系统具备可执行性和可扩展性。可执行文件可以直接在操作系统中运行,共享库文件可以被多个程序共享利用。

加载共享库:在程序运行时,动态链接器会加载共享库文件,并将共享库文件中的符号引用解析为实际的函数地址。如许,程序可以调用共享库中的函数,实当代码的复用和共享。

5.2 在Ubuntu下链接的命令

命令:ld -o hello -dynamic-linkker /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的格式

天生hello的elf的命令格式:readelf -a hello >hello.elf


  • ELF头:
区别:相较于 hello.o 文件,hello 文件的 ELF 头区别在以下几点:
文件类型差别:hello 文件的文件类型是 EXEC,而 hello.o 文件的文件类型是 REL。
程序的入口点差别:在 hello 文件中,程序的入口点是 main 函数所在的地址,而不是在地址 0x0 处。
节头数量发生变化:由于 hello 文件需要包罗可执行代码和数据,所以在其 ELF 头中会有更多的节头信息,而 hello.o 文件则只需要包罗与目标文件相关的节头信息。
换句话说,hello 文件是可执行文件,可以直接运行,而 hello.o 文件只是一个目标文件,需要进一步链接才能天生可执行文件。


 


  • 节头:
节头部表(Section Header Table)记录了可执行文件或目标文件中所有节的信息,包罗节的名称、巨细、偏移量、内存对齐、类型等。通过读取节头部表中的信息,我们可以获得每个节所占据的空间范围,进而可以利用 HexEdit 工具定位这些节所在的位置。

 


  • 重定位:          



  • 符号表:



 
5.4 hello的假造地址空间

       通过edb加载hello程序,打开Data Dump检察hello加载到假造地址的状态,并检察各段信息。

 

5.5 链接的重定位过程分析

有.init,.plt,.text三个节,而且每个节中有许多的函数。库函数的代码都已经链接到了程序中,程序各个节变的更加完备,跳转的地址也具有参考性。
hello比hello.o多出的节头表。
.interp:生存ld.so的路径
.note.ABI-tag
.note.gnu.build-i:编译信息表
.gnu.hash:gnu的扩展符号hash表
.dynsym:动态符号表
.dynstr:动态符号表中的符号名称
.gnu.version:符号版本
.gnu.version_r:符号引用版本
.rela.dyn:动态重定位表
.rela.plt:.plt节的重定位条目
.init:程序初始化
.plt:动态链接表
.fini:程序终止时需要的执行的指令
.eh_frame:程序执行错误时的指令
.dynamic:存放被ld.so利用的动态链接信息
.got:存放程序中变量全局偏移量
.got.plt:存放程序中函数的全局偏移量
.data:初始化过的全局变量或者声明过的函数
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
程序名称 地址
ld-2.27.so!_dl_start 0x7fb85a93aea0
ld-2.27.so!_dl_init 0x7f9612138630
hello!_start 0x400582
lib-2.27.so!__libc_start_main 0x7f9611d58ab0
hello!puts@plt 0x4004f0
hello!exit@plt 0x400530
5.7 Hello的动态链接分析

在举举措态链接之前,需要先举行静态链接,以天生部门链接的可执行目标文件hello。这时,共享库中的代码和数据并没有被归并到hello文件中。在加载hello文件时,动态链接器会对共享目标文件中的相应模块内的代码和数据举行重定位,并加载共享库以天生完全链接的可执行目标文件。


 
动态链接利用了耽误加载的方法,即在调用函数时才举行符号的映射。为了实现函数的动态链接,采取了偏移量表GOT和过程链接表PLT。在GOT中存储了函数的目标地址,并为每个全局函数创建一个副本函数。然后将对函数的调用转换成对副本函数的调用。

 

5.8 本章小结

链接是将一个或多个目标文件归并成一个可执行文件或动态链接库的过程。在链接过程中,目标文件中的符号(Symbol)会被解析并转换为对应的地址或者重定位信息,同时,对于差别的符号定义可能存在冲突或者未定义环境,这些题目需要被解决以确保最终天生的可执行文件或动态链接库是正确的。

Ubuntu下的 gcc 编译器提供了预处理、编译、汇编和链接等功能。在预处理、编译和汇编阶段,编译器会将源代码转换为可执行目标文件,但是这些目标文件还不能被直接执行,需要举行链接操作,将差别目标文件之间的符号引用和定义举行匹配息争析,最终天生可执行文件或动态链接库。在本例中,我们通过将 hello.o 举行链接操作,天生了可执行文件 hello。

在举行链接操作的过程中,首先需要对目标文件举行重定位,将目标文件中的符号解析为实际地址,并将符号引用和定义之间的关联关系建立起来。对于动态链接库的环境,链接过程将耽误到运行时举行,需要利用动态链接器(如 ld.so)动态加载库,并举行符号解析和重定位操作。

在重定位的过程中,我们可以观察到目标文件和可执行文件中的一些变化。例如,符号地址和节偏移量等信息发生了变化,动态链接库中的代码和数据可能会被共享等。这些变化会影响最终可执行文件或动态链接库的举动和性能,因此需要细致举行调试和测试,以确保链接效果正确。

第6章 hello进程管理

6.1 进程的概念与作用

进程是盘算机中的一种基本概念,它指正在运行的程序在操作系统中的实例。在操作系统中,每个进程都有独立的内存空间、状态信息和执行上下文,可以被操作系统调度执行。
进程在盘算机系统中具有重要的作用。首先,进程是盘算机中程序执行的基本单位。每个进程都有独立的内存空间,可以包管程序之间的隔离性,同时也可以最大限度地利用盘算机的多核处理器,实现多使命并行执行。
其次,进程也是实现多用户操作的重要手段。在多用户操作系统中,每个用户都可以通过创建自己的进程来举行独立的盘算和操作,从而实现多用户共享盘算资源的目的。在操作系统中,进程之间也可以通过进程间通信(IPC)的方式来实现数据和资源共享。
别的,进程还是操作系统中实现进程调度、资源分配和管理的基本单元。操作系统通过对进程的调度和管理,实现了盘算机资源的有效利用和管理,提高了盘算机系统的运行服从和可靠性。
总之,进程是盘算机中的基本概念,是实现多使命、多用户、资源管理和调度等功能的重要手段,是盘算机操作系统中的核心构成部门之一。
6.2 简述壳Shell-bash的作用与处理流程

Shell是盘算机操作系统中的一个用户界面,它提供了一种通过命令行交互的方式来操作盘算机系统的方法。而bash则是一种常见的Shell程序,它是Bourne-Again SHell的缩写,是Linux和其他类Unix系统中广泛利用的一种Shell程序。
bash的作用是提供一个交互式的命令行界面,答应用户通过输入命令来操作盘算机系统。它可以执行各种命令和程序,管理文件和目录,编辑文本文件等等。
在Linux系统中,bash的处理流程通常如下:
用户输入命令:用户在命令行界面输入要执行的命令,例如ls或cd等。
Shell解析命令:bash会解析用户输入的命令,并将其转化为操作系统可以明白的情势。
程序执行:Shell会调用对应的程序或脚本来执行用户输入的命令,例如运行ls程序来列出当前目录中的文件列表。
输出效果:程序执行完毕后,bash将执行效果输出到命令行界面上,供用户检察。

循环执行:假如用户输入了多个命令,bash会按照顺序循环执行它们,直到所有命令执行完毕或用户终止程序。
需要注意的是,bash除了可以解析和执行命令外,还具备了一定的编程能力。用户可以利用bash来编写Shell脚本,通过脚本的方式批量执行命令、处理文件、管理系统等操作。因此,bash不仅是Linux系统中必不可少的一个组件,也是Linux用户和系统管理员一样平常工作中的重要工具之一。
6.3 Hello的fork进程创建过程

       当运行 Hello 程序时,操作系统会为该程序创建一个进程。在该进程中,程序会执行 main 函数中的代码。当程序执行到 fork() 函数时,会创建一个子进程,然后该子进程会拷贝父进程的地址空间,包罗代码、数据、堆栈等,但是子进程会拥有自己独立的地址空间。
在 Hello 程序中,调用 fork() 函数会创建一个子进程,子进程会从 fork() 函数返回并返回值为 0。而父进程会从 fork() 函数返回并返回值为子进程的进程 ID。在子进程中,将执行与父进程雷同的代码,也就是从 fork() 函数背面的代码开始执行。在父进程中,可以通过子进程的进程 ID 来辨认子进程,并执行父进程的代码。
因此,在 Hello 程序中,当 fork() 函数被调用时,会创建一个子进程并返回子进程的进程 ID。子进程会从 fork() 函数返回并开始执行循环体内的代码,而父进程会继承执行循环体外的代码,直到程序竣事。由于子进程是从 fork() 函数背面的代码开始执行的,因此它会在循环体的第一次迭代中输出一次“Hello 学号 姓名”并暂停指定秒数,统共输出5次。而父进程会在循环体外的代码中等候用户输入恣意字符,直到用户输入后才竣事程序。
6.4 Hello的execve过程

在 Hello 程序中,execve() 函数被用来执行另一个程序。在 execve() 函数被调用时,它会替换当前进程的映像,并开始执行指定的程序。
execve() 函数需要传递三个参数:第一个参数是要执行的程序的路径,第二个参数是一个字符串数组,用来传递给程序的命令行参数,第三个参数是一个字符串数组,用来设置环境变量。在 Hello 程序中,调用 execve() 函数时,第一个参数是要执行的程序的路径,第二个参数是一个字符串数组,用来传递给程序的命令行参数,第三个参数是一个空的环境变量数组。
在 execve() 函数被调用后,操作系统会将当前进程的映像替换为指定的程序的映像,并开始执行该程序的代码。因此,当 execve() 函数被调用时,Hello 程序的代码就不会再执行了。
6.5 Hello的进程执行

       Hello 程序的进程执行过程中,涉及到了进程上下文信息、进程时间片、进程调度、用户态与核心态转换等概念。

进程上下文信息:进程的上下文信息包罗了进程的程序计数器、寄存器、内存管理信息、文件描述符等状态信息。当操作系统从一个进程切换到另一个进程时,它需要生存当前进程的上下文信息,并将下一个进程的上下文信息加载到系统中,这个过程称为进程切换。
进程时间片:操作系统对进程举行调度时,为每个进程分配一定的时间片。当进程的时间片用完之后,操作系统会停止当前进程的执行,并将 CPU 时间分配给下一个等候执行的进程。时间片的巨细可以根据系统的负载环境和优先级举行调解,以确保系统的服从和稳固性。
进程调度:进程调度是操作系统中一个重要的构成部门。它的主要作用是将 CPU 时间分配给等候运行的进程,并根据进程的优先级和调度算法来确定哪个进程应该获得 CPU 时间片。常用的调度算法包罗轮询调度、优先级调度、时间片轮转调度等。
用户态与核心态转换:在 Linux 操作系统中,进程可以运行在用户态或核心态。在用户态中,进程只能访问自己的用户空间,不能访问操作系统的核心空间。而在核心态中,进程可以访问所有的系统资源,包罗硬件设备、内核数据布局等。当进程需要访问核心态资源时,需要通过系统调用将进程从用户态转换到核心态,以便访问这些资源。在 Hello 程序中,当调用 execve() 系统调用时,操作系统会将进程从用户态转换到核心态,并将程序加载到内存中运行。
综合上述概念,Hello 程序的进程执行过程中,操作系统会为程序创建一个新的进程,并将其上下文信息加载到系统中。随后,操作系统会将进程切换到 CPU 上执行,通过进程调度算法分配 CPU 时间片,控制进程的执行。当程序需要访问核心态资源时,操作系统会将进程从用户态转换到核心态,以便访问这些资源。当程序运行竣事后,操作系统会将进程的上下文信息生存到系统中,然后将 CPU 时间分配给下一个等候执行的进程,完成进程的切换。
6.6 hello的异常与信号处理

执行过程可能出现的异常一共有四种:中断、陷阱、故障、终止。
中断:来自I/O设备的信号,异步发生,总是返回到下一条指令。
陷阱:故意的异常,同步发生,总是返回到下一条指令。
故障:潜在可规复的错误,同步发生,可能返回到当前指令或终止。
终止:不可规复的错误,同步发生,不会返回。
正常执行:

输入ctrl z:

进程被挂起,并没有被回收,
执行ps:

输入ctrl c:

进程被停止。
乱输入:

执行jobs:

执行pstree:

执行fg:

6.7本章小结

创建进程:通过调用fork函数创建子进程,子进程会复制父进程的数据段、代码段、堆栈等信息,从而得到一个新的进程控制块(PCB)。子进程的PCB会被内核标记为停当态,等候被调度执行。

进程调度:操作系统根据进程调度算法,在停当态进程中选择一个进程分配CPU时间片,举行运行。在Hello程序中,进程调度的过程由操作系统内核自动完成。

用户态与核心态转换:当进程需要执行一些特权操作时,需要切换到核心态。比如在Hello程序中,当进程需要调用系统调用函数时(如sleep、printf等),就需要由用户态切换到核心态。

异常与信号处理:在Hello程序执行过程中,假如出现异常或接收到信号,操作系统会根据事先定义的处理方式举行处理。比如,当接收到SIGINT信号时,会触发进程终止的操作。而当用户输入Ctrl-Z时,会将进程挂起,并将进程移动到背景等候规复。

进程终止:当进程执行完毕或出现异常时,操作系统会释放进程占用的资源,并将进程控制块(PCB)从进程表中删除,竣事进程的生命周期。

综上所述,进程管理是操作系统中的一个重要构成部门,通过对进程的创建、调度、资源分配、异常处理等方面的管理,实现了对盘算机资源的有效利用和掩护。在Hello程序中,我们可以通过对进程管理的分析,更好地明白操作系统中进程管理的详细实现和作用。

第7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址是指程序在执行时所利用的地址,是相对于程序自身的地址,通常是指令中的操作数或地址。逻辑地址是与详细硬件平台无关的,差别的进程可以有雷同的逻辑地址。
线性地址是指逻辑地址通太过段、分页等机制转换为的地址,也就是在内存中实际存放的地址。操作系统在管理内存时,需要把程序的逻辑地址转换为线性地址,以便在物理内存中定位所需的数据。
假造地址是指在进程中利用的地址,它不是实际存在的物理地址,而是通过操作系统和硬件的组合转换机制转换为物理地址。在操作系统中,每个进程都有自己独立的假造地址空间,进程间的假造地址是互不干扰的。
物理地址是指实际存在的物理内存地址,是操作系统将假造地址转换为物理地址后,真正的内存地址。
在hello程序的执行过程中,当操作系统加载程序时,将程序的二进制文件映射到进程的假造地址空间中。在程序执行时,程序中的逻辑地址需要通过操作系统的内存管理机制转换为假造地址、线性地址和最终的物理地址,才能访问内存中的数据。例如,程序中的变量和函数都需要通过地址访问内存中的数据,这些地址在程序中是逻辑地址,但是需要通过操作系统的内存管理机制转换为物理地址,才能真正地访问内存中的数据。
7.2 Intel逻辑地址到线性地址的变更-段式管理

Intel x86架构的内存管理采取了段式管理和分页式管理相联合的方法。在段式管理中,程序的逻辑地址会经过以下的变更过程,最终得到线性地址:
逻辑地址由段选择符(Segment Selector)和段内偏移量(Offset)构成。
段选择符指向描述符表(Descriptor Table),描述符表中存放了每个段的基地址、巨细、访问权限等信息。
根据段选择符在描述符表中找到对应的段描述符(Segment Descriptor)。
从段描述符中获取该段的基地址和巨细等信息,将段内偏移量与基地址相加,得到线性地址。
线性地址是假造地址的一种,它是由操作系统管理的地址空间中的地址,而不是直接映射到物理地址。线性地址是在分页式管理中进一步映射到物理地址的中间层。
物理地址是实际存在于盘算机物理内存中的地址。在操作系统中,物理地址通常是通过内存管理单元(MMU)举行映射,将线性地址映射到物理地址。这种映射可以通过页表实现。由于物理地址是由硬件天生的,因此用户程序无法直接访问物理地址。
7.3 Hello的线性地址到物理地址的变更-页式管理

在页式管理下,物理内存被分别为巨细相等的页帧,而逻辑地址空间被分别为巨细相等的页。线性地址由32位构成,其中高20位为页目录项的索引,中间10位为页表项的索引,低12位为页内偏移量。
在Linux系统中,进程的每个线性地址空间都有一个对应的页目录表和多个页表。进程通过利用页目录表和页表来将线性地址翻译为物理地址。
在Hello程序运行时,操作系统将Hello进程的代码和数据加载到进程的地址空间中,同时为该进程创建页目录表和页表。当进程访问某个地址时,处理器会先将线性地址分解成页目录项和页表项的索引,然后访问相应的页目录表和页表,将其翻译成对应的物理地址。这个过程在硬件上完成,处理器内部有相应的地址转换缓冲区来加快地址翻译。
页式管理使得进程的地址空间可以离散地分配在物理内存中,从而避免了内存碎片的题目。别的,页式管理也使得内存管理变得更加灵活,例如可以为差别的进程分配差别的页表,从而隔离它们的地址空间,掩护它们的数据不被其他进程访问。

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

Core i7采取四级页表的层次布局。CPU产生VA,VA传送给MMU,MMU利用VPN高位作为TLBT和TLBI向TLB中寻找匹配。假如掷中,则得到PA。假如TLB中没有掷中,MMU查询页表,CR3确定第一级页表的起始地址,VPN1确定在第一级页表中的偏移量,查询出PTE,以此类推,最终在第四级页表中找到PPN,与VPO组合成PA,添加到PLT。
7.5 三级Cache支持下的物理内存访问

三级Cache通常包罗L1 Cache、L2 Cache和L3 Cache。L1 Cache位于处理器核心内部,速率最快,容量最小,一样平常用于存储指令和数据,每个核心都有一个L1 Cache;L2 Cache位于处理器核心与主存之间,速率较快,容量比L1 Cache大,多个核心共享一个L2 Cache;L3 Cache位于处理器与内存控制器之间,速率较慢,容量最大,多个核心共享一个L3 Cache。
在物理内存访问时,当CPU需要访问某个地址时,会先查抄L1 Cache是否存在该数据。假如L1 Cache中存在该数据,则直接返回给CPU,不需要访问内存;否则,L1 Cache会将数据请求发送到L2 Cache,假如L2 Cache中存在该数据,则返回给L1 Cache,L1 Cache再返回给CPU,不需要访问内存;假如L2 Cache中也不存在该数据,则L2 Cache会将数据请求发送到L3 Cache,同样地,假如L3 Cache中存在该数据,则返回给L2 Cache,L2 Cache再返回给L1 Cache,L1 Cache最终返回给CPU,不需要访问内存;假如L3 Cache中也不存在该数据,则L3 Cache会将数据请求发送到主存,主存将数据返回给L3 Cache,L3 Cache再返回给L2 Cache,L2 Cache再返回给L1 Cache,L1 Cache最终返回给CPU,完成内存访问。
TLB(Translation Lookaside Buffer)是一种高速缓存,用于存储假造地址到物理地址的转换。在利用页式管理时,每个进程有自己的页表,用于将假造地址映射为物理地址。每次访问内存时,CPU会将假造地址发送到TLB,TLB会先查抄是否存在该假造地址对应的物理地址,假如存在,则直接返回物理地址;否则,TLB会将假造地址发送到主存,主存将对应的物理地址返回给TLB,同时,TLB会将该假造地址和物理地址的映射关系存储到TLB中,以便下次访问时可以直接返回物理地址,提高访问速率。
7.6 hello进程fork时的内存映射

在Hello进程调用fork()创建子进程时,子进程将会拥有一个新的、独立的进程地址空间,其中包罗与父进程雷同的代码和数据,但是各自拥有独立的栈空间和堆空间。
详细地,fork()系统调用在子进程中返回0,在父进程中返回子进程的进程ID。当子进程被创建时,其进程地址空间会被操作系统中的MMU(内存管理单元)重新映射,以反映新的进程地址空间,即:
子进程复制父进程的页表,并将父进程页表项中的写标志位设置为只读。这是为了确保子进程不能修改父进程的地址空间。
子进程对于那些已经被父进程修改的页,将会使对这些页的写操作产生页错误(page fault),并分配一个新的物理页来存储该页的副本。这被称为写时复制(copy-on-write)。
子进程根据需要分配新的内存,比如堆空间,利用系统调用brk()或mmap()来分配一块新的内存。
子进程根据需要加载新的可执行代码,利用系统调用execve()来执行一个新的可执行文件。
值得注意的是,由于写时复制技能的利用,父进程和子进程之间的内存共享只有在它们之间没有任何写操作时才是有效的。因此,当一个进程需要在子进程中修改内存时,必须利用进程间通信(IPC)机制来传递数据。
7.7 hello进程execve时的内存映射

当 hello 进程调用 execve 函数时,它会首先清空自己的假造地址空间,然后载入可执行文件中的代码和数据段。这个过程会重新映射新的假造地址到物理内存中。
详细来说,进程在执行 execve 函数后,内核会执行以下步调:
将 ELF 文件的程序头表读入内存,并解析出可执行文件各个段的信息。
分配新的页表,并建立新的假造地址空间。
从可执行文件中读取代码段、数据段等信息,并将它们映射到新的假造地址空间中。
将程序的入口地址设置为代码段的起始地址。
详细来说,hello 进程执行 execve 函数后,内核会在其假造地址空间中建立代码段、数据段、堆和栈,它们的巨细和位置如下:
代码段:包罗可执行代码,巨细为 4KB,起始地址为 0x400000。
数据段:包罗全局变量和静态变量,巨细为 4KB,起始地址为 0x601000。
堆:动态分配的内存地区,巨细不固定,起始地址为 0x8d9010。
栈:生存局部变量和函数调用信息,巨细为 8KB,起始地址为 0x7fffffffdef0。
在这个过程中,内核会根据物理内存的利用环境来选择合适的物理页面来存储进程的各个段。它还会根据 CPU 的 TLB 缓存来加快假造地址到物理地址的转换过程。

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

在盘算机系统中,程序访问某些假造内存时,假如对应的物理内存没有被加载到主存中,就会发生缺页故障(page fault)。此时CPU会产生一个中断信号,称为缺页中断(page fault interrupt),操作系统会捕获该中断信号并举行处理,将缺失的页加载到物理内存中,然后重新执行产生缺页中断的指令。
缺页故障的处理过程一样平常如下:
CPU在访问某个假造地址时发现对应的页表项(或页目录项)无效或没有加载到TLB中,此时就会触发缺页异常,也称为缺页中断。
操作系统的中断处理例程捕获到缺页异常信号,生存当前进程的上下文,根据缺失的页号查找磁盘上的对应页,将该页调入内存。
将该页放入空闲物理页框中,更新页表项(或页目录项)的状态,指向该物理页框,然后更新TLB中对应的页表项。
规复进程上下文,将控制权返回到引起缺页异常的指令处,重新执行该指令。
缺页故障的处理会增长一定的开销,因为需要从磁盘中读取数据到内存中,而磁盘的访问速率远慢于内存。为了镌汰缺页故障的次数,可以利用一些优化计谋,如预先加载(预取)页面、利用局部性原理(尽量访问靠近上一次访问的地址)等。同时,CPU和操作系统中都会有缓存来加快内存访问,如CPU中的缓存和操作系统中的页缓存等。

7.9动态存储分配管理

       动态存储分配管理是指在程序运行过程中,根据程序的实际需要举行内存的分配与释放的管理方式。常见的动态存储分配方式有两种:堆和栈。

堆是由程序员手动申请和释放的内存空间,由malloc函数举行申请,由free函数举行释放。堆的分配方式是基于内存池的,当申请一块内存时,系统会在内存池中找到一块足够大的空闲内存,并返回给程序员。当释放一块内存时,这块内存会被放回内存池中以供下次利用。
栈则是由系统自动分配和回收的内存空间,用于存储函数的参数、局部变量等。栈的分配方式是按照“先进后出”的原则举行的,当一个函数被调用时,系统会为该函数在栈上分配一段内存,该函数的参数和局部变量将被存储在这段内存中。当函数执行完毕时,系统会自动将这段内存释放。
在动态存储分配管理中,常常会出现内存泄漏和内存溢出的题目。内存泄漏指的是在程序运行过程中,由于程序员未能正确地释放内存,导致内存得不到回收,最终导致系统内存耗尽。内存溢出则是指程序申请的内存超出了系统所能提供的内存巨细。
为了避免这些题目,需要程序员在举举措态存储分配时,细致管理内存的申请和释放。常用的方法包罗利用RAII技能和智能指针等。RAII技能是指在对象的构造函数中申请内存,在析构函数中释放内存,从而包管内存的正确释放。智能指针则是一种封装了指针的类,能够自动管理指针的内存申请和释

7.10本章小结

本章以hello为例子,简述了在盘算机中的假造内存管理,假造地址、物理地址、线性地址、逻辑地址的区别以及它们之间的变更模式,以及段式、页式的管理模式。

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

Linux的IO设备管理方法主要涉及三个方面:设备文件、设备驱动程序和设备控制器。
设备文件:在Linux系统中,每个设备都会被视为一个文件,对设备的操作都通过打开、读、写、关闭文件来完成。设备文件的定名规则一样平常是/dev/xxx,其中xxx表示设备的名称。
设备驱动程序:设备驱动程序是毗连应用程序和设备控制器的重要桥梁,它提供了访问设备硬件的接口,可以将设备的操作(如读、写、控制等)转换为硬件的控制信号。
设备控制器:设备控制器是真正执行操作的硬件部门,它可以控制设备的输入、输出、状态等操作。
在Linux中,IO设备管理主要是通过驱动程序来实现的。驱动程序可以分为两种:字符设备驱动和块设备驱动。其中,字符设备驱动主要管理像串口、终端、鼠标等字符设备,而块设备驱动主要管理像硬盘、U盘、闪存等块设备。
Linux中的IO设备管理方法还包罗了缓冲机制、中断机制、DMA(直接内存访问)等技能,以提高IO设备的读写性能和系统响应速率。
设备的模子化:文件
设备管理:unix io接口
8.2 简述Unix IO接口及其函数

Unix提供了一套IO接口,包罗文件IO和网络IO。其中文件IO的函数主要在<stdio.h>和<fcntl.h>头文件中定义,网络IO的函数主要在<sys/socket.h>和<netinet/in.h>头文件中定义。
常见的文件IO函数有:
fopen():打开文件
fclose():关闭文件
fread():从文件中读取数据
fwrite():向文件中写入数据
fseek():移动文件指针
ftell():获取当前文件指针位置
rewind():将文件指针移到文件开头
feof():判断是否已到达文件结尾
ferror():判断是否发生错误
常见的网络IO函数有:
socket():创建套接字
bind():绑定套接字到指定的地址和端口号
listen():将套接字设置为监听状态,等候客户端毗连
accept():接受客户端的毗连请求,创建新的套接字
connect():向服务器发起毗连请求
read():从套接字中读取数据
write():向套接字中写入数据
close():关闭套接字
这些函数基本涵盖了Unix IO接口的常用操作,我们可以根据详细需求选择合适的函数举行文件IO或网络IO操作。
8.3 printf的实现分析

printf是C语言标准库中的一个函数,用于将格式化的数据输出到标准输出流(stdout)中。下面是printf的实现分析过程:

格式化字符串处理

printf函数首先会将传入的格式化字符串举行处理,根据格式化字符串中的占位符,将变量转换成指定格式并生存在缓存中。

vsprintf函数处理

接下来,printf函数将处理好的字符串传递给vsprintf函数,vsprintf函数将格式化字符串中的占位符替换成对应的值,并将天生的字符串写入到一个字符数组中。

write系统调用

printf函数将处理好的字符串传递给write系统调用,write函数会将字符串写入到标准输出流(stdout)中。

系统调用陷阱

在Linux系统中,用户程序无法直接访问内核空间,需要通过系统调用来请求内核执行一些操作。printf函数通过int 0x80或syscall等指令发起一个陷阱,将控制权转移到内核中的中断服务例程中。



内核态处理

进入内核态后,内核将从系统调用号中确定调用的函数,并根据系统调用参数,执行相应的操作。

文件描述符处理

在内核中,标准输出流(stdout)是通过文件描述符表示的,printf函数调用write系统调用时,需要传递一个文件描述符参数。在内核中,文件描述符表示为一个整数,内核根据该整数找到对应的文件句柄,并将数据写入到该句柄对应的文件中。

中断返回

当系统调用完成后,控制权返回到用户态,printf函数执行完毕,程序继承向下执行。

8.4 getchar的实现分析

getchar 的源代码为:

int getchar(void)

{

static char buf[BUFSIZ];

static char *bb = buf;

static int n = 0;

if(n == 0)

{

n = read(0, buf, BUFSIZ);

bb = buf;

}

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

}
       当程序调用getchar函数时,程序会先判断标准输入流stdin是否还有缓冲区中的字符,假如有,则直接返回该字符;假如没有,则会调用read函数从标准输入流读入一定命量的字符到缓冲区中。

假如缓冲区中有字符,则直接返回第一个字符,并将缓冲区指针后移一位,否则将会调用read函数从标准输入流读入一定命量的字符到缓冲区中。

在调用read函数时,内核会将标准输入流中的字符读入到内核空间中的缓冲区,然后再通过内核空间和用户空间之间的数据传输,将字符拷贝到用户空间的缓冲区中。

当缓冲区中的字符全部被读取后,read函数将会再次从标准输入流中读取一定命量的字符到缓冲区中,并重复第2步的操作。

假如在读取过程中遇到了文件竣事标志(EOF),则会将EOF返回给程序,同时也会将EOF置为缓冲区的第一个字符,如许下一次调用getchar函数时就可以直接返回EOF,而无需再次从标准输入流中读取字符。

假如在读取过程中遇到了错误(比如文件被删除或者权限不敷等),则会将错误码返回给程序。

程序可以通过在标准输入流中插入EOF来手动竣事输入。
异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,生存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。
8.5本章小结

第八章讲述了Linux中IO设备的管理方法,IO接口及其函数,最后分析了printf和getchar函数的实现方法
结论

用盘算机系统的语言,逐条总结hello所经历的过程。
1.编写源代码
Hello程序的源代码是经过编写的,包罗利用的头文件、定义的变量、执行的代码等。

2.预处理
在编译之前,对源代码举行预处理,主要包罗处理头文件、宏替换等操作。预处理天生的效果被编译器利用。

3.编译
编译器将预处理天生的效果转换成汇编代码,然后将汇编代码转换成可执行文件的目标代码。

4.链接
将目标代码和所需的库文件链接起来,天生可执行文件。在链接的过程中,动态链接库被加载到内存中,函数调用通过动态链接库举行。

5.加载
将可执行文件加载到内存中运行,此时操作系统将创建一个新的进程,将可执行文件的代码、数据、堆栈等加载到进程的假造地址空间中。

6.进程执行
进程开始执行,执行过程中,操作系统会对进程举行调度,分配时间片,处理IO请求等操作。进程通过系统调用访问操作系统提供的服务,例如打印输出、读取输入、创建进程等。

7.内存管理
在进程执行过程中,操作系统通太过页机制将进程假造地址映射到物理地址。进程需要分配内存时,操作系统会在进程的假造地址空间中分配一块可用的内存空间,并将其映射到物理地址空间中。

8.输入输出
在进程执行过程中,操作系统负责管理输入输出设备。当进程需要读取用户输入时,操作系统会从标准输入设备中读取数据;当进程需要输出数据时,操作系统会将数据写入标准输出设备中。

9.进程终止
当进程执行完成或出现异常环境时,操作系统会将进程终止。在终止进程时,操作系统会回收进程所占用的内存空间、关闭进程所打开的文件等操作。
你对盘算机系统的计划与实现的深切感悟,你的创新理念,如新的计划与实现方法。
作为一台盘算机系统,其计划与实现的过程中需要思量到各种差别的因素和需求,包罗性能、功耗、安全性、可靠性、可维护性等等。在计划与实现的过程中,应该注重实用性和可持续性,而不是盲目地寻求性能和规模。

附件

列出所有的中间产物的文件名,并予以阐明起作用。
hello.c:程序源代码文件。
hello.i:预处理器对hello.c举行宏替换和头文件展开后天生的中间文件。
hello.s:编译器对hello.i举行编译后天生的汇编代码文件。
hello.o:汇编器将hello.s汇编成目标文件hello.o,其中包罗了呆板码和可重定位信息。
hello:可执行文件,由链接器将目标文件和系统库链接天生,包罗了程序的呆板码和可执行信息。
helloo.elf:hello.o的ELF格式。
hello.elf:hello的ELF格式,分析重定位过程。

参考文献

[1]    Randal E.Bryant David R.O Hallaron. 深入明白盘算机系统(第三版),机器工业出书社,2016



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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

一给

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表