程序人生-Hello’s P2P
計(jì)算機(jī)系統(tǒng)
大作業(yè)
題 ????目 ?程序人生-Hello’s P2P?
專?????? 業(yè) ??計(jì)算機(jī)科學(xué)與技術(shù)??????
學(xué) ?? 號(hào) ??2021113044????????????
班 ?? 級(jí) ??2103101???????????????
學(xué)?????? 生 ??方健??????????????
指 導(dǎo) 教 師 ??劉宏偉?????????????????
計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院
2022年5月
摘? 要
本文深入的研究了hello的一生,深入研究它是如何從一個(gè)源程序hello.c轉(zhuǎn)化為一個(gè)可執(zhí)行文件的,并在shell里面的運(yùn)行步驟,詳細(xì)的分析了每一個(gè)過程的具體內(nèi)容,如編譯、匯編、鏈接、重定位等等。
關(guān)鍵詞:匯編、重定位、ELF、鏈接???????????????????????????
(摘要0分,缺失-1分,根據(jù)內(nèi)容精彩稱都酌情加分0-1分)
目? 錄
第1章 概述............................................................................................................. - 4 -
1.1 Hello簡(jiǎn)介...................................................................................................... - 4 -
1.2 環(huán)境與工具..................................................................................................... - 4 -
1.3 中間結(jié)果......................................................................................................... - 4 -
1.4 本章小結(jié)......................................................................................................... - 4 -
第2章 預(yù)處理......................................................................................................... - 5 -
2.1 預(yù)處理的概念與作用..................................................................................... - 5 -
2.2在Ubuntu下預(yù)處理的命令.......................................................................... - 5 -
2.3 Hello的預(yù)處理結(jié)果解析.............................................................................. - 5 -
2.4 本章小結(jié)......................................................................................................... - 5 -
第3章 編譯............................................................................................................. - 6 -
3.1 編譯的概念與作用......................................................................................... - 6 -
3.2 在Ubuntu下編譯的命令............................................................................. - 6 -
3.3 Hello的編譯結(jié)果解析.................................................................................. - 6 -
3.4 本章小結(jié)......................................................................................................... - 6 -
第4章 匯編............................................................................................................. - 7 -
4.1 匯編的概念與作用......................................................................................... - 7 -
4.2 在Ubuntu下匯編的命令............................................................................. - 7 -
4.3 可重定位目標(biāo)elf格式................................................................................. - 7 -
4.4 Hello.o的結(jié)果解析...................................................................................... - 7 -
4.5 本章小結(jié)......................................................................................................... - 7 -
第5章 鏈接............................................................................................................. - 8 -
5.1 鏈接的概念與作用......................................................................................... - 8 -
5.2 在Ubuntu下鏈接的命令............................................................................. - 8 -
5.3 可執(zhí)行目標(biāo)文件hello的格式.................................................................... - 8 -
5.4 hello的虛擬地址空間.................................................................................. - 8 -
5.5 鏈接的重定位過程分析................................................................................. - 8 -
5.6 hello的執(zhí)行流程.......................................................................................... - 8 -
5.7 Hello的動(dòng)態(tài)鏈接分析.................................................................................. - 8 -
5.8 本章小結(jié)......................................................................................................... - 9 -
第6章 hello進(jìn)程管理................................................................................... - 10 -
6.1 進(jìn)程的概念與作用....................................................................................... - 10 -
6.2 簡(jiǎn)述殼Shell-bash的作用與處理流程..................................................... - 10 -
6.3 Hello的fork進(jìn)程創(chuàng)建過程..................................................................... - 10 -
6.4 Hello的execve過程................................................................................. - 10 -
6.5 Hello的進(jìn)程執(zhí)行........................................................................................ - 10 -
6.6 hello的異常與信號(hào)處理............................................................................ - 10 -
6.7本章小結(jié)....................................................................................................... - 10 -
第7章 hello的存儲(chǔ)管理................................................................................ - 11 -
7.1 hello的存儲(chǔ)器地址空間............................................................................ - 11 -
7.2 Intel邏輯地址到線性地址的變換-段式管理............................................ - 11 -
7.3 Hello的線性地址到物理地址的變換-頁(yè)式管理....................................... - 11 -
7.4 TLB與四級(jí)頁(yè)表支持下的VA到PA的變換............................................. - 11 -
7.5 三級(jí)Cache支持下的物理內(nèi)存訪問.......................................................... - 11 -
7.6 hello進(jìn)程fork時(shí)的內(nèi)存映射.................................................................. - 11 -
7.7 hello進(jìn)程execve時(shí)的內(nèi)存映射.............................................................. - 11 -
7.8 缺頁(yè)故障與缺頁(yè)中斷處理........................................................................... - 11 -
7.9動(dòng)態(tài)存儲(chǔ)分配管理....................................................................................... - 11 -
7.10本章小結(jié)..................................................................................................... - 12 -
第8章 hello的IO管理................................................................................. - 13 -
8.1 Linux的IO設(shè)備管理方法.......................................................................... - 13 -
8.2 簡(jiǎn)述Unix IO接口及其函數(shù)....................................................................... - 13 -
8.3 printf的實(shí)現(xiàn)分析........................................................................................ - 13 -
8.4 getchar的實(shí)現(xiàn)分析.................................................................................... - 13 -
8.5本章小結(jié)....................................................................................................... - 13 -
結(jié)論......................................................................................................................... - 14 -
附件......................................................................................................................... - 15 -
參考文獻(xiàn)................................................................................................................. - 16 -
第1章 概述
1.1 Hello簡(jiǎn)介
P2P: From Program to Process
最開始我們將代碼一個(gè)個(gè)寫入hello.c中,它是hello程序的生命的起點(diǎn),然后調(diào)用預(yù)處理器cpp將hello.c預(yù)處理為hello.i,接下來編譯器ccl會(huì)將hello.i翻譯為hello.s,然后通過匯編器將hello.s翻譯為機(jī)器指令保存在hello.o中,最后調(diào)用鏈接器形成可執(zhí)行文件hello,在shell中輸入命令./hello,操作系統(tǒng)就會(huì)通過fork為其生成子進(jìn)程,然后通過execve將其加載,不斷進(jìn)行訪存、內(nèi)存申請(qǐng)等操作。最后,在程序結(jié)束返回后,由父進(jìn)程或祖先進(jìn)程進(jìn)行回收,程序結(jié)束。這樣就實(shí)現(xiàn)了完整的P2P過程。
020: From Zero-0 to Zero-0
指一個(gè)程序從無到擁有自己的內(nèi)存,地址和時(shí)間周期等,然后直到該程序執(zhí)行結(jié)束后,父進(jìn)程負(fù)責(zé)將其回收的全部過程。shell在子進(jìn)程中使用execve執(zhí)行這個(gè)程序時(shí),系統(tǒng)將會(huì)把這個(gè)程序加載到內(nèi)存,等待進(jìn)程結(jié)束時(shí),它的父進(jìn)程將會(huì)回收hello,而系統(tǒng)內(nèi)核將會(huì)對(duì)內(nèi)存進(jìn)行清理,這個(gè)過程就叫做020。
1.2 環(huán)境與工具
1.2.1 硬件環(huán)境:
X64 CPU;2GHz;2G RAM;256GHD Disk
1.2.2 軟件環(huán)境:
Windows11 64位以上; Vmware 16;Ubuntu 20.04 ;LTS 64位/優(yōu)麒麟 64位
1.2.3 開發(fā)工具:
Visual Studio 2021 64位;CodeBlocks 64位;vi/vim/gedit/gcc;edb
1.3 中間結(jié)果
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖1-3:中間過程文件
hello.c:源程序
hello.i:經(jīng)過預(yù)處理過后的文件;
hello.s:經(jīng)過編譯生成的匯編文件;
hello.o:匯編生成的可重定位的文件;
hello:通過鏈接產(chǎn)生的可執(zhí)行目標(biāo)文件二進(jìn)制文件;
elf.txt: 可重定位目標(biāo)文件hello.o導(dǎo)出的ELF文件;
hello_elf.txt:可執(zhí)行文件hello導(dǎo)出的ELF文件;
objdump_hello.s:可重定位目標(biāo)文件hello.o反匯編生成的文件
hello_objdump.s:可執(zhí)行文件hello反匯編生成的文件
1.4 本章小結(jié)
本章簡(jiǎn)單介紹hello程序的一生,介紹了P2P和020過程,然后又介紹我所使用的硬件環(huán)境、軟件環(huán)境和開發(fā)調(diào)試工具,最后則簡(jiǎn)述了過程中間生成的中間文件的名字和作用。
(第1章0.5分)
第2章 預(yù)處理
2.1 預(yù)處理的概念與作用
預(yù)處理概念:
預(yù)處理指的是預(yù)處理器cpp根據(jù)以字符#開頭的命令,來修改原始的C程序,最后生成.i文本文件的過程。
預(yù)處理作用:
1.將源文件中用#include 形式聲明的文件復(fù)制到新的程序中;
2..用實(shí)際值替換用#define 定義的字符串;
3.根據(jù)#if后面的條件決定需要編譯的代碼;
4.預(yù)編譯程序可以識(shí)別一些特殊符號(hào),預(yù)編譯程序?qū)τ谠谠闯绦蛑谐霈F(xiàn)的這些特殊符號(hào)串會(huì)用合適值進(jìn)行替換。
5.預(yù)處理還可以幫助程序員節(jié)省工作量,提高程序可讀性,便于維護(hù)。
2.2在Ubuntu下預(yù)處理的命令
命令:cpp hello > hello.i
??????????? 圖2-2:Ubuntu下預(yù)處理命令展示
2.3 Hello的預(yù)處理結(jié)果解析
? ? ? ?? ????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖2-3:hello.i內(nèi)容展示
解析:可以看見程序被拓展成了3060行,原始hello.c程序出現(xiàn)在3046行之后。在這之前是頭文件stdio.h、unistd.h以及stdlib.h依次展開。預(yù)處理器將它們?nèi)慷颊归_直接插入程序文本中,所以最后的.i文件特別的長(zhǎng)
2.4 本章小結(jié)
本章介紹了預(yù)處理的概念和作用,以及預(yù)處理的方法,然后展示了.i文件的部分內(nèi)容,分析了里面內(nèi)容的由來。
(第2章0.5分)
第3章 編譯
3.1 編譯的概念與作用
編譯的概念:
編譯程序是通過詞法分析和語法分析,在確認(rèn)所有的指令都符合語法規(guī)則后,將其翻譯成等價(jià)的中間代碼或匯編代碼來表示。編譯器cc1將預(yù)處理過的文本文件hello.i 翻譯轉(zhuǎn)換成匯編文本文件hello.s。
編譯的作用:
1.語法分析:編譯程序的語法分析器以單詞符號(hào)作為輸入,分析單詞符號(hào)串是否形成了符合語法規(guī)則的語法單位,方法有自上而下分析法和自下而上分析法兩種;
2.中間代碼:源程序的一種內(nèi)部表示,或稱之為中間語言。中間代碼的作用是使編譯程序的結(jié)構(gòu)在邏輯上更為簡(jiǎn)單明確,特別是使目標(biāo)代碼的優(yōu)化比較容易實(shí)現(xiàn);
3.代碼優(yōu)化:指對(duì)程序進(jìn)行多種等價(jià)變換,使得從變換后的程序出發(fā)時(shí)能生成更有效的目標(biāo)代碼;
4.目標(biāo)代碼:生成目標(biāo)代碼是編譯的最后階段。目標(biāo)代碼生成器把語法分析或優(yōu)化后生成的中間代碼轉(zhuǎn)換成目標(biāo)代碼。此處目標(biāo)代碼指匯編語言代碼,即不同種類的語言提供相同的形式,其指令與處理器的指令集類似,更貼近底層,便于匯編器將其轉(zhuǎn)換為可執(zhí)行機(jī)器語言代碼供機(jī)器執(zhí)行。
3.2 在Ubuntu下編譯的命令
命令:gcc -S hello.i -o hello.s
????????????????? 圖3-2:Ubuntu下編譯的命令展示
3.3 Hello的編譯結(jié)果解析
3.3.1結(jié)果展示:
????????????????????????????? 圖3-3-1:hello.s內(nèi)容展示
3.3.2偽指令
打開我們的hello.s文件我們可以發(fā)現(xiàn)匯編代碼有一部分是以.作為開頭,這些以‘.’開頭的行都是指導(dǎo)匯編器和鏈接器工作的偽指令,通常我們可以忽略這些行,下面給出這些偽指令的含義:如下表
| 偽指令 | 含義 |
| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.file | 聲明源文件 |
| .text | 聲明代碼節(jié) |
| ?? .section | 文件代碼段 |
| ? .rodata | 只讀文件 |
| ?.align | 數(shù)據(jù)指令地址對(duì)齊方式 |
| ?.string | 聲明字符串 |
| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .globl | 聲明全局變量 |
| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.type | 聲明變量類型 |
?????????????????? 表3-3-2:偽指令的類型、含義
3.3.3數(shù)據(jù)
(1)字符串常量
??????????????? 圖3-3-3-1:hello.s匯編代碼關(guān)于字符串常量的部分
解析:.LC0和.LC1作為兩個(gè)字符串變量被聲明對(duì)于原程序printf里面的字符串“用法: Hello 學(xué)號(hào) 姓名 秒數(shù)!”和“Hello %s %s”。而在.LC0中出現(xiàn)的\347\224等是因?yàn)橹形淖址麤]有對(duì)應(yīng)的ASCII碼值無法直接顯式顯示,所以這樣的字符方式顯示。而且這兩個(gè)字符串都在.rodata下面,所以是只讀數(shù)據(jù)。
(2)全局變量
? ? ? ? ? ? ?圖3-3-3-2:hello.s匯編代碼關(guān)于全局變量main的部分
解析:這里.globl聲明了main是一個(gè)全局變量,.type main,@function看出main是一個(gè)函數(shù)類型
(3)變量
? ? ? ? ? ???????
??????? 圖3-3-3-3:hello.s匯編代碼關(guān)于函數(shù)參數(shù)argc、argv的部分?
解析:可以看出來%edi保存的main函數(shù)的第一個(gè)參數(shù)int argc,%rsi保存的是main函數(shù)的第二個(gè)參數(shù)char *argv[],然后在棧中-20(%rbp)的位置保存變量argc占4個(gè)字節(jié),在-32(%rbp)的位置保存變量argv[],占八個(gè)字節(jié),接下來程序就可以通過-20(%rbp)和-32(%rbp)直接找到函數(shù)的參數(shù)內(nèi)容。
??????? ????????
???????? 圖3-3-3-3-1:hello.s匯編代碼關(guān)于局部變量i的部分
解析:可以看出-4(%rbp)保存的是變量i的值,初始為0,每次循環(huán)加一,循環(huán)的條件是i<=8。
3.3.4賦值
????????
圖3-3-3-4:hello.s匯編代碼關(guān)于變量i賦值操作的部分
解析:通過使用mov指令,將立即數(shù)0直接賦值給變量i。
3.3.5算術(shù)操作
????????
圖3-3-5-1:hello.s匯編代碼關(guān)于棧指針%rsp算術(shù)操作的部分
解析:通過sub將棧指針%rsp減去立即數(shù)32,代表?xiàng)I暾?qǐng)32個(gè)字節(jié)的空間。
????????
圖3-3-5-2:hello.s匯編代碼關(guān)于棧指針%rsp算術(shù)操作的部分
解析:通過add可以將-4(%rbp)里面的值加是立即數(shù)1,即i=i+1。
3.3.6關(guān)系操作/控制轉(zhuǎn)移
????????
?????????????圖3-3-6-1 hello.s匯編代碼關(guān)于控制轉(zhuǎn)移if else的部分
解析:在-20(%rbp)保存的是第一個(gè)參數(shù)argc,通過cmpl操作將argc與4做比較,je指令可以根據(jù)上一步比較結(jié)果設(shè)置的條件碼狀態(tài)實(shí)現(xiàn)跳轉(zhuǎn)(關(guān)系操作!),若argc不等于4,則跳過25行的命令,從26行開始執(zhí)行if里面的內(nèi)容;若argc等于4,則跳轉(zhuǎn)到.L2處去執(zhí)行else部分的內(nèi)容,從而實(shí)現(xiàn)if else的控制轉(zhuǎn)移。
???????? ???????
? ????????
圖3-3-6-2 hello.s匯編代碼關(guān)于控制轉(zhuǎn)移for循環(huán)的部分
解析:首先在.L2里面初始化i=0,然后通過jmp無條件跳轉(zhuǎn)到.L3,在.L3里面進(jìn)行for循環(huán)里面的條件判斷,通過cmp將i與8比較,然后通過jle實(shí)現(xiàn)條件跳轉(zhuǎn)(關(guān)系控制<=),若i<=8,則跳轉(zhuǎn)到.L4里面進(jìn)行for循環(huán)里面的操作,在.L4里面有一個(gè)add的操作,使得每次循環(huán)i++;若最后i>8了,則在.L3中不會(huì)跳轉(zhuǎn)到.L4,結(jié)束循環(huán),從而實(shí)現(xiàn)了一個(gè)完整的for循環(huán)的操作。
3.3.7數(shù)組/指針/結(jié)構(gòu)操作
????????
圖3-3-7:hello.s匯編代碼關(guān)于數(shù)組/指針操作argv的部分
解析:在之前談過,在-32(%rbp)保存著一個(gè)數(shù)組指針argv,它指向一個(gè)字符串?dāng)?shù)組,其中argv[0]保存著命令行第一個(gè)參數(shù),一般是可執(zhí)行文件的名字這里是./hello,argv[1],argv[2]則保存著我們輸入的倆個(gè)字符串,這里是我們的學(xué)號(hào)和姓名,char *占8個(gè)字節(jié),將-32(%rbp)加16就是argc[2],-32(%rbp)加8就是argv[1],從而實(shí)現(xiàn)利用指針對(duì)數(shù)組argv[]的調(diào)用。
3.3.8函數(shù)操作
????????
圖3-3-8-1:hello.s中關(guān)于函數(shù)printf的調(diào)用部分
解析:printf是庫(kù)里面的一個(gè)函數(shù),在最后鏈接的時(shí)候內(nèi)容會(huì)復(fù)制進(jìn)去,參數(shù)傳遞:call puts時(shí)只輸入字符串參數(shù)首地址,for循環(huán)中call printf時(shí)輸入argv[1]和argc[2]的地址,函數(shù)調(diào)用:if判斷滿足條件后在for循環(huán)中被調(diào)用,調(diào)用之后會(huì)在屏幕是輸出我們想要的結(jié)果
????????
圖3-3-8-2:hello.s關(guān)于函數(shù)exit的調(diào)用
解析:參數(shù)傳遞:輸入?yún)?shù)為1后再執(zhí)行退出命令,exit(1)表示正常退出,exit(0)表示非正常退出;函數(shù)調(diào)用:if判斷條件滿足后被調(diào)用;在調(diào)用之后程序就會(huì)結(jié)束進(jìn)程。
????????
圖3-3-8-3:hello.s關(guān)于函數(shù)sleep的調(diào)用
解析:參數(shù)傳遞:輸入?yún)?shù)atoi(argv[3]),函數(shù)調(diào)用:在for循環(huán)中被調(diào)用,call sleep;調(diào)用之后程序延遲。其中其中函數(shù)atoi()是把字符串轉(zhuǎn)換成整型數(shù)的一個(gè)函數(shù)。
????????
圖3-3-8-4:hello.s關(guān)于函數(shù)getchar的調(diào)用
解析:函數(shù)調(diào)用:在main中被調(diào)用,沒有參數(shù)傳遞,getchar()。
????????
圖3-3-8-5:hello.s中關(guān)于main函數(shù)的調(diào)用。
解析:main是主函數(shù),有倆參數(shù)argc和argv先前已經(jīng)介紹過,在函數(shù)里面最后會(huì)return 0,來結(jié)束main函數(shù)。
3.4 本章小結(jié)
本章首先介紹了編譯的概念與作業(yè),然后介紹了在Ubuntu下編譯的命令,展示了hello.s的內(nèi)容,最后根據(jù)hello.s文件,詳細(xì)介紹了中編譯器是怎么處理C語言的各個(gè)數(shù)據(jù)類型以及各類操作的。
(第3章2分)
第4章 匯編
4.1 匯編的概念與作用
匯編的概念:
驅(qū)動(dòng)程序運(yùn)行匯編器as將匯編語言(hello.s)翻譯成機(jī)器語言(hello.o)的過程稱為匯編,同時(shí)這個(gè)機(jī)器語言文件是可重定位目標(biāo)文件。匯編器接受.s文件作為輸入,以.o可重定位目標(biāo)文件作為輸出。可重定位目標(biāo)文件包含二進(jìn)制代碼和數(shù)據(jù),在編譯時(shí)與其他可重定位目標(biāo)文件能夠合并起來,創(chuàng)建成一個(gè)可執(zhí)行目標(biāo)文件,從而被加載到內(nèi)存中執(zhí)行。
匯編的作用:
匯編是將高級(jí)語言轉(zhuǎn)化為機(jī)器可以直接識(shí)別并且執(zhí)行的代碼文件的過程,匯編器將.s文件匯編程序翻譯成機(jī)器語言指令,并將這些指令打包成為可重定位目標(biāo)程序的格式,最后將結(jié)果保留在.o目標(biāo)文件中,.o文件是一個(gè)包含程序指令編碼的二進(jìn)制文件。
4.2 在Ubuntu下匯編的命令
??? 命令:gcc -c hello.s -o hello.o
????????
????????????? 圖4-2:Ubuntu下匯編命令展示
4.3 可重定位目標(biāo)elf格式
4.3.1導(dǎo)出ELF文件
由于hello.o文件不能直接打開,因此我們需要輸入readelf -a hello.o > ./elf.txt將其轉(zhuǎn)換成elf文件后查看,如下圖:
???????????????????????????? 圖4-3-1:Ubuntu下導(dǎo)出elf文件展示
????
??????????????????? 圖4-3-1-1:elf文件展示
4.3.2 ELF頭
? ? ? ? ? ? ? ? ????????????????????? 圖4-3-2:ELF頭信息
解析:可以看見ELF以16個(gè)字節(jié)開頭,其中前4個(gè)字節(jié)稱為ELF的魔數(shù),分別與ASCll中的DEF控制符、字符E、字符L、字符F對(duì)應(yīng),這4個(gè)字節(jié)是用來確認(rèn)文件類型的。接下來一個(gè)字節(jié)是用來表示ELF文件類型的,0x1表示32位,0x2表示64位。第六個(gè)字節(jié)用來表示字節(jié)序,0x1表示小段,0x2表示大端。第7個(gè)字節(jié)表示ELF的版本號(hào),一般為1。剩下的字節(jié)在ELF標(biāo)準(zhǔn)中沒有定義用0填充。16個(gè)字節(jié)下面也給出了文件的類別,字節(jié)序,ELF文件類型,版本號(hào),在第11行還給出了這個(gè)ELF的起始地址,在15行還給出了ELF頭的長(zhǎng)度為64個(gè)字節(jié),13行給出了節(jié)頭部表的起始位置是1240,19行說明了節(jié)頭部表里面有14個(gè)表項(xiàng),18行說明了每一個(gè)表項(xiàng)的大小是64個(gè)字節(jié)。
4.3.3節(jié)頭部表:
???????? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? 圖4-3-3:節(jié)頭部表信息
解析:上面分析過里面會(huì)有13個(gè)表項(xiàng)即13個(gè)節(jié),頭節(jié)目表描述了.o文件中出現(xiàn)的各個(gè)節(jié)的類型、位置、所占空間大小等必要有效信息。如上圖所示,其屬性分別有名稱、類型、地址(此時(shí)暫時(shí)未被分配均為0)、偏移量(節(jié)相對(duì)于文件開始的偏移)、節(jié)大小、全體(Entry)大小、旗標(biāo)(節(jié)屬性)、鏈接(與其他節(jié)的)、信息(附加節(jié))、對(duì)齊(2的Align次方)。以第一個(gè)表項(xiàng)為例,它的名稱是.test,位置在0x40,大小是146個(gè)字節(jié)(0x92)。
4.3.4重定位節(jié)
? ????????????????? 圖4-3-4重定位節(jié)信息
解析:當(dāng)匯編器生成一個(gè)目標(biāo)模塊時(shí),它并不知道數(shù)據(jù)和代碼最終將放在內(nèi)存中的什么位置。它也不知道這個(gè)模塊引用的任何外部定義的函數(shù)或者全局變量的位置。所以,無論何時(shí)匯編器遇到最終未知的目標(biāo)引用,它就會(huì)生成一個(gè)重定位條目,告訴鏈接器在將目標(biāo)文件合并到可執(zhí)行文件時(shí)如何修改新的引用。在重定位節(jié)中包含了在代碼中使用的一些外部變量等信息,在鏈接的時(shí)候需要根據(jù)重定位節(jié)的信息對(duì)這些變量符號(hào)進(jìn)行修改。本程序需要重定位的信息有:.rodata中的模式串,puts,exit,printf,slepsecs,sleep,getchar這些符號(hào)需要與相應(yīng)的地址進(jìn)行重定位這些符號(hào)的相關(guān)信息偏移量如上圖。
4.3.5符號(hào)表
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ??圖4-3-5:符號(hào)表信息
解析:在符號(hào)表中存放了所有引用的函數(shù)和全局變量的信息。Num為某個(gè)符號(hào)的編號(hào),value表示符號(hào)在對(duì)應(yīng)節(jié)中的偏移量,size是所占字節(jié)數(shù),type是符號(hào)的類型,Bind表示這個(gè)符號(hào)是本地的還是全局的,Vis在C語言中并未使用可以忽略,Ndx是section的索引值, Name是符號(hào)的名稱。這里我們發(fā)現(xiàn)main這個(gè)符號(hào)為例,它的Ndx為1,說明它的定義在./test節(jié)中(之前節(jié)頭部表可以看節(jié)的索引值),Bind是GLOBAL說明是一個(gè)全局變量,Type是FUNC說明main是一個(gè)函數(shù),size是146,說明它在./test節(jié)中占146個(gè)字節(jié),value為0,說明它偏移量為0,就在./test最上面。
4.4 Hello.o的結(jié)果解析
4.4.1生成反匯編文件
命令為:objdump -d -r hello.o > objdump_hello.s
???????? ? ? ? ? ? ????????????????? 圖4-4-1-0:Ubuntu下生成反匯編文件展示
????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖4-4-1-1:hello反匯編生成的objdump_hello.s
4.4.2對(duì)比與分析:
(1)操作數(shù)變化:
hello.s中的操作數(shù)一般是十進(jìn)制表示的,而objdump_hello.s中的操作數(shù)一般是十六進(jìn)制表示的,如下圖:
????????
??圖4-4-2-1:hello.s中立即數(shù)操作數(shù)展示
????????
???????? 圖4-4-2-2:objdump_hello.s中立即數(shù)操作數(shù)展示
(2)跳轉(zhuǎn)指令變化:
hello.s中地址使用段名稱如 je .L2,而objdump_hello.s中則使用相對(duì)偏移地址,如je? 2f <main+0x2f>,如下圖:
? ????????
??? 圖4-4-2-3:hello.s中跳轉(zhuǎn)指令展示
????????
??? 圖4-4-2-4:objdump_hello.s中跳轉(zhuǎn)指令展示
(3)函數(shù)調(diào)用:
在hello.s中,call的后面緊跟著函數(shù)名,而在objdump_hello.s中,call的后面緊跟著下一條指令的地址。這是因?yàn)樵摮绦蛘{(diào)用的函數(shù)是共享庫(kù)中的函數(shù),最終需要通過動(dòng)態(tài)鏈接器才能確定函數(shù)的運(yùn)行時(shí)執(zhí)行地址。對(duì)于這一類函數(shù)調(diào)用,call指令中的相對(duì)偏移量暫時(shí)編碼為全0,然后在.rela.text節(jié)添加重定位條目,等待鏈接時(shí)的進(jìn)一步確定。如下圖所示:
????????? ? ? ? ? ? ? ? ?圖4-4-2-5:objdump_hello.s函數(shù)調(diào)用展示
???????? ? ? ? ? ? ? ? ?圖4-4-2-6:hello.s函數(shù)調(diào)用展示
(4)數(shù)據(jù)訪問變化:
在hello.s 文件中,使用段名稱+%rip訪問 rodata(printf 中的字符串),而在反匯編得到的hello.asm中,使用 0+%rip進(jìn)行訪問。其原因與函數(shù)調(diào)用類似,rodata 中數(shù)據(jù)地址在運(yùn)行時(shí)才能確定,故訪問時(shí)也需要重定位。在匯編成為機(jī)器語言時(shí),將操作數(shù)設(shè)置為全 0 并添加相應(yīng)的重定位條目,如下圖所示:
???????? ? ? ? ?? ? ? ? ? ? ? ? 圖4-4-2-7:hello.s中數(shù)據(jù)訪問的變化
??????????????????????? 圖4-4-2-8:objdump_hello.s中數(shù)據(jù)訪問的變化
4.4.3機(jī)器語言與匯編語言:
發(fā)現(xiàn)hello.o的反匯編文件objdump_hello.s與hello.s匯編代碼基本是一致的,但是在這反匯編文件的字里行間中,也混雜著一些我們相對(duì)陌生的面孔,也就是機(jī)器代碼。這些機(jī)器代碼是二進(jìn)制機(jī)器指令的集合,每一條機(jī)器代碼都對(duì)應(yīng)一條機(jī)器指令,到這才是機(jī)器真正能識(shí)別的語言。每一條匯編語言都可以用機(jī)器二進(jìn)制數(shù)據(jù)來表示,匯編語言中的操作碼和操作數(shù)以一種相當(dāng)于映射的方式和機(jī)器語言進(jìn)行對(duì)應(yīng),從而讓機(jī)器能夠真正理解代碼的含義并且執(zhí)行相應(yīng)的功能。總之匯編語言能與機(jī)器碼建立一一對(duì)應(yīng)的關(guān)系。
4.5 本章小結(jié)
本章首先介紹了匯編的概念和作用,然后給出了在Ubuntu下啟動(dòng)匯編的指令,以及如何導(dǎo)出ELF文件。然后詳細(xì)的分析了ELF文件里面的內(nèi)容,對(duì)ELF頭、頭部表、重定向節(jié)、符號(hào)表的內(nèi)容做了詳細(xì)的解釋,然后利用objdump反匯編hello.o將反匯編文件objdump_hello.s與原匯編文件hello.s做比較,分析了倆者的異同點(diǎn),最后分析了機(jī)器語言與匯編語言的關(guān)系。
(第4章1分)
第5章 鏈接
5.1 鏈接的概念與作用
鏈接的概念:
鏈接是指將多種不同重定位目標(biāo)文件或靜態(tài)/動(dòng)態(tài)庫(kù)的代碼和數(shù)據(jù)部分收集整合起來,修改符號(hào)引用,組合并且輸出成一個(gè)單一可執(zhí)行目標(biāo)文件的過程。?
鏈接的作用:
鏈接能夠節(jié)省源程序空間,同時(shí)對(duì)于未編入的常用函數(shù)文件能夠進(jìn)行合并從而生成能夠正常工作的可執(zhí)行目標(biāo)文件。除此之外鏈接也可以表述為使得一個(gè)復(fù)雜程序被分解成諸多簡(jiǎn)明模塊來進(jìn)行編寫,并且最終被合并為一個(gè)可執(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-2:Ubuntu下匯編命令展示
5.3 可執(zhí)行目標(biāo)文件hello的格式
5.3.1導(dǎo)出hello的ELF文件
命令: readelf -a hello > ./hello_elf.txt
???????? ? ? ? ? ? ? ?????????????? 圖5-3-1:導(dǎo)出hello的ELF文件
5.3.2 ELF頭
????????
??????????????? 圖5-3-2:hello的ELF頭內(nèi)容
解析:與之前分析hello.o的過程類似,不過注意到第8行的類型變?yōu)榱薊XEC即可執(zhí)行文件,同時(shí)發(fā)現(xiàn)ELF頭的地址變了,變?yōu)榱?x4010f0,這是因?yàn)樗鼈冃枰囟ㄎ坏阶罱K運(yùn)行時(shí)的內(nèi)存地址,section也變多了變?yōu)榱?7個(gè),還多了一個(gè)program headers程序頭部表。
5.3.3節(jié)頭
?????????????????????????? 圖5-3:節(jié)頭內(nèi)容展示
解析:節(jié)頭的內(nèi)容分析和之前一樣,唯一的區(qū)別一個(gè)就是節(jié)的數(shù)量多了,由原來的14個(gè)變成了現(xiàn)在的27個(gè),多出來的節(jié)如下:
| 節(jié) | 主要作用 |
| .interp: | 包含了動(dòng)態(tài)鏈接器在文件系統(tǒng)中的路徑; |
| .note???? .ABI-tag: | ELF規(guī)范中記錄的注釋部分,包含一些版本信息; |
| .gnu???? .hash: | 符號(hào)的哈希表,用于加速查找符號(hào); |
| .dynamic??? .dynsym??? .dynstr: | 與動(dòng)態(tài)鏈接符號(hào)相關(guān); |
| .gnu.version?? .gnu.version_r: | 與版本有關(guān)的信息; |
| .init_array?? .fini_array: | 存放函數(shù)指針,其中的函數(shù)分別在main函數(shù) 之前之后調(diào)用,用于初始化和收尾; |
| .init??? .fini: | 存放上述初始化和收尾的代碼 |
| .eh_frame_hdr | 與異常處理相關(guān)。 |
?????????????????? 表5-3-3-1:多出的節(jié)主要功能展示
5.3.4程序頭
??????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖5-3-4程序頭展示
解析:?elf可執(zhí)行文件易加載到內(nèi)存,可執(zhí)行文件連續(xù)的片被映射到連續(xù)的內(nèi)存段,程序頭部表描述了這一映射關(guān)系。程序頭部表包括各程序頭的偏移量、內(nèi)存地址、對(duì)其要求、目標(biāo)文件與內(nèi)存中的段大小及運(yùn)行時(shí)訪問權(quán)限等信息。
5.3.5段節(jié)
???????????????????????????? 圖5-3-5:段節(jié)展示
解析:如圖所示的節(jié)段映射,說明了在鏈接過程中,將多個(gè)代碼段與數(shù)據(jù)段分別合并成一個(gè)單獨(dú)的代碼段和數(shù)據(jù)段,并根據(jù)段的大小以及偏移量重新設(shè)置各個(gè)符號(hào)的地址。
5.3.6重定位節(jié)
????????????????????????? 圖5-3-6:重定位節(jié)展示
解析:分析過程與之前類似,由于對(duì)于可執(zhí)行目標(biāo)文件來說,仍然會(huì)存在重定位信息,因?yàn)橛行┬枰獎(jiǎng)討B(tài)鏈接的塊還沒有被鏈接,重定位節(jié)中就給出了這些符號(hào)的相關(guān)信息。
5.3.7符號(hào)表
?? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖5-3-7:符號(hào)表內(nèi)容
解析:對(duì)于可執(zhí)行目標(biāo)文件來說,包含兩個(gè)符號(hào)表,一個(gè)符號(hào)表的名稱為.dynsym, 從名稱和符號(hào)表中的內(nèi)容來看應(yīng)該是還沒有動(dòng)態(tài)鏈接的一些未知符號(hào)。另一張符 號(hào)表就是熟知的.symtab,里面保存了程序中定義和引用的函數(shù)以及全局變量等的 信息,其余分析與之前類似。
5.3.8版本信息
??????????????????????????????? 圖5-3-8:關(guān)于版本信息的節(jié)
解析:這部分不重要,只是介紹版本信息而已。
5.4 hello的虛擬地址空間
???????? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? 圖5-4-1:Date Dump窗口
解析:打開edb,加載程序后,打開edb的Date Dump窗口,查看本進(jìn)程的虛擬地址空間各段信息,并與5.3對(duì)照分析說明。通過查看edb可以發(fā)現(xiàn)虛擬地址開始于0x400000,如上圖所示。
??????? ? ? ? ? ? ? ? ? ???????????????????????? 圖5-4-2:Date Dump窗口
解析:根據(jù)查看的信息我們發(fā)現(xiàn)虛擬地址結(jié)束于0x400ff0
然后根據(jù)節(jié)頭目表,我們可以通過edb查看各個(gè)節(jié)的信息,比如.text節(jié),虛擬地址起始于0x4010f0,大小為0x145,如下圖所示:
???????? ? ? ? ? ? ??????????????????? 圖5-4-3:Data Dump尋找./test節(jié)部分
同理我們還可以同樣找到其他節(jié)在虛擬地址空間例的信息
5.5 鏈接的重定位過程分析
5.5.1生成hello的反匯編文件
命令:objdump -d -r hello > hello_objdump.s ??如圖:
??????????????????????????? 圖5-5-1:生成hello的反匯編文件
5.5.2 hello與hello.o反匯編文件的差異:
(1)代碼增多
???????????????
??圖5-5-2-1:hello.o反匯編內(nèi)容 ???????????圖5-5-2-2:hello反匯編內(nèi)容
解析:可以看出來hello反匯編多了很多代碼,接下來我會(huì)慢慢介紹為什么會(huì)多出這些代碼
(2)增加了外部的共享庫(kù)函數(shù)
???????????????????? 圖5-5-2-3:hello反匯編代碼關(guān)于共享庫(kù)的部分
解析:在鏈接之后,鏈接器會(huì)把共享庫(kù)里面被hello.o里面調(diào)用的函數(shù)復(fù)制進(jìn)去,所以代碼增加了不少。
(3)函數(shù)調(diào)用地址不同
???????????????????? 圖5-5-2-4:hello.o反匯編文件函數(shù)調(diào)用部分
????????? ? ? ? ? ? ?圖5-5-2-5:hello反匯編文件函數(shù)調(diào)用部分
分析:hello中沒有hello.o中的重定位條目,并且跳轉(zhuǎn)和函數(shù)調(diào)用的地址在hello中都變成了虛擬內(nèi)存空間地址。對(duì)于hello.o的反匯編代碼,函數(shù)只有在鏈接之后才能確定運(yùn)行執(zhí)行的地址,所以在.rela.text節(jié)中為其添加了重定位條目。所以在hello.o文件中,函數(shù)的調(diào)用時(shí)直接用當(dāng)前的下一個(gè)地址占位,而在hello文件中是跳轉(zhuǎn)到一個(gè)【函數(shù)名@plt】的函數(shù)的地址處。
(4).text段的內(nèi)容增多了
? ??????? ???????
? 圖5-5-2-6:hello.o反匯編./test部分 ???????圖5-5-2-7:hello反匯編./test部分
分析:發(fā)現(xiàn)左方的.text段只有main函數(shù),右方的.text段不僅包含.main,更包含main函數(shù)的很多入口函數(shù),如_start之類,所以./test變長(zhǎng)了很多。
5.5.3 鏈接的過程:
通過分析hello和hello.o的不同之處,得到鏈接的主要過程就是鏈接就是鏈接器ld將各個(gè)目標(biāo)文件(即各種.o文件)整合組裝在一起時(shí)其各個(gè)目標(biāo)文件中的各個(gè)庫(kù)函數(shù)段會(huì)按照一定的規(guī)則累積在一起,需要進(jìn)行符號(hào)解析、重定位及計(jì)算符號(hào)引用地址三個(gè)步驟。
5.5.4重定位:
重定位將合并輸入模塊。并為每個(gè)符號(hào)分配運(yùn)行地址。重定位由兩個(gè)步驟組成:重定位節(jié)與符號(hào)定義、重定位節(jié)中的符號(hào)引用。
1.定位節(jié)與符號(hào)定義,鏈接器將相同類型的節(jié)合并為同一類型的新的聚合節(jié),此后鏈接器將運(yùn)行時(shí)內(nèi)存地址賦值新的聚合節(jié)、輸入模塊定義的每個(gè)節(jié),還有輸入模塊定義的每個(gè)符號(hào)。
2.重定位節(jié)中的符號(hào)引用,鏈接器修改代碼節(jié)與數(shù)據(jù)節(jié)中對(duì)每個(gè)符號(hào)的引用,使他們指向正確的運(yùn)行地址,這一步依賴hello.o中的重定位條目。
5.6 hello的執(zhí)行流程
??????????????????????? 圖5-6:Ubuntu下edb執(zhí)行
通過edb的調(diào)試記錄下每列函數(shù)調(diào)用call命令進(jìn)入的函數(shù)如下所示:
這其中的子函數(shù)名和地址(后6位)如下所示:
<_init>????? 401000
<.plt>?????? 401020
<puts@plt>? 401090
<printf@plt> 4010a0
<getchar@plt>4010b0
<atoi@plt>? 4010c0
<exit@plt>? 4010d0
<sleep@plt> 4010e0
<_start>???? 4010f0
<_dl_relocate_static_pie> 401120
<main>?????????????? 401125
<__libc_csu_init>?????? 4011c0
<__libc_csu_fini>?????? 401230
<_fini>??????????????? 401238
5.7 Hello的動(dòng)態(tài)鏈接分析
???????? ?? ? ? ? ? ? ? ? ?圖5-7-1:hello的節(jié)頭部表
分析:在elf文件中我們可以找到got pit開頭地址為0x404000,在edb中找到它
????????????????????????? 圖5-7-2:dl_init之前窗口
dl_init之后
???????? ? ? ? ? ? ? ? ? ? 圖5-7-3:dl_init之后窗口
分析:找到的地址已經(jīng)由0變成了相應(yīng)的偏移量。
5.8 本章小結(jié)
在本章中先介紹了鏈接的概念和作用,還介紹了然后在Ubuntu下利用ld命令實(shí)現(xiàn)鏈接,然后導(dǎo)出hello的ELF文件,分析該ELF文件里面的具體內(nèi)容,并與hello.o的ELF做對(duì)比,分析異同,然后分析了hello的虛擬地址空間、重定位過程、執(zhí)行流程、以及動(dòng)態(tài)鏈接過程。
(第5章1分)
第6章 hello進(jìn)程管理
6.1 進(jìn)程的概念與作用
進(jìn)程的概念:
經(jīng)典定義是一個(gè)執(zhí)行中程序的實(shí)例,提供給我們一種錯(cuò)覺:我們的程序好像是系統(tǒng)中當(dāng)前運(yùn)行的唯一程序,我們的程序獨(dú)占使用處理器和內(nèi)存,處理器好像是無間斷的執(zhí)行我們程序中的指令,我們程序的代碼和數(shù)據(jù)好像是系統(tǒng)中內(nèi)存唯一的對(duì)象。
進(jìn)程的作用:
進(jìn)程提供給了應(yīng)用程序兩個(gè)關(guān)鍵抽象
一個(gè)獨(dú)立的邏輯控制流,它提供一個(gè)假象,好像我們的程序獨(dú)占地使用處理器。
一個(gè)私有的地址空間,它提供一個(gè)假象,好像我們的程序獨(dú)占地使用內(nèi)存系統(tǒng)。
6.2 簡(jiǎn)述殼Shell-bash的作用與處理流程
作用:Shell執(zhí)行一系列的讀取解析步驟,然后終止。讀取步驟讀取來自用戶的一個(gè)命令行。解析步驟將命令行解析,并運(yùn)行程序。
處理流程:
(1)終端進(jìn)程讀取用戶由鍵盤輸入的命令行。
(2)分析命令行字符串,獲取命令行參數(shù),并構(gòu)造傳遞給execve的argv向量
(3)檢查第一個(gè)(首個(gè)、第0個(gè))命令行參數(shù)是否是一個(gè)內(nèi)置的shell命令
(4)如果不是內(nèi)部命令,調(diào)用fork( )創(chuàng)建新進(jìn)程/子進(jìn)程
(5)在子進(jìn)程中,用步驟2獲取的參數(shù),調(diào)用execve( )執(zhí)行指定程序。
(6)如果用戶沒要求后臺(tái)運(yùn)行(命令末尾沒有&號(hào))否則shell使用waitpid(或wait等待作業(yè)終止后返回。
(7)如果用戶要求后臺(tái)運(yùn)行(如果命令末尾有&號(hào)),則shell返回;
6.3 Hello的fork進(jìn)程創(chuàng)建過程
根據(jù) shell 的處理流程,可以推斷,輸入命令執(zhí)行 hello 后,父進(jìn)程如果判斷不是內(nèi)部指令,即會(huì)通過fork函數(shù)創(chuàng)建子進(jìn)程。子進(jìn)程與父進(jìn)程近似,并得到一份與父進(jìn)程用戶級(jí)虛擬空間相同且獨(dú)立的副本——包括數(shù)據(jù)段、代碼、共享庫(kù)、堆和用戶棧。父進(jìn)程打開的文件,子進(jìn)程也可讀寫。二者之間最大的不同或許在于 PID 的不同。Fork函數(shù)只會(huì)被調(diào)用一次,但會(huì)返回兩次,在父進(jìn)程中,fork返回子進(jìn)程的 PID,在子進(jìn)程中,fork返回0。
6.4 Hello的execve過程
當(dāng)Hello的進(jìn)程被創(chuàng)建之后,他會(huì)調(diào)用execve函數(shù)加載并調(diào)用程序。exevce函數(shù)在被調(diào)用時(shí)會(huì)在當(dāng)前進(jìn)程的上下文中加載并運(yùn)行一個(gè)新程序。它被調(diào)用一次從不返回,執(zhí)行過程如下:
1.刪除已存在的用戶區(qū)域
2.映射私有區(qū):為 hello 的代碼、數(shù)據(jù)、.bss 和棧區(qū)域創(chuàng)建新的區(qū)域結(jié)構(gòu),所有這些區(qū)域都是私有的、寫時(shí)才復(fù)制的
3.映射共享區(qū):比如 hello 程序與共享庫(kù) libc.so 鏈接
4.設(shè)置 PC:exceve() 做的最后一件事就是設(shè)置當(dāng)前進(jìn)程的上下文中的程序計(jì)數(shù)器,使之指向代碼區(qū)域的入口點(diǎn)
5.execve() 在調(diào)用成功的情況下不會(huì)返回,只有當(dāng)出現(xiàn)錯(cuò)誤時(shí),例如找不到需要執(zhí)行的程序時(shí),execve() 才會(huì)返回到調(diào)用程序
6.5 Hello的進(jìn)程執(zhí)行
6.5.1邏輯控制流:
一系列程序計(jì)數(shù)器PC的值的序列叫做邏輯控制流,這些值唯一地對(duì)應(yīng)于包含在程序的可執(zhí)行目標(biāo)文件中的指令,或是包含在運(yùn)行時(shí)動(dòng)態(tài)鏈接到程序的共享對(duì)象中的指令。各個(gè)進(jìn)程將輪流使用處理器,在同一個(gè)處理器核心中,每個(gè)進(jìn)程執(zhí)行它的流的一部分后被暫時(shí)掛起,然后執(zhí)行其他進(jìn)程。
6.5.2用戶模式與內(nèi)核模式:
處理器通過某個(gè)控制寄存器中的一個(gè)模式位來提供限制一個(gè)應(yīng)用可以執(zhí)行的指令以及它可以訪問的地址空間范圍的功能。該寄存器描述了當(dāng)前進(jìn)程運(yùn)行的權(quán)限。當(dāng)設(shè)置了模式位時(shí),進(jìn)程就運(yùn)行在內(nèi)核模式中。沒有設(shè)置模式位時(shí),進(jìn)程就運(yùn)行在用戶模式中。一個(gè)運(yùn)行在內(nèi)核模式的進(jìn)程可以執(zhí)行指令集中的任何指令,并且可以訪問系統(tǒng)中的任何內(nèi)存;而用戶模式的進(jìn)程不允許和執(zhí)行特權(quán)指令、也不允許用戶模式中的進(jìn)程直接引用地址空間中內(nèi)核區(qū)內(nèi)的代碼和數(shù)據(jù)。
6.5.3上下文切換:
內(nèi)核為每個(gè)進(jìn)程維持一個(gè)上下文。上下文就是內(nèi)核重新啟動(dòng)的一個(gè)進(jìn)程所需的狀態(tài)。這個(gè)狀態(tài)包括通用目的寄存器、浮點(diǎn)寄存器、程序計(jì)數(shù)器、用戶棧、狀態(tài)寄存器、內(nèi)核棧和各種內(nèi)核數(shù)據(jù)結(jié)構(gòu)。
6.5.4進(jìn)程時(shí)間片:
一個(gè)進(jìn)程執(zhí)行它的控制流的一部分的每一時(shí)間段叫做時(shí)間片。
6.5.5調(diào)度的過程:
在進(jìn)程執(zhí)行的某些時(shí)刻,內(nèi)核可以決定搶占當(dāng)前進(jìn)程,并且可以重新開始一個(gè)先前被搶占了的進(jìn)程,這種決策便稱為調(diào)度。調(diào)度是由內(nèi)核中的調(diào)度器代碼處理的。當(dāng)內(nèi)核選擇一個(gè)新進(jìn)程運(yùn)行時(shí),即內(nèi)核調(diào)度了這個(gè)進(jìn)程。在內(nèi)核調(diào)度一個(gè)新的進(jìn)程運(yùn)行之后,它就可以稱之為搶占了當(dāng)前進(jìn)程,并使用上下文切換機(jī)制來將控制轉(zhuǎn)移到新的進(jìn)程。例如執(zhí)行sleep()函數(shù),sleep()函數(shù)請(qǐng)求調(diào)用休眠進(jìn)程,sleep()將內(nèi)核搶占,進(jìn)入倒計(jì)時(shí),當(dāng)?shù)褂?jì)時(shí)結(jié)束后,hello程序重新?lián)屨純?nèi)核,繼續(xù)執(zhí)行。
6.5.6? 用戶模式與核心模式的相互轉(zhuǎn)換:
為了能讓處理器安全運(yùn)行,從而不至于損壞操作系統(tǒng),必須提前已知應(yīng)用程序可執(zhí)行指令所能訪問的地址空間范圍。因此就存在用戶模式與核心模式的劃分:核心模式可以戲稱為“創(chuàng)世模式”,因?yàn)樗鼡碛凶罡咴L問權(quán)限,此時(shí)處理器有以一個(gè)寄存器當(dāng)做模式位來描述當(dāng)前進(jìn)程的特權(quán)。進(jìn)程只有故障、中斷或陷入系統(tǒng)調(diào)用時(shí)才會(huì)得到內(nèi)核訪問權(quán)限,其他情況下始終處于用戶權(quán)限之中,從而保證了系統(tǒng)的安全性。
6.6 hello的異常與信號(hào)處理
6.6.1異常的種類:
| 類別 | 原因 | 異步/同步 | 返回行為 |
| 中斷 | 來自I/O設(shè)備的信號(hào) | 異步 | 總是返回到下一條指令 |
| 陷阱 | 有意的異常 | 同步 | 總是返回到下一條指令 |
| 故障 | 潛在可恢復(fù)的錯(cuò)誤 | 同步 | 可能返回到當(dāng)前指令 |
| 終止 | 不可恢復(fù)的錯(cuò)誤 | 同步 | 不會(huì)返回 |
?????????????????????? 表6-6-1:異常的種類
6.6.2產(chǎn)生信號(hào):
信號(hào)可以被理解為一條小消息,他通知進(jìn)程系統(tǒng)中發(fā)生了一個(gè)某種類型的事件。每種信號(hào)類型都對(duì)應(yīng)于某種系統(tǒng)事件,它提供了一種機(jī)制,通知用戶進(jìn)程發(fā)生了這些異常。發(fā)生異常后,系統(tǒng)會(huì)根據(jù)異常的種類發(fā)出信號(hào),每個(gè)信號(hào)都有對(duì)應(yīng)的序號(hào)。如下圖:
???????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????????????? 圖6-6-2 Linux信號(hào)
6.2.3執(zhí)行過程中操作實(shí)例:
正常執(zhí)行:
??????????????????????????????? 圖6-2-3-1:正常執(zhí)行
在運(yùn)行過程中按下Ctrl-Z:
??????????????????????????????? 圖6-2-3-2:按下Ctrl-Z
按下Ctrl-Z后接其他指令:
??????????????????????? 圖6-2-3-3:按下Ctrl-Z+ps
分析:分析進(jìn)程仍然處于后臺(tái)。
再按下jobs:
????????
? ? ? ? ? ? ? 圖6-2-3-4:按下jobs
結(jié)果:進(jìn)程停止。
按下fg:
?
? ? ? ? ? ? ? ? ? ? 圖6-2-3-5:按下fg
結(jié)果:使該進(jìn)程轉(zhuǎn)為前臺(tái),繼續(xù)工作
按下kill:
????????
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖6-2-3-6:按下kill
結(jié)果:殺死進(jìn)程。
按下pstree:
???????? ? ? ? ? ? ? ? ? ???? 圖6-2-3-7:按下pstree
6.2.4異常與信號(hào)處理說明:
當(dāng)按下鍵盤的時(shí)候, 引發(fā)了中斷異常.如圖:
????????
???????????????????????? 圖6-2-4-1中斷異常實(shí)例
而當(dāng)程序在進(jìn)行顯示屏輸出的時(shí)候:
引發(fā)的是陷阱:
????????
???????????????????? 圖6-2-4-1中斷異常實(shí)例
通過陷阱,調(diào)用系統(tǒng)函數(shù).將字符串打印到屏幕上面
信號(hào)處理:
對(duì)于kill -9 12350:直接將SIGKILL發(fā)送到12350進(jìn)程,使其直接終止.
對(duì)于CTRL Z:將SIGINT/SIGSTP由內(nèi)核發(fā)送到12350號(hào)進(jìn)程.SIGINT直接終止12350.而SIGSTP將12350號(hào)進(jìn)程進(jìn)行停止與掛起.
6.7本章小結(jié)
本章介紹了進(jìn)程的相關(guān)概念和作用,了解了shell的處理過程,以及如何利用fork創(chuàng)建子進(jìn)程,介紹了進(jìn)程調(diào)度和內(nèi)核用戶模式切換,好介紹了4種異常和各種信號(hào),最后根據(jù)實(shí)際操作進(jìn)行分析。
(第6章1分)
第7章 hello的存儲(chǔ)管理
7.1 hello的存儲(chǔ)器地址空間
結(jié)合hello說明邏輯地址、線性地址、虛擬地址、物理地址的概念。
7.2 Intel邏輯地址到線性地址的變換-段式管理
7.3 Hello的線性地址到物理地址的變換-頁(yè)式管理
7.4 TLB與四級(jí)頁(yè)表支持下的VA到PA的變換
7.5 三級(jí)Cache支持下的物理內(nèi)存訪問
7.6 hello進(jìn)程fork時(shí)的內(nèi)存映射
7.7 hello進(jìn)程execve時(shí)的內(nèi)存映射
7.8 缺頁(yè)故障與缺頁(yè)中斷處理
7.9動(dòng)態(tài)存儲(chǔ)分配管理
Printf會(huì)調(diào)用malloc,請(qǐng)簡(jiǎn)述動(dòng)態(tài)內(nèi)存管理的基本方法與策略。
7.10本章小結(jié)
第8章 hello的IO管理
8.1 Linux的IO設(shè)備管理方法
設(shè)備的模型化:文件
設(shè)備管理:unix io接口
8.2 簡(jiǎn)述Unix IO接口及其函數(shù)
8.3 printf的實(shí)現(xiàn)分析
[轉(zhuǎn)]printf 函數(shù)實(shí)現(xiàn)的深入剖析 - Pianistx - 博客園
從vsprintf生成顯示信息,到write系統(tǒng)函數(shù),到陷阱-系統(tǒng)調(diào)用 int 0x80或syscall等.
字符顯示驅(qū)動(dòng)子程序:從ASCII到字模庫(kù)到顯示vram(存儲(chǔ)每一個(gè)點(diǎn)的RGB顏色信息)。
顯示芯片按照刷新頻率逐行讀取vram,并通過信號(hào)線向液晶顯示器傳輸每一個(gè)點(diǎn)(RGB分量)。
8.4 getchar的實(shí)現(xiàn)分析
異步異常-鍵盤中斷的處理:鍵盤中斷處理子程序。接受按鍵掃描碼轉(zhuǎn)成ascii碼,保存到系統(tǒng)的鍵盤緩沖區(qū)。
getchar等調(diào)用read系統(tǒng)函數(shù),通過系統(tǒng)調(diào)用讀取按鍵ascii碼,直到接受到回車鍵才返回。
8.5本章小結(jié)
結(jié)論
Hello程序自被編寫出代碼起,經(jīng)歷了以下幾個(gè)階段:
首先經(jīng)過預(yù)處理解析宏定義、文件包含、條件編譯等,生成hello.i文件,然后編譯器將代碼轉(zhuǎn)化為匯編代碼,生成hello.s文件,然后將匯編語言轉(zhuǎn)換為機(jī)器語言,生成hello.o文件,然后鏈接器進(jìn)行重定位動(dòng)態(tài)解析等一系列操作,最終生成hello的可執(zhí)行文件。然后父進(jìn)程shell-bash進(jìn)程調(diào)用fork函數(shù),為hello創(chuàng)建進(jìn)程,調(diào)用execve函數(shù)在當(dāng)前進(jìn)程的上下文加載并運(yùn)行hello程序,該進(jìn)程映射它的虛擬空間到文件,運(yùn)行的過程當(dāng)中伴隨著虛擬地址到物理地址的轉(zhuǎn)換,調(diào)用的函數(shù)與I/O設(shè)備緊密結(jié)合。運(yùn)行結(jié)束后,子進(jìn)程終止,最終被父進(jìn)程回收。至此,hello程序結(jié)束了自己的一次運(yùn)行。
一學(xué)期的計(jì)算機(jī)系統(tǒng)學(xué)習(xí),雖然很累,不過收獲真的很大,感謝一學(xué)期老師和助教的辛苦付出!
(結(jié)論0分,缺失 -1分,根據(jù)內(nèi)容酌情加分)
附件
列出所有的中間產(chǎn)物的文件名,并予以說明起作用。
hello.c:源程序
hello.i:經(jīng)過預(yù)處理過后的文件;
hello.s:經(jīng)過編譯生成的匯編文件;
hello.o:匯編生成的可重定位的文件;
hello:通過鏈接產(chǎn)生的可執(zhí)行目標(biāo)文件二進(jìn)制文件;
elf.txt: 可重定位目標(biāo)文件hello.o導(dǎo)出的ELF文件;
hello_elf.txt:可執(zhí)行文件hello導(dǎo)出的ELF文件;
objdump_hello.s:可重定位目標(biāo)文件hello.o反匯編生成的文件
hello_objdump.s:可執(zhí)行文件hello反匯編生成的文件
(附件0分,缺失 -1分)
參考文獻(xiàn)
為完成本次大作業(yè)你翻閱的書籍與網(wǎng)站等
[1]?Randal E.Bryant, David O'Hallaron. 深入理解計(jì)算機(jī)系統(tǒng)[M]. 機(jī)械工業(yè)出版社.2019.6
[2] shell解析命令行的過程以及eval命令https://www.cnblogs.com/f-ck-need-u/p/7426371.html
[3]?printf?函數(shù)實(shí)現(xiàn)的深入剖析?https://www.cnblogs.com/pianist/p/3315801.html
[4]老師上課講的PPT
(參考文獻(xiàn)0分,缺失 -1分)
總結(jié)
以上是生活随笔為你收集整理的程序人生-Hello’s P2P的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cad 打开硬件加速卡_CAD:“你的图
- 下一篇: 阿里查出售假店主并索赔140万,这次是大