哈工大 计算机系统大作业 程序人生-Hello’s P2P From Program to Process
計(jì)算機(jī)系統(tǒng)
?
大作業(yè)
題 ????目 ?程序人生-Hello’s P2P?
專?????? 業(yè) ??計(jì)算學(xué)部?????????????
學(xué) ?? 號(hào) ??120L020512??????????
班 ?? 級(jí) ??2003004?????????????
學(xué)?????? 生 ? ? ? 黃鵬程? ? ? ??
指 導(dǎo) 教 師 ????史先俊?????????
計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院
2022年5月
摘? 要
??? 本次大作業(yè)旨在通過(guò)對(duì)hello程序生命歷程中各個(gè)環(huán)節(jié)的實(shí)驗(yàn)與分析,將計(jì)算機(jī)系統(tǒng)課程的整體知識(shí)進(jìn)行串聯(lián)與復(fù)現(xiàn),從而加深對(duì)課程內(nèi)容的理解。
關(guān)鍵詞:計(jì)算機(jī);匯編;進(jìn)程;存儲(chǔ)管理;??????????????????????
目? 錄
第1章 概述................................................................................................................ - 4 -
1.1 Hello簡(jiǎn)介......................................................................................................... - 4 -
1.2 環(huán)境與工具........................................................................................................ - 4 -
1.3 中間結(jié)果............................................................................................................ - 4 -
1.4 本章小結(jié)............................................................................................................ - 5 -
第2章 預(yù)處理............................................................................................................ - 6 -
2.1 預(yù)處理的概念與作用........................................................................................ - 6 -
2.2在Ubuntu下預(yù)處理的命令............................................................................. - 6 -
2.3 Hello的預(yù)處理結(jié)果解析................................................................................. - 6 -
2.4 本章小結(jié)............................................................................................................ - 7 -
第3章 編譯................................................................................................................ - 9 -
3.1 編譯的概念與作用............................................................................................ - 9 -
3.2 在Ubuntu下編譯的命令................................................................................ - 9 -
3.3 Hello的編譯結(jié)果解析..................................................................................... - 9 -
3.4 本章小結(jié).......................................................................................................... - 14 -
第4章 匯編.............................................................................................................. - 15 -
4.1 匯編的概念與作用.......................................................................................... - 15 -
4.2 在Ubuntu下匯編的命令.............................................................................. - 15 -
4.3 可重定位目標(biāo)elf格式.................................................................................. - 15 -
4.4 Hello.o的結(jié)果解析........................................................................................ - 17 -
4.5 本章小結(jié).......................................................................................................... - 18 -
第5章 鏈接.............................................................................................................. - 19 -
5.1 鏈接的概念與作用.......................................................................................... - 19 -
5.2 在Ubuntu下鏈接的命令.............................................................................. - 19 -
5.3 可執(zhí)行目標(biāo)文件hello的格式...................................................................... - 19 -
5.4 hello的虛擬地址空間................................................................................... - 21 -
5.5 鏈接的重定位過(guò)程分析.................................................................................. - 22 -
5.6 hello的執(zhí)行流程........................................................................................... - 24 -
5.7 Hello的動(dòng)態(tài)鏈接分析................................................................................... - 25 -
5.8 本章小結(jié).......................................................................................................... - 26 -
第6章 hello進(jìn)程管理....................................................................................... - 27 -
6.1 進(jìn)程的概念與作用.......................................................................................... - 27 -
6.2 簡(jiǎn)述殼Shell-bash的作用與處理流程........................................................ - 27 -
6.3 Hello的fork進(jìn)程創(chuàng)建過(guò)程......................................................................... - 28 -
6.4 Hello的execve過(guò)程..................................................................................... - 28 -
6.5 Hello的進(jìn)程執(zhí)行........................................................................................... - 28 -
6.6 hello的異常與信號(hào)處理............................................................................... - 29 -
6.7本章小結(jié).......................................................................................................... - 31 -
第7章 hello的存儲(chǔ)管理................................................................................... - 32 -
7.1 hello的存儲(chǔ)器地址空間............................................................................... - 32 -
7.2 Intel邏輯地址到線性地址的變換-段式管理............................................... - 32 -
7.3 Hello的線性地址到物理地址的變換-頁(yè)式管理......................................... - 33 -
7.4 TLB與四級(jí)頁(yè)表支持下的VA到PA的變換................................................ - 34 -
7.5 三級(jí)Cache支持下的物理內(nèi)存訪問(wèn)............................................................. - 34 -
7.6 hello進(jìn)程fork時(shí)的內(nèi)存映射..................................................................... - 35 -
7.7 hello進(jìn)程execve時(shí)的內(nèi)存映射................................................................. - 36 -
7.8 缺頁(yè)故障與缺頁(yè)中斷處理.............................................................................. - 36 -
7.9動(dòng)態(tài)存儲(chǔ)分配管理.......................................................................................... - 36 -
7.10本章小結(jié)........................................................................................................ - 37 -
第8章 hello的IO管理.................................................................................... - 38 -
8.1 Linux的IO設(shè)備管理方法............................................................................. - 38 -
8.2 簡(jiǎn)述Unix IO接口及其函數(shù).......................................................................... - 38 -
8.3 printf的實(shí)現(xiàn)分析........................................................................................... - 38 -
8.4 getchar的實(shí)現(xiàn)分析....................................................................................... - 40 -
8.5本章小結(jié).......................................................................................................... - 40 -
結(jié)論............................................................................................................................ - 40 -
附件............................................................................................................................ - 42 -
參考文獻(xiàn).................................................................................................................... - 43 -
第1章 概述
1.1 Hello簡(jiǎn)介
P2P:
?????? 如圖1.11,為c語(yǔ)言代碼源文件,即hello.c變成可執(zhí)行文件hello的過(guò)程。預(yù)處理器對(duì)源文件進(jìn)行宏替換、條件編譯的預(yù)處理操作后,生成hello.i文件;.i文件檢查語(yǔ)法后生成匯編文件hello.s;匯編文件經(jīng)過(guò)匯編被轉(zhuǎn)換為機(jī)器碼,生成可重定位文件hello.o;然后連接器將源代碼中用到的庫(kù)函數(shù)與可重定位文件合并為可執(zhí)行文件hello;我們?cè)趕hell中鍵入命令后,其fork子進(jìn)程、調(diào)用execve加載hello并運(yùn)行。
?
?????????????????????? 圖1.11 ?P2P過(guò)程
O2O:
?????? 在shell中fork子進(jìn)程后調(diào)用execve加載并執(zhí)行hello,分配虛擬內(nèi)存空間并映射到物理內(nèi)存;隨后依照CPU中邏輯控制流開(kāi)始執(zhí)行;在程序結(jié)束后,shell通過(guò)hello父進(jìn)程或祖先進(jìn)程將其回收,釋放內(nèi)存空間。
1.2 環(huán)境與工具
硬件環(huán)境:處理器Intel(R) Core(TM) i5-8300H CPU @ 2.30GHz?? 2.30 GHz;RAM 8GB; 系統(tǒng)類型:系統(tǒng)類型:64位操作系統(tǒng),基于x64的處理器;
????????????????????
軟件環(huán)境:Windows10 64位;Ubuntu 20.04
開(kāi)發(fā)與調(diào)試工具:gcc,as,ld,vim,edb,readelf,VS
1.3 中間結(jié)果
hello.i:預(yù)處理得到的文本文件
hello.s:編譯得到的匯編文件
hello.o:匯編得到的可重定位目標(biāo)文件
hello:鏈接得到的可執(zhí)行文件
objdump_hello.s:hello反匯編得到的代碼
1.4 本章小結(jié)
本章在對(duì)P2P、O2O的介紹中概括了hello從誕生到執(zhí)行再到死亡的過(guò)程;給出本次作業(yè)的實(shí)驗(yàn)環(huán)境與用到的中間結(jié)果。
第2章 預(yù)處理
2.1 預(yù)處理的概念與作用
概念:
程序設(shè)計(jì)領(lǐng)域中,預(yù)處理一般是指在程序源代碼被翻譯為目標(biāo)代碼的過(guò)程中,生成二進(jìn)制代碼之前的過(guò)程。預(yù)處理中會(huì)展開(kāi)以#起始的行,試圖解釋為預(yù)處理指令。包括#if/#ifdef/#ifndef/#else/#elif/#endif(條件編譯)、#define(宏定義)、#include(源文件包含)、#line(行控制)、#error(錯(cuò)誤指令)、#pragma(和實(shí)現(xiàn)相關(guān)的雜注)以及單獨(dú)的#(空指令)。預(yù)處理指令一般被用來(lái)使源代碼在不同的執(zhí)行環(huán)境中被方便的修改或者編譯。
作用:
將源文件中用#include形式聲明的文件復(fù)制至新的程序中;
用實(shí)際值替換用#define 定義的字符串,即將宏定義進(jìn)行替換;
2.2在Ubuntu下預(yù)處理的命令
gcc -m64 -no-pie -fno-PIC hello.c -E -o hello.i如下圖2.21,生成文件如圖2.22;
?
?
??????????????? ?????圖2.21 執(zhí)行命令
?????????? ?圖2.22 生成文件
2.3 Hello的預(yù)處理結(jié)果解析
打開(kāi)hello.i,發(fā)現(xiàn)程序已經(jīng)擴(kuò)展為3060行,如圖2.31,hello.c中main函數(shù)代碼出現(xiàn)在3047行以后,在此之前的代碼為.c源文件中含有三個(gè)庫(kù):#include <stdio.h>、#include <unistd.h>、#include <stdlib.h>的展開(kāi),將頭文件的內(nèi)容插入到該命令所在的位置,從而把頭文件和當(dāng)前源文件連接成一個(gè)源文件,如圖2.32。
?
?
?? ????????????????圖2.31 main函數(shù)
?
????????????????? 圖2.32 預(yù)處理#include
2.4 本章小結(jié)
本章使用gcc -m64 -no-pie -fno-PIC hello.c -E -o hello.i將hello.c預(yù)處理為hello.i,發(fā)現(xiàn)hello.i中插入了大量源文件包含的庫(kù)文件
第3章 編譯
3.1 編譯的概念與作用
概念:
編譯是指編譯器做詞法分析、語(yǔ)法分析、語(yǔ)義分析等,在檢查無(wú)錯(cuò)誤后,將代碼翻譯成匯編語(yǔ)言的過(guò)程。?編譯器將文本文件 hello.i 翻譯成文本文件 hello.s。
作用:
注意:這兒的編譯是指從 .i 到 .s 即預(yù)處理后的文件到生成匯編語(yǔ)言程序
???????
3.2 在Ubuntu下編譯的命令
gcc -m64 -no-pie -fno-PIC -S hello.i -o hello.s
?
????????????????? 圖3.21 編譯的命令
?
?????????????????? 圖3.22 生成hello.s
3.3 Hello的編譯結(jié)果解析
3.3.1 數(shù)據(jù)
數(shù)字:如圖3.11,3.12源文件中的常量4與8被作為立即數(shù)保存在圖3.13、3.14中,hello.s的代碼節(jié).data中;
?
??????????????????? ??圖 3.11
圖 3.12
?
?
???? ?圖 3.13 立即數(shù)4
?
?圖 3.14 立即數(shù)7(借助7來(lái)判斷小于8)
字符串:如圖3.15,源文件中的字符串"用法: Hello 學(xué)號(hào) 姓名 秒數(shù)!\n"被保存在.rodata節(jié)中,因?yàn)槠錇橹蛔x字符串;
?
????????????????????????????????? ?? 圖 3.15 保存在.rodata的字符串
局部變量:源文件中使用局部變量i:
???????????????????????????????????????????????? for ( i = 0; i < 8 ; i++ )
被保存在棧中%rsp-4位置,如圖3.16,該位置每次加1后與7進(jìn)行比較,依此決定是否再次進(jìn)入循環(huán)。
?
圖 3.16 寄存器中的局部變量i
3.3.2 賦值
上文中,局部變量i被賦初值為0,而我們已經(jīng)知道了它的存儲(chǔ)位置,故容易找到其在hello.s中的賦值語(yǔ)句,如圖3.21
?
圖 3.21 ?i賦初值為0
????????????
3.3.2 算術(shù)操作
對(duì)于上文提到的循環(huán),步長(zhǎng)為1,每次i自增1,易找到其在hello.s中的操作如圖3.22
?
???? ??圖 3.22 ?i++
3.3.3 關(guān)系操作、控制轉(zhuǎn)移
源文件中出現(xiàn)了兩次關(guān)系判斷,如圖3.31中的13行、17行:
?
?????????? ??圖 3.31
第13行在hello.s中對(duì)應(yīng)操作如圖3.32,為argc與4進(jìn)行比較,若相等則進(jìn)行跳轉(zhuǎn)操作:
?
??? 圖 3.32 判斷相等與跳轉(zhuǎn)操作
上圖中,24行為argc與4進(jìn)行比較,比較的結(jié)果保存在寄存器中,25行je根據(jù)比較結(jié)果決定是否跳轉(zhuǎn)到.L2;
?
? 圖 3.33 ?循環(huán)中i的比較與跳轉(zhuǎn)操作
3.3.4 數(shù)組/指針/結(jié)構(gòu)操作
指針數(shù)組char *argv[ ] 首地址保存在-32(%rbp)位置,如圖3.41,print函數(shù)打印argv[1]與argv[2],則在第35、38行分別將數(shù)組首地址加上偏移獲得數(shù)組元素;
?
???????????? 圖 3.41
3.3.5 函數(shù)操作
參數(shù)傳遞:第1~6個(gè)參數(shù)儲(chǔ)存在%rdi、%rsi、%rdx、%rcx、%r8、%r9這六個(gè)寄存器中,剩下的參數(shù)保存在棧當(dāng)中。
調(diào)用函數(shù):每個(gè)函數(shù)的每次調(diào)用,都有它自己獨(dú)立的一個(gè)棧幀,這個(gè)棧幀中維持著所需要的各種信息。寄存器%ebp指向當(dāng)前的棧幀的底部(高地址),寄存器%esp指向當(dāng)前的棧幀的頂部(低地址)。調(diào)用函數(shù)的棧底將會(huì)被保存,而棧頂將作為被調(diào)用函數(shù)的棧底。
函數(shù)返回:函數(shù)返回值保存在%ax中。
下對(duì)hello.s中函數(shù)分析:
參數(shù)傳遞:傳入?yún)?shù)argc和argv[],分別用寄存器%rdi和%rsi存儲(chǔ)
函數(shù)調(diào)用:C程序總是從mian函數(shù)開(kāi)始執(zhí)行
函數(shù)返回:結(jié)束時(shí)更改%eax為0
?
??? 圖 3.51 main入口
?
??? 圖 3.52 main出口
參數(shù)傳遞: call printf時(shí)傳入了 argv[1]和argc[2]的地址
?
圖 3.53 調(diào)用printf時(shí)傳入的argv[1]和
argc[2]保存在%rdx、%rsi中
參數(shù)傳遞:將%edi 設(shè)置為 1
參數(shù)傳遞:將%rdi 設(shè)置為 argv[3]
參數(shù)傳遞:將%edi 設(shè)置為atoi處理后的argv[3]
參數(shù)傳遞:將%edi 設(shè)置為 1,執(zhí)行exit(1)
3.4 本章小結(jié)
編譯是指編譯器做詞法分析、語(yǔ)法分析、語(yǔ)義分析等,在檢查無(wú)錯(cuò)誤后,將代碼翻譯成匯編語(yǔ)言的過(guò)程。本章對(duì)hello.i編譯后得到的hello.s進(jìn)行分析,探究了編譯器處理C語(yǔ)言的各個(gè)數(shù)據(jù)類型以及各類操作的過(guò)程。
第4章 匯編
4.1 匯編的概念與作用
概念:把匯編語(yǔ)言翻譯成機(jī)器語(yǔ)言的過(guò)程稱為匯編。
作用:用匯編語(yǔ)言編寫(xiě)的程序,機(jī)器不能直接識(shí)別,要由一種程序?qū)R編語(yǔ)言翻譯成機(jī)器語(yǔ)言,這種起翻譯作用的程序叫匯編程序。
4.2 在Ubuntu下匯編的命令
gcc -m64 -no-pie -fno-PIC hello.s -c -o hello.o
?
???????????????? 圖4.21 匯編的命令
?
?????????????? 圖 4.22 匯編得到hello.o
4.3 可重定位目標(biāo)elf格式
readelf -a hello.o > helloo.elf 生成文本文件
ELF頭:
以16B的序列 Magic 開(kāi)始,Magic描述了生成該文件的系統(tǒng)的字的大小和字節(jié)順序,ELF頭剩下的部分包含幫助鏈接器語(yǔ)法分析和解釋目標(biāo)文件的信息,其中包括 ELF 頭的大小、目標(biāo)文件的類型、機(jī)器類型、 字節(jié)頭部表(section header table)的文件偏移,及節(jié)頭部表中條目的大小和數(shù)量等信息。根據(jù)頭文件的信息,可以知道該文件是可重定位目標(biāo)文件,有14個(gè)節(jié)。
?
????????? ????圖 4.31 ELF頭
節(jié)頭:
?????? 描述了.o文件中出現(xiàn)的各個(gè)節(jié)的類型、位置、所占空間大小等信息。
?
?????????????? ?圖 4.32 節(jié)頭
重定位節(jié):
?????? 當(dāng)匯編器生成一個(gè)目標(biāo)模塊時(shí),它并不知道數(shù)據(jù)和代碼最終將放在內(nèi)存中的什么位置。它也不知道這個(gè)模塊引用的任何外部定義的函數(shù)或者全局變量的位置。所以,無(wú)論何時(shí)匯編器遇到最終未知未知的目標(biāo)引用,它就會(huì)生成一個(gè)重定位條目,告訴鏈接器在將目標(biāo)文件合并到可執(zhí)行文件時(shí)如何修改新的引用。
?????? 本程序需要重定位的信息有:.rodata中的模式串,puts,exit,printf,slepsecs,sleep,getchar這些符號(hào)。
????????????????? ??圖 4.33 重定位節(jié)
符號(hào)表:
?????? 符號(hào)表中存放程序定義和引用的函數(shù)和全局變量的信息。但其中不包含局部變量條目。
?
?
??????????????? 圖 4.34 符號(hào)表
??? 分析hello.o的ELF格式,用readelf等列出其各節(jié)的基本信息,特別是重定位項(xiàng)目分析。
4.4 Hello.o的結(jié)果解析
?
?
???????????????????????? ?????? 圖 4.41 hello.o反匯編
將其與hello.s進(jìn)行對(duì)照分析:
- 操作數(shù):hello.s中操作數(shù)為十進(jìn)制,而反匯編代碼中為十六進(jìn)制;
- 分支轉(zhuǎn)移:hello.s中地址使用段名稱如 je .L2,而反匯編代碼中則使用相對(duì)偏移地址,如 je 2d <main+0x2d>;
- 函數(shù)調(diào)用:hello.s中,call指令使用的是函數(shù)名稱,反匯編代碼中call指令使用相對(duì)偏移地址。原因是hello.s中調(diào)用的函數(shù)都是共享庫(kù)中的函數(shù),故需要通過(guò)等待調(diào)用動(dòng)態(tài)鏈接將重定位的函數(shù)目標(biāo)地址鏈接到共享庫(kù)程序中,最終需通過(guò)動(dòng)態(tài)鏈接器確定函數(shù)的運(yùn)行時(shí)地址;
- 除上述以外,二者沒(méi)有什么不同,這表明了匯編語(yǔ)言能與機(jī)器碼建立一一對(duì)應(yīng)的關(guān)系。
4.5 本章小結(jié)
把匯編語(yǔ)言翻譯成機(jī)器語(yǔ)言的過(guò)程稱為匯編,hello.s匯編得到hello.o后,我們閱讀了ELF文件,然后通過(guò)hello.o反匯編得到的代碼與hello.s進(jìn)行比較,發(fā)現(xiàn)機(jī)器碼與匯編語(yǔ)言的映射關(guān)系,以及它相對(duì)匯編語(yǔ)言所具有的特點(diǎn)。
第5章 鏈接
5.1 鏈接的概念與作用
概念:鏈接是將各種代碼和數(shù)據(jù)片段收集并組合成一個(gè)單一文件的過(guò)程,這個(gè)文件可被加載(復(fù)制)到內(nèi)存并執(zhí)行。
作用:節(jié)省源程序空間;將可重定位文件連接成為可執(zhí)行文件。
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.21 鏈接的命令
?
?? ?圖 5.22 生成可執(zhí)行文件
5.3 可執(zhí)行目標(biāo)文件hello的格式
?ELF文件頭:
??? ?????????
?
??????????????????????? 圖5.31 ELF文件頭
節(jié)頭:
節(jié)頭部表中包含了hello中所有節(jié)的信息,其中包括名稱、類型、大小、地址和偏移量等信息,其中地址為程序被載入到虛擬地址的起始地址,偏移量為各個(gè)節(jié)在程序中的偏移量。根據(jù)節(jié)頭部表的信息可以使用HexEdit定位各個(gè)節(jié)的起始位置及大小。
?
???? ????圖5.32 節(jié)頭
程序頭:
?????? elf可執(zhí)行文件易加載到內(nèi)存,可執(zhí)行文件連續(xù)的片被映射到連續(xù)的內(nèi)存段,程序頭部表描述了這一映射關(guān)系。程序頭部表包括各程序頭的偏移量、內(nèi)存地址、對(duì)其要求、目標(biāo)文件與內(nèi)存中的段大小及運(yùn)行時(shí)訪問(wèn)權(quán)限等信息。
????? ??????圖5.33 程序頭
重定位節(jié):
?????? 重定位節(jié)包含.text節(jié)中一些需對(duì)目標(biāo)進(jìn)行重定位的函數(shù)信息,鏈接器把函數(shù)的目標(biāo)位置文件與其他目標(biāo)文件組合在一起時(shí),需要修改這些函數(shù)的位置。
?
?????????? ?????圖5.34 重定位節(jié)
5.4 hello的虛擬地址空間
在edb的memory regions窗口中,可以看到hello的虛擬地址空間,如圖5.41,由0x400000到0x405000;
?
???????????????????????????????????????????????? 圖5.41 memory regions窗口
edb加載hello后, Data Dump 窗口可以查看加載到虛擬地址中的 hello 程序,如圖5.42;
?
?????????? ??圖5.42 Data Dump 窗口
根據(jù)5.3中的節(jié)頭部表中的地址,可在edb中找到各個(gè)節(jié)。例如:.text節(jié)的地址為0x4010f0,大小為0x145,用edb查找結(jié)果如圖5.43;
?
??????????? ????圖5.43
.data節(jié)的地址為0x404040,大小為0x4,如圖5.44
?
??????? 圖5.44
.rodata節(jié)的地址為0x402000,大小為0x3b,如圖5.45
??????????????? 圖5.45
5.5 鏈接的重定位過(guò)程分析
使用命令:objdump -d -r hello >helloobjdump.txt,獲得hello的反匯編代碼,如圖5.51;
?
?????????? ?????圖5.51 hello反匯編
對(duì)照hello與hello.o,不同點(diǎn)有:
?
?????? 圖5.52 ?hello的main函數(shù)
?
?????????????????????????? ??? ????? 圖5.53? .init節(jié).plt段
鏈接過(guò)程:
?????? 經(jīng)以上分析可知,鏈接就是將多個(gè)可重定位目標(biāo)文件合并到一起,生成可執(zhí)行文件。鏈接需要進(jìn)行符號(hào)解析、重定位及計(jì)算符號(hào)引用地址三個(gè)步驟。
重定位:
重定位將合并輸入模塊。并為每個(gè)符號(hào)分配運(yùn)行地址。重定位由兩個(gè)步驟組成:重定位節(jié)與符號(hào)定義、重定位節(jié)中的符號(hào)引用。
定位節(jié)與符號(hào)定義,鏈接器將相同類型的節(jié)合并為同一類型的新的聚合節(jié),此后鏈接器將運(yùn)行時(shí)內(nèi)存地址賦值新的聚合節(jié)、輸入模塊定義的每個(gè)節(jié),還有輸入模塊定義的每個(gè)符號(hào)。
重定位節(jié)中的符號(hào)引用,鏈接器修改代碼節(jié)與數(shù)據(jù)節(jié)中對(duì)每個(gè)符號(hào)的引用,使他們指向正確的運(yùn)行地址,這一步依賴hello.o中的重定位條目。
5.6 hello的執(zhí)行流程
Edb逐步執(zhí)行并記錄調(diào)用的函數(shù),如圖5.61
?
???????????? 圖5.61 即將調(diào)用hello!_init
得到調(diào)用函數(shù)順序如下:
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
-libc-2.27.so!__libc_csu_init
hello!_init
libc-2.27.so!_setjmp
-libc-2.27.so!_sigsetjmp
–libc-2.27.so!__sigjmp_save
hello!main
hello!puts@plt
hello!exit@plt
hello!printf@plt
hello!atoi@plt
hello!sleep@plt
hello!getchar@plt
ld-2.27.so!_dl_runtime_resolve_xsave -ld-2.27.so!_dl_fixup
–ld-2.27.so!_dl_lookup_symbol_x
libc-2.27.so!exit
5.7 Hello的動(dòng)態(tài)鏈接分析
在elf文件中可以找到動(dòng)態(tài)鏈接調(diào)用的函數(shù)的位置,如圖5.71:
?
??????????????? ?圖5.71 函數(shù)位置
??????????
動(dòng)態(tài)鏈接調(diào)用的函數(shù)的位置為0x404000,進(jìn)入edb內(nèi)存窗口查看:
?
圖5.71 ?init之前
?
圖5.72 ?init之后
對(duì)于變量而言,利用代碼段和數(shù)據(jù)段的相對(duì)位置不變的原則計(jì)算得到正確地址。對(duì)于庫(kù)函數(shù)而言,需要plt與got合作,plt初始存的是一批代碼,它們跳轉(zhuǎn)到got所指示位置,接著調(diào)用鏈接器。初始時(shí)got里面存的都為plt的第二條指令,隨后鏈接器會(huì)修改got,當(dāng)下一次再次調(diào)用plt時(shí),指向的就是正確的內(nèi)存地址。plt就能跳轉(zhuǎn)到正確的區(qū)域。
5.8 本章小結(jié)
本章分析了hello的鏈接過(guò)程,hello.o經(jīng)過(guò)鏈接生成可執(zhí)行文件hello,通過(guò)對(duì)比hello反匯編文件與hello.o之間的差別,我們可以總結(jié)出重定位的一些特點(diǎn)。在edb中逐步調(diào)試hello,我們能看到hello逐個(gè)調(diào)用的函數(shù)。
第6章 hello進(jìn)程管理
6.1 進(jìn)程的概念與作用
概念:
進(jìn)程是計(jì)算機(jī)中的程序關(guān)于某數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ)。在早期面向進(jìn)程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是程序的基本執(zhí)行實(shí)體;在當(dāng)代面向線程設(shè)計(jì)的計(jì)算機(jī)結(jié)構(gòu)中,進(jìn)程是線程的容器。程序是指令、數(shù)據(jù)及其組織形式的描述,進(jìn)程是程序的實(shí)體。
作用:
(1) 給程序創(chuàng)造這樣的假象: 我們的程序好像是系統(tǒng)中當(dāng)前運(yùn)行的唯一程序一樣,我們的程序好像是獨(dú)占的使用處理器和內(nèi)存。
(2) 處理器好像是無(wú)間斷的執(zhí)行我們程序中的指令,我們程序中的代碼和數(shù)據(jù)好像是系統(tǒng)內(nèi)存中唯一的對(duì)象。
6.2 簡(jiǎn)述殼Shell-bash的作用與處理流程
作用:shell是系統(tǒng)的用戶界面,它接收并解釋用戶輸入的命令,再將其送入內(nèi)核執(zhí)行。
處理流程:
若為內(nèi)置命令則立即執(zhí)行;
否則將命令行的參數(shù)改造為系統(tǒng)調(diào)用execve()內(nèi)部處理所要求的形式終端進(jìn)程調(diào)用fork()來(lái)創(chuàng)建子進(jìn)程,自身則用系統(tǒng)調(diào)用wait()來(lái)等待子進(jìn)程完成;
6.3 Hello的fork進(jìn)程創(chuàng)建過(guò)程
父進(jìn)程在讀取命令后,首先判斷該命令是否為內(nèi)置命令,若非內(nèi)置命令,則會(huì)調(diào)用fork命令創(chuàng)建子進(jìn)程。子進(jìn)程除PID外,與父進(jìn)程完全一致,獲得與父進(jìn)程虛擬地址空間相同但獨(dú)立的副本,其用戶棧、寄存器、代碼段等也與父進(jìn)程一致,子進(jìn)程可以讀寫(xiě)父進(jìn)程打開(kāi)的任何文件。Fork函數(shù)在父進(jìn)程中返回子進(jìn)程PID,在子進(jìn)程中則返回0。
6.4 Hello的execve過(guò)程
當(dāng)創(chuàng)建了一個(gè)新運(yùn)行的子進(jìn)程后,子進(jìn)程調(diào)用execve函數(shù)在當(dāng)前子進(jìn)程的上下文中加載并運(yùn)行hello程序。execve函數(shù)加載并運(yùn)行可執(zhí)行目標(biāo)文件hello,且?guī)?shù)列表argv和環(huán)境變量列表envp。只有當(dāng)出現(xiàn)錯(cuò)誤時(shí),例如找不到hello,execve才會(huì)返回到調(diào)用程序。所以execve調(diào)用一次且從不返回。
argv是一個(gè)參數(shù)字符串指針數(shù)組,argv[0]是可執(zhí)行目標(biāo)文件的名字,而envp的元素則指向環(huán)境變量。
6.5 Hello的進(jìn)程執(zhí)行
進(jìn)程給hello程序提供的關(guān)鍵抽象:
- 一個(gè)獨(dú)立的邏輯控制流,它提供一個(gè)假象,好像我們的進(jìn)程獨(dú)占的使用處理器。
- 一個(gè)私有的地址空間,它提供一個(gè)假象,好像我們的程序獨(dú)占的使用內(nèi)存系統(tǒng)。
上下文信息:由程序正確運(yùn)行所需的狀態(tài)組成的,它包括存放在內(nèi)存中的代碼與數(shù)據(jù),它的棧、通用目的寄存器的內(nèi)容、程序計(jì)數(shù)器、環(huán)境變量以及打開(kāi)文件描述符的集合;
進(jìn)程時(shí)間片:多個(gè)邏輯控制流并發(fā)執(zhí)行時(shí),其中一個(gè)進(jìn)程執(zhí)行它的控制流的一部分的一個(gè)時(shí)間段叫做一個(gè)時(shí)間片;
用戶模式和內(nèi)核模式:處理器通常使用一個(gè)寄存器提供兩種模式的區(qū)分,該寄存器描述了進(jìn)程當(dāng)前享有的特權(quán),當(dāng)沒(méi)有設(shè)置模式位時(shí),進(jìn)程就處于用戶模式中,用戶模式的進(jìn)程不允許執(zhí)行特權(quán)指令,也不允許直接引用地址空間中內(nèi)核區(qū)內(nèi)的代碼和數(shù)據(jù);設(shè)置模式位時(shí),進(jìn)程處于內(nèi)核模式,該進(jìn)程可以執(zhí)行指令集中的任何命令,并且可以訪問(wèn)系統(tǒng)中的任何內(nèi)存位置。
進(jìn)程調(diào)度過(guò)程:
以hello為例,作為一個(gè)獨(dú)立的進(jìn)程與其他進(jìn)程并發(fā)執(zhí)行,內(nèi)核為hello維持一個(gè)上下文,在hello的某個(gè)時(shí)間片內(nèi),若內(nèi)核判斷它已經(jīng)運(yùn)行了足夠長(zhǎng)的時(shí)間,那么內(nèi)核可以決定搶占hello進(jìn)程,并重新開(kāi)始一個(gè)之前被搶占了的進(jìn)程,并使用上下文切換的機(jī)制將控制轉(zhuǎn)移到新的進(jìn)程,該機(jī)制具體執(zhí)行分為三步:1)保存當(dāng)前進(jìn)程的上下文,2)恢復(fù)被搶占進(jìn)程被保存的上下文,3)將控制轉(zhuǎn)移給這個(gè)新的進(jìn)程;這樣,內(nèi)核就完成了對(duì)hello與其他進(jìn)程的調(diào)度。
用戶態(tài)與核心態(tài)轉(zhuǎn)換:
?????? Hello初始是在用戶模式中的,進(jìn)程從用戶模式變?yōu)閮?nèi)核模式的唯一方法是通過(guò)諸如中斷、故障或者陷入系統(tǒng)調(diào)用這樣的異常。當(dāng)hello執(zhí)行過(guò)程中異常發(fā)生時(shí),如鍵盤(pán)按下ctrl-c,控制傳遞到異常處理程序,處理器將用戶模式變?yōu)閮?nèi)核模式。處理器運(yùn)行在內(nèi)核模式中,當(dāng)他返回到用戶代碼時(shí),處理器就把模式從內(nèi)核模式改回用戶模式,以上。
6.6 hello的異常與信號(hào)處理
下表為hello運(yùn)行過(guò)程中可能出現(xiàn)的異常種類:
| 異常類別 | 可能的誘發(fā)原因 | 處理方式 |
| 中斷 | 收到I/O設(shè)備的信號(hào),如鍵盤(pán)輸入 | 處理器讀取異常號(hào),調(diào)用中斷處理程序,返回下一條指令 |
| 陷阱 | Hello的父進(jìn)程執(zhí)行syscall指令fork一個(gè)hello | 陷阱處理程序在內(nèi)核態(tài)中完成fork工作,返回syscall之后的指令 |
| ????? 故障 | 缺頁(yè)異常 | 缺頁(yè)異常處理程序從磁盤(pán)加載適當(dāng)?shù)捻?yè)面,然后重新執(zhí)行當(dāng)前指令 |
| ????? 終止 | 出現(xiàn)DRAM或SRAM位損壞的奇偶錯(cuò)誤 | Abort例程終止該程序 |
????????????????????????????????????????? ? 表6.61 ?hello執(zhí)行中出現(xiàn)的異常
Hello執(zhí)行中輸入命令及其運(yùn)行結(jié)果:
亂按:
?
?
??????????? ??圖6.61? 亂按輸入結(jié)果
如圖6.61,將屏幕的輸入緩存到緩沖區(qū)。亂碼被認(rèn)為是命令,并自動(dòng)執(zhí)行光標(biāo)選中行;
Ctrl-Z + 其他命令:
?????? ??圖6.62 ?Ctrl-Z + ps、jobs、fg
如圖6.62,按下CtrlZ后hello停止運(yùn)行,并輸出其作業(yè)號(hào)為1,此時(shí)輸入ps,屏幕輸出三個(gè)進(jìn)程號(hào)及其名字;然后輸入jobs,屏幕輸出作業(yè)信息;最后輸入fg加上hello的作業(yè)號(hào)1,hello繼續(xù)執(zhí)行。
?
圖6.63 Ctrl-Z + Ctrl-C、kill
如圖6.63,hello運(yùn)行中按下ctrl C,然后輸入ps,并未發(fā)現(xiàn)hello,說(shuō)明其已經(jīng)結(jié)束;然后再次運(yùn)行hello,輸入ctrl Z,使用ps得知hello進(jìn)程號(hào)為2621,然后再輸入kill -9 2621殺死hello,此時(shí)ps發(fā)現(xiàn)已找不到hello,證明其被殺死。
6.7本章小結(jié)
本章對(duì)進(jìn)程展開(kāi)研究,hello在處理器中可能與其他進(jìn)程并發(fā)執(zhí)行,這就會(huì)涉及進(jìn)程調(diào)度與上下文維護(hù)。進(jìn)程運(yùn)行過(guò)程中可能會(huì)發(fā)生各種異常,針對(duì)不同的異常有著不同的處理方式,但都是在內(nèi)核態(tài)完成對(duì)異常的處理。進(jìn)程同樣會(huì)對(duì)信號(hào)做出響應(yīng),例如在shell中輸入crtl z時(shí)hello會(huì)停止運(yùn)行,而輸入fg命令又會(huì)恢復(fù)其運(yùn)行狀態(tài)。
第7章 hello的存儲(chǔ)管理
7.1 hello的存儲(chǔ)器地址空間
邏輯地址:
?????? 邏輯地址即程序中的段地址,邏輯地址由兩部份組成,段標(biāo)識(shí)符和段內(nèi)偏移量。段標(biāo)識(shí)符是由一個(gè)16位長(zhǎng)的字段組成,稱為段選擇符。其中前13位是一個(gè)索引號(hào)。
線性地址:
?????? 線性地址是邏輯地址到物理地址之間的一個(gè)中間層變換,程序代碼會(huì)產(chǎn)生邏輯地址,或者說(shuō)是段中的偏移地址,加上相應(yīng)段的基地址就生成了一個(gè)線性地址。如果啟用了分頁(yè)機(jī)制,那么MMU內(nèi)存管理單元會(huì)在內(nèi)存映射表里尋找與線性地址對(duì)應(yīng)的物理地址。若沒(méi)有啟用分頁(yè)機(jī)制,那么線性地址直接就是物理地址。
虛擬地址:
虛擬地址是CPU保護(hù)模式下的一個(gè)概念,保護(hù)模式是80286系列和之后的x86兼容CPU操作模式,在CPU引導(dǎo)完操作系統(tǒng)內(nèi)核后,操作系統(tǒng)內(nèi)核會(huì)進(jìn)入一種CPU保護(hù)模式,也叫虛擬內(nèi)存管理,在這之后的程序在運(yùn)行時(shí)都處于虛擬內(nèi)存當(dāng)中,虛擬內(nèi)存里的所有地址都是不直接的,所以你有時(shí)候可以看到一個(gè)虛擬地址對(duì)應(yīng)不同的物理地址,比如hello進(jìn)程里的call函數(shù)入口虛擬地址是0x001,而另一個(gè)進(jìn)程也是,但是它倆對(duì)應(yīng)的物理地址卻是不同的,操作系統(tǒng)采用這種內(nèi)存管理方法
物理地址:
?????? 物理地址就是內(nèi)存中每個(gè)內(nèi)存單元的編號(hào),這個(gè)編號(hào)是順序排好的,物理地址的大小決定了內(nèi)存中有多少個(gè)內(nèi)存單元,物理地址的大小由地址總線的位寬決定。
7.2 Intel邏輯地址到線性地址的變換-段式管理
段偏移量加上基地址的和,構(gòu)成線性地址。其中,段偏移量為邏輯地址的組成部分;基地址存儲(chǔ)在段描述符表中,該表存儲(chǔ)有多個(gè)描述符,每個(gè)描述符都描述了某個(gè)段的起始位置與大小等信息;而邏輯地址中的另一部分:段標(biāo)識(shí)符的高13位為段選擇符,段選擇符能對(duì)應(yīng)上段描述表中的一個(gè)描述符。
綜上,邏輯地址到線性地址的變換過(guò)程為,取邏輯地址的段標(biāo)識(shí)符中的段選擇符,到段描述表中找到對(duì)應(yīng)的描述符,描述符中存有段開(kāi)始的線性地址,即段基址;段基址加上邏輯地址中的段偏移量就是線性地址。
7.3 Hello的線性地址到物理地址的變換-頁(yè)式管理
線性地址即hello程序虛擬地址空間中的虛擬地址,虛擬內(nèi)存空間與物理內(nèi)存空間都被劃分為頁(yè),與頁(yè)號(hào)相對(duì)應(yīng)。虛擬地址由虛擬頁(yè)號(hào) + 虛擬頁(yè)偏移量組成。頁(yè)表是建立虛擬頁(yè)號(hào)與物理頁(yè)號(hào)映射關(guān)系的表結(jié)構(gòu),頁(yè)表項(xiàng)包含有有效位、物理頁(yè)號(hào)、磁盤(pán)地址等信息。如下圖7.31
?
? ??圖7.31 物理內(nèi)存與虛擬內(nèi)存在頁(yè)表上的對(duì)應(yīng)
??????????????? ??? (圖片來(lái)源CSDN)
虛擬頁(yè)號(hào) + 頁(yè)表起始地址能找到相對(duì)應(yīng)的頁(yè)表項(xiàng),頁(yè)表起始地址存儲(chǔ)在頁(yè)表基址寄存器中,頁(yè)表項(xiàng)存儲(chǔ)的頁(yè)表狀態(tài)有三種:未分配,已緩沖,未緩沖。當(dāng)對(duì)應(yīng)狀態(tài)為已緩沖時(shí),說(shuō)明虛擬頁(yè)所對(duì)應(yīng)的物理頁(yè)已經(jīng)存儲(chǔ)在內(nèi)存中,此時(shí)頁(yè)表項(xiàng)存儲(chǔ)的物理頁(yè)號(hào) + 物理頁(yè)偏移量即為物理地址,而物理頁(yè)偏移量與虛擬頁(yè)偏移量相同,可以從虛擬地址中直接得出。頁(yè)表項(xiàng)狀態(tài)為已緩沖時(shí),對(duì)應(yīng)頁(yè)式管理過(guò)程如下圖7.32
?
?圖7.32 頁(yè)表項(xiàng)狀態(tài)為已緩沖的頁(yè)式管理過(guò)程
???????? ???? ???? (圖片來(lái)源CSDN)
當(dāng)頁(yè)表項(xiàng)中狀態(tài)為未緩存時(shí),若要讀取該頁(yè),會(huì)引發(fā)缺頁(yè)中斷異常,缺頁(yè)異常處理程序根據(jù)頁(yè)置換算法,選擇出一個(gè)犧牲頁(yè),如果這個(gè)頁(yè)面已經(jīng)被修改了,則寫(xiě)出到磁盤(pán)上,最后將這個(gè)犧牲頁(yè)的頁(yè)表項(xiàng)有效位設(shè)置為0,存入磁盤(pán)地址。缺頁(yè)異常程序處理程序調(diào)入新的頁(yè)面,如果該虛擬頁(yè)尚未分配磁盤(pán)空間,則分配磁盤(pán)空間,然后磁盤(pán)空間的頁(yè)數(shù)據(jù)拷貝到空閑的物理頁(yè)上,并更新頁(yè)表項(xiàng)狀態(tài)為已緩存,更新物理頁(yè)號(hào),缺頁(yè)異常處理程序返回后,再回到發(fā)生缺頁(yè)中斷的指令處,重新按照頁(yè)表項(xiàng)命中的步驟執(zhí)行。
7.4 TLB與四級(jí)頁(yè)表支持下的VA到PA的變換
多級(jí)頁(yè)表可以減小翻譯地址時(shí)的時(shí)間開(kāi)銷。多級(jí)頁(yè)表中,頁(yè)表基址寄存器存儲(chǔ)一級(jí)頁(yè)表的地址,1到3的頁(yè)表的每一項(xiàng)存儲(chǔ)的下一級(jí)頁(yè)表的起始地址,4級(jí)頁(yè)表的每一項(xiàng)存儲(chǔ)的是物理頁(yè)號(hào)或磁盤(pán)地址。解析VA時(shí),其前m位vpn1尋找一級(jí)頁(yè)表中的頁(yè)表項(xiàng),接著一次重復(fù)k次,在第k級(jí)頁(yè)表獲得了頁(yè)表?xiàng)l目,將PPN與VPO組合獲得物理地址PA。
7.5 三級(jí)Cache支持下的物理內(nèi)存訪問(wèn)
高速緩存的組織方式如圖7.51,高速緩存的結(jié)構(gòu)將地址劃分成了t個(gè)標(biāo)記位、s個(gè)組索引位和b個(gè)塊偏移位。當(dāng)cpu執(zhí)行一條讀內(nèi)存字w的指令時(shí),它會(huì)首先向一級(jí)cache請(qǐng)求這個(gè)字,如果緩存命中,那么高速緩存會(huì)很快將該字返回給cpu,若不命中,則向下一級(jí)緩存發(fā)起請(qǐng)求。
?
圖7.51 高速緩存組織方式
以組相聯(lián)高速緩存為例,判斷緩存是否命中,然后取出字的過(guò)程分為三步:
高速緩存從w的地址中抽取出s個(gè)組索引位,這些位被解釋為一個(gè)對(duì)應(yīng)于一個(gè)組號(hào)的無(wú)符號(hào)整數(shù),用于在緩存中進(jìn)行組選擇。
確定了緩存中的組i后,緩存將搜索組中的每一行,直到某行標(biāo)記位與地址中的標(biāo)記位一致,如果能找到這樣的一行,那么即為命中。
如果w不在組中的任何一行,那么就是緩存不命中,緩存會(huì)從下一級(jí)存儲(chǔ)空間(例如L1的下一級(jí)為L(zhǎng)2)中取出包含這個(gè)字的塊,并依照特定的行替換策略將該行放入緩存中,行替換策略保證被替換行的被引用概率最低。
?在命中的行中,使用塊偏移選中字w返回給cpu。
7.6 hello進(jìn)程fork時(shí)的內(nèi)存映射
當(dāng)fork函數(shù)被父進(jìn)程調(diào)用時(shí),內(nèi)核為子進(jìn)程創(chuàng)建各種數(shù)據(jù)結(jié)構(gòu),并分配它唯一的一個(gè)PID。為給這個(gè)新進(jìn)程創(chuàng)建虛擬內(nèi)存,它創(chuàng)建當(dāng)前進(jìn)程的mm_struct、區(qū)域結(jié)構(gòu)與頁(yè)表的原樣副本;它將兩個(gè)進(jìn)程中的每個(gè)頁(yè)面都標(biāo)記為只讀,并把兩個(gè)進(jìn)程中的每個(gè)區(qū)域結(jié)構(gòu)都標(biāo)記為私有的寫(xiě)時(shí)復(fù)制。
當(dāng)fork在子進(jìn)程中返回時(shí),其現(xiàn)在的虛擬內(nèi)存剛好和調(diào)用fork時(shí)存在的虛擬內(nèi)存相同。當(dāng)這兩個(gè)進(jìn)程的任一者進(jìn)行后續(xù)寫(xiě)操作時(shí),寫(xiě)時(shí)復(fù)制機(jī)制就會(huì)創(chuàng)建新頁(yè)面,也就為每個(gè)進(jìn)程保持私有地址空間的抽象概念。
7.7 hello進(jìn)程execve時(shí)的內(nèi)存映射
在子進(jìn)程中調(diào)用execve函數(shù)加載hello時(shí),將完成以下工作
1)刪除已存在的用戶區(qū)域。
2)映射私有區(qū)域
3)映射共享區(qū)域
4)設(shè)置程序計(jì)數(shù)器(PC)
?
?
?????????????????? 圖7.71 虛擬內(nèi)存空間(來(lái)源CSDN)
最后exceve設(shè)置當(dāng)前進(jìn)程的上下文中的程序計(jì)數(shù)器,指向代碼區(qū)域的入口點(diǎn)。而下一次調(diào)度這個(gè)進(jìn)程時(shí),他將從這個(gè)入口點(diǎn)開(kāi)始執(zhí)行。Linux將根據(jù)需要換入代碼和數(shù)據(jù)頁(yè)面。
7.8 缺頁(yè)故障與缺頁(yè)中斷處理
當(dāng)指令引用一個(gè)地址,而與該地址相應(yīng)的物理頁(yè)面不在內(nèi)存中,即PTE中的有效位是0,所以MMU出發(fā)了一次異常,會(huì)觸發(fā)缺頁(yè)故障,內(nèi)核調(diào)用缺頁(yè)處理程序。通過(guò)查詢頁(yè)表PTE可以知該頁(yè)在磁盤(pán)的位置。缺頁(yè)處理程序從指定的地址加載頁(yè)面到物理內(nèi)存中,然后更新PTE。再將控制返回給引起缺頁(yè)故障的指令。當(dāng)該指令再次執(zhí)行時(shí),相應(yīng)的物理頁(yè)面已加載在內(nèi)存中,因此能夠命中。
7.9動(dòng)態(tài)存儲(chǔ)分配管理
所有動(dòng)態(tài)申請(qǐng)的內(nèi)存都存在堆上面,用戶通過(guò)保存在棧上面的一個(gè)指針來(lái)使用該內(nèi)存空間。動(dòng)態(tài)內(nèi)存分配器維護(hù)著堆,堆頂指針是brk。有兩種風(fēng)格,一種叫顯式分配器,使用兩個(gè)函數(shù),malloc和free,分別用于執(zhí)行動(dòng)態(tài)內(nèi)存分配和釋放。
malloc的作用是向系統(tǒng)申請(qǐng)分配堆中指定size個(gè)字節(jié)的內(nèi)存空間。也就是說(shuō)函數(shù)返回的指針為指向堆里的一塊內(nèi)存。并且,操作系統(tǒng)中有一個(gè)記錄空閑內(nèi)存地址的鏈表。當(dāng)操作系統(tǒng)收到程序申請(qǐng)時(shí),就會(huì)遍歷該鏈表,然后尋找第一個(gè)空間大于所申請(qǐng)空間的堆結(jié)點(diǎn),將該結(jié)點(diǎn)從空閑結(jié)點(diǎn)鏈表中刪除后,將該結(jié)點(diǎn)的空間分配給到程序。在使用malloc()分配內(nèi)存空間后,需釋放內(nèi)存空間,否則就會(huì)出現(xiàn)內(nèi)存泄漏。
free()釋放的是指針指向的內(nèi)存,而不是指針。指針并沒(méi)有被釋放,它仍然指向原來(lái)的存儲(chǔ)空間。因此指針需要手動(dòng)釋放,指針是一個(gè)變量,只有當(dāng)程序結(jié)束時(shí)才被銷毀。釋放了內(nèi)存空間后,原本指向這塊空間的指針仍然存在。但此時(shí)指針指向的內(nèi)容為垃圾,是未定義的。因此,釋放內(nèi)存后要把指針指向NULL,防止該指針后續(xù)被解引用。
7.10本章小結(jié)
本章主題是hello的存儲(chǔ)管理,在不同的存儲(chǔ)空間中有著不同的地址,虛擬地址空間中有虛擬地址,物理存儲(chǔ)空間中有物理地址,而程序中使用的是邏輯地址,段式管理將邏輯地址轉(zhuǎn)換為線性地址,頁(yè)式管理將線性地址轉(zhuǎn)換為物理地址。處理器借助地址向內(nèi)存請(qǐng)求數(shù)據(jù),多級(jí)高速緩存讓計(jì)算機(jī)整體的存儲(chǔ)結(jié)構(gòu)既擁有趨近于L1cache的高速,又有巨大的存儲(chǔ)空間。當(dāng)指令引用一個(gè)地址,而與該地址相應(yīng)的物理頁(yè)面不在內(nèi)存中時(shí),就要調(diào)用缺頁(yè)故障處理程序。此外,本章還分析了hello進(jìn)程fork與execve時(shí)的內(nèi)存映射,并在最后介紹了動(dòng)態(tài)存儲(chǔ)分配管理的方法與原理。
第8章 hello的IO管理
8.1 Linux的IO設(shè)備管理方法
一個(gè)Linux文件就是一個(gè)m個(gè)字節(jié)的序列:
B0,B1,B2……Bk……Bm-1
所有的I/O設(shè)備(例如網(wǎng)絡(luò)、磁盤(pán)和終端)都被模型化為文件,而所有的輸入和輸出都被當(dāng)做對(duì)相應(yīng)文件的讀和寫(xiě)來(lái)執(zhí)行,這種將設(shè)備優(yōu)雅地映射為文件的方式,允許Linux內(nèi)核引出一個(gè)簡(jiǎn)單、低級(jí)的應(yīng)用接口,稱為Unix I/O,這使得所有的輸入和輸出都能以一種統(tǒng)一且一致的方式來(lái)執(zhí)行。
設(shè)備的模型化:文件
設(shè)備管理:unix io接口
8.2 簡(jiǎn)述Unix IO接口及其函數(shù)
Unix I/O 接口:
Shell 創(chuàng)建的每個(gè)進(jìn)程都有三個(gè)打開(kāi)的文件:標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯(cuò)誤。
8.3 printf的實(shí)現(xiàn)分析
研究printf的實(shí)現(xiàn),首先來(lái)看看printf函數(shù)的函數(shù)體
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;
}
printf程序按照格式fmt結(jié)合參數(shù)args生成格式化之后的字符串,并返回字串長(zhǎng)度。
接下來(lái)我們追蹤write:
write:
mov eax, _NR_write
mov ebx, [esp + 4]
mov ecx, [esp + 8]
int INT_VECTOR_SYS_CALL
這里是給幾個(gè)寄存器傳了幾個(gè)參數(shù),然后以int結(jié)束。將棧中的參數(shù)放入寄存器,ecx為字符個(gè)數(shù),ebx存放第一個(gè)字符地址。
再來(lái)看看sys_call的實(shí)現(xiàn):
sys_call:
call save
push dword [p_proc_ready]
sti
push ecx
push ebx
call [sys_call_table + eax * 4]
add esp, 4 * 3
mov [esi + EAXREG - P_STACKBASE], eax
cli
ret
syscall將字符串中的字節(jié)從寄存器中通過(guò)總線復(fù)制到顯卡的顯存中,顯存中存儲(chǔ)的是字符的ASCII碼。
字符顯示驅(qū)動(dòng)子程序:從ASCII到字模庫(kù)到顯示vram(存儲(chǔ)每一個(gè)點(diǎn)的RGB顏色信息)。
顯示芯片按照刷新頻率逐行讀取vram,并通過(guò)信號(hào)線向液晶顯示器傳輸每一個(gè)點(diǎn)(RGB分量)。
完成了printf。
8.4 getchar的實(shí)現(xiàn)分析
getchar代碼部分:
int getchar(void)
{
static char buf[BUFSIZ];
static char* bb=buf;
static int n=0;
if(n==0)
{
n=read(0,buf,BUFSIZ);
bb=buf;
}
return (–n>=0)?(unsigned char)*bb++:EOF;
}
當(dāng)使用getchar時(shí),程序發(fā)生陷阱的異常。當(dāng)按鍵盤(pán)時(shí)會(huì)產(chǎn)生中斷。
異步異常-鍵盤(pán)中斷的處理:鍵盤(pán)中斷處理子程序。接受按鍵掃描碼轉(zhuǎn)成ascii碼,保存到系統(tǒng)的鍵盤(pán)緩沖區(qū)。
getchar等調(diào)用read系統(tǒng)函數(shù),通過(guò)系統(tǒng)調(diào)用讀取按鍵ascii碼,直到接受到回車鍵才返回。
8.5本章小結(jié)
本章介紹了Linux I/O設(shè)備的基本概念和管理方法,簡(jiǎn)述Unix IO接口及其函數(shù),以及分析printf 函數(shù)和 getchar 函數(shù)的實(shí)現(xiàn)。
結(jié)論
Hello的一生:
Hello的一生結(jié)束了,可我的修行才剛剛開(kāi)始。計(jì)算機(jī)如同集結(jié)人類智慧的山峰,CSAPP將我引到了這山腳下,讓我得以一窺其巍峨。長(zhǎng)路漫漫,歸途何期?我認(rèn)為程序員的求索之路是無(wú)窮無(wú)盡的,無(wú)數(shù)的科學(xué)家將計(jì)算機(jī)締造,未來(lái)也將由后人不斷發(fā)展,屏幕上閃動(dòng)的光標(biāo),既是藝術(shù)的綻放,更是技術(shù)的碩果。而我應(yīng)該做的,無(wú)非虛懷若谷,小心謹(jǐn)慎地一路登攀。
附件
hello.c 源文件
hello.i 預(yù)處理后的文件
hello.s 編譯后的匯編文件
hello.o 匯編后的可重定位文件
hello 鏈接后的可執(zhí)行文件
helloelf.txt ?hello的elf文件
hellooelf.txt ?hello.o的elf文件
helloobjdump.txt ?hello的反匯編代碼
參考文獻(xiàn)
為完成本次大作業(yè)你翻閱的書(shū)籍與網(wǎng)站等
[1]? 林來(lái)興. 空間控制技術(shù)[M]. 北京:中國(guó)宇航出版社,1992:25-42.
[2]? 辛希孟. 信息技術(shù)與信息服務(wù)國(guó)際研討會(huì)論文集:A集[C]. 北京:中國(guó)科學(xué)出版社,1999.
[3]? 趙耀東. 新時(shí)代的工業(yè)工程師[M/OL]. 臺(tái)北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[4]? 諶穎. 空間交會(huì)控制理論與方法研究[D]. 哈爾濱:哈爾濱工業(yè)大學(xué),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. 深入理解計(jì)算機(jī)系統(tǒng)[M],第三版,龔奕利,機(jī)械工業(yè)出版社,2016:1-640.
[8]? 王爽. 匯編語(yǔ)言[M]. 第3版,清華大學(xué)出版社,2013:14-172.
[9]? CSDN博客 https://blog.csdn.net/daocaokafei/article/details/116207148?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165235562016781683965613%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165235562016781683965613&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-1-116207148-null-null.142^v9^pc_search_result_control_group,157^v4^new_style&utm_term=%E8%99%9A%E6%8B%9F%E5%9C%B0%E5%9D%80&spm=1018.2226.3001.4187
總結(jié)
以上是生活随笔為你收集整理的哈工大 计算机系统大作业 程序人生-Hello’s P2P From Program to Process的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 虚函数绕过 GS保护 学习
- 下一篇: 牛腩新闻系统发布