哈工大2022计算机系统大作业

计算机系统

大作业

题     目  程序人生-Hello’s P2P 

专       业        计算学部          

学     号        120L020523        

班     级        2003004          

学       生        马子涵            

指 导 教 师         史先俊             

计算机科学与技术学院

2022年5月

摘  要

本文主要探讨了一个特定的c语言程序是如何一步步变成一个可执行程序,又是如何在系统中运行并被回收的,即P2P和020两个过程,其中涉及预处理器,编译器,汇编器,链接器,shell,虚拟内存,IO等内容。从而深入理解计算机系统

关键词:预处理器;编译器;汇编器;链接器;shell;虚拟内存;IO;                           

目  录

第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语言编写的程序开始的,首先通过cpp对hello.c进行预处理得到预处理后的文本文件hello.i,再通过编译器对hello.i进行编译得到汇编语言文件hello.s,随后通过汇编器生成可重定向目标文件hello.o,最后由链接器将hello.o与其他相对应的库函数进行链接得到可执行文件hello,在shell控制台中使用“./hello 学号 姓名 秒数”命令,就可以启动该程序,首先shell程序会使用fork创建一个子进程,然后调用execuve函数在该子进程中运行hello程序,在程序执行完成后会在屏幕上输出学号和姓名,此时父进程将hello进程回收,hello生命周期结束。

1.2 环境与工具

硬件环境:Intel X64 CPU 2.30GHz;16G RAM;2T SSD

软件环境:Windows10 64位; Vmware 16 PRO; Ubuntu 20.04 LTS 64位;

开发工具:GCC;GDB

1.3 中间结果

hello.c:源代码文件

hello.i:预处理后的文本文件

hello.s:编译后的汇编代码文件

hello.o:汇编后的可重定位目标文件

hello_elf.txt:hello.o的ELF格式文件

hello:链接后的可执行文件

helloelf.txt:hello的ELF格式文件

1.4 本章小结

    本章首先简述了hello的包括P2P和020的整个生命周期,交代了本实验中用到的开发环境和调试工具同时列出了中间过程中产生的文件。

第2章 预处理

2.1 预处理的概念与作用

概念:预处理器cpp根据hello.c源文件中以#开头的命令,拓展源代码,得到hello.i

作用:插入#include后的头文件中的内容,进行宏替换并删除注释,方便以后的编译、汇编等操作

2.2在Ubuntu下预处理的命令

使用gcc -E helo.c -o hello.i或者cpp hello.c>hello,i

 

2.3 Hello的预处理结果解析

hello.i文件相较于hello.c文件被扩展到了3000多行,gcc对源文件进行了宏的展开,替换了头文件,删除了注释,具体请见附件

 

2.4 本章小结

本章叙述了预处理的概念和作用,利用指令对hello.c进行预处理得到了hello.i文件,并通过观察hello.i文件分析了处理结果

第3章 编译

3.1 编译的概念与作用

概念:读取预处理文本文件hello.i对其进行语法分析,将C语言转换成等价的汇编语言指定,得到汇编代码hello.s文件

作用:将用户编写的高级语言程序转换成汇编语言程序,方便后续的汇编和链接操作

3.2 在Ubuntu下编译的命令

使用gcc -S hello.i -o hello.s

 

3.3 Hello的编译结果解析

3.3.1数据

1.字符串:存在两个只读字符串描述格式化输出格式:

 

2.局部变量i:

    main函数中声明了一个局部循环变量i,编译器进行编译时并没有将局部变量放入寄存器中,而是将其保存在了堆栈中,通过rbp的相对偏移量来访问

 

4.参数argc和*argv[]:

    参数argc和*argv[]是用户传给main函数的参数,首先存在寄存器edi和rsi中作为参数传入main函数,随后被压入栈中,通过rbq的相对偏移来访问。

 

5.数组*argv[]

    hello.c中存在唯一一个数组*argv[],这个数组的起始元素被存储在栈中rbq-32的位置,通过数组第一个元素加上偏移量来访问数组中的任意元素

 

6.指针*argv[]

    hello.c中存在指针数组*argv[],在访问指针所指向的元素时,只需要访问指针所保存的地址对应的元素即可。

 

3.3.2算数操作

    hello.c中只存在一个算数操作i++,由于i是nt类型数据,所以通过addl指令实现该算数操作

 

3.3.3赋值

    赋值操作利用mov语句,hello.c中只存在一条赋值语句i=0,由于i是int型数据,因此使用movl指令进行赋值

 

3.3.4关系操作

    hello.c中存在两条操作分别为argc!=4的判断,以及i<8的判断,这两条先通过cmpl指令来设置条件码,再通过跳转指令进行相应跳转

 

3.3.5控制转移指令

    根据3.3.3中cmpl设置的条件码分别使用je和jle进行跳转

 

 

3.3.6函数操作

    在64位操作系统中,参数通过寄存器传递,用于传递参数的寄存器顺序依次是rdi、rsi、rdx、rcx、r8、r9,再多出来的参数通过栈元素来传递。调用参数使用的是call指令

 

 

 

 

 

 

3.4 本章小结

本章首先介绍了编译的概念和作用,将预处理得到的hello.i文件汇编得到汇编代码hello.s,将hello.s文件打开后,观察各部分C语言指令对应的汇编语言指令,分析编译器的工作原理。

第4章 汇编

4.1 汇编的概念与作用

概念:汇编指的是汇编器将.s文件翻译成机器语言,并将其生成后缀名为.o的可重定位目标文件的过程。

作用:将.s文件生成机器码,使其能够被链接器ld链接生成可执行文件。

4.2 在Ubuntu下汇编的命令

使用gcc -c hello.s -o hello.o或者as hello.s -o hello.o

 

4.3 可重定位目标elf格式

4.3.1 ELF头:

包含了系统信息,编码方式,ELF头大小,节的大小和数量等等一系列信息

 

4.3.2 节头表:

描述了.o文件中出现的各个节的类型、位置、所占空间大小等信息

 

4.3.3 符号表

符号表中存放着程序定义和引用的全局变量和函数。

 

4.4.4 重定位节:

重定位节包含了.text文件中需要重定位的信息,在链接器将目标文件与其它文件进行链接时需要修改这些信息,可执行文件中不包含重定位节。

 

4.4 Hello.o的结果解析

在数字表示法中,hello.s中的操作数用十进制表示,而hello.o反汇编代码中的操作数是十六进制。

在控制传输上,hello.s使用.L2和.LC1以及其他段名称,而反汇编代码使用目标代码的虚拟地址跳转。但是,重新定位条目保留,跳转地址为零。链接后,它们将填充到正确的位置。

在函数调用中,hello.s直接调用函数名,而反汇编代码中的调用是目标的虚拟地址。

 

 

4.5 本章小结

本章首先叙述了汇编的概念和作用,对汇编代码hello.s进行汇编,得到可重定向文件hello.o,通过readelf分析hello.o的ELF格式,分析hello.o的文件构成,再对比hello.o的反汇编代码和hello.s的汇编代码,了解汇编器对源代码进行了哪些修改,进一步证明链接的必要性。

5章 链接

5.1 链接的概念与作用

概念:将各部分呢代码和数据片段收集并组合成为一个单一的完整的可执行文件

作用:链接的存在使得将一个大型软件分开编写成为可能,再开发大型软件的过程中,可以按照功能将一个软件分成多分代码来编写,这些代码分别编译汇编生成.o文件,最后再用链接器生成一个统一的可执行文件。这使得开发大型软件的复杂度大幅下降。

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的格式

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

ELF Header:ELF头以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序

 

Section Headers:节头部表,描述了.o文件中各个节的名字、类型、地址、大小信息

 

 

  Program Headers:程序头部表,为链接器提供运行时的加载内容和提供动态链接的信息

 

.symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息

 

 

5.4 hello的虚拟地址空间

使用edb加载hello,得到本进程从0x401000开始

有5.3中的section headers节头部表信息可以得知,.init节的虚拟地址位0x401000,.rodata节的虚拟地址为0x402000,与用edb查看得到的结果相符合

 

5.5 链接的重定位过程分析

hello和hello.o相比,可以发现以下的不同:

1.hello的反汇编代码的起始地址是从0x401000开始的,而hello.o的地址则是从0开始的相对地址,这说明链接器给hello.o分配了内存地址

 

2.hello的跳转和调用函数使用的是绝对地址,而hello.o使用的是相对于main函数位置的相对地址

 

3.hello的反汇编代码相比hello.o的反汇编代码拥有更多的节,同时各种调用的库函数也被连接器链接了进来

重定位的过程,我们拿exit函数来举例

 

由上图可知,ADDR(s)=0x401125

 

而根据上图,可知,r.symbol为0x2b,通过函数main的起始地址和重定位条目中的偏移量字段相加,可以算出引用的运行时地址ref_addr=0x401150,然后更新这个符号引用,使得它在运行时指向exit函数具体计算方法则是用函数exit的起始地址减去刚才计算得到的运行时地址*ref_ptr = ADDR(exit) – ref_addr + r.addend=0x4010d0 -0x401150 -0x4=-0x84实际上这一步就是在求两个地址之间的相对位置,经过上述计算,在最终得到的可执行程序hello中,call指令的形式如图所示

 

也就是说,程序运行时call存放在0x40114f处,当CPU执行call指令是,此时PC的值为下一条指令的地址0x401154,此时更新PC,PC= PC+(-0x84)=0x4010d0,此恰好为函数exit的第一条指令,综上所述就是重定位相对引用的具体过程。

5.6 hello的执行流程

名称

地址

ld-2.23.so!_dl_start

0x7f1dda233df0

ld-2.23.so! dl_init

0x7f1dda240dd0

hello!_start

0x4010f0

hello!__libc_start_main

0x403ff0

libc-2.23.so!__libc_start_main

0x7f1dd9e86da0

libc-2.23.so! cxa_atexit

0x7f1dd9ea08e0

hello!__libc_csu_init

0x4011c0

hello!_init

0x401000

libc-2.23.so!_setjmp

0x7f1dd9e86da0

libc-2.23.so!_sigsetjmp

0x7f1dd9e9b810

hello!main

0x401125

hello!puts@plt

0x404018

hello!exit@plt

0x404038

hello!printf@plt

0x404020

hello!sleep@plt

0x404040

hello!getchar@plt

0x404028

ld-2.23.so!_dl_runtime_resolve_avx

0x7f1dda247ec0

libc-2.23.so!exit

0x7eeff0a5dc00

5.7 Hello的动态链接分析

当程序调用共享库定义的函数时,编译器无法预测该函数的运行时地址,因为定义它的共享模块可以在运行时加载到任何位置。通常的方法是为引用生成重新定位记录,然后动态链接器在加载程序时对其进行解析。但是,这需要链接器修改调用模块的代码段。GNU编译系统使用一种称为延迟绑定的技术来延迟过程地址的绑定,直到第一次调用该过程为止。

延迟绑定是通过got和PLT两种数据结构之间的交互来实现的。Got是数据段的一部分,PLT是代码段的一部分。PLT和got之间的协作可以在运行时解析函数的地址,实现函数的动态链接。

进程链接表PLT是一个数组,每个条目是16字节代码。PLT[0]是一个跳转到动态链接器的特殊条目。可执行程序调用的每个库函数都有自己的PLT条目。从节头表中可知,存储PLT节的起始地址为0x401020

我们可以在Hello的反汇编结果中看到每个PLT条目。PLT[0]是一个跳转到动态链接器的特殊条目。接下来,每个条目对应一个被调用的库函数。例如,PLT【1】对应于put函数;PLT【2】对应于printf函数

Got:Got是一个数组,其中每个条目都是一个8字节的地址。当与PLT结合使用时,got[0]和got[1]包含动态链接器在解析函数地址时将使用的信息。Got[2]是动态链接器再1d-linux.son模块中的入口点,其余的每个条目对应一个被调用的函数

do_init之前:

do_init之后:

 

5.8 本章小结

本章首先叙述了链接的概念和作用,将可重定向文件hello.o与相应的系统库函数进行链接得到可执行文件hello,对比hello和hello.o的ELF格式文件和反汇编文件,观察两者之间的不同,分析了链接器的工作内容与细节

6章 hello进程管理

6.1 进程的概念与作用

概念:进程是正在执行中程序的实例,是系统进行资源分配和调度的基本单位。系统中的每个程序都运行在某个进程的上下文中,上下文是由程序正确运行所需要的状态组成的。

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

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

作用:shell由输入设备读取命令,再将其转为计算机可以了解的机械码,然后执行它。各种操作系统都有它自己的 shell,以 DOS 为例,它的 shell 就是 command.com文件。如同 DOS 下有 NDOS,4DOS,DRDOS 等不同的命令解译程序可以取代标准的command.com ,UNIX 下除了 Bourne shell(/bin/sh) 外还有 C shell(/bin/csh)、Korn shell(/bin/ksh)、Bourne again shell(/bin/bash)、Tenex C shell(tcsh)等其它的 shell。UNIX/linux将 shell 独立于核心程序之外, 使得它就如同一般的应用程序, 可以在不影响操作系统本身的情况下进行修改、更新版本或是添加新的功能。

Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核。不仅如此,Shell有自己的编程语言用于对命令的编辑,它允许用户编写由shell命令组成的程序。Shell编程语言具有普通编程语言的很多特点,比如它也有循环结构和分支控制结构等,用这种编程语言编写的Shell程序与其他应用程序具有同样的效果

处理流程:shell首先检查命令是否是内部命令,若不是再检查是否是一个应用程序(这里的应用程序可以是Linux本身的实用程序,如ls和rm,也可以是购买的商业程序,如xv,或者是自由软件,如emacs)。然后shell在搜索路径里寻找这些应用程序(搜索路径就是一个能找到可执行程序的目录列表)。如果键入的命令不是一个内部命令并且在路径里没有找到这个可执行文件,将会显示一条错误信息。如果能够成功找到命令,该内部命令或应用程序将被分解为系统调用并传给Linux内核。

6.3 Hello的fork进程创建过程

向终端输入“./hello 120L020523 马子涵 1”命令后,shell首先会接受该命令并判断该命令是否是系统内置命令,shell发现该命令不是系统内置命令后就会调用fork函数为其创建一个子进程并返回子进程的pid,进而执行hello程序。

6.4 Hello的execve过程

用fork函数创建完子进程后,会使用解析后的命令行参数调用启动加载器来执行hello,然后启动代码设置栈,将控制传递给hello的主函数。

6.5 Hello的进程执行

上下文是内核重新启动一个进程所需要的状态,包括通用寄存器,程序计数器等数据,而一个进程执行它的控制流的每一个时间段叫做时间片。

再hello进程执行的过程中开始时shell程序将控制权交给hello函数,在用户模式下运行,在收到系统异常信号后进入内核模式,程序运行用户编写的信号处理程序,处理完成后要么直接终止进程要么返回到用户模式,将控制权返回给hello。在hello运行的过程中,hello进程与其他进程交替使用cpu和内存,实现进程调度。

6.6 hello的异常与信号处理

6.6.1异常和信号异常可分为四类:中断、陷阱、故障、终止。具体内容如下图所示:

 

hello程序中可能出现的异常可以包括以上四种异常,在发生异常时,硬件会发送各种信号给用户进程,此时用户进程将控制权移交给内核,内核运行信号处理程序,决定进程下一步如何进行

6.6.2键盘上各种操作导致的异常:

1.不停乱按键盘。结果如下图所示,乱按只是将屏幕的输入缓存近shell的读取输入缓存中,当hello进程结束后,这些字符串会作为命令输入到shell中

 

2.crtl+Z

在hello进程运行的过程中输入ctrl+Z结果默认时暂停并挂起前台作业hello,此时hello进程并没有被回收,在接收到SIGCON信号后会继续运行,通过ps指令可以查看到hello进程并没有被回收。

 

3.ctrl+C

在键盘上输入ctrl+C后会导致内核发送SIGINT信号到前台进程组的每个进程,其中包括hello进程,调用信号处理函数后,返回的默认结果是终止hello进程,同时shell程序会回收hello进程,使用ps查看后发现当前进程没有hello

 

6.7本章小结

本章首先叙述了进程的概念和作用,分析了逻辑控制流和虚拟内存的机制,随后介绍了shell的作用和处理流程,然后分析了hello程序运行过程中fork进程创建和execve过程,最后列举了hello进程运行过程中可能遇到的异常信号以及系统对相应信号的处理方法。

7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:由段标识符+段内偏移量组成,与物理地址无关,是hello程序编译为hello.o中的相对偏移地址。

线性地址:线性地址是逻辑地址到物理地址变换的中间层。逻辑地址加上相应的基地址就成为了线性地址,如果启用了分页机制,那么线性地址可以再经变换以产生一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。

虚拟地址:与线性地址相同。

物理地址:物理内存中对应的地址。

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

段式管理(segmentation),是指把一个程序分成若干个段(segment)进行存储,每个段都是一个逻辑实体(logical entity),程序员需要知道并使用它。它的产生是与程序的模块化直接有关的。段式管理是通过段表进行的,它包括段号或段名、段起点、装入位、段的长度等。此外还需要主存占用区域表、主存可用区域表。

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

保护模式下:以段描述符作为下标,到GDT/LDT表查表获得段地址,

段地址+偏移地址=线性地址。

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

将各进程的虚拟空间划分成若干个长度相等的页(page),页式管理把内存空间按页的大小划分成片或者页面(page frame),然后把页式虚拟地址与内存地址建立一一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题。页式管理采用请求调页或预调页技术实现了内外存存储器的统一管理。

在线性地址到物理地址的转换中,使用了翻译备份缓冲器(TLB)。首先,我们把线性地址分成VPN(虚拟页号)+VPO(虚拟页偏移),然后把VPN分成TLBT(TLB标签)+TLBI(TLB索引),然后到TLB缓存中找到相应的PPN(物理页号)。找到PPN后,它和VPO的组合成为PPN+VPO,这就是生成的物理地址。

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

步骤1:CPU产生一个虚拟地址。

步骤2和步骤3:MMU从TLB中获取相应的PTE。

步骤4:MMU把这个虚拟地址翻译成物理地址,并把它发送到高速缓存/主存。

步骤5:高速缓存/主存将请求的数据字返回给CPU.当TLB没有命中时,MMU必须从Ll高速缓存中检索相应的PTE.新检索的PTE被存储在TLB中,并可能覆盖已经存在的条目。

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

CPU发送一个虚拟地址,然后MMU根据上述操作得到物理地址PA,并根据缓存组的数量将PA分为CT、CS、CO,根据CS找到正确的组,并比较每个cacheline,看标记位是否有效,CT是否相等。如果命中,则直接返回所需的数据,如果不命中,则依次到L2、L3和主存确定是否命中,命中后将数据传给CPU并更新各级缓存的cacheline。

7.6 hello进程fork时的内存映射

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

当fork在新进程中返回时,新进程现在的虚拟内存刚好和调用fork时存在的虚拟内存相同。当这两个进程中的任一个后来进行写操作时,写时复制机制就会创建新页面。

7.7 hello进程execve时的内存映射

execve 函数在当前进程中加载并运行包含在可执行目标文件 hello 中的程序,用hello程序有效地替代了当前程序。

加载并运行hello需要以下几个步骤:

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

(2)映射私有区域。

(3)映射共享区域。

(4)设置程序计数器。

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

缺页故障:当指令引用一个相应的虚拟地址,而与改地址相应的物理页面不再内存中,会触发缺页故障。

 

当发生缺页时,处理流程如下:

(1)处理器生成虚拟地址,传送给MMU

(2)MMU翻译出PTE地址,并在高速缓存中查找PTE

(3)高速缓存将PTE返还给MMU

(4)PTE中有效位是0,引发缺页异常,调用缺页异常处理程序

(5)该程序选择一个牺牲页把它换到磁盘

(6)缺页处理程序页面调入新的页面,并更新内存中的PTE

(7)缺页处理程序返回到原来的进程,再次执行导致缺页的命令。

7.9动态存储分配管理

动态内存分配器维护者一个进程的虚拟内存区域,称为堆。对于每个进程,内核维护着一个变量brk,它指向堆的顶部。分配器将堆视为一组不同的大小的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。

分配器有两种基本风格。两种风格都是要求显示的释放分配块。

(1)  显式分配器:要求应用显示的释放任何已分配的块。例如C标准库提供一个叫做malloc程序包的显示分配器。

(2)  隐式分配器:要求分配器检测一个已分配块何时不再被程序使用,那么就释放这个块。

7.10本章小结

本章主要介绍了存储器地址空间的相关概念,分析了逻辑地址、线性地址、虚拟地址和物理地址四种地址空间概念的差别,结合hello程序讨论了cashe的物理内存访问、进程 fork 时的内存映射、execve 时的内存映射、缺页故障与缺页中断处理、动态存储分配管理等。

8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件

设备管理:unix io接口

所有的IO设备都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行,这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单低级的应用接口,称为Unix I/O,这使得所有的输入和输出都能以一种统一且一致的方式来执行。

8.2 简述Unix IO接口及其函数

Unix I/O接口统一操作:

1.打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个I/O设备,内核返回一个小的非负整数,叫做描述符,它在后续对此文件的所有操作中标识这个文件,内核记录有关这个打开文件的所有信息。

2.Shell创建的每个进程都有三个打开的文件:标准输入,标准输出,标准错误。

3.改变当前的文件位置:对于每个打开的文件,内核保持着一个文件位置k,初始为0,这个文件位置是从文件开头起始的字节偏移量,应用程序能够通过执行seek,显式地将改变当前文件位置k。

4.读写文件:一个读操作就是从文件复制n>0个字节到内存,从当前文件位置k开始,然后将k增加到k+n,给定一个大小为m字节的而文件,当k>=m时,触发EOF。类似一个写操作就是从内存中复制n>0个字节到一个文件,从当前文件位置k开始,然后更新k。

5.关闭文件,内核释放文件打开时创建的数据结构,并将这个描述符恢复到可用的描述符池中去。

系统级I/O函数:

(1)打开文件

函数原型:int open(char *filename, int flags, mode_t mode);

open函数将filename转换为一个文件描述符,并且返回描述符数字。flags参数指明了进程打算如何访问这个文件,mode参数则指定了新文件的访问权限位。

(2)关闭文件

函数原型:int close(int fd);

关闭描述符为fd的文件,关闭一个已关闭的描述符会出错。

(3)读和写文件

函数原型:

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

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

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

write函数从内存位置buf复制至多n个字节到描述符fd的当前文件位置。

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类型的arg,调用了vsprintf函数。

      Vsprintf函数:

int vsprintf(char *buf, const char *fmt, va_list args)

   {

    char* p;

    char tmp[256];

    va_list p_next_arg = args;

  

    for (p=buf;*fmt;fmt++) {

    if (*fmt != '%') {

    *p++ = *fmt;

    continue;

    }

  

    fmt++;

  

    switch (*fmt) {

    case 'x':

    itoa(tmp, *((int*)p_next_arg));

    strcpy(p, tmp);

    p_next_arg += 4;

    p += strlen(tmp);

    break;

    case 's':

    break;

    default:

    break;

    }

    }

  

    return (p - buf);

   }

vsprintf的作用是接受确定输出格式的格式字符串fmt,用格式字符串对个数变化的参数进行格式化,产生格式化输出并赋值给buf,最后返回输出字符串的长度。Printf中还调用了write函数,而write函数将栈中参数放入寄存器,ecx存放字符个数,ebx存放字符串首地址,最后调用sys_call。

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

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

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;

}

它的作用是从stdin流中读入一个字符,也就是说,如果stdin有数据的话不用输入它就可以直接读取了,第一次调用getchar()时,确实需要人工的输入,但是如果你输了多个字符,以后的getchar()再执行时就会直接从缓冲区中读取了

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

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

8.5本章小结

本章分析了linux下IO设备及管理方法、UNIX IO接口及其函数、PRINTF和GETCHAR函数的实现分析。

结论

hello所经历的过程:

1.编写:由C语言程序员编写hello.c

2.预处理:gcc将hello.c预处理成文本文件hello.i

3.编译:编译器将hello.i文件编译成汇编语言文件hello.s

4.汇编:汇编器将hello.s文件汇编成可重定向文件hello.o

5.链接:将hello.o与可重定位目标文件和动态链接库链接为可执行目标程序hello。

6.运行:在shell中输入./hello后面加上三个参数120L020523 马子涵 1。

7.创建子进程:shell进程调用fork为其创建子进程。

8.运行程序:shell调用execve,execve调用启动加载器,加映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入main函数。

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

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

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

12.信号:运行途中键入Ctr-C则停止,键入Ctrl-Z则挂起。

13.结束:shell父进程回收子进程,内核删除为这个进程创建的所有数据结构。

感悟:计算机系统课程虽然繁多,但逻辑结构清晰、层次分明,完成本论文让我深深的体会到了,一个普通的hello程序就能展现计算机系统的各个方面,同时CSAPP这本书及本次课程让我第一次从程序员的角度全面地认识了现代操作系统的机制与设计,而我未来也会继续深入学习相关知识,并在未来学以致用。

附件

hello.c:源代码文件

hello.i:预处理后的文本文件

hello.s:编译后的汇编代码文件

hello.o:汇编后的可重定位目标文件

hello_elf.txt:hello.o的ELF格式文件

hello:链接后的可执行文件

helloelf.txt:hello的ELF格式文件

参考文献

[1]  林来兴. 空间控制技术[M]. 北京:中国宇航出版社,1992:25-42.

[2]  辛希孟. 信息技术与信息服务国际研讨会论文集:A集[C]. 北京:中国科学出版社,1999.

[3]  赵耀东. 新时代的工业工程师[M/OL]. 台北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).

[4]  谌颖. 空间交会控制理论与方法研究[D]. 哈尔滨:哈尔滨工业大学,1992:8-13.

[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.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值