1. 程式人生 > >HITICS大作業 程式人生-Hello’s P2P

HITICS大作業 程式人生-Hello’s P2P

程式人生-Hello’s P2P

摘 要

在本篇文章之中,我們將跟隨著程式hello的腳步,暢遊計算機系統的世界。我們將從剛剛出生成為C語言程式碼的hello開始,看著他經過預處理、編譯、彙編、連結的P2P過程,漸漸的從一個文字檔案變成一個可執行的目標程式。我們也將瞭解hello從它開始執行起,在計算機的系統之中發生的種種變化,直到hello這個程式執行結束從系統之中消失。

關鍵詞:計算機系統 ;程式的生成;程序控制;儲存管理

(摘要0分,缺失-1分,根據內容精彩稱都酌情加分0-1分)

第1章 概述

1.1 Hello簡介

Hello的P2P就是hello從一個高階程式語言的文字檔案開始經歷了預處理、編譯、彙編、連結等階段的處理之後,變成一個可以執行的應用程式的過程。
Hello的O2O就是hello這個程式在shell中fork、execve成為程序,載入記憶體,然後開始執行hello的程式碼,在這個過程中系統為其分配時間片來執行邏輯控制流,最終在hello執行完畢後,通過父程序進行hello的回收,並清除hello在核心所使用的所有資源的過程。

1.2 環境與工具

硬體環境:Inter Core i5 7200U CPU;2G RAM;128G SSD;500G HDD
軟體環境:Vmware;Ubuntu 16.04 LTS 64位;Windows 7 64位
使用工具:gcc;edb;codeblocks;objdump;readelf等

1.3 中間結果

hello.i 經過預處理後的hello
hello.s 經過編譯後的組合語言檔案
hello.o 經過彙編後的可重定位目標檔案
hello.out 經過連線後生成的課執行目標檔案
asm1.txt hello.o經過objdump反彙編後的輸出
asm2.txt hello.out經過objdump反彙編後的輸出
elf1.txt hello.o使用readelf讀取出的elf的輸出
elf2.txt hello.out使用readelf讀取出的elf的輸出

1.4 本章小結

在本章之中我們大致瞭解了hello的P2P和O2O的過程,並對在實驗的過程之中對hello進行處理所使用的軟硬體環境以及工具和所生成的中間結果進行了一定的瞭解。

	(第1章0.5分)

第2章 預處理

2.1 預處理的概念與作用

預處理是根據以字元#開頭的命令,來修改源程式。
預處理是對標頭檔案以及巨集定義等的處理
其具體處理的內容如下:
1).將原始檔中用#include形式宣告的檔案複製到新的程式中。比如hello.c第6-8行中的#include<stdio.h> 等命令告訴前處理器讀取系統標頭檔案stdio.h unistd.h stdlib.h 的內容,並把它直接插入到程式文字中。
2).用實際值替換用#define定義的字串
3).根據#if後面的條件決定需要編譯的程式碼

2.2在Ubuntu下預處理的命令

cpp hello.c > hello.i
在這裡插入圖片描述在這裡插入圖片描述

2.3 Hello的預處理結果解析

檢視hello.i以後我們發現hello.c的程式碼的前端插入了具體的標頭檔案的內容
當我們把hello.i檔案用codeblocks開啟之後發現,hello.i的程式碼增長到了3119行,在這之中我們找到main函式的位置:
在這裡插入圖片描述

2.4 本章小結

本章完成了對hello的預處理,在hello的預處理階段我們對C程式的程式碼中的以字元#開頭的命令進行處理,處理的方式是對把這些命令所代表的具體內容直接插入C程式程式碼。

				(第2章0.5分)

第3章 編譯

3.1 編譯的概念與作用

編譯階段,編譯器將文字檔案hello.i翻譯成文字檔案hello.s,其中翻譯後的hello.s檔案中儲存的是與該程式對應的組合語言程式碼
編譯的主要作用:1.掃描(詞法分析),2.語法分析,3.語義分析,4.原始碼優化(中間語言生成),5.程式碼生成,目的碼優化。
1)將原始碼程式輸入掃描器,將原始碼的字元序列分割成一系列記號。
2)基於詞法分析得到的一系列記號,生成語法樹。
3)由語義分析器完成,指示判斷是否合法,並不判斷對錯。又分靜態語義:隱含浮點型到整形的轉換,會報warning。
4)中間程式碼(語言)使得編譯器分為前端和後端,前端產生與機器(或環境)無關的中間程式碼,編譯器的後端將中間程式碼轉換為目標機器程式碼,目的:一個前端對多個後端,適應不同平臺。
5)編譯器後端主要包括:程式碼生成器:依賴於目標機器,依賴目標機器的不同字長,暫存器,資料型別等
目的碼優化器:選擇合適的定址方式,左移右移代替乘除,刪除多餘指令

3.2 在Ubuntu下編譯的命令

gcc –S hello.c –o hello.s
在這裡插入圖片描述在這裡插入圖片描述

3.3 Hello的編譯結果解析

3.3.1全域性變數

int sleepsecs=2.5;
“Usage: Hello 學號 姓名!\n”
“Hello %s %s\n”
這些儲存在全域性變數區,在hello.s檔案中我們在使用全域性變數的時候就要用$地址(%rip)使用

3.3.2 區域性變數

int i
區域性變數存放在棧中
此處為-4(%rbp)

3.3.3 引數傳遞與函式呼叫

因為是64位的系統,所以說在函式呼叫時的引數傳遞使用rsi和rdi兩個暫存器,函式呼叫在彙編程式hello.s中被翻譯成呼叫語句call

3.3.4 條件控制

在彙編程式hello.s中if語句被翻譯成條件跳轉語句
此處為:在這裡插入圖片描述

3.3.5迴圈

在彙編程式hello.s中for迴圈被翻譯成條件跳轉語句
在這裡插入圖片描述
L4部分為迴圈體,在迴圈體結束時add語句對區域性變數i加一,在這之後把i的值和9進行比較,若i<=9則返回L4處再次執行迴圈體

3.3.6型別轉換

我們發現在hello中還存在著型別轉換int sleepsecs=2.5就是一種隱式的型別轉換,他把浮點型的數2.5賦值給了整型的變數sleepsecs,所以說會發生型別的轉換,而在進行編譯的時候程式改變數值和位模式的原則是:值會向零舍入,所以說最終型別轉換後的sleepsecs的值為2
在這裡插入圖片描述

3.4 本章小結

本章完成了對hello的編譯過程,在hello的編譯過程中,編譯器將經歷過預處理後的C語言檔案的語句按照一定的型別與方式進行翻譯,並最終生成彙編程式碼的檔案hello.s

					(第3章2分)

第4章 彙編

4.1 彙編的概念與作用

彙編階段是彙編器(as)將hello.s翻譯成機器指令,並把這些指令打包成可重定位目標程式的格式,並將結果儲存在hello.o中

4.2 在Ubuntu下彙編的命令

as hello.s –o hello.o
在這裡插入圖片描述在這裡插入圖片描述

4.3 可重定位目標elf格式

ELF 頭:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
類別: ELF64
資料: 2 補碼,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
型別: REL (可重定位檔案)
系統架構: Advanced Micro Devices X86-64
版本: 0x1
入口點地址: 0x0
程式頭起點: 0 (bytes into file)
Start of section headers: 1144 (bytes into file)
標誌: 0x0
本頭的大小: 64 (位元組)
程式頭大小: 0 (位元組)
Number of program headers: 0
節頭大小: 64 (位元組)
節頭數量: 13
字串表索引節頭: 12

節頭:
[號] 名稱 型別 地址 偏移量
大小 全體大小 旗標 連結 資訊 對齊
[ 0 ] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1 ] .text PROGBITS 0000000000000000 00000040
0000000000000081 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 00000338
00000000000000c0 0000000000000018 I 10 1 8
[ 3] .data PROGBITS 0000000000000000 000000c4
0000000000000004 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 000000c8
0000000000000000 0000000000000000 WA 0 0 1
[ 5] .rodata PROGBITS 0000000000000000 000000c8
000000000000002b 0000000000000000 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 000000f3
0000000000000025 0000000000000001 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 00000118
0000000000000000 0000000000000000 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 00000118
0000000000000038 0000000000000000 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 000003f8
0000000000000018 0000000000000018 I 10 8 8
[10] .symtab SYMTAB 0000000000000000 00000150
0000000000000198 0000000000000018 11 9 8
[11] .strtab STRTAB 0000000000000000 000002e8
000000000000004d 0000000000000000 0 0 1
[12] .shstrtab STRTAB 0000000000000000 00000410
0000000000000061 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)

There are no section groups in this file.

本檔案中沒有程式頭。

There is no dynamic section in this file.

重定位節 ‘.rela.text’ at offset 0x338 contains 8 entries:
偏移量 資訊 型別 符號值 符號名稱 + 加數
000000000018 000500000002 R_X86_64_PC32 0000000000000000 .rodata - 4
00000000001d 000c00000004 R_X86_64_PLT32 0000000000000000 puts - 4
000000000027 000d00000004 R_X86_64_PLT32 0000000000000000 exit - 4
000000000050 000500000002 R_X86_64_PC32 0000000000000000 .rodata + 1a
00000000005a 000e00000004 R_X86_64_PLT32 0000000000000000 printf - 4
000000000060 000900000002 R_X86_64_PC32 0000000000000000 sleepsecs - 4
000000000067 000f00000004 R_X86_64_PLT32 0000000000000000 sleep - 4
000000000076 001000000004 R_X86_64_PLT32 0000000000000000 getchar - 4

重定位節 ‘.rela.eh_frame’ at offset 0x3f8 contains 1 entry:
偏移量 資訊 型別 符號值 符號名稱 + 加數
000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table ‘.symtab’ contains 17 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 sleepsecs
10: 0000000000000000 129 FUNC GLOBAL DEFAULT 1 main
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND GLOBAL_OFFSET_TABLE
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts
13: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND exit
14: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND printf
15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND sleep
16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND getchar

No version information found in this file.

4.4 Hello.o的結果解析

  1. 在反彙編程式碼中地址的偏移量是用十六進位制數表示的,而在hello.s中則是直接採用的十進位制的數。
  2. 在反彙編程式碼中函式呼叫時採用的是相對地址的定址方法,而在hello.s中則是call 函式名,因為hello.c中呼叫的函式都是共享庫中的函式,最終需要通過動態連結器才能確定函式的執行時執行地址,在彙編成為機器語言的時候,對於這些不確定地址的函式呼叫,所以說要採用相對定址的方法,在接下來的連結的重定位中才能確定實際地址
    3.在hello.s中進行分支轉移的條件跳轉時,採用了跳轉表的方法,而在反彙編程式碼中則是使用了相對定址的方法,因為跳轉表是在hello.s中可以使程式碼更易讀,而在彙編後就不存在這種表示形式了
  3. 全域性變數訪問:在hello.s檔案中,訪問全域性變數時,使用段名稱+%rip,在反彙編程式碼中0+%rip,因為rodata中資料地址也是在執行時確定,故訪問也需要重定位。所以在彙編成為機器語言時,將運算元設定為全0並新增重定位條目。

4.5 本章小結

本章完成了hello的彙編過程,在彙編階段中我們將編譯階段生成的組合語言hello.s檔案翻譯成可重定位目標檔案hello.o,在這個過程中,hello.s中的十進位制地址偏移量以十六進位制儲存,而因為程式的地址不確定所以說機器語言在進行跳轉的時候,會以相對定址的方式進行。

				(第4章1分)

第5章 連結

5.1 連結的概念與作用

連結階段是程式將分別在不同的目標檔案中編譯或彙編的程式碼收集到一個可直接執行的檔案中,從而生成可執行目標檔案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
在這裡插入圖片描述在這裡插入圖片描述5.3 可執行目標檔案hello的格式
ELF 頭:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
類別: ELF64
資料: 2 補碼,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
型別: EXEC (可執行檔案)
系統架構: Advanced Micro Devices X86-64
版本: 0x1
入口點地址: 0x400500
程式頭起點: 64 (bytes into file)
Start of section headers: 5920 (bytes into file)
標誌: 0x0
本頭的大小: 64 (位元組)
程式頭大小: 56 (位元組)
Number of program headers: 8
節頭大小: 64 (位元組)
節頭數量: 25
字串表索引節頭: 24

節頭:
[號] 名稱 型別 地址 偏移量
大小 全體大小 旗標 連結 資訊 對齊
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400200 00000200
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 000000000040021c 0000021c
0000000000000020 0000000000000000 A 0 0 4
[ 3] .hash HASH 0000000000400240 00000240
0000000000000034 0000000000000004 A 5 0 8
[ 4] .gnu.hash GNU_HASH 0000000000400278 00000278
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000400298 00000298
00000000000000c0 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400358 00000358
0000000000000057 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 00000000004003b0 000003b0
0000000000000010 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 00000000004003c0 000003c0
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 00000000004003e0 000003e0
0000000000000030 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000400410 00000410
0000000000000078 0000000000000018 AI 5 19 8
[11] .init PROGBITS 0000000000400488 00000488
0000000000000017 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 00000000004004a0 000004a0
0000000000000060 0000000000000010 AX 0 0 16
[13] .text PROGBITS 0000000000400500 00000500
0000000000000132 0000000000000000 AX 0 0 16
[14] .fini PROGBITS 0000000000400634 00000634
0000000000000009 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 0000000000400640 00000640
000000000000002f 0000000000000000 A 0 0 4
[16] .eh_frame PROGBITS 0000000000400670 00000670
00000000000000fc 0000000000000000 A 0 0 8
[17] .dynamic DYNAMIC 0000000000600e50 00000e50
00000000000001a0 0000000000000010 WA 6 0 8
[18] .got PROGBITS 0000000000600ff0 00000ff0
0000000000000010 0000000000000008 WA 0 0 8
[19] .got.plt PROGBITS 0000000000601000 00001000
0000000000000040 0000000000000008 WA 0 0 8
[20] .data PROGBITS 0000000000601040 00001040
0000000000000008 0000000000000000 WA 0 0 4
[21] .comment PROGBITS 0000000000000000 00001048
0000000000000024 0000000000000001 MS 0 0 1
[22] .symtab SYMTAB 0000000000000000 00001070
0000000000000498 0000000000000018 23 28 8
[23] .strtab STRTAB 0000000000000000 00001508
0000000000000150 0000000000000000 0 0 1
[24] .shstrtab STRTAB 0000000000000000 00001658
00000000000000c5 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)

There are no section groups in this file.

程式頭:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001c0 0x00000000000001c0 R 0x8
INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000076c 0x000000000000076c R E 0x200000
LOAD 0x0000000000000e50 0x0000000000600e50 0x0000000000600e50
0x00000000000001f8 0x00000000000001f8 RW 0x200000
DYNAMIC 0x0000000000000e50 0x0000000000600e50 0x0000000000600e50
0x00000000000001a0 0x00000000000001a0 RW 0x8
NOTE 0x000000000000021c 0x000000000040021c 0x000000000040021c
0x0000000000000020 0x0000000000000020 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000000e50 0x0000000000600e50 0x0000000000600e50
0x00000000000001b0 0x00000000000001b0 R 0x1

Section to Segment mapping:
段節…
00
01 .interp
02 .interp .note.ABI-tag .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame
03 .dynamic .got .got.plt .data
04 .dynamic
05 .note.ABI-tag
06
07 .dynamic .got

Dynamic section at offset 0xe50 contains 21 entries:
標記 型別 名稱/值
0x0000000000000001 (NEEDED) 共享庫:[libc.so.6]
0x000000000000000c (INIT) 0x400488
0x000000000000000d (FINI) 0x400634
0x0000000000000004 (HASH) 0x400240
0x000000006ffffef5 (GNU_HASH) 0x400278
0x0000000000000005 (STRTAB) 0x400358
0x0000000000000006 (SYMTAB) 0x400298
0x000000000000000a (STRSZ) 87 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x601000
0x0000000000000002 (PLTRELSZ) 120 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x400410
0x0000000000000007 (RELA) 0x4003e0
0x0000000000000008 (RELASZ) 48 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x4003c0
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x4003b0
0x0000000000000000 (NULL) 0x0

重定位節 ‘.rela.dyn’ at offset 0x3e0 contains 2 entries:
偏移量 資訊 型別 符號值 符號名稱 + 加數
000000600ff0 000300000006 R_X86_64_GLOB_DAT 0000000000000000 [email protected]_2.2.5 + 0
000000600ff8 000500000006 R_X86_64_GLOB_DAT 0000000000000000 gmon_start + 0

重定位節 ‘.rela.plt’ at offset 0x410 contains 5 entries:
偏移量 資訊 型別 符號值 符號名稱 + 加數
000000601018 000100000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0
000000601020 000200000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0
000000601028 000400000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0
000000601030 000600000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0
000000601038 000700000007 R_X86_64_JUMP_SLO 0000000000000000 [email protected]_2.2.5 + 0

The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.

Symbol table ‘.dynsym’ contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2)
5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2)

Symbol table ‘.symtab’ contains 49 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000400200 0 SECTION LOCAL DEFAULT 1
2: 000000000040021c 0 SECTION LOCAL DEFAULT 2
3: 0000000000400240 0 SECTION LOCAL DEFAULT 3
4: 0000000000400278 0 SECTION LOCAL DEFAULT 4
5: 0000000000400298 0 SECTION LOCAL DEFAULT 5
6: 0000000000400358 0 SECTION LOCAL DEFAULT 6
7: 00000000004003b0 0 SECTION LOCAL DEFAULT 7
8: 00000000004003c0 0 SECTION LOCAL DEFAULT 8
9: 00000000004003e0 0 SECTION LOCAL DEFAULT 9
10: 0000000000400410 0 SECTION LOCAL DEFAULT 10
11: 0000000000400488 0 SECTION LOCAL DEFAULT 11
12: 00000000004004a0 0 SECTION LOCAL DEFAULT 12
13: 0000000000400500 0 SECTION LOCAL DEFAULT 13
14: 0000000000400634 0 SECTION LOCAL DEFAULT 14
15: 0000000000400640 0 SECTION LOCAL DEFAULT 15
16: 0000000000400670 0 SECTION LOCAL DEFAULT 16
17: 0000000000600e50 0 SECTION LOCAL DEFAULT 17
18: 0000000000600ff0 0 SECTION LOCAL DEFAULT 18
19: 0000000000601000 0 SECTION LOCAL DEFAULT 19
20: 0000000000601040 0 SECTION LOCAL DEFAULT 20
21: 0000000000000000 0 SECTION LOCAL DEFAULT 21
22: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
23: 0000000000000000 0 FILE LOCAL DEFAULT ABS
24: 0000000000600e50 0 NOTYPE LOCAL DEFAULT 17 __init_array_end
25: 0000000000600e50 0 OBJECT LOCAL DEFAULT 17 _DYNAMIC
26: 0000000000600e50 0 NOTYPE LOCAL DEFAULT 17 __init_array_start
27: 0000000000601000 0 OBJECT LOCAL DEFAULT 19 GLOBAL_OFFSET_TABLE
28: 0000000000400630 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
29: 0000000000601040 0 NOTYPE WEAK DEFAULT 20 data_start
30: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]@GLIBC_2.2.5
31: 0000000000601044 4 OBJECT GLOBAL DEFAULT 20 sleepsecs
32: 0000000000601048 0 NOTYPE GLOBAL DEFAULT 20 _edata
33: 0000000000400634 0 FUNC GLOBAL DEFAULT 14 _fini
34: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]@GLIBC_2.2.5
35: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _[email protected]@GLIBC
36: 0000000000601040 0 NOTYPE GLOBAL DEFAULT 20 __data_start
37: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]@GLIBC_2.2.5
38: 0000000000000000 0 NOTYPE WEAK DEFAULT UND gmon_start
39: 0000000000400640 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
40: 00000000004005c0 101 FUNC GLOBAL DEFAULT 13 __libc_csu_init
41: 0000000000601048 0 NOTYPE GLOBAL DEFAULT 20 _end
42: 0000000000400530 2 FUNC GLOBAL HIDDEN 13 _dl_relocate_static_pie
43: 0000000000400500 43 FUNC GLOBAL DEFAULT 13 _start
44: 0000000000601048 0 NOTYPE GLOBAL DEFAULT 20 __bss_start
45: 0000000000400532 129 FUNC GLOBAL DEFAULT 13 main
46: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]@GLIBC_2.2.5
47: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]@GLIBC_2.2.5
48: 0000000000400488 0 FUNC GLOBAL DEFAULT 11 _init

Histogram for bucket list length (total of 3 buckets):
Length Number % of total Coverage
0 0 ( 0.0%)
1 0 ( 0.0%) 0.0%
2 2 ( 66.7%) 57.1%
3 1 ( 33.3%) 100.0%

Version symbols section ‘.gnu.version’ contains 8 entries:
地址: 00000000004003b0 Offset: 0x0003b0 Link: 5 (.dynsym)
000: 0 (本地) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5)
004: 2 (GLIBC_2.2.5) 0 (本地) 2 (GLIBC_2.2.5) 2 (GLIBC_2.2.5)

Version needs section ‘.gnu.version_r’ contains 1 entry:
地址:0x00000000004003c0 Offset: 0x0003c0 Link: 6 (.dynstr)
000000: Version: 1 檔案:libc.so.6 計數:1
0x0010: Name: GLIBC_2.2.5 標誌:無 版本:2

Displaying notes found in: .note.ABI-tag
所有者 Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.2.0
5.4 hello的虛擬地址空間
程式碼段.text的虛擬地址空間程式碼段.text的虛擬地址空間
在這裡插入圖片描述只讀資料段.rodata的虛擬地址空間

5.5 連結的重定位過程分析

  1. 相較於hello.o來說,hello中的地址不再是使用相對定址方式的相對地址,而是在記憶體中的實際地址
  2. 在hello中除了在hello.o中已經有的main函式的機器語言的程式碼之外,還有在main函式中所使用到的位於其他檔案上的其他函式的機器語言的程式碼,是加以整合後的檔案

5.6 hello的執行流程

ld-2.27.so!_dl_start
ld-2.27.so!_dl_init
hello!_start
libc-2.27.so!__libc_start_main
libc-2.27.so!__cxa_atexit
hello!__libc_csu_init
libc-2.27.so!_setjmp
hello!_main
if內容(
[email protected]
[email protected]
)
[email protected]
[email protected]
[email protected]
ld-2.27.so!_dl_runtime_resolve_xsave
ld-2.27.so!_exit

5.7 Hello的動態連結分析

在dl_init執行之前:
在這裡插入圖片描述在dl_init執行之後:
在這裡插入圖片描述

5.8 本章小結

經歷過本章的連結階段的處理之後,hello終於在整合了他所呼叫的其他檔案中的函式等,從C語言的文字檔案變成了可以執行的程式。

				(第5章1分)

第6章 hello程序管理

6.1 程序的概念與作用

程序是一個執行中的程式的例項,每一個程序都有它自己的地址空間,一般情況下,包括文字區域、資料區域、和堆疊。文字區域儲存處理器執行的程式碼;資料區域儲存變數和程序執行期間使用的動態分配的記憶體;堆疊區域儲存區著活動過程呼叫的指令和本地變數。

6.2 簡述殼Shell-bash的作用與處理流程

Shell字面理解就是個“殼”,是作業系統(核心)與使用者之間的橋樑,充當命令直譯器的作用,將使用者輸入的命令翻譯給系統執行。Linux中的shell提供一些內建命令(shell命令)供使用者使用,可以用這些命令編寫shell指令碼來完成複雜重複性的工作。

6.3 Hello的fork程序建立過程

在命令列中輸入 ./hello 1170300219 liuxuanlin 並回車,即可建立hello的程序。系統會首先利用fork建立一個該程式的子程序,並是之與父程序可以做到併發的執行

6.4 Hello的execve過程

在進行了fork之後,系統會使用載入器execve函式來在程序的上下文中載入程序hello。載入器刪除子程序現有的虛擬記憶體段,並建立一組新的程式碼、資料、堆和棧段。新的棧和堆段被初始化為零,通過將虛擬地址空間中的頁對映到可執行檔案的頁大小的片,新的程式碼和資料段被初始化為可執行檔案中的內容,這樣hello就完成了execve的過程

6.5 Hello的程序執行

在hello的排程過程中,當呼叫sleep之前,如果hello程式不被搶佔則順序執行,假如發生被搶佔的情況,則進行上下文切換。而在hello執行到sleep的時候,
因為sleep的關係,hello發生上下文的切換,從使用者態切換到核心態,直到經過sleepsecs=2.5秒後,解除程式的休眠再次發生上下文切換,從核心態重新恢復到使用者態,其大概的過程如下圖所示,在迴圈執行的幾次之中,hello的程序都會發生上述的過程
在這裡插入圖片描述

6.6 hello的異常與訊號處理

6.6.1正常執行

在這裡插入圖片描述

6.6.2 輸入ctrl-z

在這裡插入圖片描述

6.6.3 輸入ctrl-c

在這裡插入圖片描述

6.6.4 亂按

在這裡插入圖片描述

6.7本章小結

在本章之中hello開始了執行,我們通過fork,execve建立了hello程序,分析了hello程序的呼叫與上下文的切換,並瞭解了hello程序對異常和訊號的處理。

				(第6章1分)

第7章 hello的儲存管理

7.1 hello的儲存器地址空間

實體地址:載入到記憶體地址暫存器中的地址,記憶體單元的真正地址。
邏輯地址:CPU所生成的地址。邏輯地址是內部和程式設計使用的。
線性地址或也叫虛擬地址:跟邏輯地址類似,它也是一個不真實的地址,假設邏輯地址是相應的硬體平臺段式管理轉換前地址的話,那麼線性地址則相應了硬體頁式記憶體的轉換前地址。

7.2 Intel邏輯地址到線性地址的變換-段式管理

段式管理,是指把一個程式分成若干個段進行儲存,每個段都是一個邏輯實體,它的產生是與程式的模組化直接有關的。段式管理是通過段表進行的,它包括段號或段名、段起點、裝入位、段的長度等。此外還需要主存佔用區域表、主存可用區域表。
線性地址是由邏輯地址加上隱含的DS 資料段的基地址而得到

7.3 Hello的線性地址到實體地址的變換-頁式管理

頁式儲存管理就是把記憶體物理空間劃分成大小相等的若干區域,一個區域稱為一塊。把邏輯地址空間劃分為大小相等的若干頁,頁大小與塊大小相等
在不考慮多級頁表的情況下,線性地址由頁號和頁內地址構成,而線上性地址轉化為實體地址的時候,我們需要先通過頁號找到頁的起始地址,再加上頁內地址就可以得到實體地址

7.4 TLB與四級頁表支援下的VA到PA的變換

在這裡插入圖片描述
從圖中我們可以看出在四級頁表的支援下要得到實體地址,我們需要從第一個頁表開始逐級讀取頁表中的內容,直到我們經過4級的頁表後在加上頁內地址就可以得到實體地址

7.5 三級Cache支援下的實體記憶體訪問

首先在實體地址中讀取出組索引CI位,通過組索引我們就可以得到載入該地址到cache中的具體位置,並可以根據行中的有效位判斷cache中是否有該地址的內容。若不命中則再向下一級的cache中進行尋找,查詢到資料後則會進行資料的放置。

7.6 hello程序fork時的記憶體對映

當fork函式被shell程序呼叫時,核心為新程序建立各種資料結構,並分配給它一個唯一的PID,為了給這個新程序建立虛擬記憶體,它建立了當前程序的mm_struct、區域結構和頁表的原樣副本。

7.7 hello程序execve時的記憶體對映

execve在當前程序中載入並執行包含在可執行目標檔案hello中的程式,用hello程式有效地替代了當前程式。載入並執行hello需要以下幾個步驟:
1.刪除已存在的使用者區域,刪除當前程序虛擬地址的使用者部分中的已存在的區域結構。
2.對映私有區域,為新程式的程式碼、資料、bss和棧區域建立新的區域結構,所有這些新的區域都是私有的、寫時複製的。程式碼和資料區域被對映為hello檔案中的.text和.data區,bss區域是請求二進位制零的,對映到匿名檔案,其大小包含在hello中,棧和堆地址也是請求二進位制零的,初始長度為零。
3.對映共享區域, hello程式與共享物件libc.so連結,libc.so是動態連結到這個程式中的,然後再對映到使用者虛擬地址空間中的共享區域內。
4.設定程式計數器(PC),execve做的最後一件事情就是設定當前程序上下文的程式計數器,使之指向程式碼區域的入口點。

7.8 缺頁故障與缺頁中斷處理

在執行一條指令時,如果發現他所需要訪問的頁沒有在記憶體中(即有效位為0),那麼停止該指令的執行,併產生一個頁不存在的異常,這個異常就是缺頁中斷。
缺頁中斷處理一般流程:
1.硬體陷入核心,在堆疊中儲存程式計數器,大多數當前指令的各種狀態資訊儲存在特殊的cpu暫存器中。
2.啟動一個彙編例程儲存通用暫存器和其他易丟失資訊,以免被作業系統破壞。
3.當作業系統發現缺頁中斷時,嘗試發現需要哪個虛擬頁面。通常一個硬體暫存器包含了這些資訊,如果沒有的話作業系統必須檢索程式計數器,取出當前指令,分析當前指令正在做什麼。
4.一旦知道了發生缺頁中斷的虛擬地址,作業系統會檢查地址是否有效,並檢查讀寫是否與保護許可權一致,不過不一致,則向程序發一個訊號或者殺死該程序。如果是有效地址並且沒有保護錯誤發生則系統檢查是否有空閒頁框。如果沒有,則執行頁面置換演算法淘汰頁面。
5.如果選擇的頁框髒了,則將該頁寫回磁碟,併發生一次上下文切換,掛起產生缺頁中斷的程序讓其他程序執行直到寫入磁碟結束。且回寫的頁框必須標記為忙,以免其他原因被其他程序佔用。
6.一旦頁框乾淨後,作業系統查詢所需頁面在磁碟上的地址,通過磁碟操作將其裝入,當頁面被裝入後,產生缺頁中斷的程序仍然被掛起,並且如果有其他可執行的使用者程序,則選擇另一使用者程序執行。
7.當磁碟中斷髮生時,表明該頁已經被裝入,頁表已經更新可以反映他的位置,頁框也標記位正常狀態。
8.恢復發生缺頁中斷指令以前的狀態,程式計數器重新指向這條指令。
9.排程引發缺頁中斷的程序,作業系統返回呼叫他的彙編例程
10.該例程恢復暫存器和其他狀態資訊,返回到使用者空間繼續執行,就好像缺頁中斷沒有發生過。

7.9動態儲存分配管理

實現動態記憶體分配往往要考慮以下四個問題:
(1)空閒塊組織:我們如何記錄空閒塊?
(2)選擇:我們如何選擇一個合適的空閒塊來作為一個新分配的塊?
(3)分割:在我們選了一個空閒塊分配出我們需要的大小之後,我們如何處理這個空閒塊中的剩餘部分?
(4)合併:我們如何處理一個剛剛被釋放的塊?
malloc的空閒連結串列機制:這是對問題(1)的解答。有兩種方法:顯式空閒連結串列、隱式空閒連結串列。
(1)顯式空閒連結串列:用一個連結串列將可用的記憶體塊連線為一個長長的列表,稱為空閒連結串列。將堆組織成雙向空閒連結串列,每一個空閒塊結點都包含一個祖先指標和一個後繼指標。連結串列中的每個結點記錄一個連續的、未分配的記憶體小塊。結點的結構很簡單,只需要記錄可用記憶體小塊的首地址和大小即可。
(2)隱式空閒連結串列:隱式空閒連結串列由N個塊組成,一個塊由頭部、有效載荷(只包括已分配的塊),以及可能的一些額外的填充(為了保證記憶體位元組對齊而需要的填充)和尾部組成。頭部大小為4個位元組,在強加雙字對齊的約束之後,塊大小總是8的倍數,所以用高29位來表示儲存塊大小,剩餘3位中利用最低位來表示塊是否已分配(1表示已分配,0表示空閒);尾部和頭部的內容一樣,加入尾部是為了分配器可以判斷出一個塊的起始位置和狀態。整個堆空間就是一個隱式空閒連結串列,從低地址向高地址生長,第一個和最後一個8位元組標記為已分配,以確定堆的大小。

空閒連結串列如何從中選擇分配記憶體塊?這是對問題(2)的解答。有下面四種選擇方法。
(1)首次適應法(First Fit):連結串列按塊地址排序。選擇第一個滿足要求的空閒塊。特點:低地址碎片多,高地址碎片少。
(2)最佳適應法(Best Fit):連結串列按空閒塊大小排序。選擇滿足要求的,且大小最小的空閒塊。特點:費時間,並且會出現很小的碎片。
(3)最壞適應法(Worst Fit):連結串列按空閒塊大小排序。選擇最大的空閒塊。特點:碎片少,容易缺乏大塊。
(4)迴圈首次適應法(Next Fit):連結串列按塊地址排序。從上次分配位置開始找到第一個滿足要求的空閒塊。特點:碎片分佈的又多又均勻。

如何處理被選空閒塊中的剩餘部分?這是對問題(3)的解答。一般來講,是要把剩餘的部分再插入回到空閒連結串列中去的。要注意一個空閒塊分割成兩個塊時,需要騰出若干位元組作為塊的頭部尾部等部分。

如何合併被釋放的塊?這是對問題(4)的解答。有兩種方法:立即合併、推遲合併。對於隱式空閒連結串列,合併的具體過程是,
(1)前後塊都已分配:直接釋放當前塊即可;
(2)前塊分配、後塊空閒:和後塊合併;
(3)前塊空閒、後塊分配:和前塊合併;
(4)前後塊都已空閒:和前後塊合併;

glibc對malloc的實現
目前最新版本為2.18,glibc原始碼目錄/glibc-2.18/malloc中可以看到。在glibc的malloc的實現中, 分配虛存有兩種系統呼叫可用: brk()和mmap(), 如果要分配大塊記憶體, glibc會使用mmap()去分配記憶體,這種記憶體靠近棧。
基於UNIX 的系統有兩個可對映到附加記憶體中的基本系統呼叫: brk: brk() 是一個非常簡單的系統呼叫。還記得系統中斷點嗎?該位置是程序對映的記憶體邊界。 brk()只是簡單地將這個位置向前或者向後移動,就可以向程序新增記憶體或者從程序取走記憶體。sbrk()是以增量的方式增加擴大記憶體。
mmap: mmap(),或者說是“記憶體映像”,類似於 brk(),但是更為靈活。首先,它可以對映任何位置的記憶體,而不單單隻侷限於程序。其次,它不僅可以將虛擬地址對映到物理的
RAM 或者 swap,它還可以將它們對映到檔案和檔案位置,這樣,讀寫記憶體將對檔案中的資料進行讀寫。不過在這裡,我們只關心 mmap向程序新增被對映的記憶體的能力。
在glibc的malloc的實現有一個優化:

  1. 當malloc()一塊很小的記憶體是, glib
    c呼叫brk(), 只需要在heap中移動一下指標, 即可獲得可用虛存, 這樣分配得到的地址較小.
  2. 當malloc()一塊較大記憶體時, glibc呼叫mmap(), 需要在核心中重新分配vma結構等, 他會在靠近棧的地方分配虛存, 這樣返回的地址大.

7.10本章小結

在本章的過程之中我們瞭解了hello的儲存器地址空間、intel的段式管理、hello的頁式管理,以intel Core7在的四級頁表的條件下介紹了VA到PA的變換、實體記憶體訪問,還介紹了hello程序fork時的記憶體對映、execve時的記憶體對映、缺頁故障與缺頁中斷處理、動態儲存分配管理等方面的內容。

		  	(第7章 2分)

第8章 hello的IO管理

8.1 Linux的IO裝置管理方法

裝置的模型化:檔案
裝置管理:unix io介面
作業系統對IO裝置的管理主要分為三部分:邏輯IO,裝置驅動程式,中斷服務程式
裝置驅動程式:完成了對不同裝置的各種各樣的控制,對應用層提供介面。
中斷服務程式:當裝置結束的時候,向cpu發出中斷訊號。

8.2 簡述Unix IO介面及其函式

Unix I/O介面統一操作:
1).開啟檔案。一個應用程式通過要求核心開啟相應的檔案,來宣告它想要訪問一個I/O裝置,核心返回一個小的非負整數,叫做描述符,它在後續對此檔案的所有操作中標識這個檔案,核心記錄有關這個開啟檔案的所有資訊。
2).Shell建立的每個程序都有三個開啟的檔案:標準輸入,標準輸出,標準錯誤。
3) .改變當前的檔案位置:對於每個開啟的檔案,核心保持著一個檔案位置k,初始為0,這個檔案位置是從檔案開頭起始的位元組偏移量,應用程式能夠通過執行seek,顯式地將改變當前檔案位置k。
4).讀寫檔案:一個讀操作就是從檔案複製n>0個位元組到記憶體,從當前檔案位置k開始,然後將k增加到k+n,給定一個大小為m位元組的而檔案,當k>=m時,觸發EOF。類似一個寫操作就是從記憶體中複製n>0個位元組到一個檔案,從當前檔案位置k開始,然後更新k。
5).關閉檔案,核心釋放檔案開啟時建立的資料結構,並將這個描述符恢復到可用的描述符池中去。

Unix I/O函式:
1).int open(char* filename,int flags,mode_t mode) ,程序通過呼叫open函式來開啟一個存在的檔案或是建立一個新檔案的。open函式將filename轉換為一個檔案描述符,並且返回描述符數字,返回的描述符總是在程序中當前沒有開啟的最小描述符,flags引數指明瞭程序打算如何訪問這個檔案,mode引數指定了新檔案的訪問許可權位。
2).int close(fd),fd是需要關閉的檔案的描述符,close返回操作結果。
3).ssize_t read(int fd,void *buf,size_t n),read函式從描述符為fd的當前檔案位置賦值最多n個位元組到記憶體位置buf。返回值-1表示一個錯誤,0表示EOF,否則返回值表示的是實際傳送的位元組數量。
4).ssize_t wirte(int fd,const void *buf,size_t n),write函式從記憶體位置buf複製至多n個位元組到描述符為fd的當前檔案位置

8.3 printf的實現分析

研究printf的實現,首先來看看printf函式的函式體
int printf(const char *fmt, …)
{
int i;
char buf[256];

va_list arg = (va_list)((char*)(&fmt) + 4);
i = vsprintf(buf, fmt, arg);
write(buf, i);

return i;
}
首先用vsprintf進行格式化。它接受確定輸出格式的格式字串fmt。用格式字串對個數變化的引數進行格式化,產生格式化輸出。

然後在write函式中,將棧中引數放入暫存器,ecx是字元個數,ebx存放第一個字元地址,int INT_VECTOR_SYS_CALLA代表通過系統呼叫syscall

syscall將字串中的位元組從暫存器中通過匯流排複製到顯示卡的視訊記憶體中,視訊記憶體中儲存的是字元的ASCII碼。字元顯示驅動子程式將通過ASCII碼在字模庫中找到點陣資訊將點陣資訊儲存到vram中。顯示晶片會按照一定的重新整理頻率逐行讀取vram,並通過訊號線向液晶顯示器傳輸每一個點(RGB分量)

最終就在螢幕上出現了我們所需要的字串

8.4 getchar的實現分析

非同步異常-鍵盤中斷的處理:鍵盤中斷處理子程式。接受按鍵掃描碼轉成ascii碼,儲存到系統的鍵盤緩衝區。
getchar等呼叫read系統函式,通過系統呼叫讀取按鍵ascii碼,直到接受到回車鍵才返回。

8.5本章小結

在本章的過程之中我們瞭解到在hello中所呼叫的printf和getchar函式,實際上是對系統的Unix I/O的封裝,而真正呼叫的是write和read這樣的系統呼叫函式。

		(第8章1分)

結論

Hello的P2P和O2O的過程中要經歷以下階段
1.程式的編寫 將hello文字檔案用C語言的程式碼編寫好
2. 程式的預處理 將hello中的#開頭的語句進行處理,並整合到檔案hello.i中
3. 程式的編譯 將hello.i檔案翻譯成組合語言檔案hello.s
4. 程式的彙編 將hello.s翻譯成可重定位目標檔案hello.o
5. 程式的連結 將hello.o與動態共享庫進行連結整合成可執行目標檔案hello.out
6. 程式的執行 在終端中輸入./hello 1170300219 liuxuanlin以執行程式
7. 程序的建立 利用fork函式建立hello的子程序
8. 程序的載入 利用execve函式進行hello的載入
9. 時間片的分配 hello在分配的時間片中享有CPU資源,順序執行自己的控制邏輯流
10. 記憶體的訪問 hello將虛擬地址通過段式管理和頁式管理獲得實體地址,並進行訪問
11. cache的記憶體使用 hello通過cache訪問記憶體
12. 動態記憶體分配 hello在printf中呼叫malloc進行動態記憶體申請
13. 程序控制 hello呼叫sleep函式是程序掛起一段時間
14. 程序終止 hello通過父程序回收程序並結束執行

(結論0分,缺失 -1分,根據內容酌情加分)

附件

列出所有的中間產物的檔名,並予以說明起作用。
hello.i 經過預處理後的hello
hello.s 經過編譯後的組合語言檔案
hello.o 經過彙編後的可重定位目標檔案
hello.out 經過連線後生成的課執行目標檔案
asm1.txt hello.o經過objdump反彙編後的輸出
asm2.txt hello.out經過objdump反彙編後的輸出
elf1.txt hello.o使用readelf讀取出的elf的輸出
elf2.txt hello.out使用readelf讀取出的elf的輸出

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

參考文獻

[1]深入理解計算機系統 蘭德爾 E.布萊恩特 大衛 R.奧哈拉倫 機械工業出版社
[2] gcc–編譯的四大過程及作用
https://blog.csdn.net/shiyongraow/article/details/81454995
[3] 程式的連結
https://blog.csdn.net/weixin_42749767/article/details/83901326
[4] 記憶體管理筆記(分頁,分段,邏輯地址,實體地址與地址轉換方式)
https://www.cnblogs.com/felixfang/p/3420462.html
[5] linux核心缺頁中斷處理
https://blog.csdn.net/a7980718/article/details/80895302
[6]頁式儲存管理(不連續儲存)
https://blog.csdn.net/shuxnhs/article/details/80785451
[7] linux下邏輯地址-線性地址-實體地址圖解
https://blog.csdn.net/l_nan/article/details/51188290
[8] 程序的掛起、阻塞和睡眠
https://www.cnblogs.com/ck1020/p/6669661.html

(參考文獻0分,缺失 -1分)