hello程序的一生

摘  要

本文跟随hello程序从源文件经由预处理,编译,汇编,链接成为可执行文件,再到在shell中加载hello程序的进程过程,分析hello程序的内存管理以及Linux I/O接口实现的函数。


关键词:预处理;编译;汇编;链接;进程创建;信号与异常处理;地址访问;内存映射                           


(摘要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简介

       P2P过程:hello.c经过预处理->编译->汇编->链接生成一个hello的二进制可执行文件,然后由shell新建一个进程给他执行。

020过程:shell执行./hello,为其映射出虚拟内存,然后在开始运行进程的时候分配并载入物理内存,开始执行hello的程序,将输出的东西显示到屏幕,然后hello进程结束,shell回收内存空间。


根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。

1.2 环境与工具

硬件环境X64 CPU;2GHz;2G RAM;256GHD Disk ;

软件环境Windows10家庭版 64位以上;VMware Workstation

15 Player;Ubuntu 18.04 LTS 64位

开发与调试工具texteditor gdb edb objdump

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

1.3 中间结果

hello.i 预处理后的文本文件,加载了头文件

hello.s 编译后的汇编文件,将源代码翻译成汇编语言

hello.o 汇编后的可重定位目标文件,将汇编语言翻译成机器语言指令

hello    链接产生的可执行目标文件

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

1.4 本章小结


(第1章0.5分)



第2章 预处理

2.1 预处理的概念与作用

预处理器(cpp)根据以字符#开头的命令,修改原始的C程序,得到一个以.i为文件扩展名的C程序文件。

C语言标准规定,预处理是指前4个编译阶段(phases of translation)。

(1)三字符组与双字符组的替换

(2)行拼接(Line

splicing): 把物理源码行(Physical source line)中的换行符转义字符处理为普通的换行符,从而把源程序处理为逻辑行的顺序集合。

(3)单词化(Tokenization): 处理每行的空白、注释等,使每行成为token的顺序集。

(4)宏扩展与预处理指令(directive)处理.

2.2在Ubuntu下预处理的命令

[if !vml]

[endif]

gcc –Ehello.c >hello.i

[if !vml]

[endif]

2.3 Hello的预处理结果解析

预处理器读取系统头文件stdio.h,unistd.h, stdlib.h的内容,并将其直接插入到程序文本中。

[if !vml]

[endif]

[if !vml]

[endif]

[if !vml]

[endif]

2.4 本章小结

预处理器cpp读取系统头文件stdio.h,将内容插入hello.c原来的程序文本中。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译器(ccl)将C语言文本文件翻译成汇编语言文本文件,它包含一个汇编语言程序。

编译器的作用是将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序,翻译为计算机能解读、运行的低阶机器语言的程序。

注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序



3.2 在Ubuntu下编译的命令

       gcc-S hello.i -o hello.s     


[if !vml]

[endif]

3.3 Hello的编译结果解析

3.3.1 数据:

全局变量sleepsecs:

[if !vml]

[endif]

Sleepsecs作为一个已经初始化的全局变量以4字节长度存放在.data节

修改源代码,添加输出语句

[if !vml]

[endif]

再次编译

[if !vml]

[endif]

由编译错误得,sleepsecs尽管在源程序中被赋值一个浮点数,在编译过程中被隐式类型转换为int类型。

局部变量i:

[if !vml]

[endif]

Main函数中第一个判断指令cmpl应为if语句,则相等时跳转到.L2为for语句,那么movl $0, -4(%rbp)对应i = 0; , i存放在-4(%rbp)中。

表达式:①if(argc!=3)对应cmpl $3, -20(%rbp)

               ②i=0;对应movl $0, -4(%rbp)

              ③i<8;对应cmpl $7, -4(%rbp)

              ④i++;对应addl $1, -4(%rbp)

 

3.3.2 算术操作

      i++;对应addl     $1, -4(%rbp)

3.3.3 关系操作

      argc!=3对应cmpl    $3, -20(%rbp)     /      je    .L2

i<8 对应cmpl   $7, -4(%rbp)              /      jle   .L4

3.3.4     数组操作

      char *argv[]对应 subq      $32, %rsp

                                  。。。。。。

                                  movq     %rsi, -32(%rbp)

3.3.5     控制转移

      if(argc!=3)对应 cmpl      $3, -20(%rbp)

                                  je    .L2

      for(i=0;i<8;i++)

      .L2:              //设置初始值,开始循环

      movl      $0, -4(%rbp)

      jmp .L3

      .L3:              //判断循环条件是否成立

      cmpl      $7, -4(%rbp)

      jle   .L4

3.3.6     函数操作

      main函数:参数传递movl  %edi, -20(%rbp) \      movq     %rsi, -32(%rbp)

                           函数返回movl   $0, %eax\     leave\    .cfi_def_cfa 7, 8\ ret

      printf函数:  参数传递movq %rax, %rsi\  leaq .LC1(%rip), %rdi\                                                                   movl  $0, %eax

                           函数调用call     printf@PLT

      puts函数: 参数传递leaq   .LC0(%rip), %rdi

                           函数调用call     puts@PLT

      Exit函数: 参数调用movl  $1, %edi

                           函数调用call     exit@PLT

      Sleep函数:参数传递movl %eax, %edi

                           函数调用call     sleep@PLT

      Getchar函数:函数调用 call getchar@PLT

此部分是重点,说明编译器是怎么处理C语言的各个数据类型以及各类操作的。应分3.3.1~ 3.3.x等按照类型和操作进行分析,只要hello.s中出现的属于大作业PPT中P4给出的参考C数据与操作,都应解析


3.4 本章小结

编译器(ccl)将预处理过的源程序文件从高级语言翻译成汇编语言。

(第32分)

第4章 汇编

4.1 汇编的概念与作用

       汇编概念:把汇编语言翻译成机器语言的过程。

       汇编器(as)将.s文件翻译成机器语言指令,并把这些指令打包成可重定位目标程序(relocatable object program)的格式,并将结果保存在.o格式的目标文件中,为二进制文件。


注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。

4.2 在Ubuntu下汇编的命令

as hello.s-o hello.o

[if !vml]

[endif]

应截图,展示汇编过程!

4.3 可重定位目标elf格式

    [if !vml]

[endif]


[if !vml]

[endif]


       重定位条目

       [if !vml]

[endif]

       .rela.text记录了.text的重定位条目,包括节偏移Offset,偏移类型Type,偏移调整Addend

       .rela.eh_frame是eh_frame节的重定位条目

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

4.4 Hello.o的结果解析

[if !vml]

[endif]

[if !vml]

[endif]

上图为hello.o反汇编

机器语言为十六进制数,一般由1-15个字节构成

机器语言从左到右依次为地址:指令+操作(对应汇编语言)

与hello.s中的不同之处在于hello.o反汇编多了机器语言,而且控制转移不单独分段。

cmpl     $3, -20(%rbp)

je   .L2

.L2:

      movl      $0,-4(%rbp)

      jmp .L3

变成

  f:      837d ec 03             cmpl   $0x3,-0x14(%rbp)

  13:    7416                 je     2b

2b:    c7 45 fc 0000 00 00 movl   $0x0,-0x4(%rbp)

  32:    eb3b                 jmp    6f

.L3:

      cmpl      $7,-4(%rbp)

      jle   .L4

      call getchar@PLT

      movl      $0,%eax

      leave

      .cfi_def_cfa 7, 8

      ret

      .cfi_endproc

变成

6f: 83 7d fc 07              cmpl   $0x7,-0x4(%rbp)

  73:    7ebf                  jle    34

  75:    e800 00 00 00           callq  7a

                    76: R_X86_64_PLT32      getchar-0x4

  7a:    b800 00 00 00           mov    $0x0,%eax

  7f:    c9                    leaveq

  80:    c3                    retq  


       ③


      .L4:

      movq     -32(%rbp),%rax

      addq      $16,%rax

      movq     (%rax),%rdx

      movq     -32(%rbp),%rax

      addq      $8,%rax

      movq     (%rax),%rax

      movq     %rax,%rsi

      leaq .LC1(%rip),%rdi

      movl      $0,%eax

      call printf@PLT

      movl      sleepsecs(%rip),%eax

      movl      %eax,%edi

      call sleep@PLT

              addl $1, -4(%rbp)


       变成


      34:  488b 45 e0             mov    -0x20(%rbp),%rax

  38:    4883 c0 10             add    $0x10,%rax

  3c:    488b 10               mov    (%rax),%rdx

  3f:    488b 45 e0             mov    -0x20(%rbp),%rax

  43:    4883 c0 08             add    $0x8,%rax

  47:    488b 00               mov    (%rax),%rax

  4a:    4889 c6               mov    %rax,%rsi

  4d:    488d 3d 00 00 00 00      lea    0x0(%rip),%rdi        # 54

                    50: R_X86_64_PC32 .rodata+0x1b

            54:  b8 00 00 0000           mov    $0x0,%eax


       54:     b8 00 00 00 00           mov    $0x0,%eax

  59:    e800 00 00 00           callq  5e

                    5a: R_X86_64_PLT32      printf-0x4

  5e:    8b05 00 00 00 00         mov    0x0(%rip),%eax        # 64

                    60: R_X86_64_PC32 sleepsecs-0x4

  64:    89c7                 mov    %eax,%edi

  66:    e800 00 00 00           callq  6b

                    67: R_X86_64_PLT32      sleep-0x4

        6b:   83 45 fc 01              addl   $0x1,-0x4(%rbp)

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

说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

4.5 本章小结

汇编器将.s文件从汇编语言翻译成.o文件的机器语言并使用elf格式的文件保存,elf中不同的代码保存在不同的节处,.rela开头的节保存相应后缀的节的重定位信息。

(第41分)

第5章 链接

5.1 链接的概念与作用

       链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存中并执行。


注意:这儿的链接是指从 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

[if !vml]

[endif]

使用ld的链接命令,应截图,展示汇编过程!注意不只连接hello.o文件

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

       [if !vml]

[endif]

       [if !vml]

[endif]

[if !vml]

[endif]

Address为各段起始地址,size为大小。

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

5.4 hello的虚拟地址空间

[if !vml]

[endif]

       [if !vml]

[endif]

elf文件从0x400000开始,到0x400ff0结束。

.text节从0x400500开始

[if !vml]

[endif]

.data节从0x400640开始

[if !vml]

[endif]

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

5.5 链接的重定位过程分析

Hello比hello.o多了下列节:

[if !vml]

[endif]

[if !vml]

[endif]

[if !vml]

[endif]

.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:存放程序中函数的全局偏移量

[if !vml]

[endif]

Hello.o中调用函数都是使用call+,而在hello是直接使用call+<函数名> 的方法来直接调用的。

链接的过程:

1.符号解析:把目标文件中符号的定义和引用联系起来;

2.重定位:把符号定义和内存地址对应起来,然后修改所有对符号的引用。


重定位:

以sleep函数为例(R_X86_64_PLT32类型)

由4.3   r.offset = 0x67     r.addend = -4

由5.3   ADDR(.text) = 0x400500

由5.5   ADDR(sleep) = 0x4004f0

则refaddr = ADDR(.text) + offset=0x400567

*refptr = (unsigned) (ADDR(sleep) + r.addend – refaddr)

         =(unsigned)0x4004f0 – 4 – 0x400567

         =-0x7b

-0x7b-0x32+PC= -0xad+0x40059d = 0x4004f0=ADDR(sleep)

objdump -d

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

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

5.6 hello的执行流程


ld-2.27.so!_dl_start                               0x7f4b236f4093

ld-2.27.so!_dl_init                                 0x7f4b236f40c5

hello!_start                                           

libc-2.27.so!__libc_start_main            

-libc-2.27.so!__cxa_atexit                          

-libc-2.27.so!__libc_csu_init               

hello!_init                                             

libc-2.27.so!_setjmp                            

-libc-2.27.so!_sigsetjmp                      

--libc-2.27.so!__sigjmp_save              

hello!main                                            

hello!puts@plt                                       0x40054e

hello!exit@plt                                       0x400558

*hello!printf@plt                                  0x40058b

*hello!sleep@plt                                   0x400598

*hello!getchar@plt                               0x4005a7

ld-2.27.so!_dl_runtime_resolve_xsave

-ld-2.27.so!_dl_fixup                           

--ld-2.27.so!_dl_lookup_symbol_x            

libc-2.27.so!exit                                   


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

5.7 Hello的动态链接分析

在dl_init调用之前,对于每一条PIC函数调用,调用的目标地址都实际指向PLT中的代码逻辑,GOT存放的是PLT中函数调用指令的下一条指令地址。

在dl_init调用之后, 0x60ff0,0x601008和0x601010处的三个8B数据分别发生改变,其中GOT[1]指向重定位表(依次为.plt节需要重定位的函数的运行时地址)用来确定调用的函数地址, GOT[2]指向动态链接器ld-linux.so运行时地址。

[if !vml]

[endif]


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

5.8 本章小结

链接器根据汇编器的符号表以及重定位条目等信息修改可重定位文件并且链接库函数。

(第51分)


第6章 hello进程管理

6.1 进程的概念与作用

       进程就是一个执行中程序的实例。

       进程提供给应用程序两个关键的抽象:

[if !supportLists]1.    [endif]一个独立的逻辑控制流,它提供一个假象,好像我们的程序独占地使用处理器

[if !supportLists]2.    [endif]一个私有地址空间,它提供一个假象,好像我们的程序独占地使用内存系统。


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

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

       Shell的处理流程:

[if !supportLists]1.[endif]接收并解析命令行参数

[if !supportLists]2.[endif]分析命令行参数是内置shell命令还是可执行程序文件

[if !supportLists]3.[endif]执行内置命令或者调用可执行程序文件

[if !supportLists]4.[endif]程序执行中监视输入,作出应对

6.3 Hello的fork进程创建过程

       Shell通过fork函数创建hello子进程,hello进程得到与shell用户级虚拟地址空间相同但独立的一份副本,包括代码和数据段、堆、共享库以及用户栈。Shell进程中会返回hello进程的PID,hello进程中返回0.

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

6.4 Hello的execve过程

       Execve函数加载并运行可执行目标文件hello,且带参数列表argv和环境变量envp。每一个进程都有一段唯一属于自己的内存地址段,在execve运行时,开始先是从0x00400000开始程序的执行。先是从可执行文件中加载的内容,然后是运行时的堆栈和共享库的存储器映射区域。


6.5 Hello的进程执行

       当shell 运行hello程序时,shell 进程生成hello子进程,它是shell进程的一个复制。子进程通过execve 系统调用启动加载器。加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零。通过将虚拟地址空间中的页映射到可执行文件的页大小的片(chunk), 新的代码和数据段袚初始化为可执行文件的内容。最后,加载器跳转到_start地址,它最终会调用应用程序的main函数。在内核和前端之前切换的动作被称为上下文切换。

[if !vml]

[endif]

在for循环中,执行完printf后由于调用了sleep函数,shell从用户态切换到核心态,接收并执行休眠信号后,shell又从核心态返回用户态,执行下一次循环的printf语句。


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

6.6 hello的异常与信号处理

       hello执行过程中会出现的异常种类有:

1.中断:SIGSTP:挂起程序

2.终止:SIGINT:终止程序

[endif][if !mso][endif]

CTRL+Z


[if !mso]

[endif][if !mso & !vml][endif][if !vml]

[endif][if !vml][endif][if !vml][endif][if !vml][endif][if !vml][endif][if !vml][endif][if !mso][endif]

空格

[if !mso]

[endif][if !mso & !vml][endif][if !vml]

[endif][if !vml][endif][if !vml]

[endif]

[if !vml]

[endif]

[if !vml][endif][if !vml][endif][if !vml]

[endif]

CTRL+Z

向进程发送了一个SIGSTP信号,让进程暂时挂起。

之后执行ps命令,可看到未被关闭的hello进程

执行jobs命令返回CTRL+Z表示暂停命令

执行pstree命令看到以树状图形式组织的各个进程

执行fg命令,向hello进程发送SIGCONT信号继续执行

执行kill命令,发送SIGKILL信号杀死当前的hello进程


CTRL+C

[if !vml]

[endif]

向hello进程发送SIGINT信号,结束进程。


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

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

6.7本章小结

       Hello在shell中从创建进程到进程终止的以及信号传递的各个过程分析。

(第61分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

       逻辑地址:又称相对地址,是程序运行由CPU产生的与段相关的偏移地址部分。是描述一个程序运行段的地址。

线性地址:地址空间是一个非负整数地址的有序集合,而如果此时地址空间中的整数是连续的,则称这个地址空间为线性地址空间。

虚拟地址:是线性地址。经过段机制转化之后用于描述程序分页信息的地址。是对程序运行区块的一个抽象映射,即一个程序应该在内存的哪些块上运行。

物理地址: 程序运行时加载到内存地址寄存器中的地址,内存单元的真正地址。是在前端总线上传输的而且是唯一的。在hello程序中,就表示了这个程序运行时的一条确切的指令在内存地址上的具体哪一块进行执行。



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

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

逻辑地址段标识符段内偏移量组成。段标识符是一个16位长的字段组成,称为段选择符,其中前13位是一个索引号。后面三位包含一些硬件细节。

索引号,这里可以直接理解成数组下标,它对应的“数组”就是段描述符表,段描述符具体描述了一个段地址,这样,很多段描述符就组成段描述符表。可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。

这里面,我们只用关心Base字段,它描述了一个段的开始位置的线性地址。

Intel设计的本意是,一些全局的段描述符,就放在“全局段描述符表(GDT)”中,一些局部的,例如每个进程自己的,就放在所谓的“局部段描述符表(LDT)”中。

GDT在内存中的地址和大小存放在CPU的gdtr控制寄存器中,而LDT则在ldtr寄存器中。

首先,给定一个完整的逻辑地址[段选择符:段内偏移地址],看段选择符的T1=0还是1,知道当前要转换是GDT中的段,还是LDT中的段,再根据相应寄存器,得到其地址和大小。我们就有了一个数组了。拿出段选择符中前13位,可以在这个数组中,查找到对应的段描述符,这样,它了Base,即基地址就知道了。把Base

+ offset,就是要转换的线性地址了。

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

由教材

[if !vml]

[endif]

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

由教材的i7例子:

36位VPN被划分成四个9位的片,每个片被用作一个页表的偏移量。

[if !vml]

[endif]

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

将物理地址拆分成CT(标记)+CI(索引)+CO(偏移量),然后在一级cache内部找,如果未能寻找到标记位为有效的字节(miss)的话就去二级和三级cache中寻找对应的字节,找到之后返回结果。

[if !vml]

[endif]

7.6 hello进程fork时的内存映射

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

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

7.7 hello进程execve时的内存映射

[if !supportLists]1.[endif]删除已存在的用户区域。

[if !supportLists]2.[endif]映射私有区域

[if !supportLists]3.[endif]映射共享区域

[if !supportLists]4.[endif]设置程序计数器PC,使之指向代码区域的入口点。

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

MMU翻译虚拟地址A时触发缺页,异常导致控制转移到内核的缺页处理程序,处理程序执行以下步骤:

[if !supportLists]1.     [endif]缺页处理程序搜索区域结构链表,把A与每个区域机构中的vm_start和vm_end做比较,判断虚拟地址是否在某个区域结构定义的区域内(合法)。若不合法,则触发一个段错误,从而终止进程。即下图情况①

[if !supportLists]2.     [endif]判断进行的内存访问是否合法,若不合法,则触发保护异常终止进程。即下图情况②

[if !supportLists]3.     [endif]若虚拟地址与内存访问都合法,则选择一个牺牲页,如果该牺牲页被修改过,则将它交换出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU重新启动引起缺页的指令,该指令再次将A发送到MMU。

[if !vml]

[endif]

7.9动态存储分配管理

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


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

7.10本章小结

       本章介绍了hello的地址空间表示,地址访问,内存映射,缺页处理的内容。

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

(第7 2分)

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

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

设备的模型化:文件

文件的类型:

1. 普通文件(regular file):包含任意数据的文件。

2. 目录(directory):包含一组链接的文件,每个链接都将一个文件名映射到一个文件(他还有另一个名字叫做“文件夹”)。

3. 套接字(socket):用来与另一个进程进行跨网络通信的文件

4. 命名通道(named pipe)

5. 符号链接(symbolic link)

6. 字符和块设备(character and block device)


设备管理:unix io接口

[if !supportLists]1.     [endif]打开文件

[if !supportLists]2.     [endif]Linux shell创建的每个进程开始都有三个打开的文件:标准输入、标准输出、标准错误

3. 改变当前文件位置

4. 读写文件

5.   关闭文件


8.2 简述Unix

IO接口及其函数

打开和关闭文件:

open函数原型 int open(char * filename, int flags, mode_t mode) 返回值是一个文件描述符, flags指定打开方式

close函数原型 int close(int fd) ;成功返回0,失败-1,关闭一个已关闭的文件描述符会出错。


读和写文件:

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

write函数原型 ssize_t write(int fd , const void* buf, size_t n) 返回值是文件写入字节数 fd是文件描述符将buf内容写入n个字节到文件但这里需要注意默认情况是需要在系统队列中等待写入(打开方式不同也会不同)


修改当前文件位置:

lseek函数

8.3 printf的实现分析

       printf函数的函数体如下图

       [if !vml]

[endif]

printf需要做的事情是:接受一个fmt的格式,然后将匹配到的参数按照fmt格式输出。首先arg获得第二个不定长参数,即输出的时候格式化串对应的值,其次调用了两个外部函数,一个是vsprintf,还有一个是write。

vsprintf函数体如下图:

[if !vml]

[endif]

从上面vsprintf函数可以看出,这个函数的作用是将所有的参数内容格式化之后存入buf,然后返回格式化数组的长度。

write函数是将buf中的i个元素写到终端的函数。

综上,printf的输出过程如下:

(1)从vsprintf生成显示信息,显示信息传送到write系统函数;

(2)write函数陷阱-系统调用 int 0x80或syscall.字符显示驱动子程序;

(3)从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。

(4)显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量),这样,需要打印的字符串“Hello 1161100122 xian”就显示在了屏幕上。


https://www.cnblogs.com/pianist/p/3315801.html

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

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

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

8.4 getchar的实现分析

       getchar调用了一个read函数,这个read函数是将整个缓冲区都读到了buf里面,然后将返回值是缓冲区的长度。如果buf长度为0,getchar才会调用read函数,否则是直接将保存的buf中的最前面的元素返回。

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

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

8.5本章小结

       从Linux I/O接口及其函数到printf和getchar的实现,分析了hello如何从调用输出函数到输出屏幕的过程。

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

(第81分)

结论

从hello.c开始由预处理器进行预处理添加头文件成为hello.i, 然后由编译器将文本文件hello.i编译成为汇编文件,再由汇编器将其翻译成可重定位目标文件hello.o,最后链接器将外部文件与hello.o链接成为一个可执行目标文件hello.

在shell中输入  ./hello 1161100122 xian运行程序

Shell调用fork函数创建子进程,调用execve函数启动加载器,映射虚拟内存,进入程序入口后载入物理内存,然后进入main函数

执行指令:CPU为其分配时间片,在一个时间片中,hello享有CPU资源,顺序执行自己的控制逻辑流。hello在执行的过程中可能会遇到异常和信号以及命令,执行异常信号的处理流程;

内存申请和访问:MMU将程序中使用的虚拟内存地址通过页表映射成物理地址,printf会调用malloc向动态内存分配器申请堆中的内存;

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


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

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

附件

hello.i 预处理后的文本文件,加载了头文件

hello.s 编译后的汇编文件,将源代码翻译成汇编语言

hello.o 汇编后的可重定位目标文件,将汇编语言翻译成机器语言指令

hello   链接产生的可执行目标文件

helloelf.txt     hello.o的ELF格式文件

hellorelf.txt        hello的ELF格式文件


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


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

参考文献

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

[1]  兰德尔E.布莱恩特.深入理解计算机系统[第三版]:美国:机械工业出版社,2016.

[2]  https://zh.wikipedia.org/wiki/C%E9%A2%84%E5%A4%84%E7%90%86%E5%99%A8

[3]  https://zh.wikipedia.org/wiki/%E7%B7%A8%E8%AD%AF%E5%99%A8

[4]  https://www.jianshu.com/p/b8ddb4cee7af

[5]  https://www.jianshu.com/p/863b279c941e

[6] https://github.com/torvalds/linux/commit/b21ebf2fb4cde1618915a97cc773e287ff49173e

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

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

你可能感兴趣的:(hello程序的一生)