HIT 2024春csapp程序人生大作业

打印 上一主题 下一主题

主题 871|帖子 871|积分 2613








盘算机科学与技能学院

2024年5月

摘  要

本文详细阐述了hello.c程序在Linux系统从编写到运行的完整生命周期。起首使用专业工具深入分析了程序在Linux环境下经历的预处置惩罚、编译、汇编和链接等各个阶段的具体过程与原理。接着探究了应用程序在运行时和竣事时涉及的历程管理、内存分配与回收、以及输入输出(IO)操纵等关键方面。此外,本文还介绍了shell在Linux系统中的焦点管理功能,包罗内存管理、IO管理以及历程管理,并深入探究了假造内存和异常信号等焦点概念,全面明确Linux系统下程序运行的内部机制。

关键词:盘算机系统;盘算机体系布局;hello.c;Linux                      










目  录


第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:P2P指的是程序由一个项目酿成一个历程的过程.

1.Program:Hello程序的诞生是程序员通过键盘输入得到hello.c

2.Process:C语言源程序hello.c在预处置惩罚器(cpp)处置惩罚下,得到hello.i,通过编译器(ccl),得到汇编程序hello.s,再通过汇编器(as),得到可重定位的目的程序hello.o,末了通过链接器(ld)得到可执行的目的程序hello。在shell中键入运行命令后,shell调用fork函数为其创建子历程。

020:020为程序“从无到有再到无”的过程。程序经过系统OS,shell为hello历程execve,映射假造内存,进入程序入口后程序开始载入物理内存。进入 main 函数执行目的代码,CPU为执行文件hello分配时间周期,执行逻辑控制流,每条指令在流水线上取值、译码、执行、访存、写回、更新PC。当程序运行竣事后, shell 父历程负责回收 hello 历程,内核删除相关数据布局。Hello程序从无到有再到无的这一过程就是020。

1.2 环境与工具

硬件环境:11th Gen Intel(R) Core(TM) i7-1195G7 @ 2.90GHz   2.92 GHz,16.0 GB RAM
软件环境:Windows 11 23H2 Ubuntu 20.04
开发工具:Vim、gdb、visual studio。
1.3 中心结果

列出你为编写本论文,天生的中心结果文件的名字,文件的作用等。
1.4 本章小结

本部分对hello从诞生到执行到消亡的P2P和020过程进行了简介,并介绍了整个过程中所使用的环境工具及天生的中心结果。



第2章 预处置惩罚

2.1 预处置惩罚的概念与作用

预处置惩罚的概念:预处置惩罚阶段是编译过程的前置步骤,它通过预处置惩罚器(cpp)根据以#字符开头的指令,对原始的C程序进行修改。例如,对于#include<stdio.h>,#include 指令要求预处置惩罚器读取系统头文件 stdio.h的内容,并将其直接嵌入到程序文本中。这样就天生了另一个C程序,通常以.i为文件扩展名。

预处置惩罚的作用:预处置惩罚的作用主要可分为以下三部分:

(1) 宏展开:预处置惩罚程序中的“#define”标识符文本,用实际值(可以是字符 串、代码等)更换用“#define”定义的字符串;

(2) 文件包含复制:预处置惩罚程序中用“#include”格式包含的文件,将文件的内 容插入到该命令所在的位置并删除原命令,从而把包含的文件和当前源文 件连接成一个新的源文件,这与复制粘贴类似;

(3) 条件编译处置惩罚:根据“#if”和“#endif”、“#ifdef”和“#ifndef”背面的 条件确定必要编译的源代码。

2.2在Ubuntu下预处置惩罚的命令

预处置惩罚的命令为:gcc -E hello.c -o hello.i

2.3 Hello的预处置惩罚结果解析

预处置惩罚后,文件的格式仍为文本文件。
文件的行数增长到了三千多行,其中末了几行才是原程序对应的内容,可以看出原来的c文件中的解释都被删除,余下的原程序部分没有发生任何变化。



2.4 本章小结

本章介绍了预处置惩罚的概念和作用,然后介绍了Linux系统下的两种预处置惩罚命令,末了通过查看hello.i中文件中的实际内容相识了预处置惩罚的实际结果,通过分析结果,加深了对于预处置惩罚的明确。
































第3章 编译

3.1 编译的概念与作用

概念:编译是把通常为高级语言的源代码(这里指经过预处置惩罚而天生的 hello.i)到能直接被盘算机或假造机执行的目的代码(这里指汇编文件 hello.s)的翻译过程。    
作用:
1. 词法分析,词法分析也称作扫描,是编译器的第一个步骤,词法分析器读入构成源程序的字符流,并且将它们组织成为有意义的词素的序列,对于每一个词素,词法分析器产生如下形式的词法单元作为输出。
2. 语法分析,语法分析器使用词法分析器天生的各词法单元的第一个分类来 创建树形的中心表现,在词法分析的基础上将单词序列组合成各类语法短语。该中心表现给出了词法分析产生的词法单元的语法布局,常用的表现方法为语法树。
3. 语义分析,语义分析器使用语法树和符号表中的信息来检查源程序是否和 语言定义的语义同等,它同时网络类型信息,并存放在语法树或符号表中, 为代码天生阶段做准备。
4. 代码天生和优化,在源程序的语法分析和语义分析完成后,会天生一个明 确的低级的或类及其语言的中心表现。代码优化试图改进中心代码,使天生的代码执行所必要时间和空间更少。末了代码天生以中心表现形式为输入,并把它映射为目的语言。
预处置惩罚过程并不对程序的源代码进行解析,但它把源代码分割或处置惩罚成为特定的单位--预处置惩罚记号用来支持语言特性。预处置惩罚的功能可以分为宏定义,文件包含,条件编译三方面,由预处置惩罚器解析宏定义命令、文件包含命令、条件编译命令。为进一步编译提供准备文件。
留意:这儿的编译是指从 .i 到 .s 即预处置惩罚后的文件到天生汇编语言程序     

3.2 在Ubuntu下编译的命令


3.3 Hello的编译结果解析


       3.3.1 数据
包罗常量、变量(全局/局部/静态)、表达式、类型、宏 。
本程序中只有常量和局部变量。
常量:1)字符串常量。c程序里有两处:"用法: Hello 学号 姓名 秒数!\n"和"Hello %s %s\n"

可以看出,LC1里明显存放了"Hello %s %s\n"这个字符串,;而另一个字符串在汇编代码里的存在形式不太明显,但是通过:Hello可以看出字符串"用法: Hello 学号 姓名 秒数!\n"存在了LC0处。
2)数字常量
在汇编语言中,$背面加上数字表现立即数。如c程序中argc!=5中的5就表现为这条汇编语句中的 $5


3.3.2赋值
  赋值操纵通过数据传送指令来进行,主要是MOV类指令,如movb,movw,movl,movq


3.3.3算术操纵
包罗+ - * / %  ++  --     取正/负+-   复合“+=”等
本程序里只涉及了i++,其对应的汇编代码为





3.3.4关系操纵和控制转移:
关系操纵包罗==    !=      >    <      >=     <=等。
控制转移包罗if/else switch for while  do/while  ?: continue  break等。
本程序中涉及了if(argc!=5)和for(i=0;i<10;i++)
对应的汇编语句分别为:





这两条语句将argv和5进行比较,如果相称,则if(argc!=5)中的条件不成立,跳转到L3(else部分),否则继承次序执行。





这两条语句将i和9进行比较,如果i<=9,则i跳转到L4(循环体部分),否则竣事循环。对应了for(i=0;i<10;i++)中的i<10。Hello.c中写的是i<10但汇编中是i<=9分析编译器可能会自动探求条件的等价实现。
cmp  A,B 根据B-A的盘算结果设置条件码寄存器ZF/SF/CF/OF,但不更新目的寄存器B。可以通过对条件码的组合进行判定来实现关系运算。例如,!=可以通过判定~ZF实现,<可以通过作减法(不改变值)后判定SF^OF实现。条件跳转指令就是使用条件码的组合判定大小关系是否满意来决定是否跳转,如je就是如果ZF=1(分析之前的减法或cmp的结果0即两数相称)则跳转。
3.3.5数组/指针/布局操纵:
包罗A    &v   *p    s.id    p->id等。
本程序中的例子是
printf("Hello %s %s\n",argv[1],argv[2]);
sleep(atoi(argv[3]));
都是数组操纵,以数组首地址为基址,使用变址寻址或比例变址寻址方式就可以实现对数组元素的表现。
由于main函数的实际布局为: int main(int argc,char* argv[]),
且寄存器传递参数的次序是 %rdi,%rsi,%rdx,%rcx.......
因此argc在%rdi中,argv在%rsi中。
argv是argv[]数组的首地址,由movq %rsi, -32(%rbp)可知argv存在了-32(%rbp)里。

3.3.6函数操纵
包罗参数传递(地址/值)、函数调用()、局部变量、函数返回等。
参数传递:第1~6个参数存放在寄存器%rdi,%rsi,%rdx,%rcx,%r8,%r9中,寄存器不够用会把多的参数存放在栈中。
函数调用:使用call指令。call执行的过程起首把下一条指令地址压入栈中作为返回地址,然后把PC的值更新为call背面的符号表现的地址。通过ret返回时把PC设置为栈中之前存放的指令地址值即可。
局部变量:寄存器组是唯一被所有过程共享的资源,为了包管被调用者不会覆盖调用者的寄存器值,x86-64把寄存器分为两种:

被调用者保存寄存器:rbp,rbx和r12~r15。当P调用Q时,Q必须“保存”这些寄存器的值——要么不去使用,要么压入栈中然后返回前规复。
调用者保存寄存器:剩下的所有的寄存器除了rsp都属于调用者保存寄存器,如果想要确保调用过程中这些值不被破坏那么调用者必须主动保存它们。
函数返回:使用ret指令。ret把PC设置为栈中之前存放的返回地址。返回值存放在%rax中,接下来以sleep(atoi(argv[4]));为例:

对应的源程序为sleep(atoi(argv[4]));由于已知argv存在了-32(%rbp)里,因此前四条语句取出-32(%rbp)+24处的值,即为argv[4],放入%rdi,这样就准备好了atoi的参数,call atoi调用atoi函数,其返回值存在%rax里,紧接着将%eax中的值传给%edi并调用sleep,这样就把atoi的返回值作为sleep的第一个参数传了进去。
3.4 本章小结

本章的目的是探究编译的原理和实践,以及C语言与汇编语言之间的对应关系。起首,介绍了编译的定义和功能,即将文本文件转换为汇编语言程序,为天生呆板码做好准备。其次,介绍了在Ubuntu环境下使用gcc命令对hello.c源文件进行编译,并天生hello.s汇编文件。然后,对hello.s文件中的各种数据类型和操纵进行了详细的分析和解释,展示了编译器如何处置惩罚不同的数据和指令。通过本章的学习,加深了对C语言和汇编语言的明确,为后续的链接、加载和运行过程打下了基础。




第4章 汇编

4.1 汇编的概念与作用

汇编的概念:汇编是一种面向呆板的程序筹划语言,它用易于明确和影象的符号或名称来表现呆板指令的操纵码和操纵数。汇编语言与呆板语言之间有一一对应的关系,可以通过汇编器将汇编语言翻译成呆板语言,也可以通过反汇编器将呆板语言还原成汇编语言。汇编语言的优点是程序执行效率高,缺点是难于编写和调试,且与具体的处置惩罚器密切相关。
汇编的作用:
(1)适应不同的高级语言和编译器,提供通用的输出语言。
(2)便于分析和调试底层的问题,查看反汇编代码和呆板状态。
(3)提高程序的执行效率和性能,使用处置惩罚器的特殊指令和优化本事。
(4)明确盘算机的工作原理和布局,把握数据的存储格式和寻址方式。
留意:这儿的汇编是指从 .s 到 .o 即编译后的文件到天生呆板语言二进制程序的过程。
4.2 在Ubuntu下汇编的命令

命令:gcc -m64 -no-pie -fno-PIC -c hello.s -o hello.o


4.3 可重定位目的elf格式

  在shell中输入readelf -a hello.o > hello.elf 指令获得 hello.o 文件的 ELF 格式:

  布局分析:
1.ELF头:描述了文件的总体格式。它以一个16字节的序列开始,该序列描述了天生该文件的系统的字的大小和字节次序,剩下部分包罗ELF头的大小、目的文件的类型、呆板类型、节头部表的文件偏移,以及节头部表中条目的大小和数量等,这些部分可以帮助链接器语法分析和解释目的文件的信息。

2.节头部表:是描述目的文件的节,它描述了不同节的名字、类型、位置和大小。

3.可重定位节和符号表:.rel.text表现可重定位代码,它是一个.text节中位置的列表,当后续链接器把这个目的文件和别的文件组适时,必要修改这些位置。而.symtab表现符号表,它存放在程序中定义和引用的函数和全局变量的信息。hello.elf中的可重定位节和符号表如图:

4.4 Hello.o的结果解析

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

对照分析:

  • 呆板指令
对比hello.o的反汇编文件dump.txt和hello.s,可以发现两者在汇编指令上无太大差别,但反汇编文件有呆板指令,由操纵码和地址码构成,每条呆板指令最前面有指令的相对地址。


  • 分支转移
在hello.s文件中,分支转移的目的地是使用段名标出的,而经过汇编后分支跳转有了具体确定的地址偏移。



  • 函数调用
和分支转移一样,在hello.s文件中,函数调用的具体目的地是用函数名给出的,而在汇编之后,函数调用有了具体的地址,是该条命令的下一条命令的地址。但这显然不正确,因为调用的函数还在别的库中,其具体调用的地址无法确定,只有在经过链接后函数调用的准确位置才气确定。






4.5 本章小结

本章起首从理论上阐述了汇编的概念和作用。之后以hello.o程序为例,详细分析了汇编前后程序的变化。可看出汇编后产生了可重定位目的文件,这其中有可供盘算机明确的呆板代码,同时,之前许多不确定的函数调用和分支转移有了准确的地址偏移,以便于接下来链接器进行的链接过程。


第5章 链接

5.1 链接的概念与作用

概念:链接是将各种代码和数据片断网络并组合成为一个单一文件的过程,这个文件可被加载到内存并执行。链接可以执行于编译时、加载时、运行时,它是由链接器的程序自动执行的。
作用:链接过程使得分离编译(separate compilation)成为可能,它使得程序员编程时不必将大型的应用程序组织为一个巨大的源文件,而是可以将其分解为更小、更好管理的模块,可以独立地修改和编译这些模块,当修改其中一个模块时,只必要重新编译该模块并重新链接应用,而不必编译别的文件,链接对模块化编程起到了紧张的作用。
留意:这儿的链接是指从 hello.o 到hello天生过程。
5.2 在Ubuntu下链接的命令

用ld的链接命令,应截图,展示汇编过程! 留意不但连接hello.o文件
命令:
ld -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 /usr/lib/gcc/x86_64-linux-gnu/11/crtbegin.o hello.o -lc /usr/lib/gcc/x86_64-linux-gnu/11/crtend.o /usr/lib/x86_64-linux-gnu/crtn.o -z relro -o hello

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

Hello(可执行文件)的ELF与hello.o的ELF都包含ELF 头,程序头表,.text 节,.rodata节,.bss节,.symtab节,.debug节,节头表。但是可执行文件里少了.rel.txt节和.rel.data节,因为在链接的过程中他们的使命已经完成了故天生的可执行文件里不再包含.rel.txt节和.rel.data节。但是新增了和动态链接库相关的重定位信息.rela.dyn ,同时,新增了.init节各段的起始地址,大小等信息可以在节头部表里查看。


5.4 hello的假造地址空间

使用edb加载hello,查看本历程的假造地址空间各段信息,并与5.3对照分析分析。  

在edb中打开可执行文件hello,可以看到hello可执行部分(代码段)起始地址为0x401000,竣事地址为0x401ff0
在5.3中,可以看到,.init节的起始地址是0x401000,二者之间是对应的

5.5 链接的重定位过程分析

命令:objdump -d -r hello > hello.ss


  • 链接后函数数量增长
链接后的反汇编文件hello.ss多出了.plt,puts@plt,printf@plt,getchar@plt,exit@plt,sleep@plt等函数的代码。原因是动态链接器将共享库中hello.c用到的函数加入可执行文件中。


 2.函数调用指令call的参数发生变化

链接器在链接过程中,处置惩罚了重定位条目,将call指令后的字节码修改为目的地址与下一条指令地址的差值,从而使call指令能够跳转到正确的代码段,天生了完整的反汇编代码。


3.跳转指令参数发生变化

 在链接过程中,链接器解析了重定位条目,并盘算相对距离,修改了对应位置的字节代码为PLT 中相应函数与下条指令的相对地址,从而得到完整的反汇编代码。

5.6 hello的执行流程

使用gdb/edb执行hello,分析从加载hello到_start,到call main,以及程序终止的所有过程(主要函数)。请列出其调用与跳转的各个子程序名或程序地址。
使用edb单步调试,每次跳转的函数依次记载

_start:0x00000000004010f0
__libc_start_main:0x00007fa82ac29dc0
__cxa_atexit:0x00007f57014458c0
_init:0x0000000000401000
puts@plt:0x0000000000401090
printf@plt:0x00000000004010a0
getchar@plt:0x00000000004010b0
atoi@plt:0x00000000004010c0
exit@plt:0x00000000004010d0
sleep@plt:0x00000000004010e0
deregister_tm_clones:0x0000000000401130
_dl_relocate_static_pie:0x0000000000401120
register_tm_clones:0x0000000000401160
__do_global_dtors_aux:0x00000000004011a0
frame_dummy:0x00000000004011d0
_fini:0x0000000000401270
5.7 Hello的动态链接分析

分析hello程序的动态链接项目,通过edb/gdb调试,分析在动态链接前后,这些项目的内容变化。要截图标识分析。
  在hello程序中可以发现调用共享库的函数名称背面都加了“@plt”,通过观察反汇编文件,可以发现使用这些函数时都会跳转到.plt节中,如图34所示,hello程序调用了6个共享库的函数,.plt节中就有6处跳转指令,它们跳转到了同一个地址0x401020,然后在0x401026处它们会进行一个间接跳转,跳转到<_GLOBAL_OFFSET_TABLE_+0x10>处,及0x404120内存中存放的地址处。


而这个地址在dl_init前,这个值如图左侧所示,是一个空值,而当调用一个动态链接库中的函数时,该地址内容值会发生变化,使其跳转到所调用的对应的函数地址处。
5.8 本章小结                               

本章介绍了链接的概念和作用,以及动态链接的原理。通过使用edb、gdb、objdump等工具,分析了hello程序的假造地址空间、ELF文件格式、重定位和执行过程,深入明确了链接的各个环节。

第6章 hello历程管理

6.1 历程的概念与作用

概念:历程的定义是盘算机一个执行中程序的实例,也称为是盘算机中的程序关于某数据集合上的一次运行运动。它是系统进行资源分配的根本单位,是操纵系统布局的基础,系统中的每个程序都运行在某个历程的上下文中。
作用:历程是对正在运行的程序过程的抽象,历程可以抽象地提供给应用程序一个独立的逻辑控制流和一个私有的地址空间。它可以清楚地刻画动态系统的内在规律,有用管理和调度进入盘算机系统主存储器运行的程序。
6.2 简述壳Shell-bash的作用与处置惩罚流程

作用:Shell是一种命令行解释器, 其读取用户输入的字符串命令, 解释并且执行命令. 它是一种特殊的应用程序, 介于系统调用/库与应用程序之间, 其提供了运行其他程序的的接口.它可以是交互式的, 即读取用户输入的字符串;也可以是非交互式的, 即读取脚本文件并解释执行, 直至文件竣事。
处置惩罚流程:当Shell接受一条命令后,他会起首解析命令行中的命令及相关参数,如果命令是要执行一个程序,它会起首执行fork函数创建一个新的子历程,然后在这个子历程中执行execve函数在加载并运行这个程序,之后就会在前台历程中执行waitpid函数等待子历程中程序执行完毕后回收该子历程。如果命令是一个内置的shell命令,它就直接执行该命令。
6.3 Hello的fork历程创建过程

当父历程调用fork函数可创建一个新的子历程。新创建的子历程得到与父历程用户级假造地址空间相同(但是独立的)一份副本,包罗代码和数据段、堆、共享库以及用户栈。子历程还获得与父历程任何打开文件描述符相同的副本。父历程与子历程之间最大的区别在于它们有不同的PID,父历程中fork返回子历程的PID,子历程的PID总是非0,而子历程中fork返回0。
 以运行hello程序为例,当输入./hello时,父历程为shell,它会对这条命令进行解析,因为这不是内置shell命令,它判定要执行hello这个可执行文件,于是它就调用fork函数创建一个新的子历程以便接下来将hello加载到这个历程中执行。
6.4 Hello的execve过程

execve函数声明为:int execve(char *filename,char *argv[],char *envp[])
execve函数会在当进步程的上下文中加载并运行一个新程序,它加载并运行可执行目的文件filename,且带参数列表argv和环境变量列表envp,在execve加载filename时会调用一个加载器,加载器会创建内存映像并将可执行文件的片(chunk)复制到代码段和数据段,接下来,加载器会跳转到程序的入口,即_start函数的地址点来设置用户栈,如图36所示。初始化程序后就会把控制权传递给main函数。
以运行hello程序为例,当shell调用fork函数创建一个新的子历程后,它会调用execve函数加载并运行可执行目的文件hello,如果命令./hello后跟有参数,shell也会把这些参数当作参数列表argv一起传入历程,这样hello就实现了由程序到历程的变化,之后完成初始化程序后,就会正式运行main函数。
6.5 Hello的历程执行

联合历程上下文信息、历程时间片,阐述历程调度的过程,用户态与焦点态转换等等。
一个历程和别的历程轮流进行的概念称为多使命,一个历程执行它的控制流的一部分的每一时间段叫做该历程的时间片,操纵系统内核使用一种称为上下文切换的异常控制流实现多使命,具体如图37所示。
内核为每个历程维持一个上下文,系统中的每个程序都运行在某个历程的上下文中,历程上下文信息就是内核重新启动一个被抢占的历程所需的状态,它由一些对象的值构成,包罗寄存器、程序计数器、用户栈等。
以执行程序hello中的sleep函数为例,当main函数执行了sleep系统调用函数时,触发了陷阱异常,此时从用户模式切换为内核模式,main所在历程休眠一段时间,控制权交给别的历程,执行了上下文切换,此时会切换回用户模式。当休眠竣事时,会发送信号给内核,此时又会进入异常处置惩罚程序,又从用户模式切换为内核模式,它会执行从别的历程到main所在历程的上下文切换,结果控制权又交回给main所在历程。调用别的系统函数时也会有类似的历程调度过程。
6.6 hello的异常与信号处置惩罚

hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处置惩罚的。
 程序运行过程中可以按键盘,如不停乱按,包罗回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,分析异常与信号的处置惩罚。
hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处置惩罚的。

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

hello执行过程中出现的异常种类可能会有:制止、陷阱、故障、终止。
制止:制止是来自处置惩罚器外部的I/O设备的信号的结果。如在按下ctrl-z后,会触发一个制止异常。
陷阱:陷阱是有意的异常,是执行一条指令的结果。就像制止处置惩罚程序一样,陷阱处置惩罚程序将控制返回到下一条指令。陷阱最紧张的用途是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用。
故障:故障由错误情况引起,它可能能够被故障处置惩罚程序修正。当故障发生时,处置惩罚器将控制转移给故障处置惩罚程序。如果处置惩罚程序能够修正这个错误情况,它就将控制返回到引起故障的指令,从而重新执行它。否则处置惩罚程序返回到内核中的abort例程并将其终止。
终止:终止是不可规复的致命错误造成的结果,通常是一些硬件错误,比如DRAM大概SRAM位被损坏时发生的奇偶错误。终止处置惩罚程序从不将控制返回给应用程序。
6.7本章小结

在使用shell执行hello的过程中,hello从可执行文件加载到内存中真正开始运行。Shell调用fork和execve创建一个新历程并在它的上下文里加载hello。Hello在运行中可能会收到来自键盘的信号,hello历程对于这些信号会做出相应的反应

第7章 hello的存储管理

7.1 hello的存储器地址空间

联合hello分析逻辑地址、线性地址、假造地址、物理地址的概念。
逻辑地址是指在hello反汇编代码中可见的地址,它由一个段标识符和一个段内偏移量构成,用于定位操纵数或指令的位置。
线性地址是逻辑地址向物理地址转换过程中的中心结果,它是通过段机制将逻辑地址转换为一维地址空间的地址。在掩护模式下,假造地址也可以用“段:偏移量”的形式表现,其中的段是指段选择器。Hello反汇编的地址即为假造地址。
假造地址是掩护模式下程序访问存储器所使用的逻辑地址,它可以通过分页机制映射到不同的物理地址。
物理地址是加载到内存地址寄存器中的地址,它是内存单元的实际地址。CPU通过地址总线寻址时,使用的是物理地址。在前端总线上传输的内存地址也都是物理地址。
7.2 Intel逻辑地址到线性地址的变换-段式管理

段式存储管理是一种以段为单位进行内存分配和回收的方法,它根据程序的逻辑布局将程序划分为若干个段,每个段具有一个段名和一个段内偏移量,用于定位程序或数据的位置。每个段在内存中占据一个连续的空间,但不同段之间可以不连续。段式存储管理必要维护一个段表,记载每个段的基址、长度、存取控制等信息。地址变换时,通过查找段表,将逻辑地址转换为物理地址。
段式管理的优点是能够实现程序的动态链接、掩护和共享。动态链接是指在程序运行过程中,根据必要动态地装入或卸载某些段,实现程序的模块化。掩护是指通过设置每个段的存取控制位,防止非法访问或修改。共享是指允许多个历程共同使用某些公共的程序或数据段,节省内存空间。
线性地址是逻辑地址向物理地址转换过程中的中心结果,它是通过将逻辑地址中的段内偏移量加上相应的段基址得到的。线性地址可以看作是一维的假造地址空间,它可以进一步通过分页机制映射到物理地址空间。
7.3 Hello的线性地址到物理地址的变换-页式管理

为了实现线性地址到物理地址的转换,操纵系统采用了分页机制,将线性地址空间划分为等长的单元,称为页。由于页表可能占用大量的内存空间,操纵系统使用了两级页表布局,即页目录表和页表。页目录表的物理地址保存在cr3寄存器中。
当处置惩罚器访问一个线性地址时,它会通过内存管理单元(MMU)天生一个假造地址,并根据假造地址的高10位在页目录表中查找对应的页表地址。然后,它会根据假造地址的中心10位在页表中查找对应的页帧地址。末了,它会将页帧地址和假造地址的低10位相加,得到终极的物理地址。
在这个过程中,处置惩罚器必要向高速缓存或主存发出请求,获取页目录表项和页表项。如果这些项已经存在于高速缓存中,那么处置惩罚器可以快速地得到物理地址。如果这些项不存在于高速缓存中,那么处置惩罚器必要从主存中读取它们,并将它们缓存在高速缓存中,以便下次使用。
7.4 TLB与四级页表支持下的VA到PA的变换

TLB是一个存储单个PTE的块的小型假造地址缓存,每个块对应一个假造页。当MMU接收到假造地址时,它会在TLB中查找匹配的块。如果找到,它会从PTE中获取物理页号,并与假造页偏移量组合成52位的物理地址。
如果TLB未掷中,MMU必要访问四级页表来获取PTE。CR3寄存器指向第一级页表的基址。假造页号的第一部分作为第一级页表的索引,得到一个PTE。如果该PTE有用且权限正确,它会指向第二级页表的基址。同样地,假造页号的第二、第三和第四部分分别作为第二、第三和第四级页表的索引,得到终极的PTE。该PTE包含物理页号,与假造页偏移量组合成物理地址,并添加到TLB中。
如果在访问任何级别的页表时发现PTE无效或权限错误,MMU会触发缺页制止。
7.5 三级Cache支持下的物理内存访问

采用CT、CI、CO三个字段对物理地址进行分割,并按照以下步骤实现cache的访问和更换。起首,根据CI字段在一级cache中定位相应的组,并检查该组中所有标志位为有用的块的CT字段是否与物理地址的CT字段相匹配。如果匹配成功,则表现一级cache掷中,并返回相应的数据块。如果匹配失败,则表现一级cache不掷中,必要继承在二级cache中查找。同样地,根据CI字段在二级cache中定位相应的组,并检查该组中所有标志位为有用的块的CT字段是否与物理地址的CT字段相匹配。如果匹配成功,则表现二级cache掷中,并将该数据块复制到一级cache中,并返回数据块。如果匹配失败,则表现二级cache不掷中,必要继承在三级cache中查找。依此类推,如果三级cache也不掷中,则必要从主存中读取数据块,并将其复制到各级cache中,并返回数据块。如果主存发生缺页制止,则必要从硬盘中读取数据块,并将其复制到主存和各级cache中,并返回数据块。
7.6 hello历程fork时的内存映射

shell执行fork函数时,内核为hello历程分配了唯一的PID,并建立了相应的数据布局和task_struct,同时创建了独立的假造地址空间。为了实现假造内存,内核复制了当进步程的mm_struct、区域布局和页表。它将两个历程共享的页面都设为只读,并将两个历程的区域布局都设为私有的写时复制。这样,fork在新历程返回时,新历程的假造内存与fork调用时的假造内存完全同等。当两个历程中恣意一个进行写操纵时,写时复制机制会天生新页面,从而包管了每个历程都拥有私有的地址空间抽象
7.7 hello历程execve时的内存映射

在加载新程序时,操纵系统必要对当进步程的用户假造地址空间进行重新映射。起首,它会删除已存在的用户区域布局,释放原来的代码、数据、bss、栈和堆区域所占用的物理内存。然后,它会为新程序的代码、数据、bss和栈区域创建新的用户区域布局,并将它们映射到相应的文件或匿名文件中。这些新的区域都是私有的,并且采用写时复制的策略。接着,它会检查新程序是否与共享对象链接,如果是的话,它会动态地将这些对象映射到用户假造地址空间中的共享区域中。末了,它会设置程序计数器(PC),使其指向新程序的代码区域的入口点。
7.8 缺页故障与缺页制止处置惩罚


缺页故障:当CPU访问假造内存中的某个地址,而该地址对应的物理页已经加载在主存中时,称为页掷中。反之,如果主存中没有该物理页,称为缺页。
缺页制止处置惩罚:当发生缺页时,CPU会调用缺页异常处置惩罚程序,该程序负责从主存中选择一个可更换的物理页,称为捐躯页。如果捐躯页已经被修改过,那么必要将其写回磁盘。然后,从磁盘中读取所需的物理页,并将其放入捐躯页的位置。同时,更新相关的PTE,使其指向新的物理页。末了,规复引起缺页的指令,并让CPU重新发送假造地址给MMU。
7.9动态存储分配管理

Printf会调用malloc,请简述动态内存管理的根本方法与策略。
动态内存管理是系统软件的根本组件之一,它负责在堆上为应用程序动态地分配和回收内存空间。动态内存分配器通过维护堆上的不同大小的内存块来实现动态内存管理,其中每个内存块都是一段连续的假造地址空间,可以是已经被分配给应用程序的大概是尚未被使用的。当应用程序请求一定大小的内存时,动态内存分配器必要从空闲块中选择一个合适的块来满意请求,并将其标记为已分配状态。当应用程序释放已分配的内存时,动态内存分配器必要将其规复为空闲状态,并实验与相邻的空闲块进行归并,以淘汰内存碎片。
为了高效地管理堆上的内存块,动态内存分配器通常采用以下三种策略:
记载空闲块策略:这种策略决定了如何组织和查找空闲块,以便快速地找到合适的空闲块来满意应用程序的请求。常见的记载空闲块策略有表现空闲链表、隐式空闲链表、分离空闲链表和红黑树等。
放置策略:这种策略决定了如何从找到的空闲块中划分出所需大小的内存给应用程序,并处置惩罚剩余部分。常见的放置策略有首次适配、下一次适配和最佳适配等。
归并策略:这种策略决定了何时以及如何将相邻的空闲块进行归并,以避免或淘汰内存碎片。常见的归并策略有立即归并和延迟归并等。
7.10本章小结

本章主要介绍了hello 的存储器地址空间、intel 的段式管理、hello 的页式管理, VA 到PA 的变换、物理内存访问,hello历程fork、execve 时的内存映射、缺页故障与缺页制止处置惩罚、动态存储分配管理的相关内容,对hello的存储管理有了较为深入的讲解。

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件
设备管理:unix io接口
Linux内核采用了一种优雅的设备抽象方式,即将所有的I/O设备(如网络、磁盘和终端)视为文件,并将所有的输入和输出操纵统一为对应文件的读写操纵。这样,Linux内核就可以提供一个简单而低级的接口,即Unix I/O,实现了输入和输出操纵的同等性和统一性。
8.2 简述Unix IO接口及其函数

接口:
(1)打开文件:程序通过调用open()函数来请求内核分配一个可用的描述符,并将其与指定路径名的文件关联起来。描述符是一个非负整数,可以唯一地标识历程打开的某个文件。内核维护了一个打开文件表,记载了每个打开文件的相关信息,如访问权限、当前位置、引用计数等。程序在后续对该文件的操纵中只需使用该描述符即可。
(2)linux shell创建的每个历程都有三个预定义的描述符:标准输入、标准输出和标准错误。它们分别对应着0、1和2这三个常量值,可以在头文件中找到它们的定义。它们分别表现历程从终端读取输入、向终端输出结果和向终端陈诉错误信息所使用的描述符。
(3)改变当前的文件位置:对于每个打开的文件,内核都会记载其当前位置,即从该文件起始处开始盘算的字节偏移量。程序可以通过调用lseek()函数来显式地改变某个描述符所对应的当前位置,以便在随机访问模式下读写该文件。
(4)读写文件。读操纵就是通过调用read()函数来将指定描述符所对应的当前位置开始的多个字节复制到内存缓冲区中。如果在给定大小范围内执行读操纵时遇到了该文件的末尾,则会产生一个称为EOF(end-of-file)的条件,并返回实际读取到的字节数。应用程序可以通过检测这个条件来判定是否已经读完了该文件。类似地,写操纵就是通过调用write()函数来将内存缓冲区中的多个字节复制到指定描述符所对应的当前位置开始的那些字节上,并更新当前位置。
(5)关闭文件。当程序不再必要访问某个打开的文件时,它可以通过调用close()函数来通知内核释放该描述符,并更新打开文件表中相应条目的信息。作为响应,内核会淘汰该条目的引用计数,并在引用计数为零时将其删除。当历程因为任何原因终止时,内核也会自动关闭该历程打开的所有文件,并释放相关资源。
函数:
(1)open()函数用于打开或创建文件。
(2)close()函数用于关闭一个被打开的的文件。
(3)read()函数用于从文件读取数据。
(4)write()函数用于向文件写入数据。
(5)lseek()函数用于在指定的文件描述符中将将文件指针定位到相应位置。
8.3 printf的实现分析


printf函数的函数体如图52所示。
起首printf函数输入的参数中含有“…”,这代表可变形参,即传递参数的个数不确定。
之后va_list arg = (va_list)((char*)(&fmt) + 4)中va_list为一个定义的字符指针类型,arg即为一个字符指针,而fmt是一个指针,这个指针指向第一个const参数中的第一个元素。那么清楚arg即为printf可变形参中的第一个参数的地址。
vsprintf的定义为:int vsprintf(char *buf,const char *fmt,va_list args)。这命令的目的是将从第一个传递参数开始,依次把这个字符串传入buf缓冲区中,其中返回值为i便是要打印出来的字符串的长度。
末了write函数就会把buf中的i个元素的值写到终端。
但从硬件上看,从vsprintf到终极表现器上表现出字符串另有对应漫长过程:
从vsprintf天生表现信息,到write系统函数,到陷阱-系统调用 int 0x80或syscall等。接着字符表现驱动子程序:从ASCII到字模库到表现vram(存储每一个点的RGB颜色信息)。末了表现芯片按照刷新频率逐行读取vram,并通过信号线向液晶表现器传输每一个点(RGB分量)。
8.4 getchar的实现分析

异步异常-键盘制止的处置惩罚:键盘制止处置惩罚子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。
getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

当用键盘输入字符串时,会触发键盘制止异常,执行键盘制止处置惩罚子程序。字符接受按键扫描码转化成ASCII码,保存到系统的键盘缓冲区,getchar调用read函数,将键盘缓冲区中的字符读入buf中,并测得该字符串的长度为n,然后令字符指针bb指向buf。末了返回buf中第一个字符。如果长度n<0,则会报EOF错误。
8.5本章小结

本章主要介绍了Linux系统中的I/O设备管理和Unix I/O接口,以及它们在实现根本的输入输出功能中的作用。窗体顶端
结论

编写一个输出“Hello, world!”的hello.c源文件
使用gcc命令对hello.c进行预处置惩罚、编译、汇编和链接,天生可执行程序hello
使用./hello命令运行可执行程序hello
shell历程调用fork()函数创建一个子历程,并复制父历程的地址空间和状态
子历程调用execve()函数加载可执行程序到假造内存,并覆盖原来的地址空间和状态
MMU负责将程序中的假造地址转换为物理地址,并检查地址是否正当和可访问
处置惩罚器根据程序中的指令执行逻辑控制流,包罗次序执行、条件分支、循环等
程序正常竣事大概出现异常时,调用信号处置惩罚函数进行处置惩罚,并返回相应的退出码
历程竣事后,操纵系统负责释放和回收其占用的资源,包罗内存、文件描述符等
盘算机系统的筹划非常精妙,实现这整个系统每个环节环环相扣,缺一不可。
盘算机系统的筹划是一个高度复杂且风雅的工程,它涉及到编程、编译、历程管理、内存管理、程序加载、异常处置惩罚等多个方面。这些部分相互依赖、相互协作,共同构成了一个功能强大、稳定可靠的盘算机系统。

附件

列出所有的中心产物的文件名,并予以分析起作用。
hello.c源程序

hello.i hello.c预处置惩罚后的预编译处置惩罚文件

hello.s hello.i编译后的汇编文件

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

hello hello.o链接后的可执行目的文件

dump.txt hello.o反汇编后文件

hello.ss hello反汇编后文件

hello.elf hello.o的ELF格式文件

hello.elf hello的ELF格式文件

printf.txt print函数的函数体

getchar.txt getchar函数的函数体

参考文献

[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.
[7]  《深入明确盘算机系统》Randal E. Bryant David R.O`Hallaron
[8]  printf函数实现的深入剖析.



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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

鼠扑

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表