不到断气不罢休 发表于 2024-7-22 17:48:50

哈工大计算机体系大作业--程序人生






计算机体系

大作业


题     目  程序人生-Hello’s P2P 
专       业   数据科学与大数据技术                     
学     号    2022110xxx                    
班   级    2203501                    
学       生    xx               
指 导 教 师    吴锐                 






计算机科学与技术学院
2024年5月
摘  要
本文重要围绕“hello程序的一生”,介绍了从hello.c经过预处理、编译、汇编、链接等一直到生成可执行目标文件hello的过程,且在Ubuntu的环境下通过各种调试工具详细分析了每一阶段产生的结果,对于深入理解计算机体系有很大帮助。

关键词:hello;预处理;编译;汇编;链接;进程管理;存储管理;I/O管理;                            
目  录

第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.3.1数据
3.3.2赋值
3.3.3算术操纵
3.3.4关系操纵
3.3.5数组操纵
3.3.6控制转移
3.3.7函数操纵
3.4 本章小结
第4章 汇编
4.1 汇编的概念与作用
4.2 在Ubuntu下汇编的命令
4.3 可重定位目标elf格式
4.3.1命令
4.3.2 ELF头
4.3.3 节头
4.3.4重定位节
4.3.5符号表
4.4 Hello.o的结果剖析
4.5 本章小结
第5章 链接
5.1 链接的概念与作用
5.2 在Ubuntu下链接的命令
5.3 可执行目标文件hello的格式
5.3.1 readelf命令
5.3.2 ELF头
5.3.3 节头
5.3.4 程序头
5.3.5 重定位节
5.3.6 符号表
5.3.7 动态节
5.4 hello的捏造地址空间
5.5 链接的重定位过程分析
5.5.1 objdump命令
5.5.2 hello与hello.o对比及重定位过程分析
5.5.3 链接过程
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.6.1 异常的种类
6.6.2 产生的信号
6.6.3处理方式
6.6.4命令、运行结果、信号与异常的处理
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.2.1 Unix I/O接口
8.2.2 Unix I/O函数
8.3 printf的实现分析
8.4 getchar的实现分析
8.5本章小结
结论
附件
参考文献
第1章 概述

1.1 Hello简介

P2P(From Program to Process):
程序通过预处理、编译、汇编、链接最终形成可执行文件。在shell中输入干系命令,调用fork()函数创建子进程。
https://img-blog.csdnimg.cn/direct/bf1185173f0a4e618cbaf565d3cadae7.png
图1.1-1 编译体系
预处理阶段:预处理器(cpp)根据以字符#开头的命令,修改原始c程序。
编译阶段:编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包罗一个汇编语言的程序。
汇编阶段:汇编器(as)将hello.s翻译成呆板语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在hello.o中。
链接阶段:链接器(ld)将单独的预编译好了的目标文件合并到hello.o中,结果得到hello文件
O2O(From Zero-0 to Zero-0):
在shell中输入干系命令,调用fork函数创建新的子进程,execve函数加载并运行程序,通过内存映射映射到相应的捏造内存空间,加载需要的物理内存,运行hello,CPU在流水线上逐条执行指令。当hello运行完毕后,父进程回收hello进程,内核扫除干系信息。
1.2 环境与工具

硬件环境:X64 CPU;2GHz;2G RAM;256GHD Disk 以上
软件环境:Windows10 64位;VMware Workstation Pro15.5.1;Ubuntu 20.04.4
开发和调试工具:gdb;edb;readelf;objdump;Code::Blocks20.03
1.3 中心结果

文件名称
文件作用
hello.c
hello程序源代码
hello.i
源代码hello.c经过预处理产生的文件
hello.s
hello程序对应的汇编文件
hello.o
可重定位目标文件
hello
hello链接后的可执行目标文件
hello_elf.txt
hello.o的ELF格式文件

1.4 本章小结

本章根据hello的自白,简述了hello的P2P和O2O的整个过程,此外,还介绍了完成本次实行的软硬件环境、调试和开发工具及中心结果的文件名称和文件作用。
第2章 预处理

2.1 预处理的概念与作用

1.预处理的概念:
预处理器(cpp)根据以字符#开头的命令,修改原始c程序,结果得到另一个C程序,通常是以.i作为文件扩展名。
2.预处理的作用:

[*]条件编译:根据条件有选择性的保留大概放弃源文件中的内容。常见的条件包罗#if、#ifdef、#ifndef指令开始,以#endif结束。
[*]源文件包罗:搜索指定的文件,并将它的内容包罗进来,放在当前所在的位置。源文件包罗有两种,包罗体系文件以及用户自界说文件。
[*]宏更换:把一个标识符指定为其他一些成为更换列表的预处理记号,当这个标识符出现在后面的文本中时,将用对应的预处理记号把它更换掉,宏的本质是更换。宏的界说分为两种方式:有参和无参。
[*]行控制:行控制指令以"#"和“line”引导,后面是行号和可选的字面串。它用于改变预界说宏"__LINE__"的值,如果后面的字面串存在,则改变“__FILE__”的值。
[*]抛错:抛错指令是以“#”和“error”引导,抛错指令用于在预处理期间发出一个诊断信息,在停止转换。抛错是人为的动作。
[*]杂注:杂注指令用于向C实现通报额外的信息(编译选项),对程序的某些方面进行控制。杂注指令以“#”开始,跟着“pragma”,后面是其他预处理记号,即所谓的选项。
2.2在Ubuntu下预处理的命令

命令:gcc -E hello.c -o hello.i
https://img-blog.csdnimg.cn/direct/ca2b8a8ae014464481f5e30e05cf06d2.png
图2.2-1 预处理命令截图
2.3 Hello的预处理结果剖析

查看hello.i文件内容,下面只展示部分内容。
https://img-blog.csdnimg.cn/direct/ebe8b54156ec49d49c802a43e1bf7288.png
图2.3-1 hello.i部分结果截图
https://img-blog.csdnimg.cn/direct/1647135a085940a6ab4aad949f1dd1be.png
图2.3-2 hello.i部分结果截图
预处理操纵将“#include”指令进行了处理,对头文件stdio.h、unistd.h、stdlib.h中的内容睁开。
https://img-blog.csdnimg.cn/direct/ff5ab0195709418a9f146a74c2424c26.png
图2.3-3 hello.i部分结果截图
这里展示了删去注释的源代码。
hello.c只有24行,而经过预处理得到的hello.i有3061行,可见hello.i文件大了许多,且是一个文本文件。
2.4 本章小结

本章重要介绍了预处理的概念与作用,在Ubuntu环境下实现了预处理过程,重要包括源文件包罗、宏更换、条件编译,并对预处理得到的hello.i文件进行分析。
第3章 编译

3.1 编译的概念与作用

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

[*]编译的概念:
编译器(ccl)将文本文件hello.i翻译成文本文件hello.s,它包罗一个汇编语言的程序。
2.编译的作用:

[*]词法分析:对由字符组成的单词进行处理,从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中心程序;
[*]语法分析:以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位,是否构成一个符合要求的程序,按该语言利用的语法规则分析检查每条语句是否有正确的逻辑结构;
[*]语义检查和中心代码生成:使编译程序的结构在逻辑上更为简单明白,特别是使目标代码的优化比较轻易实现中心代码;
[*]代码优化:对程序进行多种等价变动,使得从变动后的程序出发,能生成更有效的目标代码;
[*]目标代码生成:把语法分析后或优化后的中心代码变动成目标代码。
3.2 在Ubuntu下编译的命令

编译的命令为:gcc -S hello.i -o hello.s
https://img-blog.csdnimg.cn/direct/86643280bc104b308ac7d88d217b684a.png
图3.2-1 编译命令截图
3.3 Hello的编译结果剖析

3.3.1数据

1.常数:
常数以立即数情势出现,数字前面加‘$’。
hello.c文件中if(argc!=5)中的5:
https://img-blog.csdnimg.cn/direct/ae56c714df354bd3b6ef624b2d42a8f7.png
hello.c文件中exit(1)中的1:
https://img-blog.csdnimg.cn/direct/824d0b362a2748c3bdda7f072b6f9f4e.png
hello.c文件中for(i=0;i<10;i++)中i<10被优化为i<=9,立即数9:
https://img-blog.csdnimg.cn/direct/d53df080fd03448eb9e7e99c3ed83b49.png
2.局部变量
以hello.c文件中的变量i为例进行分析:
初始值为0:
https://img-blog.csdnimg.cn/direct/3f61bfe8e42c4c23a1d94b368025dfe3.png
每次循环i=i+1,并与9进行比较:
https://img-blog.csdnimg.cn/direct/be60da21e99c4dc59036c10cdcd06fab.png
3.表达式
hello.c文件中argc!=5,argc与5比较:
https://img-blog.csdnimg.cn/direct/eb3e79c8012d4ac7b94e476c431aebec.png
hello.c文件中i=0,赋初值:
https://img-blog.csdnimg.cn/direct/3f61bfe8e42c4c23a1d94b368025dfe3.png
hello.c文件中i++,在每次循环+1:
https://img-blog.csdnimg.cn/direct/41f175b8059b4268a745f01ef151864f.png
hello.c文件中i<10,已被优化为i<=9,以是与9进行比较:
https://img-blog.csdnimg.cn/direct/3997aca87c224da4a255df1bc7f9a7ba.png
3.3.2赋值

hello.c文件中i=0,为i赋初值,通过movl指令实现:
https://img-blog.csdnimg.cn/direct/ea5a5d512c5f4ca6a053a35c981c6482.png
hello.c文件中i++,在每次循环i赋值为i+1,通过addl指令实现:
https://img-blog.csdnimg.cn/direct/916b3e3812654643bd6cb1b0c12d0ba8.png
3.3.3算术操纵

hello.c文件中i++,通过addl指令实现:
https://img-blog.csdnimg.cn/direct/932ce3ab3eeb4251af6d6492ae5f88cd.png
3.3.4关系操纵

hello.c文件中argc!=5,将argc与5比较,通过cmpl指令实现:
https://img-blog.csdnimg.cn/direct/a0b1fe848b8c428ea0c945af9c504c1d.png
hello.c文件中i<10,已被优化为i<=9,以是与9进行比较:
https://img-blog.csdnimg.cn/direct/e2481729cc054d5eb49b7a0e261d2743.png
3.3.5数组操纵

hello.c文件中printf("Hello %s %s %s\n",argv,argv ,argv)。先在%rax中存储-32(%rbp),再将%rax加24,末了将%rax指向的数据通报给%rcx;先在%rax中存储-32(%rbp),再将%rax加16,将%rax指向的数据存储在%rdx中;先在%rax中存储-32(%rbp),再将%rax加8,将%rax指向的数据存储在%rax中,将%rax的值通报给%rsi;也就是argv、argv和argv的值存储在%rsi、%rdx和%rcx中,末了调用printf函数:
https://img-blog.csdnimg.cn/direct/45fb0ad4d96f488487e6a6fd13720ee9.png
图3.3.5-1 数组操纵截图例一
hello.c文件中sleep(atoi(argv)),将argv存储在%rdi中:
https://img-blog.csdnimg.cn/direct/bd853ca5bbd1447a8e250b48a3f4ad00.png
图3.3.5-2 数组操纵截图例一
3.3.6控制转移

1.if语句
https://img-blog.csdnimg.cn/direct/f6d95d95a82242ef8b1706b2a240ec78.png
图3.3.6-1 if语句源代码截图
利用cmpl指令和je指令,将agrc与5作比较,如果argc==5则跳转至.L2部分执行,否则继续执行下面的语句,直到末了退出:
https://img-blog.csdnimg.cn/direct/ab1ad4cd89be4133b110db67739c1abc.png
图3.3.6-2 if语句截图
2.for循环
https://img-blog.csdnimg.cn/direct/4deb1f4c01ac4324a4df17931919e691.png
图3.3.6-3 for循环语句源代码截图
执行循环中的内容和i++操纵在.L4部分,判定是否继续执行循环在.L3部分,利用cmpl指令和jle指令,如果i<=9,则跳转到.L4部分继续执行循环:
https://img-blog.csdnimg.cn/direct/dfa2064101944639b29820d57659145e.png
图3.3.6-4 for循环语句截图
3.3.7函数操纵

1.main()
传入参数argc和*argv[],并返回0:
https://img-blog.csdnimg.cn/direct/45f902623c284bb5b281f1aa7e8a456f.png
图3.3.7-1 main函数截图
2.printf()
printf("Hello 2022110525 张菲 18947955718 3!\n");传入字符串首地址,通过call指令转移到指定程序:
https://img-blog.csdnimg.cn/direct/05316ee029cc4cd3b984196d0855d4ae.png
图3.3.7-2printf函数例一截图
printf("Hello %s %s %s\n",argv,argv,argv); 传入参数argv和argv,通过call指令转移到指定程序:
https://img-blog.csdnimg.cn/direct/566285142cc94ecd87b3070a407aa0a8.png
图3.3.7-3 printf函数例二截图
3.exit()
通过movl指令通报参数1到%rdi,之后通过call指令转移到指定位置:
https://img-blog.csdnimg.cn/direct/bad8a75461314574bf0346721c9b8ee8.png
图3.3.7-4 exit函数截图
4.sleep()
通过movq通报参数,之后通过call指令转移到指定位置:
https://img-blog.csdnimg.cn/direct/a6d06a0fa933426cbb35da0449142acf.png
图3.3.7-5 sleep函数截图
3.4 本章小结

本章重要介绍了编译的概念与作用,在Ubuntu下进行编译的命令,详细分析了编译器如那边理C语言中的数据、赋值、算术运算、关系操纵、数组操纵、控制转移和函数操纵。帮助理解c代码在汇编语言中的表现情势,及对c代码产生了一定的优化。
第4章 汇编

4.1 汇编的概念与作用

注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成呆板语言二进制程序的过程。
1.汇编的概念
汇编器(as)将hello.s翻译成呆板语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,并将结果保存在hello.o中。
2.汇编的作用
将汇编代码转换成呆板可以执行的指令,每一个汇编语句险些都对应一条呆板指令。

[*]
[*]在Ubuntu下汇编的命令

汇编的命令为:gcc -m64 -no-pie -fno-PIC -c -o hello.o hello.s
https://img-blog.csdnimg.cn/direct/b5823e400d4c4753b5b6c2d45a51c05c.png
图4.2-1 汇编命令截图
4.3 可重定位目标elf格式

4.3.1命令

利用readelf -a hello.o > hello_elf.txt命令将elf文件导出至hello_elf.txt
4.3.2 ELF头

ELF头以一个16字节的序列开始,这个序列形貌了生成该文件的体系的字的巨细和字节次序。ELF头剩下的部分包罗帮助链接器语法分析和解释目标文件的信息。其中包括ELF头的巨细、目标文件的类型、呆板类型、节头部表的文件偏移,以及节头部表中条目的巨细和数目。
https://img-blog.csdnimg.cn/direct/82b08ff941ab45e2b4f5346bcd3b57d6.png
图4.3.2-1 ELF头内容截图
4.3.3 节头

差异节的位置和巨细是由节头部表形貌的,其中目标文件中每个节都有一个固定巨细的条目。
https://img-blog.csdnimg.cn/direct/15b7a3287dc14b8e892b9a184a416670.png
图4.3.3-1 节头内容截图
4.3.4重定位节

重定位节中包罗了在代码中利用的一些外部变量等信息,在链接的时候需要根据重定位节的信息对这些变量符号进行修改。链接的时候链接器会根据重定位节的信息对外部变量符号决定选择何种方式计算正确的地址,通过偏移量等信息计算出正确的地址。
https://img-blog.csdnimg.cn/direct/b6a7d766c9bd4d5ca3b4ecd8a5b8c089.png
图4.3.4-1 重定位节内容截图
这里出现了R_X86_64_PC_32和R_X86_64_PLT32两种根本的重定位类型。
4.3.5符号表

.symtab是一个符号表,存放在程序中界说和引用的函数和全局变量的信息,和编译器的符号表差异,.symtab符号表不包罗局部变量的条目。
https://img-blog.csdnimg.cn/direct/1ee752a6ea67495488c9eabadda5dc48.png
图4.3.5-1 符号表内容截图
4.4 Hello.o的结果剖析

objdump -d -r hello.o  分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。
说明呆板语言的构成,与汇编语言的映射关系。特别是呆板语言中的操纵数与汇编语言不划一,特别是分支转移函数调用等。
输入objdump -d -r hello.o命令得到以下输出:
hello.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0: f3 0f 1e fa           endbr64
   4: 55                    push   %rbp
   5: 48 89 e5              mov    %rsp,%rbp
   8: 48 83 ec 20           sub    $0x20,%rsp
   c: 89 7d ec              mov    %edi,-0x14(%rbp)
   f: 48 89 75 e0           mov    %rsi,-0x20(%rbp)
  13: 83 7d ec 05           cmpl   $0x5,-0x14(%rbp)
  17: 74 16                 je     2f <main+0x2f>
  19: 48 8d 3d 00 00 00 00 lea    0x0(%rip),%rdi        # 20 <main+0x20>
1c: R_X86_64_PC32 .rodata-0x4
  20: e8 00 00 00 00        callq  25 <main+0x25>
21: R_X86_64_PLT32 puts-0x4
  25: bf 01 00 00 00        mov    $0x1,%edi
  2a: e8 00 00 00 00        callq  2f <main+0x2f>
2b: R_X86_64_PLT32 exit-0x4
  2f: c7 45 fc 00 00 00 00 movl   $0x0,-0x4(%rbp)
  36: eb 53                 jmp    8b <main+0x8b>
  38: 48 8b 45 e0           mov    -0x20(%rbp),%rax
  3c: 48 83 c0 18           add    $0x18,%rax
  40: 48 8b 08              mov    (%rax),%rcx
  43: 48 8b 45 e0           mov    -0x20(%rbp),%rax
  47: 48 83 c0 10           add    $0x10,%rax
  4b: 48 8b 10              mov    (%rax),%rdx
  4e: 48 8b 45 e0           mov    -0x20(%rbp),%rax
  52: 48 83 c0 08           add    $0x8,%rax
  56: 48 8b 00              mov    (%rax),%rax
  59: 48 89 c6              mov    %rax,%rsi
  5c: 48 8d 3d 00 00 00 00 lea    0x0(%rip),%rdi        # 63 <main+0x63>
5f: R_X86_64_PC32 .rodata+0x25
  63: b8 00 00 00 00        mov    $0x0,%eax
  68: e8 00 00 00 00        callq  6d <main+0x6d>
69: R_X86_64_PLT32 printf-0x4
  6d: 48 8b 45 e0           mov    -0x20(%rbp),%rax
  71: 48 83 c0 20           add    $0x20,%rax
  75: 48 8b 00              mov    (%rax),%rax
  78: 48 89 c7              mov    %rax,%rdi
  7b: e8 00 00 00 00        callq  80 <main+0x80>
7c: R_X86_64_PLT32 atoi-0x4
  80: 89 c7                 mov    %eax,%edi
  82: e8 00 00 00 00        callq  87 <main+0x87>
83: R_X86_64_PLT32 sleep-0x4
  87: 83 45 fc 01           addl   $0x1,-0x4(%rbp)
  8b: 83 7d fc 09           cmpl   $0x9,-0x4(%rbp)
  8f: 7e a7                 jle    38 <main+0x38>
  91: e8 00 00 00 00        callq  96 <main+0x96>
92: R_X86_64_PLT32 getchar-0x4
  96: b8 00 00 00 00        mov    $0x0,%eax
  9b: c9                    leaveq
    9c: c3                    retq   

[*]操纵数
操纵数的表现方式差异,hello.s中的操纵数是十进制数,hello.o反汇编中的操纵数是十六进制数。
https://img-blog.csdnimg.cn/direct/17d1ce3e43e64c03bd917fa29347d7c8.png
图4.4-1hello.o的反汇编中操纵数截图
https://img-blog.csdnimg.cn/direct/29e5c2282fde486c812244216584c5cc.png
图4.4-2 hello.s中操纵数截图
2.分支转移
hello.s中,差异的跳转被分成了差异的段,通过段名称进行跳转定位。hello.o的反汇编中,跳转指令根据确定的相对main的偏移地址进行跳转定位。
https://img-blog.csdnimg.cn/direct/467e6a073fbd464ba2d957259b981b3a.png
图4.4-3 hello.o的反汇编中分支转移截图
https://img-blog.csdnimg.cn/direct/340c7a610d8842e99d7118ddfff391f2.png
图4.4-4 hello.s中分支转移截图
3.函数调用
hello.s中call指令后表现的是函数名称,即根据函数名称确定调用的函数,hello.o的反汇编中call指令后表现的是与main函数的相对偏移地址,即根据确定的目的地址确定调用的函数,这是由于需要调用的函数最终需要动态链接器才气确定函数的运行时的执行地址。
https://img-blog.csdnimg.cn/direct/61fafd9ffd2d45c7b8214c0acdc7c2e3.png
图4.4-5 hello.o的反汇编中函数调用截图
https://img-blog.csdnimg.cn/direct/a3ab811b100641aea1e2db11fbcf33c8.png
图4.4-6 hello.s中函数调用截图
4.5 本章小结

本章重要介绍了汇编的概念与作用,汇编的命令,对hello.o的可重定位目标elf格式进行分析,包括ELF头、节头、重定位节和符号表内容,剖析了hello.o的结果,对比分析了hello.o反汇编的结果与hello.s,好比操纵数、分支转移、函数调用的区别。
第5章 链接

5.1 链接的概念与作用

注意:这儿的链接是指从 hello.o 到hello生成过程。

[*]链接的概念:
链接是指将各种代码和数据片断网络并组合成为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。

[*]链接的作用:
使得分离编译成为大概,不用将一个大型的应用程序构造为一个巨大的源文件,而是可以把它分解为更小、更好管理的模块,可以独立地修改和编译这些模块。当改变这些模块中的一个时,只需简单地重新编译它,并重新链接应用,而不必重新编译其他文件。
5.2 在Ubuntu下链接的命令

https://img-blog.csdnimg.cn/direct/cfd9e801e3004265a7698b810a9772a7.png
图5.2-1 链接命令截图
5.3 可执行目标文件hello的格式

分析hello的ELF格式,用readelf等列出其各段的根本信息,包括各段的起始地址,巨细等信息。
5.3.1 readelf命令

https://img-blog.csdnimg.cn/direct/eaf99d2a669e46d5ab4f6e4c565ac0c9.png
图5.3.1-1 readelf命令及部分输出截图
5.3.2 ELF头

ELF头以一个16字节的序列开始,这个序列形貌了生成该文件的体系的字的巨细和字节次序。ELF头剩下的部分包罗帮助链接器语法分析和解释目标文件的信息。其中包括ELF头的巨细、目标文件的类型、呆板类型、节头部表的文件偏移,以及节头部表中条目的巨细和数目。与4.3.2节中介绍内容类似。
https://img-blog.csdnimg.cn/direct/cf3c76f7d6cc4c98a9d018a6f28e25c4.png
图5.3.2-1 ELF头内容截图
5.3.3 节头

详细标识了每个节的名称、类型、地址、偏移量、巨细、读取权限、对齐方式等,如.text节,类型为PROGBITS,起始地址为0,偏移量0x40,巨细为0x92,属性为AX,即可装入可执行,对齐方式为1字节。
https://img-blog.csdnimg.cn/direct/59be2e24849c482d80b9633ec2ce3008.png
https://img-blog.csdnimg.cn/direct/c438fb4610a941c1a95ed67978c21920.png
图5.3.3-1 节头内容截图
5.3.4 程序头

程序头的重要作用是形貌磁盘上可执行文件的内存结构以及如何映射到内存中。
https://img-blog.csdnimg.cn/direct/8efcc69da56e4740b78273c80b75f1f0.png
图5.3.4-1 程序头内容截图
5.3.5 重定位节

重定位节有偏移地址、基址信息、链接器辨认修改类型、重定位目标的名称等。
https://img-blog.csdnimg.cn/direct/30652965f1a5499898d325adc1c2bb5c.png
图5.3.5-1 重定位节内容截图
5.3.6 符号表

Hello模块中界说和引用的函数和全局变量的信息,连接后符号表条目增加。
https://img-blog.csdnimg.cn/direct/7a0ac88bc1ca47bb92d0dd6197fcb15b.pnghttps://img-blog.csdnimg.cn/direct/1bb1264aa8f24036a669adda189d3b48.png
图5.3.6-1 符号表内容截图
5.3.7 动态节

动态节提供动态链接信息、标志依靠关系和指示符号表和重定位表的位置。
https://img-blog.csdnimg.cn/direct/2ff3d2a6401342e2b7685da7d2128b67.png
图5.3.7-1 动态节内容截图
5.4 hello的捏造地址空间

利用edb加载hello,查看本进程的捏造地址空间各段信息,并与5.3对照分析说明。
利用edb加载hello截图如下:
https://img-blog.csdnimg.cn/direct/03244e4f1f724cb8a75bcdaa5ea66f19.png
图5.4-1 edb加载hello截图
从Data Dump窗口可以看到hello的捏造地址空间分配情况。

https://img-blog.csdnimg.cn/direct/2af4e7d9b55e4a77b0dae44c1bca23da.png
图5.4-2 Data Dump窗口内容截图
https://img-blog.csdnimg.cn/direct/26c791d5937b46f6bfe2a45fe530a97c.png
图5.4-3 ELF文件中.init节捏造地址截图
从图中可以看出起始捏造地址为00000000:00401000,与hello的ELF文件中节头部表中的init节捏造地址相同。
通过edb中的SymbolView窗口可以查看hello各节的起始地址,与5.3对照可以看到二者对应的捏造地址相同。
https://img-blog.csdnimg.cn/direct/8d8ebfc7157a4d95ab979e7cf0c0c75c.png
图5.4-4 edb中SymbolView内容截图
5.5 链接的重定位过程分析

objdump -d -r hello 分析hello与hello.o的差异,说明链接的过程。
结合hello.o的重定位项目,分析hello中对其怎么重定位的。
5.5.1 objdump命令

命令:objdump -d -r hello
https://img-blog.csdnimg.cn/direct/aca02d1e5ada4538af39b0392cf8bb47.png
图5.5.1-1 objdump命令及部分输出截图
5.5.2 hello与hello.o对比及重定位过程分析

1.函数变革
在hello的反汇编文件中,链接器将hello.c中用到的库函数,如puts,printf,getchar等加入到了文件中。
https://img-blog.csdnimg.cn/direct/dcaab29326ce4e3fb5e1c8dd7380aaa7.png
图5.5.2-1 反汇编文件中函数变革截图
2.增加部分节
hello比hello.o增加了.init、.plt、.plt.sec等节。
https://img-blog.csdnimg.cn/direct/d4be3c3b5f11425a943f3149c7f96e0b.png
图5.5.2-2 .init节
https://img-blog.csdnimg.cn/direct/f164743dde4344e88622205f12b2f142.png
图5.5.2-3 .plt节
https://img-blog.csdnimg.cn/direct/fb4287e3353c4f8bbb42e9c1cc4d5796.png
图5.5.2-4 .plt.sec节
3.地址变革
hello删去了hello.o中的重定位条目,hello.o中跳转的目的地址和函数地址都是与main函数的相对偏移地址,hello中跳转的目的地址和函数地址都是捏造内存地址。
https://img-blog.csdnimg.cn/direct/52ea821337054b23b19e2768ce1d46ce.png
图5.5.2-5 hello中跳转截图
https://img-blog.csdnimg.cn/direct/15b5997d9e8046fe892d7c1754322602.png
图5.5.2-6 hello.o中跳转截图
5.5.3 链接过程

重要分为两步:

[*]符号剖析:
目标文件界说和引用符号,每个符号对应于一个函数、一个局部变量或一个静态变量。符号剖析的目的是将每个符号引用恰好和一个符号界说关联起来。

[*]重定位:
编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号界说与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得它们指向这个内存位置。链接器利用汇编器产生的重定位条目的详细指令,不加甄别地执行这样的重定位。
5.6 hello的执行流程

利用gdb/edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程(重要函数)。请列出其调用与跳转的各个子程序名或程序地址。
https://img-blog.csdnimg.cn/direct/18bff3e8b00b4d299130661910f55edf.png
图5.6-1 edb执行hello过程截图
0000000000401000 <_init>
0000000000401020 <.plt>
0000000000401030 <puts@plt>
0000000000401040 <printf@plt>
0000000000401050 <getchar@plt>
0000000000401060 <atoi@plt>
0000000000401070 <exit@plt>
0000000000401080 <sleep@plt>
00000000004010f0 <_start>
0000000000401120 <_dl_relocate_static_pie>
0000000000401125 <main>
00000000004011d0 <__libc_csu_init>
0000000000401240 <__libc_csu_fini>
0000000000401248 <_fini>
5.7 Hello的动态链接分析

分析hello程序的动态链接项目,通过edb/gdb调试,分析在动态链接前后,这些项目的内容变革。要截图标识说明。
程序调用由共享库界说的函数,编译器没有办法预测这个函数的运行时地址,由于界说它的共享模块在运行时可以加载到任意位置。GNU编译体系利用延迟绑定将过程地址的绑定过程推迟到第一次调用该过程时。通过两个数据结构——GOT和PLT协作在运行时剖析函数的地址。
延迟绑定是通过GOT和PLT实现的。GOT是数据段的一部分,PLT是代码段的一部分。
PLT:PLT是一个数组,其中每个条目是16字节代码。PLT是一个特别条目,它跳转到动态链接器中。每个被可执行程序调用的库函数都有它本身的PLT条目。每个条目都负责调用一个详细的函数。
GOT:GOT是一个数组,其中每个条目是8字节地址。和PLT连合利用时,GOT和GOT包罗动态链接器在剖析函数地址时会利用的信息。GOT是动态链接器在1d-linux.so模块中的入口点。其余的每个条目对应于一个被调用的函数,其地址需要在运行时被剖析。每个条目都有一个相匹配的PLT条目。
由5.3.3节中节头关于.got.plt的信息可知,其首地址为0x0000000000404000,通过edb中的data dump窗口观察该地址在_init前后的变革。
https://img-blog.csdnimg.cn/direct/78f94bcbc71447908a902c715ff99962.png
图5.7-1 节头中关于.got.plt信息截图
https://img-blog.csdnimg.cn/direct/eee96de42d4e4513b89b00f40c12898c.png
图5.7-2 .got.plt在_init前内容截图
https://img-blog.csdnimg.cn/direct/ee8c2942f9ed4fdf9d019a34937319d2.png
图5.7-3 .got.plt在_init后内容截图

5.8 本章小结

本章重要介绍了链接的概念与作用,链接的命令,可执行目标文件hello的格式,包括ELF头、节头、重定位节、符号表,通过对比hello与hello.o,更好地理解链接与重定位的干系过程,对hello的执行流程和动态链接进行分析。

第6章 hello进程管理

6.1 进程的概念与作用

1.进程的概念:
进程的经典界说就是一个执行中程序的实例。
2.进程的作用:
每次用户向shell输入一个可执行目标文件的名字,运行程序时,shell就会创建一个新的进程,然后在这个新进程的上下文中运行这个可执行目标文件。应用程序也能够创建新进程,并且在这个新进程的上下文运行它们本身的代码或其他应用程序。进程提供给应用程序两个关键抽象:
①一个独立的逻辑流,它提供一个假象,好像我们的程序独占地利用处理器。
②一个私有的地址空间,它提供一个假象,好像我们的程序独占地利用内存体系。
6.2 简述壳Shell-bash的作用与处理流程

1.壳shell-bash的作用:
shell是一个程序,可以称之为壳程序,用于用户与操纵体系进行交互。Linux预设就是bash,bash可以实现以下功能:

[*]记录历史命令:bash可以记录曾经的命令,保持在~/.bash_history文件中,只保存上次注销登录之后的命令
[*]tab键主动补全:利用tab见可以主动不全命令大概目次i
[*]alias命令别名:可以利用alias ll='ls -al'来设置命令的别名
[*]工作控制:可以将某些任务放在背景去运行,这里不多种介绍
[*]程序脚本:可以执行shell脚本文件
[*]通配符:在查找干系文件大概执行干系命令时,可以利用通配符*
[*]内建命令type:可以利用type 命令来查看某个命令是否为内建在bash当中的命令
2.壳shell-bash的处理流程:

[*]判定命令是否通过绝对路径执行;
[*]判定命令是否存在alias别名;
[*]判定用户输入的是内部命令还是外部命令;
[*]Bash内部命令直接执行,外部命令检测是否存在缓存;
[*]通过PATH路径查找命令,有执行,无报错;
6.3 Hello的fork进程创建过程

父进程通过调用fork函数创建一个新的运行的子进程;子进程返回0,父进程返回子进程的PID;新创建的子进程险些但不完全与父进程相同:子进程得到与父进程捏造地址空间相同的(但是独立的)一份副本(代码、数据段、堆、共享库以及用户栈);子进程获得与父进程任何打开文件形貌符相同的副本,子进程有差异于父进程的PID;fork函数:被调用一次,却返回两次。
6.4 Hello的execve过程

execve函数在当前进程的上下文中加载并运行一个新程序。execve函数加载并运行可执行目标文件,且带参数列表和环境变量列表。只有当出现错误时,execve才会返回到调用程序。以是,与fork依次调用返回两次差异,execve调用一次并从不返回。在execve加载了可执行目标文件之后,其调用启动代码。启动代码设置栈,并将控制通报给新程序的主函数。
6.5 Hello的进程执行

结合进程上下文信息、进程时间片,阐述进程调理的过程,用户态与核心态转换等等。
(1)进程上下文信息:
体系中的每个程序都运行在某个进程的上下文中。上下文是由程序正确运行所需的状态组成的。这个状态包括存放在内存中的程序的代码和数据,它的栈、通用目的寄存器的内容、程序计数器、环境变量以及打开文件形貌符的集合。
(2)进程时间片:
一个进程执行它的控制流的每一部分的每一时间段叫做时间片。
(3)进程调理的过程:
在进程执行的某些时候,内核可以决定抢占当前进程,并重新开始一个先前被抢占了的进程。这种决议就叫做调理,是由内核中被称为调理器的代码处理的。当内核选择一个新的进程运行时,我们说内核调理了这个进程。在内核调理了一个新的进程运行后,他就抢占当前进程,并利用一种称为上下文切换的机制来将控制转移到新的进程,上下文切换①保存当前进程的上下文;②恢复某个先前进程被抢占的进程被保存的上下文;③将控制通报给这个新恢复的进程。
(4)用户态与核心态转换:
运行应用程序代码的进程初始时是在用户模式中的。进程从用户模式变为内核模式的唯一方法是通过诸如停止、故障大概陷入体系调用这样的异常。当异常发生时,控制通报到异常处理程序,处理器将模式从用户模式变为内核模式。处理程序运行在内核模式中,当它返回到应用程序代码时,处理器就把模式从内核模式改回到用户模式。
6.6 hello的异常与信号处理

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

异常可以分为四类:停止、陷阱、故障和终止。
https://img-blog.csdnimg.cn/direct/823558d53b614447b07e153388b6de5a.png
图6.6.1-1 异常的种类
6.6.2 产生的信号

①停止:信号SIGTSTP,默认举动是停止直到下一个SIGCONT;
②终止:信号SIGINT,默认举动是终止。
6.6.3处理方式

停止:
https://img-blog.csdnimg.cn/direct/651ba91473254e5cb93f47091f49c15f.png
图6.6.3-1 停止处理
陷阱:
https://img-blog.csdnimg.cn/direct/648d1f40a68145a5a1c706a4125c7b12.png
图6.6.3-2 陷阱处理
故障:
https://img-blog.csdnimg.cn/direct/059d64a26aa247208effaf4ba083d905.png
图6.6.3-3 故障处理
终止:
https://img-blog.csdnimg.cn/direct/c837818e902947d384173b1c2d929d50.png
图6.6.3-4 终止处理
6.6.4命令、运行结果、信号与异常的处理

1.正常运行:程序每隔三秒输出一次,共输出10次。
https://img-blog.csdnimg.cn/direct/5131a00a82ec429e8bb8ca45eb14ee33.png
图6.6.3-4 终止处理
6.6.4命令、运行结果、信号与异常的处理

1.正常运行:程序每隔三秒输出一次,共输出10次。
https://img-blog.csdnimg.cn/direct/2472fa5af35e4c8b92b474d9fab879b2.png
图6.6.4-2 不停乱按输出截图
2.回车:不影响当前进程执行,在结束hello进程后,shell会把之前敲击的回车当做命令行,读入终端。
https://img-blog.csdnimg.cn/direct/b9cb3bd8c0e14f46a57768499f8804b9.png
图6.6.4-3 多次按下回车输出截图

3.运行时按Ctrl+C:进程接收到SIGINT信号,进程终止。输入ps查看发现没有干系信息。
https://img-blog.csdnimg.cn/direct/65c4255e9d634e86b38188cb44b14f59.png
图6.6.4-4 运行时按Ctrl+C输出截图

4.运行时按Ctrl+Z,之后输ps:Ctrl-Z挂起前台作业,ps表现进程的详细信息。
https://img-blog.csdnimg.cn/direct/d9f3bfaf730e4617b354b65f71c68de2.png
图6.6.4-5 运行时按Ctrl+Z命令,之后输ps输出截图
6. 运行时按Ctrl+Z,之后输jobs:Ctrl-Z挂起前台作业,jobs表现任务列表和任务状态。
https://img-blog.csdnimg.cn/direct/817cbe90958b4a27a8812fb794493a6e.png
图6.6.4-6 运行时按Ctrl+Z,之后输jobs输出截图
7.运行时按Ctrl+Z,之后输pstree:Ctrl-Z挂起前台作业,pstree以树状结构表现进程之间的关系。
https://img-blog.csdnimg.cn/direct/d7af89990a574d3594cd087b62756f1d.png
图6.6.4-7 运行时按Ctrl+Z,之后输pstree输出截图1

https://img-blog.csdnimg.cn/direct/1f83747a5155439b95ad981c1da06f8c.png
图6.6.4-8 运行时按Ctrl+Z,之后输pstree输出截图2
8. 运行时按Ctrl+Z,之后输fg %1:Ctrl-Z挂起前台作业,fg %n使第n个任务在前台运行。
https://img-blog.csdnimg.cn/direct/f47c3012f2b04330a3038229213cd669.png
图6.6.4-9 运行时按Ctrl+Z,之后输fg %1输出截图
9. 运行时按Ctrl+Z,之后输kill -9 2861:Ctrl-Z挂起前台作业,kill命令杀死进程。通过kill -9 2861给进程2861发送SIGKILL信号,终止hello进程。
https://img-blog.csdnimg.cn/direct/9d059e3cad8b4dfdb9b70705a084d65f.png
图6.6.4-10 运行时按Ctrl+Z,之后输kill -9 2861输出截图

6.7本章小结

本章重要介绍了进程的概念与作用、壳Shell-bash的作用与处理流程、fork进程创建过程、execve过程、进程执行和异常与信号处理等内容。
第7章 hello的存储管理

7.1 hello的存储器地址空间

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

[*]逻辑地址:
由程序产生的与段干系的偏移地址部分,hello.o中跳转的目的地址和函数地址都是相对main函数偏移地址。

[*]线性地址:
逻辑地址到物理地址变动之间的中心层,线性地址=段地址+偏移地址。程序hello的代码会产生段中的偏移地址,加上相应段的基地址就生成了一个线性地址。

[*]捏造地址:
捏造内存是对整个内存的抽象形貌,是相对于物理内存来讲的。捏造内存也就是线性地址空间。如hello中main函数的段内偏移地址。

[*]物理地址:
用于内存芯片级的单位寻址,与处理器和CPU连接的地址总线相对应。在hello运行中,需要根据捏造地址通过地址翻译(MMU)得到物理地址,并通过物理地址访问在内存中的位置。物理内存是以字节为单位编址的。
7.2 Intel逻辑地址到线性地址的变动-段式管理

1.段寄存器:
https://img-blog.csdnimg.cn/direct/2c7aaa28bd5d4a518f838192b883ebd1.png
图7.2-1 段寄存器寄义
段寄存器(16位)用于存放段选择符
CS(代码段):程序代码所在段
SS(栈段):栈区所在段
DS(数据段):全局静态数据区所在段
其他3个段寄存器ES、GS和FS可指向任意数据段
2.段选择符:
逻辑地址一共有 48 位。前 16 位是段选择符。
https://img-blog.csdnimg.cn/direct/0742c7130d224898973afe2abdcf430f.png
图7.2-2 段选择符格式
这 16 位的格式如上图。

[*]索引:形貌符表的索引(Index)
[*]TI:如果 TI 是 0。形貌符表是全局形貌符表(GDT),如果 TI 是 1。形貌符表是局部形貌表(LDT)
[*]RPL:段的级别。为 0,位于最高级别的内核态。为 11,位于最低级别的用户态。在 linux 中也仅有这两种级别。
3.段形貌符:
段形貌符就是表项,一种记录每个段信息的数据结构。段选择符就是形貌符表(段表)中的索引。
4.形貌符表:
现实上就是段表,由段形貌符(段表项)组成。有三种类型:

[*]全局形貌符 GDT:只有一个,用来存放体系内用来存放体系内每个任务共用的形貌符,例如,内核代码段、内核数据段、用户代码段、用户数据段以及 TSS(任务状态段)等都属于 GDT 中形貌的段。
[*]局部形貌符表 LDT:存放某任务(即用户进程)专用的形貌符
[*]停止形貌符表 IDT:包罗 256 个停止门、陷阱门和任务门形貌符
5.整体过程:
通过索引在形貌符表中找到段基址,如下图形貌:
https://img-blog.csdnimg.cn/direct/7d2c9a3962184b378db4b42c2317b980.png
图7.2-3 整体过程
6.段式管理:
根据段选择符定位到相应的段形貌符,根据段形貌符在形貌符表中得到相应的段基址,加上偏移量,得到线性地址。
7.3 Hello的线性地址到物理地址的变动-页式管理

捏造内存机制利用页表的数据结构进行页式管理,它把线性地址空间和物理地址空间划分成巨细相同的页,然后通过建立线性地址空间的页同物理地址空间中的页的映射,实现线性地址到物理地址的转化。其对应过程如下图所示,MMU利用捏造页号(VPN)找到对应的物理页号(PPN),然后将找到的PPN与由捏造页偏移量(VPO)得到物理页偏移量(PPO)组合就构成了现实的物理地址。
https://img-blog.csdnimg.cn/direct/841cee1cbf5446eea380bc55a827baf7.png
图7.3-1 页式管理
7.4 TLB与四级页表支持下的VA到PA的变动

若TLB命中,则MMU从TLB中取出相应的PTE,将这个捏造地址翻译为物理地址;若TLB不命中,根据VPN1在一级页表选择对应的PTE,该PTE包罗二级页表的基地址;根据VPN2在二级页表选择对应的PTE,该PTE包罗三级页表的基地址;根据VPN3在三级页表选择对应的PTE,该PTE包罗四级页表的基地址;在四级页表取出对应的PPN,与VPO串联起来,就得到相应的物理地址。
https://img-blog.csdnimg.cn/direct/7ee6e1dfd1204c3393383175747e17da.png
图7.4-1 TLB与四级页表支持下的VA到PA的变动
https://img-blog.csdnimg.cn/direct/a3813877639f43518dcdd3278432687f.png
图7.4-2 捏造地址中用以访问TLB的组成部分
7.5 三级Cache支持下的物理内存访问

https://img-blog.csdnimg.cn/direct/7943f61849774f24a3cdcdcfafc366a8.png
图7.5-1 Core i7 的内存体系
起首访问一级Cache,寻找该物理内存对应的内容是否已被缓存且有效,若已被缓存且有效,则缓存命中;否则缓存不命中,则需要访问二级Cache,重复上述步调;若二级Cache中依然缓存不命中,则需要访问三级Cache,直到访问主存。将访问到的内容分别加载进上一层缓存,再进行后续操纵。
7.6 hello进程fork时的内存映射

当fork函数被当前进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID。为了给这个新进程创建捏造内存,它创建了当前进程的mm_struct、地区结构和页表的原样副本。它将两个进程中的每个页面都标志为只读,并将两个进程中的每个地区结构都标志为私有的写时复制。
当fork函数在新进程中返回时,新进程现在的捏造内存刚好和调用fork时存在的捏造内存相同。当这两个进程中的任一个厥后进行写操纵时,写时复制机制就会创建新页面,因此,也就为每个进程保持了私有地址空间的抽象概念。
7.7 hello进程execve时的内存映射

execve函数在当前进程中加载并运行包罗在可执行目标文件hello中的程序,用hello程序有效地替代了当前程序。加载并运行hello需要以下几个步调:
①删除已存在的用户地区。删除当前进程捏造地址的用户部分中的已存在的地区结构。
②映射私有地区。为新程序的代码、数据、bss和栈地区创建新的地区结构。所有这些新的地区都是私有的、写时复制的。代码和数据地区都被映射为hello文件中的.text和.data区.bss地区是哀求二进制零的,映射到匿名文件,其巨细包罗在hello中。栈和堆地区也是哀求二进制零的,初始长度为零。
③映射共享地区。如果hello程序与共享对象(或目标)链接,那么这些对象都是动态链接到这个程序的,然后再映射到用户捏造地址空间中的共享地区内。
④设置程序计数器(PC)。execve做的末了一件事情就是设置当前进程上下文中的程序计数器,使之指向代码地区的入口点。
https://img-blog.csdnimg.cn/direct/2d8c601916db45509cf0208a57cc28b2.png
图7.7-1 加载器是如何映射用户地址空间地区的
7.8 缺页故障与缺页停止处理

缺页故障:DRAM缓存不命中称为缺页,CPU引用了VPm中的一个字,VPm并未缓存在DRAM中。地址翻译硬件从内存中读取PTEm,从有效位推断出PTEm未被缓存,并且触发一个缺页异常。
缺页停止处理:缺页异常调用内核中的缺页异常处理程序,该程序会选择一个捐躯页(假设其为VPn)。如果VPn已经被修改了,那么内核就会将它复制回磁盘。无论哪种情况,内核都会修改VPn的页表条目,反映出VPn已经不在缓存在主存中这一究竟。接下来,内核从磁盘复制VPm到内存中,更新PTEm,随后返回。当异常处理程序返回时,它会重新启动导致缺页的指令,该指令会把导致缺页的捏造地址重发送到地址翻译硬件。
7.9动态存储分配管理

Printf会调用malloc,请简述动态内存管理的根本方法与计谋。
动态内存分配器维护着一个进程的捏造内存地区,称为堆。体系之间细节差异,但是不失通用性,假设堆是一个哀求二进制零的地区,它紧接着未初始化的数据地区后开始,并向上生长。对于每个进程,内核维护着一个变量brk,它指向堆的顶部。
分配器将堆视为一组巨细差异的块的集合来维护。每个块就是一个连续的捏造内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序利用。空闲块可用来分配。空闲块保持空闲,直到它显式地被应用所分配。一个已分配的块保持已分配状态,直到它被释放,这种释放要么是应用程序显式执行的,要么是内存分配器自身隐式执行的。
分配器有两种根本风格。两种风格都要求应用显式地分配块。它们的差异之处在于由哪个实体来负责释放已分配的块。
①显式分配器,要求应用显式地释放任何已分配的块。
②隐式分配器,另一方面,要求分配器检测一个已分配块何时不再被程序所利用,那么就释放这个块。隐式分配器也叫做垃圾网络器,而主动释放未利用的已分配的块的过程就叫做垃圾网络。
7.10本章小结

本章重要介绍了hello的存储器地址空间、Intel逻辑地址到线性地址的变动-段式管理、Intel线性地址到物理地址的变动-页式管理、TLB与四级页表支持下的VA到VP的变动、三级Cache支持下的物理内存访问,hello进程fork和execve时的内存映射、缺页故障与缺页停止处理、动态存储分配管理等内容。

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

设备的模型化:文件,所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被看成对对应文件的读和写来执行。
设备管理:unix io接口,将设备映射为文件的方式,使得Linux内核引出一个简单、低级的应用接口,称为I/O接口,这使得所有的输入和输出都能以一种同一且划一的方式来执行。
8.2 简述Unix IO接口及其函数

8.2.1 Unix I/O接口

所有的 I/O 设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被看成对相应文件的读和写来执行。这种将设备优雅地映射为文件的方式,答应 Linux 内核引出一个简单、低级的应用接口,称为 Unix I/O ,这使得所有的输入和输出都能以一种同一且划一的方式来执行。  
8.2.2 Unix I/O函数  

1.打开文件:int open(char *filename, int flag, mode_t mode);
进程通过调用open函数来打开一个已存在的文件大概创建一个新文件。open函数将filename转换为一个文件形貌符,并且返回形貌符数字。返回的形貌符总是在进程中当前没有打开的最小形貌符。flags参数指明了进程计划如何访问这个文件, mode参数指定了新文件的访问权限位。
2. 关闭文件:int close(int fd);
进程通过调用close函数关闭一个打开的文件。关闭一个已关闭的形貌符会出错。
3. 读文件:ssize_t read(int fd, void *buf, size_t n);
应用程序通过调用read函数执行输入。read函数从形貌符为fd的当前文件位置复制最多n个字节到内存位置buf。返回值-1表现一个错误,而返回值0表现EOF。否则,返回值表现的是现实传送的字节数目。
4. 写文件:ssize_t write(int fd, const void *buf, size_t n);
应用程序通过调用write函数执行输出。write函数从内存位置buf复制至多n个字节到形貌符fd的当前文件位置。
8.3 printf的实现分析

https://www.cnblogs.com/pianist/p/3315801.html
从vsprintf生成表现信息,到write体系函数,到陷阱-体系调用 int 0x80或syscall等.
字符表现驱动子程序:从ASCII到字模库到表现vram(存储每一个点的RGB颜色信息)。
表现芯片按照刷新频率逐行读取vram,并通过信号线向液晶表现器传输每一个点(RGB分量)。
printf函数体:
int printf(const char *fmt, ...)
{
int i;
char buf;
   
     va_list arg = (va_list)((char*)(&fmt) + 4);
     i = vsprintf(buf, fmt, arg);
     write(buf, i);
   
     return i;
    }
printf函数调用vsprintf函数进行格式化,将所有的参数内容格式化后放到buf中,返回格式化数组的长度给i,担当确定输特别式的格式字符串fmt,;调用write函数把buf中的i个元素的值写到终端。syscall函数不断地打印出字符,直到遇到'\0'结束。
8.4 getchar的实现分析

getchar函数体:
int getchar(void)
{
      static char buf;
      static char *b=buf;
      static int n=0;
      if(n==0)
      {
             read(0,buf,BUFSIZE);
             b=buf;
      }
      return ((--n)>0) ? (unsigned char) *b++ : EOF;
}
异步异常-键盘停止的处理:当用户按下键盘上的一个键时,键盘的接口电路会生成一个扫描码, 键盘接口将这个扫描码发送到计算机的主板上,并哀求一个停止, CPU响应停止,将当前执行的任务挂起,并将控制权转移给停止处理程序, 键盘停止处理程序担当扫描码,将其转换为ASCII码, 转换后的ASCII码被保存到体系的键盘缓冲区中。
getchar等调用read体系函数,通过体系调用读取按键ascii码,直到担当到回车键才返回。getchar的返回值是用户输入字符的ascii码,若到文件结尾则返回-1(EOF),且将用户输入表现到屏幕。
8.5本章小结

本章重要介绍了Linux的IO设备管理方法、Unix I/O接口及其函数,同时对printf和getchar的实现进行分析。
结论

1.用计算机体系的语言,逐条总结hello所经历的过程。

[*]预处理:hello.c经过预处理器(cpp)得到修改了的源程序(文本)hello.i;
[*]编译:hello.i经过编译器(ccl)得到汇编程序(文本)hello.s;
[*]汇编:hello.s经过汇编器(as)得到可重定位目标程序(二进制)hello.o;
[*]链接:hello.o经过链接器(ld)将其与其它可重定位目标文件和动态库进行链接得到可执行目标文件hello;
[*]shell调用fork函数创建子进程;
[*]shell调用execve函数加载hello程序,映射到对应的捏造内存;
[*]hello程序执行过程中通过进程管理实现异常与信号的处理,存储管理实现内存访问,同时相应的IO设备配合hello程序实现输入输出等功能;
[*]运行结束,父进程回收子进程,内核将其从体系中扫除,释放资源。
2.你对计算机体系的计划与实现的深切感悟,你的创新理念,如新的计划与实现方法。
通过对hello程序的一生的探索,了解一个hello.c程序如何从预处理、编译、汇编…一直到最终被回收的整个过程,让我对计算机体系的知识有了更加深刻的理解,同时对进程管理、存储管理、I/O管理等都有了进一步的理解。
附件

列出所有的中心产物的文件名,并予以说明起作用。
文件名称
文件作用
hello.c
hello程序源代码
hello.i
源代码hello.c经过预处理产生的文件
hello.s
hello程序对应的汇编文件
hello.o
可重定位目标文件
hello
hello链接后的可执行目标文件
hello_elf.txt
hello.o的ELF格式文件


参考文献

程序预处理阶段,在做什么_预处理阶段重要做的是哪两件事-CSDN博客http://t.csdnimg.cn/Z5Vfu
Linux之bash介绍-CSDN博客
 Linux Bash shell - 知乎 (zhihu.com)
 逻辑地址、物理地址、捏造地址_捏造地址 逻辑地址-CSDN博客
 段页式访存——逻辑地址到线性地址的转换_某采取段页式管理体系中,一操纵数的逻辑地址为9976h,若逻辑地址格式为段号(3-CSDN博客
 https://blog.csdn.net/m0_63712213/article/details/130487255
 《深入理解计算机体系》 Randal E.Bryant & David R.O’Hallaron 呆板工业出书社



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 哈工大计算机体系大作业--程序人生