ICS大作业论文

题     目  程序人生-Hello’s P2P 

专       业        计算学部         

学     号       1190200614        

班     级        1903004         

学       生         任思雨      

指 导 教 师          史先俊       

摘  要

一个C程序在编写完成后,我们点击编译并运行,不到1秒就会产生输出结果。而在这短短的过程中,计算机系统却针对这个C程序执行了复杂的操作。

论文主要讲述了hello.c程序在计算机系统中编译、加载、执行的过程。编译要经过预处理、编译、汇编、链接等各个阶段,加载与执行离不开进程、内存分配、I/O管理的联合操作。

本文通过hello.c例程,对程序从源代码到可执行文件,再到被载入内存执行的过程,使用计算机系统的相关知识概念,进行解释。

关键词:计算机系统、编译,汇编,链接,进程,内存,shell,信号,I/O;

目  录

第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的预处理结果解析... - 6 -

2.4 本章小结... - 6 -

第3章 编译... - 7 -

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

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

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

3.3.1 argc与argv. - 10 -

3.3.2 if条件结构... - 10 -

3.3.3 for循环结构... - 10 -

3.3.4 调用函数... - 10 -

3.3.5 字符串... - 10 -

3.3.6 控制转移... - 11 -

3.3.7 算数加法运算... - 11 -

3.4 本章小结... - 11 -

第4章 汇编... - 12 -

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

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

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

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

4.4.1 反汇编hello.o. - 14 -

4.4.2 反汇编结果与最开始汇编代码的对比... - 15 -

4.5 本章小结... - 15 -

第5章 链接... - 16 -

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

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

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

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

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

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

5.7 hello的动态链接分析... - 22 -

5.8 本章小结... - 23 -

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

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

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

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

6.4 hello的execve过程... - 25 -

6.5 hello的进程执行... - 25 -

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

6.7本章小结... - 28 -

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

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

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

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

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

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

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

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

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

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

7.10本章小结... - 34 -

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

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

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

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

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

8.5本章小结... - 36 -

结论... - 37 -

附件... - 38 -

参考文献... - 39 -

第1章 概述

1.1 Hello简介

hello.c为源程序。对hello.c这一源程序依次进行预处理,编译,汇编,链接的操作之后,生成可执行程序hello。然后调用shell命令行输入运行hello的指令,fork产生子进程,hello从program成为process,这个过程即为P2P(Program to process)。

紧接着调用程序execve,通过依次进行对虚拟内存的映射,物理内存的载入,进入主函数执行指令。在程序执行完毕后shell父进程回收hello子进程,一切相关的数据结构被删除。即为020(From Zero to Zero)。

1.2 环境与工具

硬件环境

X64 CPU;2.6GHz;8G RAM;256GHD Disk

软件环境

Windows10 64位; VMware Workstation Pro;Ubuntu 20.04 LTS 64位

开发工具

gcc;edb 1.3.0;gdb;Code::Blocks

1.3 中间结果

hello.i:预处理hello.c生成的文件。

hello.s:编译hello.i生成的文件。

hello.o:汇编hello.s生成的文件。

hello:链接hello.o与系统库后生成的文件。

1.4 本章小结

       本章介绍了helloP2P与020的过程,列出了实验的环境与工具,并列出文章编写过程中产生的中间结果。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。比如#include 命令使cpp读取头文件stdio.h的内容,并把它插入到程序文本中。最后得到另一个C程序,它以.i作为文件拓展名。

预处理的作用根据字符#开头的命令不同而有不同。主要的作用有:针对#define的宏替换;##运算符将出现在##两侧的参数合并为一个符号;对#if--#endif进行条件编译;对#include后的文件进行文件包含等。

2.2在Ubuntu下预处理的命令

预处理命令:gcc -E hello.c  -o hello.i

预处理结果:

图2-1       执行预处理命令后文件管理器多出hello.i文件

图2-2       hello.i文件的部分内容

2.3 Hello的预处理结果解析

      经过对hello.c文件进行预处理,生成了hello.i文件。Hello.i文件既包含了原hello.c的全部内容,又将系统头文件stdio.h、unistd.h、stdlib.h中的内容包含在内。

2.4 本章小结

本节描述了预处理的概念与作用,并在Ubuntu中对hello.c进行了预处理,生成了hello.i文件。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

在编译阶段起作用的是编译器(ccl)。编译器对.i文件做词法分析、语法分析、语义分析等,在检查无错误后,将原本的C语言程序翻译成汇编语言程序,并将汇编语言程序保存到以.s作为拓展名的文本文件中。

3.2 在Ubuntu下编译的命令

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

编译结果:

图3-1     执行编译命令后文件管理器多出hello.s文件

图3-2       hello.s文件的部分内容

3.3 Hello的编译结果解析

本节将详细描述编译器是如何处理hello.c中C语言的各数据类型与操作。

以下是原C语言程序:

图3-3      hello.c中main()函数内容

以下是原C语言的汇编程序程序:

图3-4      hello.s中main()函数内容

3.3.1 argc与argv

       argc与argv在main()函数中作为函数参数出现。在汇编程序中刚进入函数时,由寄存器%edi保存第一个参数argc,由寄存器%rsi保存第二个参数argv。在(图3-4)第22、23行,汇编程序将两参数保存到栈中。

3.3.2 if条件结构

       C程序中的if(argc!=4)在汇编程序中为第24、25行。

24.  cmpl     $4, -20(%rbp)    //将argc与4相减

       25.  je           .L2                      //如果结果为0,即argc == 4,则程序跳转到.L2

       .L2为main()函数从for循环开始接下来的内容,如果没有跳转则继续执行第26行汇编指令。从第26行开始到第29行为if内的程序。

3.3.3 for循环结构

       for循环从.L2开始。

.L2

       31. movl      $0, -4(%rbp)             //为C程序中的局部变量i分配栈空间-4(%rbp)

                                                        //将i初始化为0

       32. jmp        .L3                      //跳转到.L3进行循环

.L3:

       53. cmpl      $7, -4(%rbp)          //for循环中的条件判断i<8

       54. jle           .L4                      //如果i – 7 <= 0,跳转到.L4执行循环体的内容

                                                        //否则跳出循环执行第55行指令

.L4:

51. addl        $1, -4(%rbp)             //在循环体程序执行完后,将i加1,接着执行

//第52行指令。从52行开始是.L3,即再次进行

//for循环条件判断。

3.3.4 调用函数

       main.c中C程序调用的函数有:printf(),exit(),sleep(),getchar(),sleep(),atoi()。

       以exit()函数为例说明汇编语言调用函数的方法:

       28. movl      $1, %edi      //先将函数参数放入参数寄存器中(如果有参数)

       29. call         exit@PLT   //用call指令调用函数

3.3.5 字符串

C程序的printf()函数的参数是字符串,字符串的具体内容存在内存中。当需要用字符串作函数参数时,汇编语言取出它的首字符地址放入参数寄存器中。

3.3.6 控制转移

       要想实现3.3.2与3.3.3中的控制转移,需要用到测试指令类test、比较指令类cmp、跳转指令类jmp。

       test与cmp能设置条件码,jmp指令类能根据条件码而选择是否进行跳转。

3.3.7 算数加法运算

       在3.3.3中,i的加1使用了汇编指令addl    $1, -4(%rbp),该指令将后一参数(寄存器或内存)中的数据自加前一参数,并将结果保存在后一参数中。

3.4 本章小结

本节介绍了汇编的概念和作用,在Ubuntu中对hello.i进行编译,生成了hello.s文件,并将hello.s中的汇编程序与C语言中的语句进行对比,对汇编代码进行了分析和解释。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

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

4.2 在Ubuntu下汇编的命令

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

汇编结果:

图4-1     执行汇编命令后文件管理器多出hello.o文件

4.3 可重定位目标elf格式

可重定位目标文件的格式:

ELF头:包含生成该文件的系统的字的大小和字节顺序、以及帮助连接器语法分

析和解释目标文件的信息。

.text:已经编译程序的机器代码

.rodata:只读数据

.data:初始化的全局和静态变量

.bss:未初始化的全局和静态变量

.symtab:符号表

.rel.text:.text节中位置的列表

.rel.data:被模块引用或定义的所有全局变量和重定位信息

.debug:调试符号表

.line:原始C源程序中的行号与.text的机器指令之间的映射

.strtab:字符串表

图4-2      可重定位段信息

图4-3     节头信息

4.4 Hello.o的结果解析

4.4.1 反汇编hello.o

在Ubuntu下执行objdump -d -r hello.o得到反汇编代码如下:

图4-4      Objdump反汇编hello.o得到的汇编代码

4.4.2 反汇编结果与最开始汇编代码的对比

机器语言由操作码和操作数组成,机器语言和汇编语言是一对一的映射关系。

在机器语言中:

1、立即数操作数由原汇编语言的十进制表示变为十六进制表示。

2、调用函数时,call的参数由原汇编语言的字符标识(printf @PLT)变为相对地址。

3、在控制转移时,跳转的目的位置由标号变为相对地址。

4.5 本章小结

本节介绍了汇编的概念与作用,对hello.s汇编生成了hello.o文件,对汇编后产生的可重定位文件的结构与组成部分进行了说明,比较了汇编语言与其对应的机器语言的区别。

(第41分)

5章 链接

5.1 链接的概念与作用

链接(linking)是将各种代码和数据片段收集组合成一个单一文件的过程。

链接使得分离编译成为可能,我们不必将一个大型的应用程序组织为一个巨大的源文件,而是可以将它分解为更小、更好管理的模块,我们可以独立地修改和编译这些模块,并重新链接他们,从而避免仅修改一小部分代码就重新编译整个巨大地源文件

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-1     执行链接命令后文件管理器多出hello文件

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

可执行目标文件hello的节头部表:

图5-2      可执行目标文件hello的节头部表:

5.4 hello的虚拟地址空间

用edb运行hello,hello的虚拟地址空间如下:

图5-2     执行hello时,hello的虚拟地址空间

只读代码段:

自0x400000开始,到0x403000结束。

该段包含5.3中hello文件的.init节、./text节、./rodata节。

读/写数据段:

       自0x403000开始,到0x405000结束。

该段包含5.3中hello文件的.data节、./bss节。

运行时堆:

       未在图中显示,位于数据段与共享库的内存映射区域之间。

共享库的内存映射区域:

       自0x7fc2baab3000开始,到0x7fc2baae2000结束。

用户栈

       自0x7ffclaa3b000开始,到0x7ffclaa5c000结束。

5.5 链接的重定位过程分析

执行objdump -d -r hello main函数部分如下:

图5-3       反汇编hello后main函数部分

图5-4      反汇编hello.o后main函数部分

hellohello.o的区别:

1、在hello中,main函数即其他二进制代码有了具体的地址;而在hello.o中,main函数地址默认未0,其他地址为相对main函数的地址。

链接确定了main函数的虚拟地址,进而通过相对地址确定其他代码的虚拟地址。

(a) 

(b)

5-5    对比1

2、在hello中,程序跳转指令的参数都是直接寻址;而在hello.o中,其参数为间接寻址。

链接确定了其它被调用函数的虚拟地址,使得调用函数或程序跳转能直接寻址。 

 (a) 

 (b)

5-6    对比2

3、在为printf函数读取字符串时,hello.o中的不同字符串的指令为同一个,而在hello中不同。

      链接将确定了符号代表的参数的具体位置,进而替换执行文件中的参数地址。

      

 (a)

(b)

5-7    对比3

5.6 hello的执行流程

使用edb执行hello,执行命令为:./edb --run hello 1190200614 任思雨 1

函数地址

函数名

0x7fb539b5bdf0

未知

0x7fb539b6bc10

未知

0x4010f0

_start

0x7fb539977fc0

__libc_start_main

0x7fb53999af60

__cxa_atexit

0x4011c0

__libc_csu_init

0x401000

_init

0x7fb539996e00

_setjmp

0x7fb539996d30

__sigsetjmp

0x401125

main

0x401040

printf@plt

0x401020

.plt

0x7f9ea54b2e10

printf

0x401060

atoi@plt

0x401020

.plt

0x7f81fe3ca730

atoi

0x7fc70b8cc030

nanosleep

0x7fc70b8c62e0

Clock_nonasleep

0x401050

grtchar@plt

0x401020

.plt

0x7ff172d7a6e0

getchar

0x7ff529496bc0

exit

0x7f51d6189400

__call_tis_dtors

5.7 hello的动态链接分析

图5-8      hello文件.got.plt段信息

图5-9      执行_dl_init前.got.plt段

图5-10    执行_dl_init后.got.plt段

用edb观察.got.plt,发现在_dl_init前后.got.plt的第8到15个字节发生了变化。

在这里,它们对应GOT[1]和GOT[2]的位置。其中,这时GOT[1]包含动态链接器在解析函数地址时使用的信息,而GOT[2]是动态链接器ld-linux.so模块中的入口点。加载时,动态链接器重定位GOT中的这些条目,使它们包含正确的绝对地址。

5.8 本章小结

本节介绍了链接的过程,对hello.o文件进行链接生成可执行文件hello,通过edb执行hello,我们将链接前后的文件进行对比分析,进一步确定了链接对可重定位目标文件做了哪些修改。

(第51分)

6章 hello进程管理

6.1 进程的概念与作用

进程是一个执行中的程序的实例。系统中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需要的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件描述符的集合。

进程为应用程序提供一种抽象:好像程序独占地使用处理器,好像程序独占地使用内存系统。前者方便处理器在不同进程之间切换提高程序整体地执行效率,后者为程序提供虚拟地址,方便我们对程序的地址空间进行管理。

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

shell  是一个交互型应用级程序,代表用户运行其他程序。

功能:shell执行一系列的读/求值步骤,读步骤读取用户的命令行,求值步骤解析命令,代表用户运行。

处理流程:

1、等待用户在stdin上输入命令行,获取命令行。

2、调用函数解析以空格分隔的命令行参数,并构造argv向量。

3、解析了命令行后,若第一个参数是内置的shell命令,则马上解释该命令;若是一个可执行目标文件,shell创建一个子进程,在新的子进程的上下文中加载并运行这个文件。

4、若用户要求在后台运行程序,那么shell立刻回复到等待接受命令行状态,否则等待前一个命令执行结束,shell开始下一轮循环。

6.3 Hello的fork进程创建过程

父进程可以通过调用fork函数创建一个新的运行的子进程。

新创建的子进程几乎但不完全与父进程相同。子进程得到与父进程用户级虚拟地址空间相同的(但是独立的)一个副本,包括代码和数据段、堆、共享库以及用户栈。子进程还获得与父进程任何打开文件描述符相同的副本。父进程与子进程最大的区别在于二者有着不同的PID。

在创建完子进程后,fork函数会返回两次。一次是在父进程中,返回新生成的子进程的PID;另一次是在子进程中,返回0。

图6-1       fork函数创建进程概念图

6.4 hello的execve过程

execve函数加载并运行可执行目标文件hello,且带参数列表argv和环境变量列表envp。

在execve加载了hello后,它调用启动代码,启动代码设置栈,并将控制传递给新程序的主函数,主函数原型如下int main(int argc, char **argv, char **envp);

6.5 hello的进程执行

内核为每一个进程维护一个上下文,上下文就是内核重新启动一个被抢占的进程所需要的状态。它由一些对象的值组成,这些对象包括通用目的寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构,比如描述地址空间的页表、包含有关当前信息的进程表,以及包含进程已打开文件的信息的文件表。

在进程执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程,这种决策叫做调度。在内核调度了一个新的进程运行后,它就抢占当前进程,并用一种上下文切换的机制来将控制转移到新的进程,上下文切换:1)保存当前进程的上下文,2)回复某个先前被抢占的进程被保存的上下文,3)将控制传递给这个新恢复的进程。

hello程序在执行过程中,当调用sleep函数时,会挂起一段时间,在这段时间中,内核会抢占该进程,直到sleep函数计时结束返回给内核一个信号,内核才会将hello进程恢复。sleep函数令hello进程挂起的时间对控制转移到的进程而言是时间片。

图6-2        进程的上下文切换

6.6 hello的异常与信号处理

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

异常种类:

      1、中断:挂起进程

      2、终止:结束进程

       信号处理:

              1、回车:程序正常进行

             

图6-3      按回车后hello执行结果

                  2、ctrl-z:进程收到信号SIGINT而中断

             

图6-4      按ctrl-z后hello执行结果

              3、ctrl-c:进程收到信号SIGQUIT而终止

图6-5      按ctrl-c后hello执行结果

              4、ctrl-z + ps:输出bash中当前存在的进程

             

图6-6      按ctrl-z + ps后hello执行结果

              5、ctrl-z + jobs:输出bash中的工作进程及其状态

图6-7      按ctrl-z + jobs后hello执行结果

6、ctrl-z + pstree:输出系统中当前存在的所有进程及其父子关系

图6-8      按ctrl-z + pstree后hello执行结果

7、ctrl-z + fg:发送SIGCONT信号使挂起的hello进程继续执行

图6-9      按ctrl-z + fg后hello执行结果

8、ctrl-z + kill:

用kill发送信号SIGKILL给hello进程,hello进程终止

图6-10    按ctrl-z + kill后hello执行结果

6.7本章小结

本章介绍了进程和bash的概念,分析了hello进程在执行过程中的上下文切换,介绍了进程管理方法,并应用这些方法对hello进程进行管理,

(第61分)

7章 hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:逻辑地址(LogicalAddress)是指由程序产生的与段相关的偏移地址部分。在hello.o中指令的地址是由对某个段的相对偏移表示的,这种相对偏移就是逻辑地址。

线性地址:逻辑地址包含段标识符和段内偏移量,通过段标识符找到目标段的基地址,再将这个基地址与段内偏移量连接,就得到线性地址了。

虚拟地址:CPU产生的需要访问主存的地址(Virtual Address)。就是hello的虚拟内存空间中的地址。

物理地址:物理地址(Physical Address) 是指出现CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。在hello执行过程中,进程对内存的访问要先将虚拟地址翻译为物理地址,再根据物理地址在硬件设备上找到数据。

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

一个逻辑地址由两部分组成,段选择符和段内偏移量,段选择符是由一个16位长的字段组成,其结构如图7-1所示。其中前13位是一个索引号,该索引号是段描述符表的索引,段描述符的结构如图7-2所示。每个段描述符由8个字节组成,其中BASE字段描述了一个段开始位置的线性地址。TI用于标记预访问的段描述符表是全局的还是局部的。

图7-1     段选择符结构

图7-2       段描述符结构

       逻辑地址到线性地址的变换过程:

       1、根据TI的值选择段描述符表;

       2、取出段选择符的前13位,用它作为索引在段描述表中找到目标段描述符;

       3、取出该段描述符的BASE,将它与段内偏移量offset连接得到线性地址。

图7-3       逻辑地址变换线性地址

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

线性地址就是虚拟地址。图7-4展示了如何利用页表来实现这种虚拟地址到物理地址的。虚拟地址包含两部分:虚拟页号(VPN)和虚拟页偏移量(VPO)。物理地址也包含两部分:物理页号(PPN)和物理页偏移量(PPO)。其中相对应的虚拟地址与物理地址的PPO与VPO是相同的,VPN与PPN是有映射关系的,这种映射关系保存在页表中,页表中的每一行被称为PTE,表示一个VPN到PPN的映射。

以虚拟地址VPN作为页表的索引就能在页表中找到其对应的PPN,将找到的PPN与VPO相连就得到了原虚拟地址对应的物理地址。

图7-4       页表地址翻译

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

TLB是一个小的、虚拟寻址的缓存,其每一行保存着一个由单个PTE组成的块。在MMU访问页表前,先在TLB中查询,若TLB命中,则无需再访问页表,否则正常访问页表,并将页表中命中的PTE放入TLB中。

 a)TLB命中

 b)TLB不命中

图7-5       TLB命中与未命中操作图

在页表查询时使用层次结构的多级页表能有效地减少页表在内存中占用的空间。在四级页表的前提下,VPN被分成了四个字段,即VPN1、VPN2、VPN3、VPN4。在四级页表中除了最后一级页表以外,其他页表的PTE不再是VPN到PPN的映射,而是VPNi到下一级页表首地址的映射。虚拟地址与物理地址之间通过多级页表的映射结构图7-6所示。

      

图7-6       多级页表地址翻译

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

下图是包含三级cache的存储结构:

图7-7       三级cache的存储结构

在访问物理内存时,一级cache视作二级cache的缓存,二级cache视作三级cache的缓存,三级cache视作主存的缓存,主存视作磁盘的缓存。

无论是直接映射高速缓存、组相联缓存还是全相联缓存,它们都包含行这个存储单位。当CPU或上一级缓存向当前级缓存发送物理地址时,当前级缓存通过组选择与行匹配判断是否命中。若命中,则进行字选择并返回选择的块;若不命中,则向下一级缓存或磁盘发送物理地址,从下一级得到数据行后,进行行替换,再重新根据物理地址进行内存访问。

7.6 hello进程fork时的内存映射

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

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

7.7 hello进程execve时的内存映射

通过加载并运行hello需要一下几个步骤:

1、删除已经存在的用户区域。删除当前进程虚拟地址的用户部分中的已存在区域结构。

2、映射私有区域。为新程序的代码、数据、bss和栈区域创建新的区域结构。所有这些新的区域都是私有的、写时复制的。代码和数据区被映射为hello文件中的.text和.data区。bss区是请求二进制零的,映射到匿名文件,其大小包含在hello中。栈和堆区域也是请求二进制零的,初始长度为零。

3、映射共享区域。如果hello程序与共享对象链接,那么这些对象都是动态链接到这个程序的,然后在映射到用户虚拟地址空间中的共享区域内。

4、设置程序计数器(PC)。设置当前进程上下文中的程序计数器,使之指向代码区域的入口。

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

DRAM缓存不命中成为缺页。当CPU引用了某个虚拟内存,地址翻译硬件从内存中读取页表,通过页表中对应PTE的有效位判断出该虚拟地址对应的物理地址的数据未被DRAM缓存,此时出发一个缺页异常。

缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,若该页已被修改了,那么内核就会将它复制会磁盘。加下来,内核从磁盘复制目标数据到内存中的物理地址,更新页表中的对应PTE,随后返回。当一场处理程序返回时,它会重新启动导致缺页的指令。

7.9动态存储分配管理

程序使用动态内存分配的最重要原因是经常直到程序实际运行时,才知道某些数据结构的具体大小。

动态内存分配器维护着一个进程的虚拟内存区域——堆。分配器将堆是为一组大小不同的块的集合来维护。每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。空闲块可用来分配。一个已分配块保持已分配状态直到它被释放,一个空闲块保持空闲状态直到它被应用显式地分配。

C标准库提供了一个称为malloc程序包的显示分配器,我们可以通过malloc函数分配块,通过free函数释放块。

void *malloc(size_t size);

malloc函数返回一个指针,指向大小至少为size字节的内存块。但若malloc函数遇到问题找不到满足要求的可分配块,则会返回NULL,并设置errno。

void free(void *ptr);

free函数的参数必须指向一个从malloc等获得的已分配块的起始位置。

在hello中printf函数会调用malloc构建输出字符串,

7.10本章小结

本节介绍了计算机系统存在的四种地址及其转换方式。现代系统将虚拟内存片与磁盘上的文件片关联起来,来初始化虚拟内存片,这个过程称为内存映射,内存映射在创建新进程与加载可执行文件时会发生。CPU想访问内存数据要先进行虚拟地址到物理地址的转换,在这一过程中用到了页表,在地址翻译过程中可以使用TLB与多级页表减少翻译在时间以及空间上的开销。在地址翻译的过程中会遇到缺页故障与缺页中断,内核会调用缺页异常处理程序进行处理。在hello运行过程中会根据需要动态申请更多的内存,这一操作由动态内存分配器帮助完成。

(第7 2分)

8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件

      所有的I/O设备都被模型化为文件,而所有的输入和输出都被当作对相应

文件的读和写来执行。

设备管理:unix io接口

      这种将设备用设为文件的方式,允许Linux内核引出一个简单、低级的应

用窗口,称为UnixI/O,这使得所有的输入和输出都能以一种同意且一致的方

式来执行。

8.2 简述Unix IO接口及其函数

进程是通过调用open函数来打开一个已存在的文件或者创建一个新文件

int open(char *filename,  int flags,  mode_t mode);

open函数将filename转换为一个文件描述符,而且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。Flags参数置命了进程打算如何访问这个文件。

进程通过close函数关闭一个打开的文件。

int close(int fd);

关闭一个已关闭的描述符会出错。

应用程序通过用read函数来执行输入

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

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

应用程序通过用write函数来执行输入

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

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

8.3 printf的实现分析

printf先从vsprintf生成显示信息。vsprintf的作用就是格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。

printf再调用write系统函数,将格式化后的字符串输出。

在执行write时系统调用sys_call,显示格式化了的字符串。

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

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

8.4 getchar的实现分析

getchar向当前进程发送键盘中断信号,进程异步调用键盘中断处理子程序。该处理程序接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。

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

8.5本章小结

本章简述了linux的I/O设备管理机制,介绍了开、关、读、写文件的接口及相关函数,简单分析了printf和getchar函数的实现方法以及操作过程。

(第81分)

结论

hello.c在计算机系统中主要经历一下流程:

1.预处理:处理hello.c中#开头的语句,根据语句要求对hello.c进行扩充或修改,生成hello.i文件。

2.编译:将hello.i中的C语言指令翻译成为汇编语言保存在hello.s中。

3.汇编:将hello.s汇编语言翻译成机器语言生成可重定位目标文件hello.o。

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

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

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

8.执行指令:hello享有CPU资源,顺序执行自己的控制逻辑流.

9.访问内存:MMU将程序中使用的虚拟内存地址通过页表映射成物理地址,再根据物理地址访问对应的存储块。

11.信号:如果在程序运行途中键入中断,则调用shell的信号处理函数分别停止、挂起。

12.结束:shell父进程回收子进程,内核删除为这个进程创建的所有数据结构。如果父程序不能回收该子程序,则由中断直接收回。

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

附件

hello.i:预处理hello.c生成的文件。

hello.s:编译hello.i生成的文件。

hello.o:汇编hello.s生成的文件。

hello:链接hello.o与系统库后生成的文件。

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

参考文献

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值