哈工大计算机系统大作业: 程序人生-Hello’s P2P/ hello 的一生

计算机系统

大作业

题 目 程序人生-Hello’s P2P

专 业 计算学部

学 号 1190201801

班 级 1903012

学 生 耿健

指 导 教 师 史先俊

计算机科学与技术学院

2021年5月

摘 要

本文将从计算机系统的视角,从预处理阶段开始逐步分析hello.c的生命进程,从预处理,到编译,再到执行,理清其背后的整套流程。从信息的表示,到进程的管理,再到内存管理以及生命结束后的一系列处理。彻底弄清一个程序的生命过程。

关键词:计算机系统,编译,进程,内存,IO,异常;

(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分)

目 录

第1章 概述 - 4 -

1.1 Hello简介 - 4 -

1.2 环境与工具 - 4 -

1.3 中间结果 - 4 -

1.4 本章小结 - 4 -

第2章 预处理 - 6 -

2.1 预处理的概念与作用 - 6 -

2.2在Ubuntu下预处理的命令 - 6 -

2.3 Hello的预处理结果解析 - 7 -

2.4 本章小结 - 8 -

第3章 编译 - 9 -

3.1 编译的概念与作用 - 9 -

3.2 在Ubuntu下编译的命令 - 9 -

3.3 Hello的编译结果解析 - 10 -

3.4 本章小结 - 13 -

第4章 汇编 - 14 -

4.1 汇编的概念与作用 - 14 -

4.2 在Ubuntu下汇编的命令 - 14 -

4.3 可重定位目标elf格式 - 14 -

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

4.5 本章小结 - 19 -

第5章 链接 - 20 -

5.1 链接的概念与作用 - 20 -

5.2 在Ubuntu下链接的命令 - 20 -

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

5.4 hello的虚拟地址空间 - 22 -

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

5.6 hello的执行流程 - 26 -

5.7 Hello的动态链接分析 - 26 -

5.8 本章小结 - 27 -

第6章 hello进程管理 - 28 -

6.1 进程的概念与作用 - 28 -

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

6.3 Hello的fork进程创建过程 - 28 -

6.4 Hello的execve过程 - 28 -

6.5 Hello的进程执行 - 29 -

6.6 hello的异常与信号处理 - 30 -

6.7本章小结 - 33 -

第7章 hello的存储管理 - 34 -

7.1 hello的存储器地址空间 - 34 -

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

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

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

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

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

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

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

7.9动态存储分配管理 - 39 -

7.10本章小结 - 42 -

第8章 hello的IO管理 - 43 -

8.1 Linux的IO设备管理方法 - 43 -

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

8.3 printf的实现分析 - 43 -

8.4 getchar的实现分析 - 44 -

8.5本章小结 - 44 -

结论 - 44 -

附件 - 46 -

参考文献 - 47 -

第1章 概述

1.1 Hello简介

1) From Program to
Process。首先我们需要编程,即program编写程序hello.c,然后这个文件会进行一系列的预处理,编译,汇编,链接生成一个.out可执行文件,我们打开shell之后,如果去运行它,那么hello就会为hello创建一个新的进程,即progress,然后调用execve函数,将hello的可执行文件进行加载,运行。

2) FROM ZERO TO
ZERO。首先在真正加载前shell这个壳需要为hello申请虚拟内存空间,然后将物理内存与虚拟内存进行映射,同时处理器内核还需要为hello分配时间片,使得其看似独享整个资源,当需要物理地址时,cpu会向mmu发出一个虚拟地址,mmu将虚拟地址转化为物理地址之后,在提供相应的数据。同时当我们在程序运行期间,输入信号时,会使内核产生一个中断,当按下ctrl
z 或 ctrl
c时,会向hello进程发送一个信号,如果子进程终止之后,会有父进程将僵尸进程进行回收,避免占用资源。

1.2 环境与工具

硬件: Core I7-10875H X64 ; 32G RAM; 1TB SSD

软件: Ubuntu 20.10

工具: VIM , GCC, EDB, OBJDUMP, READELF, LD

1.3 中间结果

hello.c:源 程序

hello.i: 经过预处理的源程序

hello.s:hello.i 经 过编译的汇编程序

hello.o:hello.s 经过汇编的可重定位目标程序

hello:hello.o 经过链接后的可 执行目标程序

hello.elf:hello.o 的 elf 格 式

hello.elf:hello 的 elf 格式

helloo.objdump:hello.o的反汇编程序

hello.objdump:hello的反汇编程序

1.4 本章小结

本章节简述了 P2P 和 020 的含义,列出了测试环境和工具和中间结果的文件名
和文件作用。从整体上大致介 绍了 hello
的一生,并且列出了做本次作业的软硬件环境以及工具。也相当于漫游了一下hello的一生。

(第1章0.5分)

第2章 预处理

2.1 预处理的概念与作用

预处理的概念:

预处理器根 据 以 字 符# 开头的 命令修改 原始 的 C 程序。 比如 hello.c 中的 命
令告诉预处理 器 读 取 对 应 的 三 个 系 统 头 文件的内 容,并把它 直接插入到程
序文 本中,结果就得 到了另一个 C 程序。 其中,ISO
C/C++要求支持的包括#define(宏定义)、
#include(源文件包含)、#line(行控制)、#error(错误指令)、#pragma(和实
现 相 关的 杂注 )以 及 单 独 的 #(空指令)。

作用:

  1. #include 指令告诉预处理器(cpp)读取 源程序所 引用 的系统 源文件,并把 源文
    件直接 插入程序文本中。

  2. 执行宏替换。将目 标的字 符替换 为我们所定 义的字符。

  3. 条件编译。根 据 定 于 的 条 件,来确 定 编译的 条件,即目 标是否 是
    真正需要的,类似于ifelse。

  4. 特殊符号,预编译程序可以识别一些特殊的符号,预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

2.2在Ubuntu下预处理的命令

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DsJAZ0bH-1624587058467)(media/5c90b9ac0de65191cf0f31f452af8e0e.png)]

图 1

2.3 Hello的预处理结果解析

用vim打开hello.i,开始寻找main函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Awl7CHPc-1624587058468)(media/3f31de459486434c31ea49e566af873d.png)]

图 2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YJmCRkuh-1624587058468)(media/613f4e2a43e28df1cbdee7fc70d4b7ff.png)]

图 3

这里找到mian函数的位置,在这之前出现的是头文件 stdio.h unistd.h stdlib.h
的依次展开。

以 stdio.h 的展开为例:stdio.h 是 标准库文 件,cpp 到Ubuntu中 默 认 的 环
境变量下寻找 stdio.h,打开文件/usr/include/stdio.h ,发 现 其 中依 然 使 用
了#define语句,cpp 对stdio中的define宏定义 递 归 展 开。

最终.i文件将预处理指令进行替换,而原来的预处理指令就不需要了;发
现其中使用了大量的#ifdef #ifndef条件编译的语句,cpp 会对 条件 值 进 行 判 断
来 决 定 是 否执 行包含其中的逻辑。 特殊符号,预 编译程序可以 识别一 些 特 殊 的
符 号,预编译程序 对于 在 源 程 序 中出现 的 这 些 串 将 用 合适的值进行替换。在
下面我们可以看到其寻找的头文件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9Fa2Ld6-1624587058469)(media/da67a33707c9335a2c452d1418b657d1.png)]

图 4 头文件

2.4 本章小结

本章主要 介绍了预处理的
定义与作用,并且分析了.i文件的整个分析过程、并结合.i程序对预处理结果进
行了分析。

(第2章0.5分)

第3章 编译

3.1 编译的概念与作用

编译的概念:

编译是利用编译程序 从预处理文本 文件产生汇编程序的过程。主要包含五个阶段:词法分析;语 法分析;语义检查、中间代码生成、 目标代码 生成。

编译的作用:

  • 词法分析。将源代码程序输入扫描器,将源代码的字符序列分割成一 系列记号。

  • 语法分析。基于词法分析得到的一系列记号,生成语法树。

  • 语义分析。由语义分析器完成,指示判断 是否合法,并不判断对错。

  • 目标代码的生成与优化。目标代码生 成 阶 段 编 译器 会选 择合 适的寻址
    方式,左移 右 移代替乘除,删除多余指令。

3.2 在Ubuntu下编译的命令

命令行: gcc -S hello.i -o hello.s

>   [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Pk8Do1s-1624587058469)(media/c07ccb9ff035ca44dd48cccc179de872.png)]

图 5

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9505jSrx-1624587058470)(media/eb4c6096d52fd2f85ce2a0d12ead878c.png)]

图 6 .s文件

3.3 Hello的编译结果解析

hello.c 中的数据类型有整型、字符串和数组。

整数

可以看出.data 节是 8 字节对齐的,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AfZiBJPb-1624587058470)(media/1758a07b0cf2b0285ef5a01bce584e3c.png)]

图 7

hello.c 中还有 argc 和 i 两 个 整 型 变 量。其中 int i 是 循 环中 用来
计数的局部变量,argc 是从终端 输 入 的 参 数 的个数 ,也是 main 函 数 的
第一个参数。 hello.s 将 i 存 储 在 -4(%rbp)中,初始值为 0,每 次 循 环 加 一,
退出循环条件是 i 大于 7。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ac3HSfD7-1624587058471)(media/8b7b4457b09aa89485e93b46f0c0c5f9.png)]

图 8

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OkI3TGvK-1624587058471)(media/3fea27b6e53277cb75b1a092377a9275.png)]

图 9

字符串 编 译 器 一 般 会 将 字符串存 放在.rodata 节。hello.c
中共有两个字符串,分 别是 两 个 printf 格 式 化 输 出 的 字 符串。 对应找 到
he ll o.s 中的.rodata 节:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rgsjR0cs-1624587058472)(media/72482affb008447571ed7f2490f2a9cd.png)]

图 10

可以看出 hello.s 中字符串由.string 声明,第一个字符串.LC0 包 含 汉字,每
个汉字在 utf-8 编码中被编码为三个字节,第二个字符串的两个%s 为用户
在终端输入的两个参数。

数组 hello.c 中的 数组是 main()函 数的第二个参 数 char *argv[],argv
是字符串指 针的数组,每个元素是一个指向一个字符串首 地 址 的 指
针,作为函数的第 二 个 参数 ,argv[]开始被保存在寄存器%rsi 中,然 后 又 被
保 存 到 栈 中32 (%rbp)的 位 置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1I982irT-1624587058472)(media/911f340a80c2f608bcd9aa1abb41ff70.png)]

图 11

可以看到 main 函数并没有访问 argv[0],而是访问了 argv[1]和 argv[2],这 是因为
argv[0]指 向 程 序 运 行 的 全 路 径 名。

函数操作 程序运行时先进入程序入口处,然后自动调用 main
函数。若程序员需要调用 函数,在汇编代码中需要使用 call
指令,在使用之前需要先设置好参数。进行传参。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-afFO6bZD-1624587058472)(media/15f18b3848d61bdbc80b49262114e701.png)]

图 12

结束后的ret

%rbp为栈帧的底部,函数在%rbp上分配空间

leave指令:相当于mov %rbp,%rsp ,pop %rbp,恢 复 栈 空 间 为 调 用 main函 数 之
前 的 状 态。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JbCDQffZ-1624587058473)(media/e42599551a8b7685654cffaa2dc424e2.png)]

图 13

3.4 本章小结

本节对应于书上与汇编语言相关的章节,总 结 并 分 析 了 编 译 器 是
如何处理c语言的各个数据类型和各类操作,如算术操作,关系操作 和
函数调用的。经过该步骤 hello.s已经是更 加 接 近 机器层面的汇编代码。

(第3章2分)

第4章 汇编

4.1 汇编的概念与作用

  1. 汇编的概念:汇编器(as)将hello.s翻译 成机器 能读懂 的 机器 语
    言指令,并将这些指令打包成可重定位目标程序hello.o,hello.o是一个二 进 制
    文件

  2. 作用:产生 机器能 读 懂 的代 码,使得 程序能 被 机 器 执 行。由 于 几
    乎每一条汇 编指 令都对应 于一 条机 器 代码。

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

4.2 在Ubuntu下汇编的命令

命令为: gcc -c hello.s -o hello.o

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nDiwh8qN-1624587058473)(media/ffdad8f0989374b0abe144582bb0e4bb.png)]

图 14

应截图,展示汇编过程!

4.3 可重定位目标elf格式

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9t3DAwkk-1624587058474)(media/5be2b02ec7b556ba3d85ab8a68c47065.png)]

图 15

  1. ELF Header:以 16B 的序列 Magic 开始,Magic 描 述了生成该
    文件的系统的字的大小和字节顺序,ELF 头剩下的 部 分 包 含帮助 链 接 器 语 法
    分 析和 解 释 目标文件的信息,其中包括 ELF 头 的 大 小、
    目标文件的类型、机器类型、字节头部表(section header table)的 文 件 偏 移,
    以 及 节 头 部 表 中 条 目 的 大 小 和 数 量等 信 息。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FPtz4HBW-1624587058474)(media/10b938e0a754df3f6060ecdf1a752130.png)]

    图 16

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ygOqyxZq-1624587058475)(media/9a810bf00d4bc312ba00d460803290bc.png)]

图 17

  1. Section Headers:节 头 部表,包含了文件中出 现 的 各 个 节的 语 义,
    包括节的类型、位置和大小等信息。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-55Vrj5Zl-1624587058475)(media/47b7226a19180e79cd2e5d58df493b48.png)]

    图 18

  2. 重 定 位节.rela.text ,一个.text 节 中 位 置的列表,包含.text 节 中 需要
    进行重定位的信息,当链接器 把这 个目 标文件和 其他文 件 组
    合时,需要修改这些位置。8 条重定位信息分别是 对.L0(第一 个 printf
    中的字符串)、puts 函数、exit 函数、.L1(第二个 printf 中的字符串)、printf
    函数、sleepsecs、sleep 函数、getchar 函数进行重定位声明。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3DkVvsa-1624587058475)(media/194cd7798d1d44989f85ffb5b050af03.png)]

    图 19

    以.L1 的 重定 位 为 例阐 述 之后的 重定 位过程: 链接器根据 info
    信息向.symtab 节中查 询 链 接目标 的符号,由 info.symbol=0x05,可 以 发 现
    重 定位 目标链 接到.rodata 的.L1,设重定位条目为 r,

    r 的构造为:r.offset=0x18, r.symbol=.rodata, r.type=R_X86_64_PC32,
    r.addend=-4重定位一个使用 32 位 PC
    相对地址的引用。计算重定位目标地址的算法如下(设需要重定位的.text
    节中的位置为 src,设重定位的目的位置 dst):

    refptr = s +r.offset (1)

    refaddr = ADDR(s) + r.offset (2)

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

    其中(1)指向 src 的指针(2)计算 src
    的运行时地址,(3)中,ADDR(r.symbol)计算 dst
    的运行时地址,在本例中,ADDR(r.symbol)获得的是 dst
    的运行时地址,因为需要设置的是绝对地址,即 dst
    与下一条指令之间的地址之差,所以需要加上 r.addend=-4。之后将 src
    处设置为运行时值*refptr,完成该处重定位。

  3. 符号表(Symbol
    Table)目标文件的符号表中包含用来定位、重定位程序中符号定义和引用的信息。符号表索引是对此数组的索引。索引
    0 表示表中的第一表项,同时也作为定义符号的索引。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZV4fBpO2-1624587058476)(media/540ca34057347137ba9400e6bd2d258a.png)]

    图 20

4.4 Hello.o的结果解析

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

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

使用 objdump -d -r hello.o > helloo.objdump
获得反汇编代码。Hello.s和helloo.objdunp除去显示格式之外两者差别不大,主要差别如下:

  1. 分支转移:反汇编代码跳转指令的操作数使用的不是段名称如.L3,因为段名称只是在汇编语言中便于编写的助记符,所以在汇编成机器语言之后显然不存在,而是确定的地址。

    1. 函数调用:在.s
      文件中,函数调用之后直接跟着函数名称,而在反汇编序中,call
      的目标地址是当前下一条指令。这是因为 hello.c
      中调用的函数都是共享库中的函数,最终需要通过动态链接器才能确定函数的运行时执行地址,在汇编成为机器语言的时候,对于这些不确定地址的函数调用,将其
      call 指令后的相对地址设置为全
      0(目标地址正是下一条指令),然后在.rela.text
      节中为其添加重定位条目,等待静态链接的进一步确定。

    2. 全局变量访问:在.s 文件中,访问 rodata(printf
      中的字符串),使用段名称+%rip,在反汇编代码中 0+%rip,因为 rodata
      中数据地址也是在运行时确定,故访问也需要重定位。所以在汇编成为机器语言时,将操作数设置为全
      0 并添加重定位条目。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1LE4tjoK-1624587058477)(media/2d3e9521092f5e76ea55d1d3bacfdb8d.png)]

图 21

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

4.5 本章小结

本章介绍了 hello 从 hello.s 到 hello.o 的汇编过程,通过查看 hello.o 的 elf
格式和使用 objdump 得到反汇编代码与 hello.s
进行比较的方式,间接了解到从汇编语言映射到机器语言汇编器需要实现的转换

(第4章1分)

第5章 链接

5.1 链接的概念与作用

概念:

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

作用:

1)链接可以执行于编译时,也就是源代码被翻译成机器代码时;也可以执行于加载时,即程序被加载器加载到内存并执行时;甚至执行于运行时,也就是由应用程序来执行。

2)链接使得分离编译(seperate compila)成为可能。更便于我们维护管理,我们可以独立的修改和编译我们需要修改的小的模块。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CmgXSiMr-1624587058477)(media/eff7e011eab7362d656dd21d206cb32e.png)]

图 22

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6r5LrEDq-1624587058478)(media/30ac530914c40bb1f7031e4351ccb3a6.png)]

图 23

  • 各节的基本信息均在节头表(描述目标文件的节)中进行了声明。节头表(包括名称,大小,类型,全体大小,地址,旗标,偏移量,对齐等信息),下面是它的截图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9euw8yMY-1624587058478)(media/7f9c9347f844062021d737ef2bc15ed3.png)]

图 24

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-COuggLEq-1624587058479)(media/96f216a6a838188984b71120a156769d.png
)]

图 25

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

5.4 hello的虚拟地址空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZUuW49HG-1624587058479)(media/49068676567e9757429329a616444f99.png)]

图 26

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q4OejOhI-1624587058479)(media/e7e8a2bea528b921423afc2d02bf5994.png)]

图 27 edb加载hello后

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jq0LT4Aa-1624587058480)(media/648e68fd68cc4b1dc9b35df3179e6b94.png)]

图 28

  • 在这里可以看到0x400000开始的位置内存信息,从0x400000-0x400fff之间的节对应上文中的节表头声明。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rI2YwPB0-1624587058480)(media/c8d38c5de7115f7bf9f9ea31a0b42da6.png)]

图 29

  • 关于5.3节节头表中的.dynamic到.shstrtab的处理。首先查看hello的elf格式文件重的程序头,它包含的信息:类型,偏移,虚拟地址,物理地址,对齐,标志等,如截图5。通过Data
    Dump窗口查看虚拟地址段
    0x600000到0x602000的部分,在0到fff的空间中,与0x400000到0x401000段的存放的程序相同;而在
    fff之后存放的是.dynamic到.shstrtab节。

5.5 链接的重定位过程分析

  • 使用 objdump -d -r hello > hello.objdump 获得 hello 的反汇编代码。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g9Fr6W65-1624587058481)(media/c3cf58f24041df3b7abda4ed0a45da85.png)]

图 30

其中右侧是对hello进行反汇编,而左侧是对hello.o的可重定位文件进行的反汇编。

  • 第2步,分析列举hello反汇编文件与hello.o反汇编文件的区别(即helloo.objdump与hello.objdump的对比)。
  1. 我们发现hello.objdump比helloo. objdump多了 许 多 文 件 节 。比 如.
    interp节和.hash节等而hello.o反 汇 编 得 到 的 程 序 直 接 从.text 的 代 码
    段开始 。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1gajTH2x-1624587058481)(media/c1ab6cbe00861b3994fcd1baafa5630a.png)]

    图 31

  2. hello.objdump中增加了许多外部链接的共享库函数。如puts@plt共享库函数,printf@plt共享库函数以及getchar@plt函数。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mFH1YEfc-1624587058485)(media/cab068112c5e43ac8384c94c1cb92a23.png)]

    图 32

  3. 跳转和函数调用的地址在hello.objdump中是虚拟内存地址:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tqqtd4u9-1624587058485)(media/48d684960365fa4d8ed458675dafc221.png)]

    图 33

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

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

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

5.6 hello的执行流程

使用EDB,只需要逐步执行,将程序名列出即可:

函数地址
ld-2.27.so!_dl_start0x7fce 8cc38ea0
ld-2.27.so!_dl_init0x7fce 8cc47630
hello!_start0x400500
libc-2.27.so!__libc_start_main0x7fce 8c867ab0
-libc-2.27.so!__cxa_atexit0x7fce 8c889430
-libc-2.27.so!__libc_csu_init0x4005c0
hello!_init0x400488
libc-2.27.so!_setjmp0x7fce 8c884c10
-libc-2.27.so!_sigsetjmp0x7fce 8c884b70
–libc-2.27.so!__sigjmp_save0x7fce 8c884bd0
hello!main0x400532
hello!puts@plt0x4004b0
hello!exit@plt0x4004e0
*hello!printf@plt
*hello!sleep@plt
*hello!getchar@plt
ld-2.27.so!_dl_runtime_resolve_xsave0x7fce 8cc4e680
-ld-2.27.so!_dl_fixup0x7fce 8cc46df0
–ld-2.27.so!_dl_lookup_symbol_x0x7fce 8cc420b0
libc-2.27.so!exit0x7fce 8c889128

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

5.7 Hello的动态链接分析

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

  • dl_init 调用后,0x601008 和 0x601010 两个地址 的数 据 都 产 生 变 化。
    由于编译器无法预测函数的运行时地址,所以需要添加重定位记录。链接器
    采用延迟绑定的策略,使用 PLT+GOT 实 现 函 数的 动 态 链 接。

  • PLT 使用 GOT 中的 地 址 跳到目标函数。

  • 在 dl_init 调用之前,函数调用都指向 PLT 中的代码逻辑。第一次执行时,为 GOT
    赋上相应的偏移量,初始化了函数调用,dl_init
    就是做了这件事。此后每次执行时不需要经过如此操作,每次都直接跳转到目标函数的地址。

5.8 本章小结

在本章中主要介绍了链接的概念与作用、hello的ELF格式,分析了hello的
虚拟地址空间、重定位过程、执行流程、动态链接过程。

链接器的两个主要任务就是符号解析和重定位,符号解析将目标文件中的每个全局符号都绑定到一个唯一的定义,而重定位确定每个信号的最终内存地址,并且修改对那些目标符号的引用。

(第5章1分)

第6章 hello进程管理

6.1 进程的概念与作用

概念:

进程是程序的一个实例,每一个进程都有它自己的地址空间。在地址 空间内,每个进程的地址空间结构的一样的。

进程为用户提供了如下假象:程序好像在独占处理器、内存,处理器无间断 地运行进程,该进程好像是系统中唯一运行的程序。

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

  • Shell 是一个程序,它可以读取用户输入的命令,执行相应的操作。

  • Shell 应用 程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
    处理流程: 当从 shell
    里输入命令(字符串)时,第一个单词是可执行程序的名称,后面 则是参数列表。

  • shell 会传进参数列表来执行对应程序,创建进程,并在进程终止 后回收进程。
    读入后,shell 先解析字符串,得到命令行参数(char **argv)。

  • 若命令行参数 的最后一个单词是&,表示要在后台执行,shell
    可以继续输入命令来做其他工作, 否则则为前台执行,必须等待该进程结束并回收。

6.3 Hello的fork进程创建过程

在终端Terminal中键入./hello 1190201801
耿健,运行的终端程序会对输入的命令行进行解析。

  1. hello 不是一个内置的shell命令所以解析之后终端程序判断./hello
    的语义为执行当前目录下的可执行目标文件 hello。

  2. 之后终端程序首先会调用 fork
    函数创建一个新的运行的子进程,新创建的子进程几乎父进程相同,但不完全与相同。

  3. 父进程与子进程之间最大的区别在于它们拥有不同的
    PID。子进程得到与父进程用户级虚拟地址空间相同的一份副本,当父进程调用 fork
    时,子进程可以读写父进程中打开的任何文件。

  4. 内核能够以任意方式交替执行父子进程的逻辑控制流的指令,父进程与子进程是并发运行而独立的。在子进程执行期间,父进程默认选项是显示等待子进程的完成。

  5. 父进程和子进程独立 运行,二者结束顺序不可知。父进程负责回收子进程。
    所以可以根据 fork 的返回值不同来区分子进程和父进程。

6.4 Hello的execve过程

  • 进程调用 execve
    函数,该函数从不返回,它将删除该进程的代码和地址空间内的内容并将其初始化。

  • 加载目标程序。换句话说,execve 函数将用目 标 程 序 的 进 程 替 换
    当前进程,并传入相应的参数和环境变量,控制转移到新程序的 main 函数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K1VFrhKj-1624587058486)(media/ad6c70d751dbed3845b29202ee7f4425.jpeg)]

图 34

6.5 Hello的进程执行

  • 由于运行 hello 进程的同时可能在同时运行其他进程,内核并发运行多个进程
    需要用到上下文切换来实现多任务。内存为每个进程维持一个上下文,即内核重
    新启动一个被抢占的进程所需的状态。假如 hello 没有被抢占,则一条条运行汇编
    指令;若被抢占,内核则进行上下文切换:假如从 hello 进程切换到 A 进程,则
  1. 保存 hello 进程的上下文

  2. 恢复 A 进程的上下文

  3. 控制转移给 A 进程

  • hello 进程最初运行在用户模式,但是程序调用了 sleep 函数,调用时产生了用
    户态和核心态的转变。进程主动请求休眠,于是产生上述的上下文切换,内核将
    控制转移到其他进程,将 hello 进程从运行队列加入等待队列,从用户模式变成内
    核模式,并开始计时。当计时结束时,发送中断信号,将 hello 进程从等待队列中
    移出,从内核模式转为用户模式。此时 hello 进程就可以继续执行逻辑控制流了。

  • 在 getchar 时,实际上也是执行了 read 的调用,此时也产生了如上所述的上下
    文切换,进程等待键盘缓冲区的输入。当完成键盘缓冲区到内存的数据传输后,内核从其他进程上下文切换到
    hello 进程。
    结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。

6.6 hello的异常与信号处理

  • ctrl + z 向进程发送信号,使进程挂起。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TW84SztA-1624587058486)(media/b81e0303a382ab3b04652082267ad22e.png)]

图 35 输入ctrl + z

  • ctrl + c 向进程发送信号, 令进程终止:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WbTn8WDx-1624587058486)(media/c8e81e0906bce80c3910b05365058082.png)]

图 36

  • ps fg 将进程放置后台(前台)运行:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pJhZhgP1-1624587058487)(media/33e5f3aa8c1f125d5456e18f292a64b7.png)]

图 37

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aaBComMo-1624587058487)(media/71ff98edc0fb448e2c7521e46c19da47.png)]
图 38 执行jobs

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l8wXNMYN-1624587058488)(media/1681a804014bfe16f8472639b363ea26.png)]
图 39 pstree

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7b078ov3-1624587058488)(media/178c902acbb6f942bac78dc0d81b45bc.png)]

图 40

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AXjl5Yi8-1624587058488)(media/0ced0e6435b5f076ab937d6007917970.png)]

图 41 当前进程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qJwtzIaq-1624587058489)(media/dd8cb6bbe75604d4488f72c8b2ab81c7.png)]

图 42 kill之后的进程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARLihVba-1624587058489)(media/f04b49f98d0e3ad44b544627e61fba99.png)]

图 43

  • 在进行乱按的时候,第一个字符串会被读入,而其他的字符串会在缓冲区中储存,然后再程序结束的时候,当成命令行命令读入.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l0FZrup7-1624587058489)(media/28f9efa495a3335ea74129d8e9fb02c0.png)]

图 44

6.7本章小结

异常控制流发生在计算机系统的各个层次,是计算机系统中提供并发的基本机制。有四种不同类型的异常,中断,故障,终止,和陷阱。

在操作系统层,内核用ECF提供进程的基本概念,给应用程序两个重要的抽象: 1)
逻辑控制流2)私有地址空间.

应用程序可以创建子进程,等待他们的子进程停止或者终止,运行新的程序,以及捕获来自其他进程的信号.

(第6章1分)

第7章 hello的存储管理

7.1 hello的存储器地址空间

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

  • 逻辑地址:程序代码经过编译后出现在汇编程序中地址。

  • 线性地址:逻辑地址经过段机制转化后为线性地址,用于描述程序分页信息
    的地址。以 hello 为例,线性地址就是 hello 应该在内存的哪些块上运行。
    虚拟地址:同线性地址。

  • 物理地址:处理器通过地址总线的寻址,找到真实的物理内存对应地址。是
    内存单元的真实地址。以 hello 为例,物理地址就是 hello 真正应该在内存的哪些
    地址上运行。

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

段式管理,是指把一个程序分成若干个段进行存储,每个段都是一个逻辑实 体,程序员需要知道并使用它。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nq3lZAye-1624587058490)(media/8400087ef76a5cdedde36cf7ec6033b6.jpeg)]

图 45

它的产生是与程序的模块化直接有关的。段式管 理是通过段表进行的,它包括段号或段名、段起点、装入位、段的长度等。此外 还需要主存占用区域表、主存可用区域表。

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

首先查看页表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69gAAPxz-1624587058490)(media/b3e9c8578d4b2e561a354df09f03c1b1.jpeg)]

图 46 页表

然后使用页表的地址翻译:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s79AlRSx-1624587058490)(media/e2b3b4e73ddbda765527cee706072292.jpeg)]

图 47 翻译地址

线性地址转换成物理地址的过程如下:

首先我们先将线性地址分为 VPN(虚拟页号)+VPO(虚拟页偏移)的形式。
然后再将 VPN 拆分成 TLBT(TLB 标记)+TLBI(TLB 索引)然后去 TLB 缓存里 找所对应的 PPN(物理页号)如果发生缺页情况则直接查找对应的 PPN,找到 PPN 之后,将其与 VPO 组合变为 PPN+VPO 就是生成的物理地址了。

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

  • 书中的二级页表示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HEsKfizW-1624587058491)(media/9234d53189a475cf180c5bfa635150e2.jpeg)]

图 48

  • 在实际的运行的过程中,二级页表是远远不够的,因此我们需要多级页表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-keIXMDHd-1624587058491)(media/179d28c8dd7b35e44ba9d56c00d7d7ad.jpeg)]

图 49

  • 在多级页表中,每一级页表中的一个项都对应下一页表的起始位置,这样不需要的页表就不需要载入内存中,节省空间。

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

此时我们已经得到物理地址,只需要在 cache 寻找即可。与课本高速缓存章节 类似,将物理地址分为 CT(标记)+CI(索引)+CO(偏移量),然后在一级 cache 内部找,如果没有一直向下递归寻找。找到之后将其写入cache, 返回结果。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nDlHtqsy-1624587058491)(media/a0fc2e326767451a60b1ca8813ba1e8d.jpeg)]

图 50 整体寻址过程

  • 我们先在L1缓存中寻找结果,如果命中,就将缓存中的数据项保存,返回递归向下一级存储结构中寻找。

7.6 hello进程fork时的内存映射

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

7.7 hello进程execve时的内存映射

execve
函数调用驻留在内核区域的启动加载器代码,在当前进程中加载并运行包含在可执行目标文件
hello 中的程序

  • 删除已存在的用户区域

  • 映射私有区域,为新程序的代码、数据、bss 和栈区域创建新的区域结构。

  • 映射共享区域, hello 程序与共享对象 libc.so 链接,libc.so
    是动态链接到这个程序中的,然后再映射到用户虚拟地址空间中的共享区域内。

  • 设置程序计数器(PC),使之指向代码区域的入口点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jvd68am8-1624587058492)(media/86f1948a394fd902c26f7ddd7ed6a6c2.jpeg)]

图 51

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

假设当前虚拟地址是 A,现在翻译地址 A 触发了一个缺页异常,导致控制转
移到内核的缺页处理程序,处理程序将执行以下步骤:

  • A
    在某个区域结构定义的区域是否合法,若不合法,则产生一个段错误,然后终止这个进程。

  • 该内存访问呢是否合法,若访问是不合法的,那么缺页处理程序会触发一个保护异常,终止这个进程。

  • 此时内核知道该操作是合法的,那么将把对应页面加载并更新页表,选择一个牺牲页面,如果这个牺牲页面被修改过,那么就将它交换
    出去,换入新的页面并更新页表。当缺页处理程序返回时,CPU 重新启动
    引起缺页的指令。即可。

7.9动态存储分配管理

printf 函数会调用 malloc,下面简述动态内存管理的基本方法与策略:

  • 动态内存分配器维护着一个进程的虚拟内存区域,称为堆。分配器将堆视为一组不同大小的块的集合来维护。

  • 每个块就是一个连续的虚拟内存片,要么是已分配的,要么是空闲的。已分配的块显式地保留为供应用程序使用。

  • 空闲块可用来分配。空闲块保 持 空 闲,直到它显式地被应用所分配。

  • 一个已分配的块保持已分配状态,直到它被释放。

带边界标签的隐式空闲链表

  • 堆及堆中内存块的组织结构:

在内存块中增加4B的Header(用于寻找下一个blcok)和4B的Footer(用于寻找上一个block)。Footer的设计是专门为了合并空闲块方便的。因为Header和Footer大小已知。

  1. 隐式链表

对比于显式空闲链表,隐式空闲链表代表并不直接对空闲块进行链接,而是将对内存空间中的所有块组织成一个大链表。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L00XEM7B-1624587058492)(media/f2aa6bba66dd27517f9e6a96fd08108e.jpeg)]

图 52

  1. 空闲块合并

可以利用Footer方便的对前面的空闲块进行合并。合并的情况一共分为四种:前空后不空,前不空后空,前后都空,前后都不空。通过改变Header和Footer四种情况分别进行空闲块合并。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-STZbq8nM-1624587058493)(media/3083d56f3bcbfe846f2686246756603d.jpeg)]

图 53

显示空间链表

将空闲块组织成链表形式的数据结构。堆可以组织成一个双向空闲链表,在每个空闲块中,都包含一个
pred(前驱)和 succ(后继)指针。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RrDuctbA-1624587058493)(media/d3698d3f7b10b2088950a477e929c517.jpeg)]

图 54

7.10本章小结

虚拟内存是对主存的一个抽象,它提供三个重要的功能,第一,自动缓存最近使用的存放磁盘上的虚拟地址空间。第二,简化内存管理,进而简化链接,在进程间共享数据,以及程序的加载。

内存的使用和释放时一个容易出错的地方,需要我们进一步理解对它的认识,也要在编程工作中,优化内存管理的策略。

(第7章 2分)

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

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

设备的模型化:所有的 IO 设备都 被模 型化
为文件,而所有的输入和输出都被当做对相应文件的读和写来执行,这种将设备优雅地映射为文件的方式,允许Linux
内核引出一个简单低级的应用接口,称为 Unix I/O。

这就使得所有的输入和输出都能一一个统一且一致的方式来执行:

  • 打开文件。

  • Linux
    Shell创建的每个进程开始时都有三个打开的文件:标准输入,标准输出,标准错误。

  • 改变当前文件的位置。

  • 读写文件。

  • 关闭文件。

8.2 简述Unix IO接口及其函数

  • 打开文件。一个应用程序通过要求内核打开相应的文件,来宣告它想要访问一个 I/O
    设备。内核返回一个小 的非 负 整数 ,叫做描述符。描述符在 后 续 对此文件的 所
    有 操作 中标识这个文件 ,内核记 录 有 关 这 个 打 开 文件的所有信息。

  • Shell 创建的每个进程都有三个打开的文 件:标准输入,标准输出,标准错误。

  • 改变当前的文件位置:内核保持着每个打开的文件的一个文件位置k。k初
    始为0。这个文件位置k表示 的 是 从 文 件 开 头 起 始的 字节 偏 移 量。

  • 读写文件:

    读操作就是从文件复制n>0个字节到内存。

    写操作就是从内存中复制n>0个字节到一个文件。

  • 关闭文件:内 核 释 放 文件打开时创建的数据结构,无论 一 个 进 程
    以何种原因终止时,内核都会关闭所有打开的文件,并且 释 放 他 们 的 内
    存资源。

函数:

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

int close (int fd);

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

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

8.3 printf的实现分析

从vsprintf生成显示信息,到write系统函数,到陷阱-系统调用 int
0x80或syscall等.字符显示驱动子程序:从ASCII 到 字 模 库 到 显 示vram。显 示
芯片按照刷新频率逐 行 读 取vram,并通过信号 线 向 液 晶 显 示 器 传 输
每一个点。

8.4 getchar的实现分析

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

getchar 函数在实现时到底层调用了系统函数 read,通过系统调用read读取存储在 键 盘
缓冲 区中的 ASCII 码直到读到回车符然后 返 回 整 个 字 串,getchar
进行封装,大体逻 辑是读 取字符 串的第一 个字符然后返回。

8.5本章小结

Linux 提供了少量的基于 Unix
i/o模型的系统函数,他们允许应用程序打开,关闭,读写文件,执行io重定向。

而标准io库是基于unix io 实现的,并且提供了一组强大的高 级io 例
程,对于大多数应用程序而言,标准io更加简单,是优于 unix io 的选择,但 是 对
标准io和网络文件的一些互相不兼容的显式,unix io 比标准io 更适合 用 于 网 络
应用程序的编程。

(第8章1分)

结论

伟大而又可怜的HelloWorld,全世界都知道它的名字,但全世界都抛弃了它,它的一生是伟大的,是短小的,是精彩的。

它一生经历的阶段,正如我们人生的一个个阶段,它的一生也许比我们想象的要单调,也许比某些碌碌无为的人的一生更加丰富。让我这个无名的小程序员,斗胆总结hello的一生:

  1. hello 的 C 语言程序代码被程序员写成,保存为 hello.c。

  2. 预处理:将 hello.c 经过预处理器(cpp),将所有以#开头的预处理命令解
    析,得到 hello.i

  3. 编译:将 hello.i 经过编译器翻译成汇编程序 hello.s

  4. 汇编:将汇编程序 hello.s 经过汇编器翻译成二进制文件 hello.o,一种可重
    定位目标程序的格式。

  5. 链接:将 hello.o 与各种库链接称为可执行目标程序 hello

  6. 用户用 shell 输入参数列表,运行 hello

  7. shell 用 fork 和 execve 创建 hello 的子进程。为 hello 创建 地 址区域,进入
    程 序 入口进行 初始化,然 后 转 入 main 函数

  8. CPU 将一步步执行 hello 的指令,hello 进程与其他进程并发运行。

  9. 访存:hello 有自己的地址空间,当有访存操作时,MMU 把虚拟地址翻译
    成物理地址,通过三级 cache 访问内存。

  10. 异常:程 序 运行 时可能 会有 许多异常,比如 ctrl+z/c,hello 中的 sleep
    函数, 会触发上下文切换,hello 会在用户态和内核态切换。

  11. getchar 函 数 会 调用 read 触 发 中 断 异 常,从缓冲区读取字符进行处理。

  12. hello 进程结束,shell 回收子进程,hello 进程消失。

学习CSAPP是一个痛苦并快乐的过程,写这篇大作业也是…
虽然这门课很折磨,但我也从中学到了计算机系统深刻的知识,让我换了一个视角来看待我面前这个精美而复杂的计算机,Hello轻轻地来了,正如它轻轻地走,它挥一挥衣袖,不带走一片云彩!

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

附件

hellohello 的执行程序
hello.chello 的源程序
hello.elfhello 的elf头部
hello.ihello 的预处理程序
hello.ohello 的可重定位的目标程序
hello.objdumphello 的反汇编文件
hello.shello 的汇编程序
helloElfhello.o 的Elf头部
helloo.objdumphello.o 的反汇编程序

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

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

附件

hellohello 的执行程序
hello.chello 的源程序
hello.elfhello 的elf头部
hello.ihello 的预处理程序
hello.ohello 的可重定位的目标程序
hello.objdumphello 的反汇编文件
hello.shello 的汇编程序
helloElfhello.o 的Elf头部
helloo.objdumphello.o 的反汇编程序

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

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

参考文献

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

[1] 深入理解计算机系统第三版

[2] 百度,知乎,掘金,CSDN。

[3] bilibili cmu网课

[4] gcc–编译的四大过程及作用:https://blog.csdn.net/shiyongraow/article/details/8

1454995

[5] 网络用户. 阿里云. ELF格式文件符号表全解析及readelf命令使用方法. 2018:07-19.
https://www.aliyun.com/zixun/wenji/1246586.html

[6] C语言预处理命令之条件编译. 2009:08-16.
http://www.kuqin.com/language/20090806/66164.html

[7] CSDN. 编译器工作流程详解. 2014:04-27.
https://blog.csdn.net/u012491514/article/details/24590467

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值