程序人生-Hello’s P2P

题     目  程序人生-Hellos P2P  

专       业   航天学院                     

学     号    7203610502                    

班   级     2036016                   

学       生        陈*航          

指 导 教 师         史先俊            

计算机科学与技术学院

2022年5月

摘  要

关键词:预处理,汇编,计算机系统,编译;                            

本文将针对hello文件在linux环境下通过编译器进行预处理、编译、汇编、链接与反汇编的各个过程,并利用其他linux内置程序对运行过程进行分析,从而了解计算机系统是如何运行的。

目  录

第1章 概述

1.1 Hello简介

1.2 环境与工具

1.3 中间结果

1.4 本章小结

第2章 预处理

2.1 预处理的概念与作用

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

2.4 本章小结

第3章 编译

3.1 编译的概念与作用

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

3.4 本章小结

第4章 汇编

4.1 汇编的概念与作用

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

4.4 Hello.o的结果解析

4.5 本章小结

第5章 链接

5.1 链接的概念与作用

5.2 在Ubuntu下链接的命令

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

5.4 hello的虚拟地址空间

5.5 链接的重定位过程分析

5.6 hello的执行流程

5.7 Hello的动态链接分析

5.8 本章小结

第6章 hello进程管理

6.1 进程的概念与作用

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

6.3 Hello的fork进程创建过程

6.4 Hello的execve过程

6.5 Hello的进程执行

6.6 hello的异常与信号处理

6.7本章小结

第7章 hello的存储管理

7.1 hello的存储器地址空间

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

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

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

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

7.6 hello进程fork时的内存映射

7.7 hello进程execve时的内存映射

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

7.9动态存储分配管理

7.10本章小结

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

8.2 简述Unix IO接口及其函数

8.3 printf的实现分析

8.4 getchar的实现分析

8.5本章小结

结论

附件

参考文献


第1章 概述

1.1 Hello简介

Hello的P2P:hello.c源文件经过cpp的预处理得到hello.i、ccl的编译得到hello.s、as的汇编得到hello.o以及ld的链接一系列过程后成为可执行目标文件hello;在shell中输入该可执行文件后,shell对它fork而产生子进程,于是hello成为了Process。

Hello的020:在进行P2P后shell再对该可执行文件进行execve来映射虚拟内存,进入程序入口后则载入物理内存,然后开始进入main函数来执行目标代码,CPU为此代码分配时间片。当程序运行结束后,父进程回收hello进程,内核删除相关数据结构。

 

1.2 环境与工具

开发环境:Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz   1.19 GHz, ubuntu20.03

开发工具:gcc,edb,gdb,as,ld,odjdump,readelf

1.3 中间结果

hello.i :hello.c预处理后的文本文件
hello.s :hello.i编译后的汇编文件
hello.o :hello.s汇编后的可重定位文件
hello: 链接后的可执行目标文件

helloobj.s:hello.o的反汇编
helloobj1.s:hello的反汇编

hello.elf:hello的elf格式

1.4 本章小结

本章介绍了hello的P2P和020过程,并介绍了开发环境与工具以及开发过程中的中间结果。


第2章 预处理

2.1 预处理的概念与作用

概念:预处理器根据以字符#开头的命令,这些命令指引预处理器读取哪些相应的文件从而修改原始的C程序,将读取的文件直接插入程序文本中,得到一个新的C程序,一般以.i作为文件拓展名。

作用:用实际值替换用#define定义的字符串,根据#if后面的条件决定需要编译的代码,对宏定义的内容进行对应处理

2.2在Ubuntu下预处理的命令

gcc -E hello.c -o hello.i

 

图1 预处理结果

2.3 Hello的预处理结果解析

这些大段#号部分是对hello.c文件中各头文件的替换,且源代码注释部分被删去

 

 

图2 hello.i文件的内容

代码被拓展成3060行,代码的最后部分为原本的main函数部分。

2.4 本章小结

本章介绍了预处理的概念与作用,并在linux上实践使用gcc对hello.c文件进行预处理得到了hello.i,对结果进行了解析。


第3章 编译

3.1 编译的概念与作用

编译的概念:利用编译程序将源语言编写的源程序转化为汇编语言程序的过程。        

编译的作用:将.i文件翻译为.s文件,从而可以进行后续的汇编、链接操作。

3.2 在Ubuntu下编译的命令

gcc -S hello.i -o hello.s

 

图3 编译的结果

3.3 Hello的编译结果解析

编译结果总体如下:

 

 

图4 hello.s文件

3.3.1数据

   字符常量“Hello 学号 姓名 秒数!\n”和“Hello %s %s\n”在.rodata(只读数据段中)。

 

图5 字符常量

整数:

局部变量:主函数中有局部变量i和argc,编译器会将局部变量存储在寄存器或栈中。

 

图6 变量 i

 

 

图7 变量 argc

立即数:直接会在编译程序中表示。

数组:

 

图8 数组

3.3.2赋值

  对于变量的赋值,使用mov指令进行。

 

 

图9  赋值指令

后缀根据传输数据大小可分为:b(1byte)、w(2byte)、l(4byte)、q(8byte)

3.3.3算术操作

进行算数操作的指令有:
leaq S,D、 inc D、 dec D 、add S,D、 sub S,D

此.s文件中涉及到的算术操作为add指令操作,计数器i++,。

 

图10 算术指令

3.3.4关系操作

  关系操作的指令有:
(1)cmp S1,S2  比较并设置条件码
(2)test S1,S2  测试并设置条件码
(3)j** ****** 根据**和条件码进行跳转
(4)set D  设置条件码

此.s文件中涉及到的关系操作有

i与数字8的比较,以及语句跳转。

 

argc与数字4的比较,以及语句跳转。

 

图11 跳转指令

3.3.5数组/指针

 程序中的数组是main的参数argv,存放char指针。

 

图12 数组/指针

rdx存第三个参数argv[2],rsi存第二个参数argv[1],rdi存规格化字符串。在访问数组元素时,按照起始地址加偏移量的方式取出数据。

3.3.6控制转移

  1. if(argc!=4):当argc不等于4的时候跳转到L2处

 

 

图13 argc控制转移

  1. for(i=0;i<8;i++):当i小于8的时候,执行for语句跳转到L4

 

图14  i控制转移

3.3.7函数操作

从函数M中调用函数N的过程为:

(1)传递控制,把程序计数器设为N的起始地址。

(2)传递参数,64位程序用寄存器传参,32位程序用栈传参。

(3)分配内存,通过移动rsp寄存器所存指针。

(4)执行N中的指令。

(5)释放内存,移动rsp栈指针。

(6)返回数据,rax存放返回值。

(7)返回控制,N栈帧最底部存放返回地址,即返回到M之前的指令的下一句。   

 

 

图15 函数调用过程

hello文件中调用函数有printf、atoi、sleep、exit

                                                        

 

 

 

参数存储顺序:%rdi, %rsi, %rdx, %rcx, %r8, %r9,其余参数倒序压入栈中。

  1. main函数:外部向main函数传递argc和argv,两者分别用%rdi和%rsi存储,在main函数结束时执行return 0,此时会将%eax设置为0;对内存进行操作,调用%rbp,%rsp来记录栈信息。main函数结束时,调用leave指令来恢复栈空间,最后使用ret返回。
  2. printf函数:第一次printf函数只打印一个字符串,故将字符串首地址设置为%rdi来作为唯一参数传入,第二次print函数有两个参数,故设置rsi为argv[1],设置rdx为argv[2],调用printf的第一次使用call puts@PLT;第二次使用call printf@PLT。
  3. exit函数:退出函数,把返回值edi设置为1,调用时使用call exit@PLT。

3.4 本章小结

本章介绍了编译器如何将hello.i处理为hello.s汇编文件,在linux对hello.i文件进行了编译操作,对编译结果进行了解析,解释了编译器如何处理整数,字符串、数组/指针、赋值、算术操作、关系操作、控制转移、函数操作。


第4章 汇编

4.1 汇编的概念与作用

汇编的概念与作用:汇编器将.s汇编程序翻译成机器语言指令,把这些指令打包成可重定位目标程序的格式,并将结果保存在.o目标文件中。

4.2 在Ubuntu下汇编的命令

gcc -C hello.s -o hello.o

 

图15 汇编结果

4.3 可重定位目标elf格式

1. ELF 头:该部分描述了hello.o系统的字的大小和字节顺序,包括ELF头的大小、目标文件的类别、机器类型、字节头部表的文件偏移以及节头部表中条目的大小和数量等信息。

 

图16 ELF头

2.节头:该部分讲述了每个字的语义以及节的类型、地址、大小、偏移量等信息。

 

 

图17 节头

3.重定位节:记录.text节中需要进行重定位的信息。在链接时,将对这些信息进行修改。

 

图18 重定位节

4.4 Hello.o的结果解析

(1)分支转移:在.s文件中跳转指令的操作数使用的是段名称,如L1,L2等;在反汇编代码中执行跳转指令的操作数使用的并非段名称,而是确定的地址。

(2)函数调用:在.s文件中,函数调用后直接跟着函数名称;在反汇编程序中,call指令的目标地址是当前下一条指令,并添加重定位条目再通过链接器去寻找目标指令地址。  

(3)全局变量访问:在.s文件中,访问.rodata,使用的是“段名称+%rip”的形式;在反汇编代码中则是0x0(%rip)的形式,并添加重定位条目寻址。

 

 

图19 helloo.objdump文件

4.5 本章小结

本章介绍了hello.s变为hello.o的汇编过程,并通过汇编生成了重定位文件,对于可重定位文件elf格式进行了具体介绍,还进行了反汇编,将汇编文件与反汇编文件进行了比较解析。


5链接

5.1 链接的概念与作用

链接的概念:将多个可重定位文件合成一个可执行文件的过程。

链接的作用:链接可执行于编译时,也可执行于加载时,链接器为分离编译提供了可能。

5.2 在Ubuntu下链接的命令

 

 

图20 链接结果

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

使用readelf -a hello > hello.elf得到hello的ELF格式

 

 

 

图21 hello的FLF格式

节头对hello中所有的节信息进行了阐述,包括每个节的大小、地址类型以及其在程序中的偏移量,由此可以定位各节的起始位置和大小。

5.4 hello的虚拟地址空间

    使用edb加载hello,查看本进程的虚拟地址空间各段信息。

 

图22 edb结果

(1)PHDR:程序头表,

 

图23 PHDR

(2)LOAD:一个需要从二进制文件映射到虚拟空间的段,一般保存目标代码和常量数据,

 

 

图24 LOAD

(3)INTERP:需要使用的解释器,

 

 

 

 

图25 INTERP

其内容:

 

图26 INTERP内容

  1. DYNAMIC:动态链接器要使用的信息,

 

 

图27 DYNAMIC

  1. NOTE:辅助信息,

 

图28 NOTE

  1. GUN_STACK:判断栈是否可以执行。

(6)GUN_RELRO:指定重定位之后设置只读的内存区域。

5.5 链接的重定位过程分析

hello的反汇编文件:

 

图29 hello的反汇编

hello.o的反汇编文件:

 

图30 hello.o的反汇编

(1)经查找资料后知道hello的反汇编中增加了以下的内容:

.interp:ld.so的路径;

.note.ABI-tag:Linux特有的段;

.hash:符号的哈希表;

.gnu.hash:GNU中拓展符号的哈希表;

.dynsym: 运行时/动态符号表;

.dynstr:存放.hynsym节中的符号名称;

.gnu.version:符号版本;

.gun.version_r:符号引用版本;

.rela.dyn: 运行时/动态重定位表;

.rela.plt:.plt节的重定位条目;

.init:程序初始化需要执行的代码;

.plt:动态链接-过程链接表;

.fini: 当程序正常终止时需要执行的代码;

.eh_frame:异常展开和源语言的信息;

.dynamic:存放被ld.so使用的动态链接信息;

.got:存放程序中跟变量全局偏移量;

.got.plt:存放程序中函数的偏移量;

.data:初始化过的全局变量或生命过的函数。

  1. 在函数调用中两者的偏移量不同,hello的反汇编要经过重定位,偏移量非零;而hello.o的反汇编不经过重定位,偏移量都为零。
    链接操作就是将各个.o里面的函数引用,全局变量引用进行重定位。
  2. 如何进行重定位:

refaddr = ADDR(s) + r.offset= ADDR(main)+r.offset;

*refptr=(unsigned)(ADDR(r.symbol)+r.addend-refaddr) =ADDR(str1)+r.addend-refaddr

5.6 hello的执行流程

Id-2.27.so!_dl_start 0x7fce 8cc38ea0

Id-2.27.so!_dl_init 0x7fce 8cc47630

Hello!_start 0x400500

Libc-2.27.so!__libc_start_main 0x7fce 8c867ab0

-libc-2.27.so!__cxa_atexit 0x7fce 8c889430

-libc-2.27.so!__libc_csu_init 0x4005c0

Hello!_init 0x400488

Libc-2.27.so!_setjmp 0x7fce 8c884c10

-libc-2.27.so!_sigsetjmp 0x7fce 8c884b70

-libc-2.27.so!_sigjmp_save 0x7fce 8c884bd0

Hello!main 0x400532

Hello!puts@plt 0x4004b0

Hello!exit@plt 0x4004e0

*hello!printf@plt -

*hello!sleep@plt -

*hello!getchar@plt -

ld-2.27.so!_dl_runtime_resolve_xsave 0x7fce 8cc4e680

-ld-2.27.so!_dl_fixup 0x7fce 8cc46df0

–ld-2.27.so!_dl_lookup_symbol_x 0x7fce 8cc420b0

libc-2.27.so!exit 0x7fce 8c889128

5.7 Hello的动态链接分析

 在调用dl_init之前,每一条动态共享链接库中的PIC函数的调用由于编译器无法确定函数运行时的地址因此需要进行重定位。目标地址是PLT的代码逻辑,GOT存放PLT中函数调用的下一条地址。其中PLT是一个数组,其中每个条目是16字节代码,每个库函数都有自己的PLT条目,PLT[0]是一个特殊的条目,其跳转到动态链接器中,而从PLT[2]开始的条目调用用户代码调用的函数;GOT同样是一个数组,每个条目是8字节的地址,和PLT联合使用时,GOT[2]是动态链接在ld-linux.so模块的入口点,其余条目对应于被调用的函数,在运行时被解析。每个条目都有匹配的PLT条目。

由5.3中的截图可判断GOT的起始位置在0x00404000,

 

图31 调用dl_init前

在调用dl_init之后

 

图32 调用dl_init后

可发现有两处数据发生变化,这两者就是GOT[1]、GOT[2]

其中GOT[1]指向重定位表,GOT[2]指向动态链接器,

5.8 本章小结

本章介绍了链接的概念和作用,对可执行文件hello分配的虚拟空间,它的重定位过程,执行流程及动态链接过程进行了分析。


6hello进程管理

6.1 进程的概念与作用

进程的概念:是程序的一次执行,可以和别的计算并行执行,是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的一个独立单位。

进程的作用:使得我们的程序就像是系统中当前运行的唯一程序,我们的程序独占系统资源,多进程可以进行多任务工作。

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

Shell是系统的用户界面,是一个用户系统交互操作程序;

作用:接收用户输入的命令并将其送至内核执行、并解释用户输入的命令,可以处理信号。

处理流程:从终端读取用户输入的命令,将输入的字符串切分来获得所有的参数,再判断是否为内置命令,如果是内置命令则立刻执行,否则在搜索路径中寻找对应的可执行文件,若找到则调用相应的程序执行,否则报错。

6.3 Hello的fork进程创建过程

通过shell输入一个./hello 学号 姓名,运行程序时shell就会创建一个新进程;父进程通过调用fork函数创建一个新的运行子进程,子进程与父进程的虚拟内存地址相同,但两者有个很大的区别:PID不同。

6.4 Hello的execve过程

创建进程后,在子进程中通过判断pid来判断是否处于子进程,若处于则会通过execve函数在当前进程的上下文中加载运行一个新程序--hello程序,加载器删去子进程现有的虚拟内存段,创建新的栈和堆段并将其初始化为零,新的代码和数据段被初始化为可执行文件中的内容,最后加载器设置PC指向_start地址,_start再调用hello中的main函数。

6.5 Hello的进程执行

上下文信息:内核重新启动一个被抢占的进程所需要的状态,由通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。

进程时间片:一个进程执行它的控制流的一部分的每一时间段。

 

图33 hello的进程执行

如图所示,当运行到sleep函数时,shell会并发去执行其他进程,在上下文切换时,会由用户模式进入内核模式,此时其保存hello的上下文,恢复其他进程的上下文,并将控制交给其他进程来完成上下文切换。

6.6 hello的异常与信号处理

hello执行过程中会出现中断、陷阱、故障和终止。

  1. 正常运行

 

图34 正常运行

  1. 在运行过程中按回车

 

图35 按下回车

不影响到程序的运行,只是多出几段空命令行。

  1. 在运行时按Ctrl-C

 

图36 按下Ctrl_C

shell父进程收到SIGINT信号,该信号处理函数的逻辑是结束hello,并回收hello进程,程序运行直接停止。

  1. 在运行时按Ctrl-Z

 

图37 按下Ctrl-Z

向父进程发送SIGSTP信号,进程挂起,但并未停止;

运行ps,jobs,fg命令:

 

图38 运行ps、jobs、fg命令

发现进程列表中包括了hello,运行fg后程序会从停止处开始继续往下执行。

运行pstree命令:

 

 

图39 运行pstree命令

运行kill命令:

 

图40 运行kill命令

可以看到在执行kill后进程列表中的hello消失了。

  1. 在运行时乱按

 

图41 运行时乱按

6.7本章小结

本章介绍了进程的概念与作用,介绍了shell的功能和处理流程,以及hello可执行文件fork进程创建过程和execve过程,并对hello执行过程中可能出现的异常及其处理进行了解析。


7hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:由一个段标识符加上一个指定段内相对地址的偏移量。   

线性地址:CPU在保护模式下,“段基址+段内偏移地址”叫做线性地址。
    虚拟地址:如果CPU在保护模式下没有开启分页功能,则线性地址就被当做最终的物理地址来用,若开启了分页功能,则线性地址就叫虚拟地址。

物理地址:物理地址就是内存单元的绝对地址,不管CPU内部怎么处理地址,最终访问的都是物理地址,在CPU实模式下“段基址+段内偏移地址”就是物理地址,CPU可以使用此地址直接访问内存。

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

一个逻辑地址由两部分组成:段标识符和段内偏移量。段内偏移量是一个16位长的字段组成,称为段选择符,前13位是序列号。根据一个段描述符可以找到一个段的首地址,若干段描述符就可以组成段描述符表。再通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,进而确定一个段。Intel处理器在通过段式管理寻址时,被选中的段描述符先被送至描述符cache,每次从描述符cache中取32位段基址,与32位段内偏移量相加得到线性地址。

 

图42 段式管理

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

由分页机制进行管理,在这个过程中,内核将维护hello的段的结构,即task_struct,其中mm指向某一个mm_struct,而mm_struct中的pgd为本级页表的首地址,mmap在指向vm_area_struct链表,一个链表对应程序的一个段,若干链表相连hello的所有段。系统将虚拟内存的每个段分为若干个小单位,并以此为单位将数据装入内存,这个最小的单位被称为页。同时,物理内存中也会分为同样大小的若干个页。CPU中的MMU单元负责翻译地址,将虚拟地址根据页表中的记录翻译为物理地址。如果改组中有有效位为1且标记值相同的,则TLB命中,取出其为PPN。

 

 

图43 页式管理

图中VPN为虚拟页号,VPO为虚拟页偏移,PPN为物理页号,PPO为物理页偏移。

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

CPU首先给出虚拟地址VA,VA的后12位将被MMU直接作为物理地址结果的后12位。对于VA的前36位即VPN,MMU会将其拆分为两部分:32位的TLBT和4位的TLBI,并由此去TLB中寻找相应PPN。如果TLB中标号为TLBI的组中有标记值为TLBT的,且有效值为1的项,则查找命中,直接将TLB中存储的该项的PPN输出作为PA的前40位;如果TLB中没有满足条件的项,则查找未命中,MMU将会去TLB的下一级存储页表中继续查找,这时,VPN将不再被分为TLBT和TLBI,而是会被分为4个9位的VPNn,MMU会先根据VPN1在第一级页表中查找,得到的结果将告诉MMU内存中是否存在,及第二级页表的起始地址,之后MMU会再根据VPN2去第二级页表中查找,以此类推,直到再第四级页表中查找到PPN,MMU将其输出,与PPO组合,作为物理地址输出,如果页表中也没有对应的PPN,则发生缺页。

 

图44 MMU访问TLB

 

 

图45 TLB是否命中的情况

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

首先,物理地址在被用来去L1级缓存中寻找内容时,将被分为三个部分:40位CT、6位CI、6位CO;

图46 物理地址的分成

 

L1 Cache是8路64组相联。块大小为64B,故需要6bit CO表示数据偏移位置;使用CI进行组索引,每组8路,对8路的块分别匹配CT如果匹配成功且块的有效标志位为1,则命中,根据数据偏移量CO取出数据返回;如果没有匹配成功或者匹配成功但是标志位是1,则不命中,进而向下一级缓存中查询数据(L2 Cache->L3 Cache)。

7.6 hello进程fork时的内存映射

fork为新进程创建虚拟内存,创建当前进程的的mm_struct, vm_area_struct和页表的原样副本,再将两个进程中的每个页面都标记为只读。两个进程中的每个区域结构(vm_area_struct)都标记为私有的写时复制(COW),在新进程中返回时,新进程拥有与调用fork进程相同的虚拟内存,随后的写操作通过写时复制机制创建新页面。

7.7 hello进程execve时的内存映射

execve在当前程序加载运行新程序时会删除已存在的用户区域,创建新的区域结构:代码和初始化数据映射到.text和.data区(由目标文件提供)、.bss和栈堆映射到匿名文件 ,栈堆的初始长度为0,其共享对象由动态链接映射到本进程共享区域设置PC,该PC指向代码区域的入口点,然后Linux根据需要换入代码和数据页面。

 

图47 execve时的内存映射

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

在MMU翻译虚拟地址时出现了缺页异常时,首先判断虚拟地址是否合法,缺页处理程序会搜索区域结构的链表,把该虚拟地址和每个区域结构中的vm_start和vm_end做比较。如果指令不合法则触发段错误,从而终止该进程;然后处理程序会判断要进行的内存访问是否合法,如果访问不合法,那么处理程序会触发一个保护异常来终止这个进程。

 

图48 缺页处理

7.9动态存储分配管理

在程序运行时程序员使用动态内存分配器 (比如 malloc) 获得虚拟内存.,数据结构的大小只有运行时才知道,动态内存分配器维护者一个进程的虚拟内存区域,称为堆.。分配器将堆视为一组不同大小的块(blocks)的集合来维护,每个块要么是已分配的,要么是空闲的。

分配器的类型:

(1)显式分配器:  要求应用显式地释放任何已分配的块。

(2)隐式分配器: 应用检测到已分配块不再被程序所使用,就释放这个块。

对于空闲块,释放块的多少可由下面的方法来确定:

  1. 隐式空闲链表:通过头部中的大小字段—隐含地连接所有块。

 

图49 隐式空闲链表

  1. 显式空闲链表:在空闲块中使用指针。

 

图50 显式空闲链表

(3)分离的空闲列表:按照大小分类,构成不同大小的空闲链表。

(4)按块按大小排序—平衡树:在每个空闲块中使用一个带指针的平衡树,并使用长度作为权值。

如果只是释放,则将会产生大量的内存碎片,所以合并机制是必不可少的。根据内存之间的关系,可以将合并分为四种情况:前空后空、前不空后不空、前空后不空、前不空后空。通过改变头部和脚部中的值就可以完成这些操作

7.10本章小结

本章介绍了hello的存储器各地址空间的概念,inter的段式管理和页式管理,解析了从虚拟地址VA到物理地址PA的变换方式、三级cache的内存访问过程,还介绍了fork、execve的内存映射,缺页故障与缺页中断处理,动态存储分配管理。


8hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件

所有的I/O设备都被模型化为文件,甚至内核也被映射为文件所有的输入和输出,都变成对对应文件的读或写。

设备管理:unix io接口

8.2 简述Unix IO接口及其函数

这种将设备优雅地映射为文件的方式,允许Linux内核引出一个简单、低级的应用接口,称为Unix I/O。

函数:打开和关闭文件:open()和close();

读写文件:read()和write();

改变当前的文件位置  (seek):指示文件要读写位置的偏移量,lseek();

8.3 printf的实现分析

prinf函数体:

 

图51 printf函数体

vsprint生成的显示信息:

 

图52 vsprint

vsprintf的作用就是格式化,它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。

Write系统函数

 

图53 write

这里给几个寄存器传递了几个参数,然后一个int结束。

sys call:

 

图54 syscall

其功能:显示格式化了的字符串。

syscall将字符串中的字节“Hello 7203610502 chenshihang”从寄存器中通过总线复制到显卡的显存中,显存中存储的是字符的ASCII码,字符显示驱动子程序:从ASCII码在字模库中找到点阵信息将点阵信息存储到vram(存储每一个点的RGB颜色信息)中,显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。这样就能将字符串“Hello 7203610502 chenshihang”显示在屏幕上了。

8.4 getchar的实现分析

异步异常-键盘中断的处理:键盘中断处理子程序。

键盘接口接收按键扫描码并得到中断请求,将中断当前执行程序,并运行中断子程序,中断子程序获得按键扫描码后,将其转成ascii码,然后保存到系统的键盘缓冲区。

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

8.5本章小结

本章介绍了Linux的IO设备管理方法,简述了Unix IO接口及其函数,对printf函数的实现进行了解析,对getchar的实现进行了分析。

结论

Hello的一生:

(1)预处理:将hello.c调用的外部的库和头文件全部整合到一起成为hello.i文件;

(2)编译:将hello.i编译成为汇编文件hello.s,转变为汇编语言;

(3)汇编:将hello.s变成可重定位目标文件hello.o;

(4)链接:将hello.oy与可重定位目标文件和动态链接库成为可执行目件hello;

(5)进程管理:从shell中输入并读取命令进而向系统发送信号,接着利用fork函数为该可执行文件创建一个新的子进程,然后利用execve,在进入程序入口后载入物理内存,程序开始运行;CPU将其分配到一些事件片运行,在一个事件片中,程序将可以调动所有CPU资源。

(6)存储管理:MMU将CPU给出的虚拟地址翻译为物理地址,再根据物理地址去内存或磁盘中寻找数据;通过调用malloc,从堆中申请内存实现动态申请内存。

(7)I/O管理:通过Unix I/O接口将得到的结果显示在屏幕上。

(8)结束:shell父进程将会负责回收子进程的资源,内核删除此子进程的所有数据结构。

到此,hello程序的执行要经历如此多的过程,由此我也进一步了解了各个步骤的作用意义,但仍存在大量的不足之处,我将在以后的学习、资料查询中对此进行补充学习。


附件

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

hello.i:hello.c预处理之后的程序文本。

hello.s:hello.i编译成汇编语言之后的程序文本。

hello.o:hello.s生成的二进制文件。

hello:可执行的hello二进制文件。

hello.elf: hello的elf格式文件。

helloo.elf:hello.o的elf格式文件。

helloo.objdump:hello.o的反汇编文件。


参考文献

[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分)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值