哈工大计统实验
計算機系統(tǒng)
大作業(yè)
題 ????目 ?程序人生-Hello’s P2P???
專?????? 業(yè) ??????計算學(xué)部??????????
學(xué) ?? 號 ???????120L020815???????
班 ?? 級 ?????????2036011????????
學(xué)?????? 生 ???????趙美含????????? ??
指 導(dǎo) 教 師 ??????????劉松波?????????
計算機科學(xué)與技術(shù)學(xué)院
2021年5月
摘? 要
本文從簡單的hello.c程序入手,介紹了該程序經(jīng)過預(yù)處理,編譯,匯編,鏈接后生成可執(zhí)行文件的過程。同時本文還介紹了linux下的內(nèi)存管理、進程管理、虛擬內(nèi)存、異常信號的相關(guān)內(nèi)容,通過本篇文章,對本學(xué)習(xí)的知識進行了總結(jié)。
關(guān)鍵詞:hello.c;預(yù)處理;編譯;鏈接;進程;存儲;???????????????????????????
(摘要0分,缺失-1分,根據(jù)內(nèi)容精彩稱都酌情加分0-1分)
目? 錄
第1章 概述................................................... - 4 -
1.1 Hello簡介............................................ - 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 可重定位目標elf格式........................ - 7 -
4.4 Hello.o的結(jié)果解析............................. - 7 -
4.5 本章小結(jié)............................................... - 7 -
第5章 鏈接................................................... - 8 -
5.1 鏈接的概念與作用............................... - 8 -
5.2 在Ubuntu下鏈接的命令.................... - 8 -
5.3 可執(zhí)行目標文件hello的格式........... - 8 -
5.4 hello的虛擬地址空間......................... - 8 -
5.5 鏈接的重定位過程分析....................... - 8 -
5.6 hello的執(zhí)行流程................................. - 8 -
5.7 Hello的動態(tài)鏈接分析........................ - 8 -
5.8 本章小結(jié)............................................... - 9 -
第6章 hello進程管理.......................... - 10 -
6.1 進程的概念與作用............................. - 10 -
6.2 簡述殼Shell-bash的作用與處理流程.. - 10 -
6.3 Hello的fork進程創(chuàng)建過程............ - 10 -
6.4 Hello的execve過程........................ - 10 -
6.5 Hello的進程執(zhí)行.............................. - 10 -
6.6 hello的異常與信號處理................... - 10 -
6.7本章小結(jié).............................................. - 10 -
第7章 hello的存儲管理...................... - 11 -
7.1 hello的存儲器地址空間................... - 11 -
7.2 Intel邏輯地址到線性地址的變換-段式管理............................................................ - 11 -
7.3 Hello的線性地址到物理地址的變換-頁式管理........................................................ - 11 -
7.4 TLB與四級頁表支持下的VA到PA的變換................................................................ - 11 -
7.5 三級Cache支持下的物理內(nèi)存訪問 - 11 -
7.6 hello進程fork時的內(nèi)存映射......... - 11 -
7.7 hello進程execve時的內(nèi)存映射..... - 11 -
7.8 缺頁故障與缺頁中斷處理................. - 11 -
7.9動態(tài)存儲分配管理.............................. - 11 -
7.10本章小結(jié)............................................ - 12 -
第8章 hello的IO管理....................... - 13 -
8.1 Linux的IO設(shè)備管理方法................. - 13 -
8.2 簡述Unix IO接口及其函數(shù).............. - 13 -
8.3 printf的實現(xiàn)分析.............................. - 13 -
8.4 getchar的實現(xiàn)分析.......................... - 13 -
8.5本章小結(jié).............................................. - 13 -
結(jié)論............................................................... - 14 -
附件............................................................... - 15 -
參考文獻....................................................... - 16 -
第1章 概述
1.1 Hello簡介
作為用戶的我們首先需要按照語法規(guī)則編寫hello.c,當我們想要運行hello.c文件時,hello.c文件得首先經(jīng)過預(yù)處理,編譯,匯編,鏈接4個階段從hello.c文本文件轉(zhuǎn)變?yōu)榭蓤?zhí)行文件,這樣我們的機器才能運行這個文件。
以linux系統(tǒng)為例,當我們在終端運行可執(zhí)行文件時,shell解析這段命令,使用fork為我們的hello創(chuàng)建子進程,至此完成了從program到progress的過程。
當程序完成p2p過程之后,就進入了020過程
在此過程中, shell 為我們的程序調(diào)用 execve函數(shù),execve函數(shù)啟動加載器為該進程分配獨立的虛擬內(nèi)存空間,程序運行在物理內(nèi)存中,CPU 為其分配時間片執(zhí)行指令,調(diào)用系統(tǒng)I/O,printf函數(shù)顯示相應(yīng)的功能。當程序運行結(jié)束后,shell 父進程負責回收 hello 進程。
至此,020過程也告一段落。
1.2 環(huán)境與工具
1.2.1 硬件環(huán)境
?
圖1 硬件環(huán)境
1.2.2 軟件環(huán)境
Windows:??? windows10、Visual studio 2022、VMware Workstation Pro、code blocks
Ubuntu:?????? Ubuntu20.04.3(64位)、code blocks
1.2.3 開發(fā)工具
Visual studio 2022
Code Blocks
1.3 中間結(jié)果
Hello.c hello.i hello.o hello.s
1.4 本章小結(jié)
本章為本篇論文的一個概述,簡單介紹了p2p過程和020過程,介紹了編寫此論文使用的軟硬件環(huán)境和開發(fā)調(diào)試工具以及編寫論文過程中的中間結(jié)果。
(第1章0.5分)
第2章 預(yù)處理
2.1 預(yù)處理的概念與作用
概念:預(yù)處理一般是指在程序源代碼被翻譯為目標代碼的過程中,生成二進制代碼之前的過程。預(yù)處理器(cpp)根據(jù)以#開頭的命令,修改顯示的C程序。
作用:
1)將源文件中用#include形式聲明的文件復(fù)制到新的程序中。比如hello.c第6-8行中的#include<stdio.h> 等命令告訴預(yù)處理器讀取系統(tǒng)頭文件stdio.h unistd.h stdlib.h 的內(nèi)容,并把它直接插入到程序文本中。
2)用實際值替換用#define定義的字符串
3)根據(jù)#if后面的條件決定需要編譯的代碼
4)還包括此次hello中沒有的#line,#error,#pragma,以及單獨的空指令的處理。
2.2在Ubuntu下預(yù)處理的命令
預(yù)處理指令:gcc -E hello.c -o hello.i
如下圖所示:
得到hello.i文件:
2.3 Hello的預(yù)處理結(jié)果解析
·針對如下三條語句的預(yù)處理
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
2.4 本章小結(jié)
本章介紹了預(yù)處理的概念和作用,以及預(yù)處理的命令和結(jié)果解析。預(yù)處理器cpp將一個hello.c源程序編譯為hello.i,原理是根據(jù)以字符#開頭的命令,修改原始的C程序。
(第2章0.5分)
第3章 編譯
3.1 編譯的概念與作用
概念:編譯階段是編譯器ccl將文本文件hello.i翻譯成hello.s,它包含一個匯編語言程序,該程序包含一個main的定義
作用:把高級語言翻譯成機器可以“聽懂”的機器語言,連接高級語言和機器語言的橋梁。
3.2 在Ubuntu下編譯的命令
指令:gcc -S hello.i -o hello.s,下圖為編譯展示:
編譯結(jié)果:
3.3 Hello的編譯結(jié)果解析
3.3.1開頭匯編指令:
.file——C文件聲明
.text——代碼段
.section .radata——只讀數(shù)據(jù)段
.align 8——聲明對指令或者數(shù)據(jù)的存放地址進行對齊的方式
.string——聲明string型數(shù)據(jù)
3.3.2.cfi指令
Call Frame infromation的意思,.cfi_startproc 用在每個函數(shù)的開始,用于初始化一些內(nèi)部數(shù)據(jù)結(jié)構(gòu),.cfi_endproc 在函數(shù)結(jié)束的時候使用與.cfi_startproc相配套使用。
3.3.3 常量
即字符串常量
保存在如下數(shù)據(jù)段中
3.3.4變量
局部變量為for循環(huán)中的i,由于i最初被賦值為0,所以找到賦值0操作的,即為i,為第42行。
由上圖還可以看到第一個參數(shù)被復(fù)制到-20(%rbp)棧中
該程序包含argv[]數(shù)組的三個元素:
數(shù)組中的元素一般保存在連續(xù)地址中,尋址方式即可找到數(shù)組:
3.3.5賦值操作
本代碼中復(fù)制操作是在進行for循壞過程中,對局部變量i進行的賦值操作,把0賦值給i:
3.3.6算數(shù)操作
也就是for循環(huán)中每次的i++,見下圖51行,每次都加一,每次都在L3中判斷for循環(huán)中的條件,滿足則跳到L4,執(zhí)行一系列指令后將i+1。
3.3.7判斷操作
3.3.8控制轉(zhuǎn)移
在判斷之后會有條件控制,不同的CC會跳轉(zhuǎn)到不同的指令,控制轉(zhuǎn)移分別分為if控制轉(zhuǎn)移和for循環(huán)的控制轉(zhuǎn)移。
相等的話跳轉(zhuǎn)到L2,L2賦值之后跳轉(zhuǎn)到L3,不相等的話繼續(xù)執(zhí)行,打印語句。
L2轉(zhuǎn)移到L3,L3是一個for循環(huán),每次都會比較i和7的大小,如果小于等于就會跳轉(zhuǎn)到L4執(zhí)行for循環(huán)中的指令,不相等則退出for循環(huán)。
3.3.9數(shù)組指針操作
數(shù)組元素有字符串數(shù)組argv[],指針有指向數(shù)組argv的指針。
對于數(shù)組元素的操作,通常是將數(shù)組首個元素的地址放入棧中,而在該程序中也是這樣,將數(shù)組argv首元素,也是指向argv的指針的地址放入了-32(%rbp)中。
在for循環(huán)體中,用起始地址加上偏移量字節(jié)大小來訪問的,采用這種尋址方式,就能訪問到數(shù)組中的每個元素,如下圖所示:
3.3.10函數(shù)操作
函數(shù)調(diào)用使用call指令
函數(shù)返回使用ret指令
3.4 本章小結(jié)
本章主要介紹匯編過程,使用指令gcc -S hello.i -o hello.s,把hello.i轉(zhuǎn)為hello.s,即把hello.s翻譯成機器能聽懂的匯編語言,是高級語言和機器語言之間的橋梁,本章介紹了匯編語言的各種指令,如call ret move 判斷等指令,使學(xué)生掌握了看懂匯編語言的能力。
(第3章2分)
第4章 匯編
4.1 匯編的概念與作用
匯編概念:匯編階段是指匯編器(as)將hello.s翻譯成機器語言指令,把這些指令打包成一種叫做可重定位目標程序的格式,并且保存在hello.o中。
匯編作用:將匯編語言翻譯為機器語言,并將相關(guān)指令以可重定位目標程序格式保存在.o文件中
4.2 在Ubuntu下匯編的命令
命令:as hello.s -o hello.o
4.3 可重定位目標elf格式
ELF頭以一個16字節(jié)的序列開始,這個序列描述了生成該文件系統(tǒng)下的字的大小以及一些其他信息。ELF頭剩下的部分包含幫助鏈接器語法分析和解釋目標文件的信息:包括ELF頭的大小、目標文件的類型、機器類型、節(jié)頭部表的文件偏移,以及節(jié)頭部表中條目的大小和數(shù)量。具體ELF頭的代碼如下:
描述了.o文件中每一個節(jié)出現(xiàn)的位置,大小,目標文件中的每一個節(jié)都有一個固定大小的條目。具體內(nèi)容如下圖所示:
重定位是將EFL文件中的未定義符號關(guān)聯(lián)到有效值的處理過程。在hello.o中,對printf,exit等函數(shù)的未定義的引用和全局變量(sleepsecs)替換為該進程的虛擬地址空間中機器代碼所在的地址。
符號表(.symtab)是用來存放程序中定義和引用的函數(shù)和全局變量的信息。重定位需要引用的符號都在其中聲明。具體信息如下所示:
4.4 Hello.o的結(jié)果解析
通過objdump -d -r hello.o? 生成hello.o的反匯編,下面與第3章的 hello.s進行對照分析。
與hello.s對比的差別如下:
4.5 本章小結(jié)
本章介紹了hello從hello.s到hello.o的匯編過程,查看了hello.o的ELF格式,使用objdump得到反匯編代碼,還將其與hello.s進行了比較,了解到了從匯編到機器匯編的轉(zhuǎn)換。
(第4章1分)
第5章 鏈接
5.1 鏈接的概念與作用
鏈接概念:連接的概念是指連接器(ld)將存有printf等函數(shù)的預(yù)編譯好的目標文件合并到hello.o程序的過程。如在hello函數(shù)中調(diào)用了printf函數(shù),它是每個編譯器都提供的標準C庫中的一個函數(shù),printf函數(shù)存在于一個預(yù)編譯好的目標文件中,而這個文件要通過ld合并到我們的hello.o文件中。
鏈接的作用:將存有printf等函數(shù)的預(yù)編譯好的目標文件合并到hello.o程序。
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
生成可執(zhí)行文件hello.o
5.3 可執(zhí)行目標文件hello的格式
ELF Header格式如下:
類型為EXEC表示為可執(zhí)行文件。
Section Headers格式如下:
Section Headers:節(jié)頭部表,記錄各節(jié)名稱、類型、地址、偏移量、大小、全體大小、旗標、鏈接、信息、對齊。根據(jù)Section Headers中的信息我們就可以用HexEdit定位各個節(jié)所占的區(qū)間(起始位置,大小)。
Symbol table的信息如下:
5.4 hello的虛擬地址空間
使用edb加載hello,結(jié)果如下:
虛擬地址信息如下:
5.5 鏈接的重定位過程分析
鏈接加入了新的函數(shù),如printf,getchar,sleep 等函數(shù)。
增加了.init和.plt節(jié)
hello.o中標注了重定位信息的部分均被具體的地址,數(shù)據(jù)所代替
5.6 hello的執(zhí)行流程
使用edb執(zhí)行hello,下表展示從加載hello到_start,到call main,以及程序終止的所有過程并列出其調(diào)用與跳轉(zhuǎn)的各個子程序名或程序地址。
5.7 Hello的動態(tài)鏈接分析
在進行動態(tài)鏈接前,首先要進行靜態(tài)鏈接,生成部分鏈接的可執(zhí)行目標文件hello。動態(tài)鏈接采用了延遲加載的策略,即在調(diào)用函數(shù)時才進行符號的映射。使用偏移量表got+過程鏈接表plt實現(xiàn)函數(shù)的動態(tài)鏈接。got中存放函數(shù)目標地址,為每個全局函數(shù)創(chuàng)建一個副本函數(shù),并將對函數(shù)的調(diào)用轉(zhuǎn)換成對副本函數(shù)調(diào)用。
查看dl_init函數(shù)調(diào)用前后.got.plt節(jié)的變化
5.8 本章小結(jié)
鏈接是編譯hello.c程序的最后一步,我們首先了解了連接的概念和作用,然后分析了hello的ELF格式,使用edb加載hello并進行了重定位分析。
(第5章1分)
第6章 hello進程管理
6.1 進程的概念與作用
進程概念:進程是操作系統(tǒng)對一個正在運行的程序的一種抽象。在一個系統(tǒng)上可以同時運行多個進程,而每個進程都好像是單獨的使用硬件,在大多數(shù)系統(tǒng)中,需要運行的進程數(shù)可以多于他們的CPU個數(shù),一個CPU像是在并發(fā)的執(zhí)行多個進程,這就是處理器在進程間切換來實現(xiàn)的。
進程作用:想hello這樣的程序在現(xiàn)代系統(tǒng)上運行時,進程會提供一種假象,好像系統(tǒng)上只有這個程序在運行,程序看上去是獨占的使用處理器,主存和io設(shè)備,處理器看上去好像在不間斷地執(zhí)行程序中的指令,這些假象都是通過進程中的概念來實現(xiàn)的。
6.2 簡述殼Shell-bash的作用與處理流程
shell是一個應(yīng)用程序,他在操作系統(tǒng)中提供了一個用戶與系統(tǒng)內(nèi)核進行交互的界面。它解釋由用戶輸入的命令并且把它們送到內(nèi)核。
1.讀取用戶由鍵盤輸入的命令行。
2.分析命令,以命令名作為文件名,并將其它參數(shù)改造為系統(tǒng)調(diào)用execve( )內(nèi)部處理所要求的形式。
3.終端進程調(diào)用fork( )建立一個子進程。
4.終端進程本身調(diào)用wait4()來等待子進程完成(如果是后臺命令,則不等待)。當子進程運行時調(diào)用execve(),子進程根據(jù)文件名到目錄中查找有關(guān)文件,調(diào)入內(nèi)存,執(zhí)行這個程序。
5.如果命令末尾有&,則終端進程不用執(zhí)行系統(tǒng)調(diào)用wait4(),立即發(fā)提示符,讓用戶輸入下一條命令;否則終端進程會一直等待,當子進程完成工作后,向父進程報告,此時中斷進程醒來,作必要的判別工作后,終端發(fā)出命令提示符,重復(fù)上述處理過程。
6.3 Hello的fork進程創(chuàng)建過程
當我們在終端運行之前得到的可執(zhí)行文件,使用./hello的命令,shell會對輸入的命令行進行解析,因為 hello 不是一個內(nèi)置的shell 命令所以解析之后終端程序判斷./hello的語義為執(zhí)行當前目錄下的可執(zhí)行目標文件 hello,之后終端程序首先會調(diào)用 fork 函數(shù)創(chuàng)建一個新的運行的子進程。
Shell通過調(diào)用fork函數(shù)創(chuàng)建一個新的運行的子程序,新創(chuàng)建的子進程得到與父進程用戶及虛擬內(nèi)存空間相同的一份副本,包括代和數(shù)據(jù)段,堆棧和共享庫。
6.4 Hello的execve過程
調(diào)用函數(shù)fork創(chuàng)建新的子進程之后,子進程會調(diào)用execve函數(shù),在當前進程的上下文中加載并運行一個新程序hello。execve 函數(shù)從不返回,它將刪除該進程的代碼和地址空間內(nèi)的內(nèi)容并將其初始化,然后通過跳轉(zhuǎn)到程序的第一條指令或入口點來運行該程序。將私有的區(qū)域映射進來,然后將公共的區(qū)域映射進來。后面加載器跳轉(zhuǎn)到程序的入口點,即設(shè)置PC指向_start 地址。_start函數(shù)最終調(diào)用hello中的 main 函數(shù),這樣,就完成成了在子進程中的加載。
6.5 Hello的進程執(zhí)行
結(jié)合進程上下文信息、進程時間片,闡述進程調(diào)度的過程,用戶態(tài)與核心態(tài)轉(zhuǎn)換等等。
處理器使用一個寄存器提供兩種模式的區(qū)分。用戶模式的進程不允許執(zhí)行特殊指令,不允許直接引用地址空間中內(nèi)核區(qū)的代碼和數(shù)據(jù);內(nèi)核模式進程可以執(zhí)行指令集中的任何命令,并且可以訪問系統(tǒng)中的任何內(nèi)存位置。
一個進程執(zhí)行它的控制流的一部分的每一時間段叫做時間片。
對于hello的進程執(zhí)行,具體過程如下:鍵盤輸入./hello zmh yeye 1
首先shell通過加載器加載可執(zhí)行目標文件hello,操作系統(tǒng)進行上下文切換,切換到hello的進程中,此時為用戶態(tài),執(zhí)行完相應(yīng)函數(shù)后,調(diào)用sleep函數(shù),進入內(nèi)核態(tài),當sleep的時間完成后時定時器發(fā)送一個中斷信號,此時進入內(nèi)核狀態(tài)執(zhí)行中斷處理,將hello進程從等待隊列中移出重新加入到運行隊列,上下文切換再進入hello進程,回到用戶態(tài)。
6.6 hello的異常與信號處理
hello的異常可以分為四類:中斷,陷阱,故障和終止
1 中斷
來自I/O設(shè)備的信號 異步 總是返回到下一條指令
2 陷阱
有意的異常 同步 總是返回到下一條指令
3 故障
潛在可恢復(fù)的錯誤 同步 可能返回到當前指令
4 終止
不可恢復(fù)的錯誤 同步 不會返回
鍵盤中各種操作導(dǎo)致的異常
6.7本章小結(jié)
本章了解了進程的概念與作用,敘述了shell的處理流程,說明了hello的進程處理和異常信號執(zhí)行。
(第6章1分)
第7章 hello的存儲管理
7.1 hello的存儲器地址空間
程序代碼經(jīng)過編譯后出現(xiàn)在 匯編程序中地址。邏輯地址由選擇符 (在實模式下是描述符,在保護模式下是用來選擇描述符的選擇符)和偏移量(偏 移部分)組成。
也叫虛擬地址,是邏輯地址到物理地址變換之間的中間層,即連續(xù)的虛擬地址。
虛擬地址是程序保護模式下,程序訪問存儲器所使用的邏輯地址稱為虛擬地址,與實地址模式下的分段地址類似,虛擬地址也可以寫為[段:偏移量]的形式,這里的段是指段選擇器。
計算機系統(tǒng)的主存被組織成一個由M個連續(xù)的字節(jié)大小的單元組成的數(shù)組。每個字節(jié)都有一個唯一的物理地址。
7.2 Intel邏輯地址到線性地址的變換-段式管理
一個邏輯地址由兩部份組成,段標識符:段內(nèi)偏移量。段標識符是由一個16位長的字段組成,稱為段選擇符。其中前13位是一個索引號。后面3位包含一些硬件細節(jié)。轉(zhuǎn)換過程如下所示:
7.3 Hello的線性地址到物理地址的變換-頁式管理
通過分頁機制完成,即對虛擬地址的內(nèi)存空間進行分頁。在任意時刻,虛擬頁面的集合都分成三個不相交的子集
系統(tǒng)將每個段分割為被稱為虛擬頁(VP)的大小固定的塊來作為進行數(shù)據(jù)傳輸?shù)膯卧?#xff0c;虛擬地址分為虛擬頁號VPN和虛擬頁偏移量VPO,根據(jù)位數(shù)限制分析可以確定VPN和VPO分別占多少位是多少。
通過頁表基址寄存器PTBR+VPN在頁表中獲得條目PTE,一條PTE中包含有效位、權(quán)限信息、物理頁號。如果有效位是0+NULL則代表沒有在虛擬內(nèi)存空間中分配該內(nèi)存。如果是有效位0+非NULL,則代表在虛擬內(nèi)存空間中分配了但是沒有被緩存到物理內(nèi)存中。如果有效位是1則代表該內(nèi)存已經(jīng)緩存在了物理內(nèi)存中,可以得到其物理頁號PPN,與虛擬頁偏移量共同構(gòu)成物理地址PA。
7.4 TLB與四級頁表支持下的VA到PA的變換
36位VPN被劃分成四個9位的片,每個篇被用作到一個頁表的偏移量。CR3寄存器包含L1頁表的物理地址。VPN1提供一個到L1 PTE的偏移量,這個PTE包含L2頁表的基地址。VPN2提供到一個L2 PTE的偏移量,以此類推。
7.5 三級Cache支持下的物理內(nèi)存訪問
使用上一步得到的PA,首先取組索引對應(yīng)位,向L1cache中尋找對應(yīng)組。如果存在,則比較標志位,并檢查對應(yīng)行的有效位是否為1。如果上述條件均滿足則命中。否則按順序?qū)2cache、L3cache、內(nèi)存進行相同操作,直到出現(xiàn)命中。然后向上級cache返回直到L1cache。如果有空閑塊則將目標塊放置到空閑塊中,否則將緩存中的某個塊驅(qū)逐,將目標塊放到被驅(qū)逐塊的原位置。
7.6 hello進程fork時的內(nèi)存映射
當fork函數(shù)被shell調(diào)用時,會分配給hello一個唯一的PID。為了給hello創(chuàng)建虛擬內(nèi)存,fork創(chuàng)建了當前進程的mm_struct、區(qū)域結(jié)構(gòu)和頁表的原樣副本。它將兩個進程中的每個頁面都標記位只讀,并將兩個進程中的每個區(qū)域結(jié)構(gòu)都標記為私有的寫時復(fù)制。
當fork在hello中返回時,hello現(xiàn)在的虛擬內(nèi)存剛好和調(diào)用shell的虛擬內(nèi)存相同。當這兩個進程中的任何一個進行寫操作時,寫時復(fù)制機制會創(chuàng)建新頁面。因此也就為每個進程保持了私有地址空間的概念。
7.7 hello進程execve時的內(nèi)存映射
Execve函數(shù)在當前進程中加載并運行包含目標文件a.out中的程序,用a.out程序有效的替換了當前的程序。加載并運行a.out需要以下幾個步驟:
之后調(diào)度此進程時,它將從這個入口點開始執(zhí)行。
7.8 缺頁故障與缺頁中斷處理
當指令引用一個虛擬地址,在MMU中查找頁表時發(fā)現(xiàn)與該地址相對應(yīng)的物理地址不在內(nèi)存中,因此必須從磁盤中取出時就會發(fā)生故障。缺頁故障后會有如下處理:
7.9動態(tài)存儲分配管理
當運行時需要額外內(nèi)存時,用動態(tài)內(nèi)存分配器更方便,也有更好的移植性。
動態(tài)內(nèi)存分配器維護著一個進程的虛擬內(nèi)存區(qū)域,稱為堆。對于每個進程,內(nèi)核維護一個變量brk,它指向堆的頂部。分配器將堆視為一組不同大小的塊的集合來維護。每個塊就是一個連續(xù)的虛擬內(nèi)存片,要么是已分配的,要么是空閑的。已分配的塊顯式地保留為供應(yīng)用程序使用。空閑塊可用來分配。空閑塊保持空閑,直到它顯式地被應(yīng)用所分配。一個已分配的塊保持已分配狀態(tài),直到它被釋放,這種釋放要么是應(yīng)用程序顯式執(zhí)行的,要么是內(nèi)存分配器自身隱式執(zhí)行的。
分配器分為兩種基本風(fēng)格。兩種風(fēng)格都要求應(yīng)用顯示的分配塊,它們的不同之處在于有哪個實體來負責釋放已分配的塊:
malloc函數(shù)返回一個指針,指向大小至少為size字節(jié)的內(nèi)存塊,這個塊可能包含在這個塊內(nèi)的任何數(shù)據(jù)對象類型做對齊。
對比于顯式空閑鏈表,代表并不直接對空閑塊進行鏈接,而是將對內(nèi)存空間中的所有塊組織成一個大鏈表,在塊的首尾的四個字節(jié)分別添加header和footer,負責維護當前塊的信息(大小和是否分配)。由于每個塊是對齊的,所以每個塊的地址低位總是0,可以用該位標注當前塊是否已經(jīng)分配。可以利用header和footer中存放的塊大小尋找當前塊兩側(cè)的鄰接塊,方便進行空閑塊的合并操作。優(yōu)點是簡單,缺點是搜索所需時間與堆中以分配塊和空閑塊的總數(shù)成線性關(guān)系。
顯式結(jié)構(gòu)在空閑塊中增加了8個字節(jié),分別保存當前空閑塊的前驅(qū)空閑塊的地址和后繼空閑塊的地址。顯式的結(jié)構(gòu)比隱式結(jié)構(gòu)多維護了一個鏈表,就是空閑塊的鏈表。這樣做的好處就是在malloc的時候,隱式的方法是要遍歷所有的塊,包括空閑塊了分配塊。但是顯式的結(jié)構(gòu)只需要在空閑塊中維護的鏈表檢索就可以了,這樣降低了在malloc時候的復(fù)雜度。
關(guān)于空閑塊的維護方式一共有兩種,一種是后進先出的方式,另一種是按照地址的方式。按照地址維護很好理解,與隱式的結(jié)構(gòu)大致相同。后進先出的方式的思想是,當一個分配塊被free之后,將這個塊放到鏈表的最開頭,這樣在malloc的時候會首先看一下最后被free的塊是否符合要求。這樣的好處是釋放一個塊的時候比較高效,直接放在頭部就可以。
7.10本章小結(jié)
本章介紹了hello的線性地址空間和物理到線性,線性到邏輯的變換,還介紹了hello進程fork,execve時的內(nèi)存映射,以及缺頁故障與缺頁中斷處理和動態(tài)存儲分配管理。
(第7章 2分)
結(jié)論
Hello.c只是一個簡單的代碼文件,但是通過這個文件我們了解了一個C語言程序從被寫出到執(zhí)行的一生,平時運行代碼文件只是簡單的點個運行按鈕,但是真正了解如何編譯后才發(fā)現(xiàn)里面內(nèi)容之豐富,知識之廣泛。本文的hello.c一共經(jīng)歷了一下幾種剖析:
(結(jié)論0分,缺失 -1分,根據(jù)內(nèi)容酌情加分)
附件
列出所有的中間產(chǎn)物的文件名,并予以說明起作用。
(附件0分,缺失 -1分)
參考文獻
為完成本次大作業(yè)你翻閱的書籍與網(wǎng)站等
[1]? 林來興. 空間控制技術(shù)[M]. 北京:中國宇航出版社,1992:25-42.
[2]? 辛希孟. 信息技術(shù)與信息服務(wù)國際研討會論文集:A集[C]. 北京:中國科學(xué)出版社,1999.
[3]? 趙耀東. 新時代的工業(yè)工程師[M/OL]. 臺北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[4]? 諶穎. 空間交會控制理論與方法研究[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.
(參考文獻0分,缺失 -1分)
總結(jié)
- 上一篇: Android阿面试积累,讲的真透彻
- 下一篇: c语言调用延时子程序的例子,C语言精确延