CSAPP 計(jì)算機(jī)大作業(yè) hello的一生

摘 要

為了對計(jì)算機(jī)系統(tǒng)有著更深入的了解以及研究系統(tǒng)間的協(xié)作關(guān)系尝抖。本大作業(yè)針對hello程序運(yùn)行的一系列過程對c文件的預(yù)處理痒钝、編譯帆卓、匯編、鏈接学少,Linux操作系統(tǒng)的進(jìn)程管理剪个,Linux虛擬內(nèi)存管理以及硬件上的內(nèi)存訪問,LinuxIO管理進(jìn)行研究版确。本大作業(yè)的實(shí)驗(yàn)環(huán)境為Ubuntu 18.04,gcc編譯器乎折,gdb绒疗,objdump反匯編工具。本大作業(yè)對于以后研究計(jì)算機(jī)系統(tǒng)相關(guān)領(lǐng)域有著鋪墊和引導(dǎo)作用骂澄。

關(guān)鍵詞:計(jì)算機(jī)系統(tǒng)吓蘑;編譯;虛擬內(nèi)存坟冲;IO管理磨镶;進(jìn)程管理

目 錄

第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 可重定位目標(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的動態(tài)鏈接分析..................................................................................... - 8 -

5.8 本章小結(jié)............................................................................................................ - 9 -

第6章 hello進(jìn)程管理...................................................................................... - 10 -

6.1 進(jìn)程的概念與作用.......................................................................................... - 10 -

6.2 簡述殼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的異常與信號處理............................................................................... - 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進(jìn)程fork時(shí)的內(nèi)存映射..................................................................... - 11 -

7.7 hello進(jìn)程execve時(shí)的內(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的實(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簡介

根據(jù)Hello的自白,利用計(jì)算機(jī)系統(tǒng)的術(shù)語健提,簡述Hello的P2P琳猫,020的整個(gè)過程。

P2P(from program to process):

在notepad或IDE中將hello的代碼輸入保存為c格式的文件私痹,形成hello.c文件脐嫂。這就是program统刮,程序。在OS(例如Linux)中账千,通過交互式應(yīng)用程序shell上侥蒙,輸入命令行對c文件進(jìn)行編譯,通過cpp(預(yù)處理器)預(yù)處理匀奏,ccl(編譯器)編譯鞭衩,as(匯編器)匯編,最后通過ld(鏈接器)鏈接生成hello可執(zhí)行目標(biāo)程序娃善,接著操作系統(tǒng)將fork()的子進(jìn)程通過excve()函數(shù)將hello程序載入并創(chuàng)建運(yùn)行環(huán)境醋旦,例如mmap將hello.c所編譯處的可執(zhí)行文件映射到虛擬內(nèi)存中,操作系統(tǒng)分配cpu的時(shí)間片給hello進(jìn)程会放。

這樣一個(gè)hello的進(jìn)程(process)就誕生了饲齐。以上就是P2P,從程序到進(jìn)程的過程咧最。

020(from zero-0 to zero-0):

在上述的P2P過程完成后捂人,hello進(jìn)程通過操作系統(tǒng)的調(diào)用獲得cpu的控制權(quán),并且在操作系統(tǒng)的高層抽象下矢沿,硬件I/O設(shè)備和其他資源被抽象為文件供hello進(jìn)程使用(Linux下)滥搭。每當(dāng)hello程序需要數(shù)據(jù)時(shí),cpu會執(zhí)行讀取某一虛擬地址的命令捣鲸,通過MMU將va翻譯為pa瑟匆,再去緩存或內(nèi)存中讀取。在程序使用完畢后栽惶,無論通過自身調(diào)用exit()終止函數(shù)還是通過外部的鍵盤發(fā)送終止信號又或者因?yàn)楫惓1Wo(hù)被操作系統(tǒng)終止退出愁溜,信號處理函數(shù)都會接受hello進(jìn)程結(jié)束的SIGCHLD信號或者SIGINT信號并執(zhí)行回收處理。操作系統(tǒng)或者其父進(jìn)程shell會回收子進(jìn)程并釋放內(nèi)存空間外厂,hello進(jìn)程重歸于零冕象。

hello程序從無到有,hello進(jìn)程從執(zhí)行到結(jié)束汁蝶,這就是020渐扮,從零到零的過程。

1.2 環(huán)境與工具

列出你為編寫本論文掖棉,折騰Hello的整個(gè)過程中墓律,使用的軟硬件環(huán)境,以及開發(fā)與調(diào)試工具幔亥。

硬件環(huán)境:

CPU:Intel Core i5 6300HQ耻讽;2.3GHz;8G RAM紫谷;128G SSD齐饮;1T HDD

軟件環(huán)境:

OS:

windows 10 64bit捐寥;Ubuntu 18.04 LTS 64bit

開發(fā)調(diào)試工具:

GDB;EDB祖驱;OBJDUMP握恳;READELF;vim捺僻;gcc

1.3 中間結(jié)果

列出你為編寫本論文乡洼,生成的中間結(jié)果文件的名字,文件的作用等匕坯。

hello.i:hello.c文件預(yù)編譯后的文件束昵,用于預(yù)編譯章節(jié)說明和用于匯編匯編文件。

hello.s:hello.i經(jīng)過編譯器編譯后的文件葛峻,用于匯編章節(jié)說明以及用于匯編器匯編可重定位文件锹雏。

hello.o:hello.s經(jīng)過匯編器匯編后的文件,為16進(jìn)制代碼文件术奖,用于鏈接器鏈接生成可執(zhí)行文件礁遵。

hello:hello.o鏈接后生成的可執(zhí)行目標(biāo)文件,用來進(jìn)行反匯編研究匯編代碼或者用來運(yùn)行采记。

hello:同hello.out文件佣耐,后綴不為.out,用于執(zhí)行.

1.4 本章小結(jié)

本章對hello程序的運(yùn)行的整個(gè)過程進(jìn)行了簡單的介紹,以及介紹了此次大作業(yè)所使用的相關(guān)軟硬件工具,最后介紹了大作業(yè)所生成的中間文件以及作用.

第2章 預(yù)處理

2.1 預(yù)處理的概念與作用

2.1.1預(yù)處理的概念

預(yù)處理是指,預(yù)處理階段,預(yù)處理器(cpp)根據(jù)以字符#開頭的命令,修改原始的C程序.預(yù)處理指令為#開頭的命令行,包含條件編譯(#if,#ifdef),宏定義(#define),源文件包含(#include),行控制(#line),錯誤指令(#error)等.

2.1.2預(yù)處理的作用

用以支持特定的語言特性,對代碼進(jìn)行替換,以便接下來對文件的編譯.

2.2在Ubuntu下預(yù)處理的命令

2.2.1預(yù)處理的相關(guān)命令

Ubuntu下預(yù)處理的命令為:

$ gcc -E hello.c -o hello.i

或者

$ cpp hello.c hello.i

2.2.2預(yù)處理過程演示

image003.png

圖2-1


image005.png

圖2-2

2.3 Hello的預(yù)處理結(jié)果解析

2.3.1預(yù)處理結(jié)果演示

下面是部分預(yù)處理后的代碼截圖,例如開頭(圖2-3),調(diào)用外部函數(shù)(圖2-4),對一些變量進(jìn)行重新定義命名(圖2-5)以及最后的源代碼部分(圖2-6)

image006.png

圖2-3

![![image012.png](https://upload-images.jianshu.io/upload_images/8130705-a0500aa055409214.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ](https://upload-images.jianshu.io/upload_images/8130705-f1a3d314bb68b484.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

圖2-4

image010.png

圖2-5

image012.png

圖2-6

2.3.2預(yù)處理結(jié)果解析

從hello.i文件中可以看到,預(yù)處理對代碼中#開頭的命令進(jìn)行替換,將所引用的頭文件中的函數(shù)進(jìn)行引入.將hello文件擴(kuò)充為一個(gè)完整的代碼文件.

2.4 本章小結(jié)

本章介紹了預(yù)處理的相關(guān)概念和作用.并在Ubuntu中進(jìn)行了實(shí)際演示,并且對生成的.i文件進(jìn)行了解析.預(yù)處理是對#開頭的命令進(jìn)行替換,將完整的代碼引入. 預(yù)處理指令為#開頭的命令行,包含條件編譯(#if,#ifdef),宏定義(#define),源文件包含(#include),行控制(#line),錯誤指令(#error)等.

第3章 編譯

3.1 編譯的概念與作用

3.1.1 編譯的概念

編譯指從.i文件經(jīng)過編譯器處理后產(chǎn)生.s文件的過程.其中包括五個(gè)階段:

詞法分析,語法分析,語義檢查,中間代碼生成,目標(biāo)代碼生成。

3. 1. 2 編譯的作用

編譯將.i文件編譯為.s匯編代碼文件唧龄,用于接下來匯編器將其處理為可重定位目標(biāo)文件兼砖,以進(jìn)行接下來的鏈接等操作。當(dāng)然既棺,也可以用來進(jìn)行匯編代碼的分析讽挟。

注意:這兒的編譯是指從 .i 到 .s 即預(yù)處理后的文件到生成匯編語言程序

3.1 在Ubuntu下編譯的命令

3.2.1 編譯的相關(guān)命令

Ubuntu下編譯命令為:

$ gcc -S hello.i -o hello.s

或者

$ cc1 hello.i -Og hello.s

3.1.2 編譯過程的演示

![image016.png](https://upload-images.jianshu.io/upload_images/8130705-e25fac274acd9f93.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

圖3-1 編譯命令


image016.png

圖3-2 編譯結(jié)果

3.1 Hello的編譯結(jié)果解析

image017.png

image019.png

圖3-3 hello.s代碼

匯編文件中幾個(gè)指令解釋

|

指令

|

解釋

|
|

.file

|

聲明源文件

|
|

.text

|

以下是代碼描述

|
|

.globl

|

表明后面所跟為全局變量

|
|

.data

|

以下是數(shù)據(jù)描述

|
|

.align

|

表明對齊方式

|
|

.type

|

表明對象是對象還是函數(shù)

|
|

.size

|

表明大小

|
|

.long

|

未知

|
|

.section

|

表明數(shù)據(jù)所在節(jié)

|
|

.string

|

表明為字符串,后跟字符串內(nèi)容

|
|

.ident

|

表明所用編譯器版本

|

表3-1 指令含義

3.3.1 常量

hello文件中所用到的常量為字符串援制,hello.c中一共有兩個(gè)字符串常量:

1. "Usage: Hello 學(xué)號 姓名戏挡!\n"

2. "Hello %s %s\n"

匯編文件中關(guān)于這兩個(gè)字符串的描述中,開頭為.LC0和.LC1晨仑,其中一個(gè)漢字對應(yīng)三個(gè)\xxx,所以hello后面跟的是漢字“學(xué)號”“姓名”以及“拆檬!”洪己,常量存放在.rodata節(jié),為只讀數(shù)據(jù)竟贯。

image021.png

圖3-4 字符串相關(guān)指令

3.3.2 變量

1. 全局變量

全局變量作為程序的公共變量答捕,可以為程序中所有函數(shù)所用。hello文件中包含一個(gè)全局變量sleepsecs屑那。在代碼中可以看到sleepsecs被聲明為全局變量拱镐,在數(shù)據(jù)描述上艘款,對齊方式為4字節(jié),并且為對象沃琅,sleepsecs大小為4個(gè)字節(jié)

圖3-5 sleepsecs相關(guān)指令

2. 局部變量

局部變量相對于全局變量哗咆,位于函數(shù)內(nèi)部,在使用時(shí)被創(chuàng)建益眉,在函數(shù)結(jié)束后被銷毀晌柬。hello文件中的局部變量為計(jì)數(shù)變量i。由于i是局部變量郭脂,所以編譯器會將i的值存儲在棧中或者寄存器中年碘。hello.s中局部變量i被編譯器存放在了棧中,位置為-4(%rbp)展鸡,在.L2節(jié)中屿衅,i被初始化為0,隨后進(jìn)入.L3節(jié)中進(jìn)行循環(huán)莹弊。

image025.png

圖3-6 局部變量i的存放

3.3.3 表達(dá)式涤久、類型

1. 表達(dá)式

C語言中全局變量是表達(dá)式,一條語句也是表達(dá)式箱硕,編譯器將一條表達(dá)式拆分為多條匯編語句的組合來表達(dá)表達(dá)式拴竹。例如圖3-8就是語句

printf("Hello %s %s\n",argv[1],argv[2]);

的匯編代碼。

2. 類型

這里的類型理解為數(shù)據(jù)類型剧罩,hello.c中出現(xiàn)了這幾種數(shù)據(jù)類型:

整型栓拜、字符數(shù)組(字符串)

   編譯器將全局變量整型存儲在只讀數(shù)據(jù)中,調(diào)用時(shí)存入寄存器惠昔,字符數(shù)組的地址存放在棧中幕与。如圖3-5,3-6.

3.3.4 賦值操作

hello.c中出現(xiàn)的賦值操作為為全局變量sleepsecs賦值時(shí)镇防,變量i初始化時(shí)啦鸣,編譯器對兩種賦值有不同的編譯處理。

在對全局變量sleepsecs賦值時(shí)来氧,編譯器直接通過指令將值設(shè)為2

[圖片上傳失敗...(image-ba1d8f-1546271099965)]

圖3-10 sleepsecs賦值

在對i初始化賦值時(shí)诫给,通過movl指令將立即數(shù)$0傳給i

[圖片上傳失敗...(image-64670d-1546271099965)]

圖3-11 i初始化

3.3.5 賦初值/不賦初值

在hello.c中main函數(shù)的局部變量i沒有賦初值,同時(shí)全局變量sleepsecs初始化時(shí)即賦初值,關(guān)于sleepsecs不再贅述啦扬。建立變量i的語句出現(xiàn)在判斷argc之前中狂,而在編譯后有關(guān)判斷argc代碼之前沒有出現(xiàn)任何關(guān)于i的語句,但是實(shí)際上編譯器已經(jīng)為i申請了棧中存儲的位置-4(%rbp),也就是說扑毡,對于不賦初值的局部變量胃榕,編譯器會提前申請棧中空間。而全局未初始化變量則在.bss節(jié)中不處理瞄摊。

[圖片上傳失敗...(image-39b981-1546271099964)]

圖3-12 源碼

i103j??c<?
3.3.9 控制轉(zhuǎn)移

文件中出現(xiàn)的控制轉(zhuǎn)移為if(),for()結(jié)構(gòu)分別解析

  1. if條件分支結(jié)構(gòu)

編譯器中通過jmp語句實(shí)現(xiàn)分支結(jié)構(gòu)的跳轉(zhuǎn)(或者cmoveX)勋又,通過cmp進(jìn)行判斷苦掘。hello.c中出現(xiàn)的條件分支結(jié)構(gòu)為

[圖片上傳失敗...(image-79308f-1546768413784)]

圖3-17 if結(jié)構(gòu)

編譯器將將其解釋為匯編代碼

[圖片上傳失敗...(image-331aa8-1546768413784)]

圖3-18 if結(jié)構(gòu)對應(yīng)匯編

  1. for循環(huán)結(jié)構(gòu)

編譯器對于for循環(huán)結(jié)構(gòu)的處理邏輯比較像dowhile結(jié)構(gòu),首先會跳轉(zhuǎn)到底部的i的比較代碼楔壤,然后比較后才正式開始循環(huán)鹤啡。hello.c中for循環(huán)結(jié)構(gòu)為

[圖片上傳失敗...(image-a33f38-1546768413784)]

圖3-19 for結(jié)構(gòu)

可以看到編譯器首先跳轉(zhuǎn)到L2進(jìn)行i的初始化,接著直接跳轉(zhuǎn)到L3進(jìn)行比較挺邀,小于等于9則跳回L4進(jìn)行正式的循環(huán)

[圖片上傳失敗...(image-93e98e-1546768413784)]

圖3-20 for匯編

3.3.10 函數(shù)操作

  1. 參數(shù)傳遞(地址/值)

函數(shù)參數(shù)屬于形參揉忘, hello.c文件中main函數(shù)傳入時(shí)使用了參數(shù),在調(diào)用printf中端铛,也使用了參數(shù)泣矛,sleep函數(shù)中也傳入了sleepsecs參數(shù),具體來說禾蚕,參數(shù)不屬于變量您朽,這里描述參數(shù)傳遞時(shí)的匯編代碼形式。

如3-7圖所示换淆,寄存器%edi保存argc參數(shù)哗总,%rsi保存argv[]數(shù)組的指針。通過movl(雙字傳送)和movq(四字傳送)指令傳值倍试,這里是因?yàn)閍rgc為4字節(jié)而數(shù)組指針為8字節(jié)讯屈,也分別對應(yīng)了值和地址的傳送。

[圖片上傳失敗...(image-c390f-1546768413784)]

圖3-7 main函數(shù)參數(shù)

如圖3-8县习,printf函數(shù)傳入的三個(gè)參數(shù)分別保存在了%rdi涮母,%rsi,%rdx中躁愿,%rdi中傳入.LC1叛本,也就是格式串,argv參數(shù)列表分別通過兩次內(nèi)存引用取到參數(shù)1彤钟,2地址傳入寄存器来候。

[圖片上傳失敗...(image-975992-1546768413784)]

圖3-8 printf函數(shù)參數(shù)

sleep函數(shù)的參數(shù)通過%edi傳入。其中由于sleepsecs是全局變量逸雹,最后鏈接后的匯編代碼會通過相對地址偏移在.data節(jié)中讀值营搅,%rip指代的就是存儲地址(可能)

[圖片上傳失敗...(image-148152-1546768413784)]

圖3-9 sleep函數(shù)參數(shù)

  1. 函數(shù)調(diào)用

這里要先說明一下通用棧幀的結(jié)構(gòu),在調(diào)用函數(shù)時(shí)梆砸,調(diào)用者通常會將調(diào)用完畢后的返回地址壓入棧中剧防,在棧的返回地址之前,調(diào)用者還有可能會保存一些參數(shù)用來傳遞辫樱。在返回地址之后,被調(diào)用者在棧中開辟新的空間使用俊庇,從返回地址為止狮暑,所有調(diào)用者在棧中的結(jié)構(gòu)稱為“調(diào)用者的棧幀”

[圖片上傳失敗...(image-bbf26d-1546768413784)]

圖3-21 棧幀結(jié)構(gòu)

hello.c中出現(xiàn)的函數(shù)調(diào)用有printf函數(shù)調(diào)用鸡挠,exit函數(shù)調(diào)用,sleep函數(shù)調(diào)用搬男,getchar函數(shù)調(diào)用拣展。這里需要注意各個(gè)寄存器的使用規(guī)則,傳參用寄存器依次為%rdi,%rsi,%rdx.%rcx缔逛,保存返回值的寄存器為%rax备埃。編譯器使用call指令對函數(shù)進(jìn)行調(diào)用,通過寄存器傳值或者棧傳值褐奴,通過rax讀取函數(shù)的返回值按脚。

[圖片上傳失敗...(image-c99cd2-1546768413784)]

圖3-22 main函數(shù)開辟新的棧空間作為棧幀

[圖片上傳失敗...(image-f50619-1546768413783)]

圖3-23 exit函數(shù)調(diào)用

[圖片上傳失敗...(image-80be5-1546768413783)]

圖3-24 printf調(diào)用和傳參

[圖片上傳失敗...(image-f63758-1546768413783)]

圖3-25 寄存器規(guī)則

  1. 函數(shù)返回

編譯器使用ret指令進(jìn)行函數(shù)返回敦冬,在此之前會用%rax保存函數(shù)的返回值辅搬,ret指令的作用為:取出棧頂?shù)牡刂纷鳛樘D(zhuǎn)目標(biāo)并進(jìn)行跳轉(zhuǎn),這也說明了為什么call指令時(shí)會將返回地址壓入棧中脖旱。

[圖片上傳失敗...(image-a5b8f-1546768413783)]

圖3-26 main函數(shù)返回代碼

3.4 本章小結(jié)

本章主要介紹了編譯器對于源代碼的相關(guān)處理的內(nèi)容堪遂。編譯器通過不同的匯編代碼結(jié)構(gòu)將c語言代碼轉(zhuǎn)換為匯編代碼,從而方便下面的匯編器和鏈接器將其實(shí)現(xiàn)為最終的可執(zhí)行目標(biāo)文件萌庆。編譯器對于c中的數(shù)據(jù)和操作有著不同的處理方式溶褪,數(shù)據(jù)包括常量、變量践险、表達(dá)式猿妈、宏等,操作包括賦值捏境、轉(zhuǎn)換于游、算術(shù)操作、關(guān)系操作垫言、控制轉(zhuǎn)移贰剥、函數(shù)操作等。

第4章 匯編

4.1 匯編的概念與作用

4.1.1. 匯編的概念

匯編是指匯編器將.s文件翻譯為.o文件的過程筷频,匯編器將匯編代碼編譯為對應(yīng)的機(jī)器指令蚌成,并將結(jié)果保存在.o文件中。

4.1.2. 匯編的作用

將匯編代碼翻譯為對應(yīng)的機(jī)器語言凛捏,以便能夠?yàn)殒溄悠魉谩?/p>

注意:這兒的匯編是指從 .s 到 .o 即編譯后的文件到生成機(jī)器語言二進(jìn)制程序的過程担忧。

4.2 在Ubuntu下匯編的命令

4.2.1. 匯編的相關(guān)命令

Ubuntu下的匯編相關(guān)命令為:

$ gcc -c hello.s -o hello.o

或者

$ as -o hello.o hello.s

4.2.2. 匯編的過程演示

[圖片上傳失敗...(image-d489c9-1546768413783)]

圖4-1 匯編命令

[圖片上傳失敗...(image-997a8f-1546768413783)]

圖4-2 生成.o文件

4.3 可重定位目標(biāo)elf格式

分析hello.o的ELF格式,用readelf等列出其各節(jié)的基本信息坯癣,特別是重定位項(xiàng)目分析瓶盛。

|

ELF格式名稱

|

相關(guān)解釋

|
|

ELF頭

|

描述生成該文件的系統(tǒng)的字大小和順序以及ELF頭大小、目標(biāo)文件類型、機(jī)器類型惩猫、節(jié)頭部表的偏移芝硬、節(jié)頭部表中條目大小和數(shù)量

|
|

.text

|

以編譯的機(jī)器代碼

|
|

.rodata

|

只讀數(shù)據(jù)

|
|

.data

|

已初始化的全局和靜態(tài)C變量

|
|

.bss

|

未初始化的全局和靜態(tài)C變量以及初始化為0的全局靜態(tài)變量

|
|

.symtab

|

符號表,存放代碼中定義或引用的函數(shù)和全局變量的信息

|
|

.rel.text

|

一個(gè)表示.text節(jié)中位置的列表轧房,指向的是鏈接時(shí)需要修改位置的代碼在text節(jié)中的位置

|
|

.rel.data

|

被模塊引用的或定義的全局變量的重定位信息

|
|

.debug

|

調(diào)試符號表拌阴,包含局部/全局變量和C源文件

|
|

.line

|

行號和text節(jié)中機(jī)器指令的映射

|
|

.strtab

|

字符串表,.symtab和.debug中符號表及節(jié)頭部中節(jié)的名字

|
|

節(jié)頭部表

|

描述目標(biāo)文件的節(jié)

|

表4-1 ELF文件格式

[圖片上傳失敗...(image-f04dc0-1546768413783)]

圖4-3 hello.o的ELF文件頭

[圖片上傳失敗...(image-4c1ee6-1546768413783)]

圖4-4 hello.o文件的節(jié)頭信息(名稱奶镶、類型迟赃、地址、偏移量)

[圖片上傳失敗...(image-9a8630-1546768413783)]

圖4-5 重定位節(jié)

鏈接器會根據(jù)重定位節(jié)中的信息對重定位文件進(jìn)行重定位厂镇。

[圖片上傳失敗...(image-a6b765-1546768413783)]

圖4-6 .symtab節(jié)

重定位項(xiàng)目分析:

.rel.text中每一條重定位條目都包括如下四個(gè)內(nèi)容:

|

名稱

|

含義

|
|

offset

|

被修改的引用在節(jié)中的偏移

|
|

type(32bit)

|

重定位的方式

|
|

symbol(32bit)

|

被修改的引用應(yīng)該指向的符號

|
|

addend

|

被修改引用的偏移調(diào)整

|

表4-2 ELF重定位條目

圖4-5中重定位節(jié)中以第一條.rodata為例纤壁,實(shí)際上這是.LC0的字符串的重定位條目

[圖片上傳失敗...(image-2952a-1546768413783)]

圖4-7 L1重定位條目

偏移量就是offset,信息是type和symbol的總和剪撬,類型為PC32位相對引用摄乒。

由于rela起始于0x338,使用hexedit查看如圖4-8残黑,接下來簡單說明鏈接器之后會如何使用重定位條目

[圖片上傳失敗...(image-8fd26a-1546768413783)]

圖4-8 hexedit重定位條目

假設(shè)LC0重定位條目為r, r.offset = 0x18,r.type = R_X86_64_PC32, r.symbol = .rodata, r.addend = -4

用PC相對地址引用馍佑,令.text節(jié)中需要修改的位置為s,重定位的目標(biāo)位置為d梨水,那么

引用的運(yùn)行位置 refaddr = ADDR(s) + offset;

更新此處的內(nèi)容 *refptr = (unsigned) (ADDR(s) – refaddr + addend);

上述計(jì)算完成后拭荤,即完成了關(guān)于LC0的重定位內(nèi)容,printf函數(shù)中字符串指向的地址改為了.data節(jié)中字符串存放的地址疫诽。其余幾條同理舅世。

4.4 Hello.o的結(jié)果解析

[圖片上傳失敗...(image-4c7e50-1546768413783)]

圖4-9 反匯編內(nèi)容

[圖片上傳失敗...(image-f6be3f-1546768413783)]

[圖片上傳失敗...(image-28a1c6-1546768413783)]

圖4-10 hello.s中main函數(shù)內(nèi)容

由圖4-11對比圖可以看出,兩者之間的差別并不是很大奇徒,而經(jīng)過匯編生成的可重定位文件與之前的匯編文件區(qū)別主要在于以下四點(diǎn):

1. 立即數(shù)的進(jìn)制改變:

立即數(shù)由原先的10進(jìn)制改變成了16進(jìn)制雏亚,向機(jī)器級代碼更近了一步。

2. 分支轉(zhuǎn)移:

匯編文件中的偽代碼段地址被替換成了真正的地址(暫時(shí))摩钙,可以看到凡是jxx指令后跟的Lx都被替換為了十六進(jìn)制main函數(shù)中的地址罢低。

3. 函數(shù)調(diào)用:

因?yàn)檎{(diào)用的都是共享庫中的函數(shù),調(diào)用函數(shù)的call指令由原先的簡單指令被替換為了下一條地址胖笛,設(shè)置為了重定位格式并且指明了重定位類型网持,等待最終鏈接器將其確定最終的位置。

4. 全局變量訪問:

所有全局變量訪問都由偽代碼段名稱+(%rip)的形式改為了0 + (%rip)形式长踊,并且添加了重定位條目功舀,等待最終鏈接器的確定。

[圖片上傳失敗...(image-dc062a-1546768413783)]

[圖片上傳失敗...(image-625f5c-1546768413783)]

圖4-11 兩者對應(yīng)關(guān)系

objdump -d -r hello.o 分析hello.o的反匯編身弊,并請與第3章的 hello.s進(jìn)行對照分析辟汰。

說明機(jī)器語言的構(gòu)成列敲,與匯編語言的映射關(guān)系。特別是機(jī)器語言中的操作數(shù)與匯編語言不一致莉擒,特別是分支轉(zhuǎn)移函數(shù)調(diào)用等酿炸。

4.5 本章小結(jié)

本章介紹了程序生成過程中編譯器匯編的相關(guān)內(nèi)容。匯編過程將匯編語言轉(zhuǎn)換為機(jī)器代碼涨冀,生成可重定位的目標(biāo)文件,使機(jī)器能夠直接處理與執(zhí)行麦萤。通過readelf讀取其elf信息與重定位信息鹿鳖,得到相關(guān)信息。通過objdump反匯編目標(biāo)文件壮莹,進(jìn)行對照翅帜。

第5章 鏈接

5.1 鏈接的概念與作用

5.1.1.鏈接的概念

鏈接是將各種代碼和數(shù)據(jù)片段收集并組合成為一個(gè)單一文件的過程,這個(gè)文件可被加載到內(nèi)存執(zhí)行命满。鏈接可以執(zhí)行與編譯時(shí)涝滴,也就是在源代碼被翻譯為機(jī)器代碼時(shí);也可以執(zhí)行于加載時(shí)胶台,也就是在程序被加載器加載到內(nèi)存執(zhí)行時(shí)歼疮;甚至執(zhí)行于運(yùn)行時(shí)痢士,由應(yīng)用程序來執(zhí)行泽论。這里的鏈接指的是.o文件經(jīng)過鏈接器處理為可執(zhí)行文件的過程。

5.1.2.鏈接的作用

通過鏈接窒升,將可重定位目標(biāo)文件中引用的外部函數(shù)铸磅、數(shù)據(jù)統(tǒng)一合并到一個(gè)文件赡矢,處理為完整的可執(zhí)行目標(biāo)文件。

注意:這兒的鏈接是指從 hello.o 到hello生成過程阅仔。

5.2 在Ubuntu下鏈接的命令

5.2.1. 鏈接的相關(guān)命令

Ubuntu下鏈接相關(guān)命令為

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.2. 鏈接的過程演示

[圖片上傳失敗...(image-dae0db-1546768413782)]

圖5-1 鏈接命令

[圖片上傳失敗...(image-363549-1546768413782)]

圖5-2 生成文件 hello

使用ld的鏈接命令吹散,應(yīng)截圖,展示匯編過程八酒! 注意不只連接hello.o文件

5.3 可執(zhí)行目標(biāo)文件hello的格式

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

|

ELF格式名稱

|

解釋

|
|

ELF頭

|

描述文件的總體格式,包括程序的入口點(diǎn)闭树,第一條指令的地址

|
|

段頭部表

|

將連續(xù)文件節(jié)映射到內(nèi)存段

|
|

.init

|

包含函數(shù)_init()初始化代碼

|
|

.text

|

鏈接后的代碼段

|
|

.rodata

|

只讀數(shù)據(jù)

|
|

.data

|

已初始化的全局和靜態(tài)C變量

|
|

.bss

|

未初始化的全局和靜態(tài)C變量和初始化為0的

|
|

.symtab

|

符號表耸棒,顯示引用的函數(shù)和全局變量符號

|
|

debug

|

調(diào)試符號表,包含局部/全局變量和C源文件

|
|

.line

|

行號和text節(jié)中機(jī)器指令的映射

|
|

.strtab

|

字符串表报辱,.symtab和.debug中符號表及節(jié)頭部中節(jié)的名字

|
|

節(jié)頭部表

|

描述目標(biāo)文件的節(jié)

|

表5-1 可執(zhí)行文件的ELF格式

[圖片上傳失敗...(image-be3c9c-1546768413782)]

圖5-3 ELF頭

: initi??z?X??
節(jié)頭信息中包含了各段的基本信息与殃,其中包括了各段的起始地址,大小,偏移量以及類型幅疼,通過這些可以找到相關(guān)的段內(nèi)容米奸。

[圖片上傳失敗...(image-456635-1546768649790)]

圖5-5 程序頭

[圖片上傳失敗...(image-fbfe06-1546768649790)]

圖5-6 各段的映射以及動態(tài)庫信息

[圖片上傳失敗...(image-47f7c7-1546768649790)]

圖5-7 重定位的信息

[圖片上傳失敗...(image-314bc1-1546768649790)]

[圖片上傳失敗...(image-57435e-1546768649790)]

[圖片上傳失敗...(image-5d2687-1546768649790)]

圖5-8 符號表中的信息

5.4 hello的虛擬地址空間

使用edb加載hello,查看本進(jìn)程的虛擬地址空間各段信息爽篷,并與5.3對照分析說明悴晰。

舉幾個(gè)節(jié)來分析

1. .init節(jié)

如圖所示,init節(jié)的起始的虛擬地址為0x400488逐工,對hello中init節(jié)進(jìn)行反匯編得到_init函數(shù)的反匯編代碼如圖5-10铡溪,通過edb的memorydump查看該地址的相關(guān)信息(圖5-11),可以看見操作碼與反匯編一致泪喊,即此處為intit節(jié)的位置棕硫。

[圖片上傳失敗...(image-5d7446-1546768649790)]

圖5-9 init起始地址

[圖片上傳失敗...(image-f9f93-1546768649790)]

圖5-10 反匯編

[圖片上傳失敗...(image-28fd42-1546768649790)]

圖5-11 edb對應(yīng)地址的內(nèi)容

2..text節(jié)

同init節(jié)的方法

[圖片上傳失敗...(image-2aa473-1546768649790)]

圖5-12 .text節(jié)的起始地址

[圖片上傳失敗...(image-6dfa9a-1546768649790)]

圖5-13 _start函數(shù)的反匯編

[圖片上傳失敗...(image-a4752c-1546768649790)]

圖5-14 text節(jié)的起始地址內(nèi)容

3. plt節(jié)

[圖片上傳失敗...(image-e941ce-1546768649790)]

圖5-15 plt節(jié)起始地址

[圖片上傳失敗...(image-7a9bdc-1546768649790)]

圖5-16 .plt函數(shù)反匯編

[圖片上傳失敗...(image-60c31a-1546768649790)]

圖5-17 edb中地址相關(guān)內(nèi)容

5.5 鏈接的重定位過程分析

[圖片上傳失敗...(image-118633-1546768649790)]

圖5-18 .o反匯編

[圖片上傳失敗...(image-b1ba9e-1546768649790)]

圖5-19 可執(zhí)行文件反匯編(main部分)

hello.o文件反匯編后只有main的匯編代碼,因此下面只比較分析main函數(shù)部分的變化袒啼。

1. 虛擬地址

經(jīng)過鏈接器處理后哈扮,main中指令的虛擬地址被最終確定,.o文件中main函數(shù)指令地址是以0開始蚓再,而最終的地址是以0x400532開始滑肉,鏈接器通過將函數(shù)中調(diào)用的外部函數(shù)和全局變量重定位后,分配最終地址对途。

2. 對于函數(shù)和數(shù)據(jù)的引用

.o文件中的函數(shù)和全局變量使用R_X86_64_PC32和R_X86_64_PLT32表明重定位方式而且沒有指明具體的函數(shù)和變量位置赦邻,這是因?yàn)樵谕ㄟ^鏈接器處理以前,匯編器并不知道相關(guān)數(shù)據(jù)的位置实檀,而在ld鏈接器鏈接其他文件時(shí)惶洲,動態(tài)庫中函數(shù)加入到其中,其中函數(shù)和數(shù)據(jù)位置可以確定膳犹,ld就通過定位方式進(jìn)行相對偏移量的計(jì)算恬吕,指向相應(yīng)函數(shù),關(guān)于****hello****如何對hello.o****中的重定位項(xiàng)目進(jìn)行重定位须床,具體過程第4****章4.3****有說明铐料,在此不再贅述

3. 函數(shù)數(shù)量

除了main函數(shù)之外豺旬,可執(zhí)行目標(biāo)文件的反匯編中增加了很多額外的輔助函數(shù)钠惩,這些函數(shù)都來自與所引用的庫文件中,比如_start入口函數(shù)族阅,_libc_start_main函數(shù)用于調(diào)用main篓跛。這些函數(shù)幫助程序正常的運(yùn)行,為程序提供了入口和退出后的處理流程坦刀。

objdump -d -r hello 分析hello與hello.o的不同愧沟,說明鏈接的過程蔬咬。

結(jié)合hello.o的重定位項(xiàng)目,分析hello中對其怎么重定位的沐寺。

5.6 hello的執(zhí)行流程

|

程序

|

地址(0x)

|
|

ld-2.27.so!_dl_start

|

00007f8f6e2aeea0

|
|

ld-2.27.so!_dl_init

|

00007ff99ff450c5

|
|

hello!_start

|

00007ff99fb74ab0

|
|

ibc-2.27.so!__libc_start_main

|

00007fff9c867ab0

|
|

hello!puts@plt

|

000000000040054e

|
|

hello!exit@plt

|

0000000000400558

|
|

*hello!printf@plt

| |
|

|

*hello!sleep@plt

|

| |
|

|

*hello!getchar@plt

|

| |
|

libc-2.27.so!exit

|

000007ff9c8898127

|

表5-2 執(zhí)行流程

使用edb執(zhí)行hello林艘,說明從加載hello到_start,到call main,以及程序終止的所有過程混坞。請列出其調(diào)用與跳轉(zhuǎn)的各個(gè)子程序名或程序地址狐援。

5.7 Hello的動態(tài)鏈接分析

如果程序調(diào)用了一個(gè)由共享庫定義的函數(shù),編譯器沒有辦法預(yù)測這個(gè)函數(shù)的運(yùn)行時(shí)地址拔第,因?yàn)槎x它的共享庫函數(shù)可以在運(yùn)行時(shí)被加載到任意位置咕村。GNU編譯系統(tǒng)使用了一種叫做延遲綁定的技術(shù),將過程地址的綁定推遲到第一次調(diào)用這個(gè)函數(shù)時(shí)蚊俺。

具體是通過GOT和PLT來實(shí)現(xiàn),GOT是數(shù)據(jù)段的一部分逛万,PLT是代碼段的一部分泳猬。

GOT是一個(gè)數(shù)組,每個(gè)條目是8字節(jié)的地址宇植,GOT[0]GOT[1]保存的是動態(tài)鏈接器在解析函數(shù)地址時(shí)會使用的信息得封。GOT[2]是動態(tài)鏈接器在ld-linux.so模塊中的入口點(diǎn)。

PLT是一個(gè)數(shù)組指郁,每個(gè)條目是16字節(jié)的代碼忙上,從PLT[2]開始的條目調(diào)用用戶代碼調(diào)用的函數(shù)。

在hello文件中闲坎,plt節(jié)的起始位置是0x4004a0疫粥,除去0、1腰懂,從PLT[2]開始的條目為用戶代碼調(diào)用的庫函數(shù)梗逮,這里以exit()函數(shù)為例,使用edb查看plt第3個(gè)條目的相關(guān)代碼绣溜,第一條指令一定為跳轉(zhuǎn)到GOT中的相關(guān)條目慷彤,由于在dl_init之前,所以GOT中條目指向的應(yīng)該是PLT條目的下一條地址怖喻。

[圖片上傳失敗...(image-ef444d-1546768649790)]

圖5-20 plt起始地址

從PLT[2]開始查看底哗,其對應(yīng)的GOT條目地址為601020,edb查看對應(yīng)條目內(nèi)容

[圖片上傳失敗...(image-86467a-1546768649790)]

圖5-21 printf對應(yīng)的PLT條目

[圖片上傳失敗...(image-2773d0-1546768649790)]

圖5-22 GOT中的地址锚沸,為PLT下一條指令

在調(diào)用dl_init函數(shù)后再次查看相關(guān)的GOT條目

[圖片上傳失敗...(image-877b9b-1546768649790)]

圖5-23 init后GOT

可以看見跋选,經(jīng)過init函數(shù)調(diào)用后GOT中GOT[1],GOT[2]發(fā)生了變化,其中GOT[2]中保存的是動態(tài)鏈接器的入口地址咒吐,edb中查看相關(guān)動態(tài)鏈接器野建,經(jīng)過init函數(shù)初始化后属划,動態(tài)鏈接器被加載入GOT中,但是此時(shí)函數(shù)的地址仍然沒有加載候生,延遲綁定技術(shù)使得直到程序調(diào)用函數(shù)時(shí)同眯,才會動態(tài)重定位函數(shù)的位置。

[圖片上傳失敗...(image-19f73c-1546768649790)]

圖5-24 動態(tài)連接器

為了說明延遲綁定技術(shù)唯鸭,圖5-25是init后须蜗,沒有執(zhí)行到第一個(gè)函數(shù)之前的GOT[2]內(nèi)容,此時(shí)內(nèi)容仍然為PLT條目的下一條地址目溉。

[圖片上傳失敗...(image-e0a60d-1546768649790)]

圖5-25 未執(zhí)行printf前

執(zhí)行到printf后明肮,GOT[2]內(nèi)容發(fā)生了變化,此時(shí)就是printf在虛擬內(nèi)存中的真正地址了缭付,而且之后調(diào)用printf就不用像上述再執(zhí)行一遍了柿估。

[圖片上傳失敗...(image-ec2aca-1546768649790)]

圖5-26 執(zhí)行printf后

5.8 本章小結(jié)

本章討論了鏈接過程是如何實(shí)現(xiàn)的,介紹了可重定位文件和可執(zhí)行文件的ELF格式陷猫,經(jīng)過鏈接處理秫舌,hello變成了可執(zhí)行文件,而此時(shí)還有動態(tài)鏈接內(nèi)容沒有完成绣檬,直到程序在運(yùn)行到函數(shù)時(shí)足陨,動態(tài)鏈接器才將相關(guān)函數(shù)的地址確定。此時(shí)娇未,hello程序真正的完整了墨缘。

第6章 hello進(jìn)程管理

6.1 進(jìn)程的概念與作用

6.1.1. 進(jìn)程的概念

進(jìn)程是一個(gè)執(zhí)行中程序的實(shí)例。系統(tǒng)中的每個(gè)程序都運(yùn)行在某個(gè)進(jìn)程的上下文中零抬,上下文是由程序正確運(yùn)行所需要的狀態(tài)組成的镊讼。這個(gè)狀態(tài)包括存放在內(nèi)存中的程序的代碼和數(shù)據(jù),棧媚值,通用目的寄存器內(nèi)容狠毯,PC,環(huán)境變量等褥芒。

6.1.2. 進(jìn)程的作用

提供一個(gè)假象嚼松,當(dāng)前程序?yàn)橄到y(tǒng)中唯一運(yùn)行的程序,程序仿佛獨(dú)占處理器和內(nèi)存锰扶,程序中的代碼和數(shù)據(jù)好像是系統(tǒng)內(nèi)存中唯一的對象献酗,這給操作系統(tǒng)和用戶管理程序帶來了極大的便利。

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

6.2.1.shell-bash 的作用

shell是一種交互型的應(yīng)用級程序坷牛,它為用戶提供了一種界面罕偎,用戶通過這個(gè)界面運(yùn)行應(yīng)用程序。

6.2.2. shell-bash 的處理流程

shell的處理流程為一系列的讀/求值步驟京闰,然后終止颜及,讀步驟讀取來自用戶的命令行甩苛,求值步驟對命令行進(jìn)行解析,并代表用戶運(yùn)行程序俏站。

6.3 Hello的fork進(jìn)程創(chuàng)建過程

作為hello父進(jìn)程的shell程序讯蒲,在接受到用戶輸入的命令行

./hello 1163450201 jjd

后,shell程序會先對命令行進(jìn)行解析肄扎,首先判斷這不是一個(gè)shell的內(nèi)置命令墨林,然后認(rèn)為./是要運(yùn)行當(dāng)前目錄下的一個(gè)名為hello的程序且參數(shù)為:1163450201 jjd

接著shell會調(diào)用fork函數(shù)創(chuàng)建一個(gè)子進(jìn)程,新建的子進(jìn)程繼承了父進(jìn)程所打開的文件(屏幕犯祠、鍵盤等)旭等,創(chuàng)建父進(jìn)程數(shù)據(jù)的一個(gè)副本(獨(dú)立的),與父進(jìn)程有著相同的虛擬空間地址(獨(dú)立的)衡载,同時(shí)將子進(jìn)程的PID設(shè)置為與父進(jìn)程不同搔耕。至此,hello的fork進(jìn)程創(chuàng)建完畢痰娱。

6.4 Hello的execve過程

創(chuàng)建進(jìn)程完成后度迂,子進(jìn)程中會有一個(gè)關(guān)于pid的判斷邏輯,這是因?yàn)閒ork程序在創(chuàng)建子進(jìn)程后猜揪,對于父進(jìn)程會返回子進(jìn)程的pid,而子進(jìn)程則會返回0.通過判斷語句

if ( pid == 0)

如果判斷出位于子進(jìn)程中坛梁,程序就會執(zhí)行execve函數(shù)將hello程序加載到當(dāng)前進(jìn)程的上下文中而姐。并且同時(shí)帶有參數(shù)列表argv和環(huán)境變量列表envp,同時(shí)execve函數(shù)調(diào)用一次划咐,從不返回拴念。加載了hello之后,它調(diào)用啟動代碼褐缠。啟動代碼設(shè)置棧政鼠,并將控制傳遞給新程序的主函數(shù),執(zhí)行main队魏。

6.5 Hello的進(jìn)程執(zhí)行

hello進(jìn)程的執(zhí)行通過上下文切換來實(shí)現(xiàn)公般,控制權(quán)通過時(shí)間片分配來實(shí)現(xiàn)。

操作系統(tǒng)內(nèi)核通過使用一種稱為上下文切換的異澈埃控制流來實(shí)現(xiàn)多任務(wù)官帘。多任務(wù)的實(shí)現(xiàn)基礎(chǔ)是并發(fā)。多個(gè)邏輯流并發(fā)的執(zhí)行成為并發(fā)昧谊,一個(gè)進(jìn)程和其他進(jìn)程輪流運(yùn)行的概念成為多任務(wù)刽虹。一個(gè)進(jìn)程執(zhí)行它的控制流的一部分的每一個(gè)時(shí)間段稱為時(shí)間片。多任務(wù)也成為時(shí)間分片呢诬。

說回上下文涌哲,內(nèi)核為每個(gè)進(jìn)程維持一個(gè)上下文胖缤。上下文就是內(nèi)核重新啟動一個(gè)被搶占的進(jìn)程所需的狀態(tài)。在進(jìn)程執(zhí)行的某些時(shí)刻阀圾,內(nèi)核可以決定搶占當(dāng)前進(jìn)程哪廓,并重新開始一個(gè)先前被搶占了的進(jìn)程。這種叫做調(diào)度稍刀。

內(nèi)核通過上下文切換機(jī)制實(shí)現(xiàn)將控制轉(zhuǎn)移到新的進(jìn)程撩独。上下文切換

1. 保存當(dāng)前進(jìn)程的上下文

2. 恢復(fù)某個(gè)之前被搶占的進(jìn)程的上下文

3. 控制轉(zhuǎn)移給新的進(jìn)程

hello進(jìn)程執(zhí)行就是在上述基礎(chǔ)上實(shí)現(xiàn)的。上下文切換時(shí)账月,操作系統(tǒng)會進(jìn)入內(nèi)核模式综膀,而結(jié)束時(shí)進(jìn)入用戶模式。這里內(nèi)核模式指的是進(jìn)程可以執(zhí)行指令集中任何操作局齿,訪問任何數(shù)據(jù)的模式剧劝。內(nèi)核模式只有通過異常和系統(tǒng)調(diào)用進(jìn)入,其余都是用戶模式抓歼。當(dāng)然hello進(jìn)程的執(zhí)行也是在用戶模式中讥此。

[圖片上傳失敗...(image-41af27-1546768649790)]

圖6-1 進(jìn)程上下文切換

6.6 hello的異常與信號處理

hello執(zhí)行中可能遇到的異常以及對應(yīng)的信號以及處理辦法

一般來說,操作系統(tǒng)中的異常為下面四種

|

類別

|

原因

|

異步/同步

|

返回此行為

|
|

中斷

|

來自I/O設(shè)備的信號異常

|

異步

|

總是返回到下一條地址

|
|

陷阱

|

有意的異常

|

同步

|

總是返回到下一條指令

|
|

故障

|

潛在可以恢復(fù)的錯誤

|

同步

|

可能返回到當(dāng)前地址

|
|

終止

|

不可恢復(fù)的錯誤

|

同步

|

不返回

|

hello中都有可能遇到谣妻,但是這里只能進(jìn)行其中的幾種測試萄喳,例如回車,Ctrl-Z蹋半,Ctrl-C等

a. 回車

[圖片上傳失敗...(image-4dea54-1546768649790)]

圖6-2 回車

回車并不會對程序運(yùn)行造成什么影響他巨,回車僅僅影響了shell的輸出,導(dǎo)致多了幾個(gè)空行减江。染突。。最后這些回車會進(jìn)入緩沖區(qū)辈灼,作為shell的命令行被讀取份企。

b. Ctrl-C

[圖片上傳失敗...(image-846015-1546768649790)]

圖6-3 終止信號

Ctrl-C直接導(dǎo)致了程序的終止,這是因?yàn)镃trl-C會發(fā)送一個(gè)鍵盤的中斷信號SIGINT給hello巡莹,而hello接受后會執(zhí)行信號處理程序使得程序退出司志。

c. Ctrl-Z

Ctrl-Z發(fā)送一個(gè)來自終端的停止信號SIGTSTP使得程序停止,這是可以使用linuxshell自帶的一些指令對其進(jìn)行觀察榕莺。這種異常是陷阱俐芯。

[圖片上傳失敗...(image-d7e0e1-1546768649790)]

圖6-4 輸入CTRLZ后顯示hello已停止

[圖片上傳失敗...(image-ff2271-1546768649790)]

圖6-5 輸入命令ps查看進(jìn)程

[圖片上傳失敗...(image-39b544-1546768649790)]

圖6-6 使用jobs命令查看當(dāng)前作業(yè),注意此時(shí)hello是停止的

[圖片上傳失敗...(image-26f433-1546768649790)]

[圖片上傳失敗...(image-30346b-1546768649790)]

圖6-7 pstree命令钉鸯,查看進(jìn)程樹

[圖片上傳失敗...(image-dcf99-1546768649790)]

圖6-8 輸入命令fg吧史,恢復(fù)前臺進(jìn)程hello,注意到此時(shí)hello恢復(fù)運(yùn)行

[圖片上傳失敗...(image-5ef687-1546768649790)]

圖6-9 輸入命令kill 向hello發(fā)送終止信號

6.7本章小結(jié)

本章說明了hello進(jìn)程的創(chuàng)建過程和和加載過程,也介紹了hello的信號和異常處理贸营。hello此時(shí)已經(jīng)存在于進(jìn)程中吨述,經(jīng)過這些步驟,hello運(yùn)行了起來钞脂。

第7章 hello的存儲管理

7.1 hello的存儲器地址空間

邏輯地址:

CPU所生成的地址揣云。邏輯地址是內(nèi)部和編程使用的、并不唯一冰啃。例如邓夕,你在進(jìn)行C語言指針編程中,可以讀取指針變量本身值(&操作)阎毅,實(shí)際上這個(gè)值就是邏輯地址焚刚,它是相對于你當(dāng)前進(jìn)程數(shù)據(jù)段的地址(偏移地址),不和絕對物理地址相干扇调。

物理地址:

加載到內(nèi)存地址寄存器中的地址矿咕,內(nèi)存單元的真正地址。在前端總線上傳輸?shù)膬?nèi)存地址都是物理內(nèi)存地址狼钮,編號從0開始一直到可用物理內(nèi)存的最高端碳柱。這些數(shù)字被北橋(Nortbridge chip)映射到實(shí)際的內(nèi)存條上。物理地址是明確的熬芜、最終用在總線上的編號莲镣,不必轉(zhuǎn)換,不必分頁涎拉,也沒有特權(quán)級檢查

線性地址:

線性地址空間是指一個(gè)非負(fù)整數(shù)地址的有序集合剥悟,例如{0,1,2,3……}

虛擬地址:

以線性地址為基礎(chǔ),CPU從地址空間中生成的地址成為虛擬地址曼库,hello反匯編文件中,指令前面的地址就是虛擬地址

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

Linux將虛擬內(nèi)存組織為一些區(qū)域(也稱為段)的集合(下面統(tǒng)稱為段)略板。一個(gè)段就是已經(jīng)存在著的虛擬內(nèi)存的連續(xù)片毁枯。這些頁是以某種方式相關(guān)聯(lián)的。例如hello中的代碼段叮称,數(shù)據(jù)段种玛、堆,共享庫段等瓤檐。每個(gè)存在的虛擬頁面都保存在某個(gè)段中赂韵。而不屬于某個(gè)段的虛擬頁面是不存在的,并且不能被進(jìn)程引用挠蛉。段允許你虛擬地址空間有間隙祭示。內(nèi)核為系統(tǒng)中每個(gè)進(jìn)程維護(hù)這一個(gè)單獨(dú)的任務(wù)結(jié)構(gòu)(task_struct)。任務(wù)結(jié)構(gòu)中的元素包含或指向內(nèi)核運(yùn)行該進(jìn)程所需要的信息谴古。任務(wù)結(jié)構(gòu)中的一個(gè)條目指向mm_struct质涛,描述了虛擬內(nèi)存的當(dāng)前狀態(tài)稠歉。pgd指向第一級頁表的基址,mmap指向一個(gè)vm_area_structs的鏈表汇陆,每個(gè)vm_area_structs都描述了當(dāng)前虛擬地址空間的一個(gè)段怒炸。pgd存放在CR3控制寄存器中。

其中

  1. vm_start:指向段的開始處

  2. vm_end:指向段的結(jié)束處

  3. vm_prot:描述段的頁的讀寫權(quán)限

  4. vm_flags:描述段的頁面是共享的還是私有的

  5. vm_next:指向下一個(gè)段結(jié)構(gòu)

[圖片上傳失敗...(image-2f3fc9-1546768649789)]

圖7-1 虛擬內(nèi)存的段管理

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

虛擬內(nèi)存被組織為一個(gè)由存放在磁盤上的N個(gè)連續(xù)的字節(jié)大小的單元數(shù)組毡代。VM系統(tǒng)通過將虛擬內(nèi)存分割為稱為虛擬頁(VP)的大小固定的塊來處理這個(gè)問題阅羹,物理內(nèi)存被分割為物理頁(PP),大小同VP教寂。虛擬頁面分為三種狀態(tài)

1. 未分配的:未分配塊沒有任何數(shù)據(jù)和它們關(guān)聯(lián)捏鱼。

2. 緩存的:當(dāng)前已緩存在物理內(nèi)存中的已分配頁

3. 未緩存的:未緩存在物理內(nèi)存中的已分配頁

虛擬頁的大小通常在4KB-2MB,為了管理這也虛擬頁孝宗,有了頁表的概念穷躁。頁表就是一個(gè)頁表?xiàng)l目的數(shù)組。每個(gè)頁在頁表的一個(gè)固定偏移量處都有一個(gè)PTE因妇,PTE由一個(gè)有效位和n為地址字段組成问潭。地址字段表示物理頁的起始地址。頁表存放在內(nèi)存中婚被,通過MMU狡忙,可以實(shí)現(xiàn)從虛擬地址翻譯到物理地址的過程。虛擬地址可以分為VPN和VPO址芯,其中VPO的位數(shù)取決與頁的大小灾茁。通過VPN可以確定虛擬頁號,找到相應(yīng)的物理頁號PPN之后谷炸,與VPO合并即可得到對應(yīng)的物理地址北专。

[圖片上傳失敗...(image-4bc7b8-1546768649789)]

圖7-2 虛擬頁和物理頁映射

[圖片上傳失敗...(image-e98715-1546768649789)]

圖7-3 通過VA讀取PT的PPN

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

因?yàn)閺膬?nèi)存中訪問頁表速度還是很慢,所以之后人們又在MMU中增加了頁表的緩存TLB旬陡。TLB通常有著很高的相連度拓颓。TLB地址分為TLBI,TLB索引和TLBT描孟,TLB標(biāo)記驶睦。

[圖片上傳失敗...(image-cd13a0-1546768649789)]

圖7-4 VPN的構(gòu)成

[圖片上傳失敗...(image-6b6da8-1546768649789)]

圖7-5 帶有TLB的VA翻譯過程

如果出現(xiàn)頁表數(shù)量非常多的情況,不僅會大大占用內(nèi)存匿醒,還會對尋找虛擬頁帶來巨大的開銷场航,所以提出了多級頁表的概念,每一級的頁表內(nèi)容是下一級頁表的基址廉羔,當(dāng)頁表內(nèi)容為空時(shí)溉痢,就不創(chuàng)建下一級頁表,等到需要時(shí)在進(jìn)行創(chuàng)建,節(jié)約內(nèi)存空間适室。Intel i7處理器支持4級頁表嫡意,在4級頁表中,從VA到PA的處理過程如下圖

[圖片上傳失敗...(image-9c1c85-1546768649789)]

圖7-6 i7四級頁表的VA到PA

7.5 三級Cache支持下的物理內(nèi)存訪問

緩存的出現(xiàn)是為了緩解存儲設(shè)備和CPU之間巨大的速度差異捣辆。處理器對內(nèi)存數(shù)據(jù)的訪問蔬螟,一般是通過cache進(jìn)行。具體過程為

通過地址解析出緩存的索引和偏移汽畴,對緩存進(jìn)行訪問旧巾,匹配標(biāo)記查找是否含有相關(guān)的字,如果命中忍些,則將數(shù)據(jù)發(fā)送給CPU鲁猩,如果沒有命中,則訪問下一級緩存罢坝,取出這個(gè)字廓握,存入高一級緩存,返回?cái)?shù)據(jù)給CPU

地址一般分為CO嘁酿、CI隙券、CT

[圖片上傳失敗...(image-c2197a-1546768649789)]

圖7-7 PA的構(gòu)成和尋找數(shù)據(jù)示意圖

緩存一共有三種形式:全相聯(lián)高速緩存、組相聯(lián)高速緩存闹司、直接映射高速緩存娱仔。其中最本質(zhì)的區(qū)別就是同一個(gè)組中有多少行。

7.6 hello進(jìn)程fork時(shí)的內(nèi)存映射

當(dāng)fork函數(shù)被當(dāng)前進(jìn)程調(diào)用時(shí)游桩,內(nèi)核為新進(jìn)程創(chuàng)建各種數(shù)據(jù)結(jié)構(gòu)牲迫,并分配給它唯一的PID。為了給這個(gè)新進(jìn)程創(chuàng)建虛擬內(nèi)存借卧,它創(chuàng)建了當(dāng)前進(jìn)進(jìn)程的mm_struct盹憎、段結(jié)構(gòu)和頁表的原樣副本。它將兩個(gè)進(jìn)程中的每個(gè)頁面都標(biāo)記為只讀铐刘,并將兩個(gè)進(jìn)程中的每個(gè)段結(jié)構(gòu)都標(biāo)記為私有的寫時(shí)復(fù)制脚乡。

私有寫式復(fù)制保證了對數(shù)據(jù)的操作不會相互干擾。

[圖片上傳失敗...(image-11c313-1546768649789)]

圖7-8 私有的寫時(shí)復(fù)制

7.7 hello進(jìn)程execve時(shí)的內(nèi)存映射

以hello程序舉例滨达,execve函數(shù)在當(dāng)前進(jìn)程中(子進(jìn)程)加載并運(yùn)行包含在可執(zhí)行文件hello.out中的程序,加載并運(yùn)行hello經(jīng)過了以下幾個(gè)步驟

1. 刪除已存在的用戶區(qū)域

刪除當(dāng)前進(jìn)程的虛擬地址的用戶部分中已存在的區(qū)域結(jié)構(gòu)

2. 映射私有區(qū)域

為新程序的代碼數(shù)據(jù)俯艰、bss和棧段創(chuàng)建新的段結(jié)構(gòu)

3. 映射共享區(qū)域

動態(tài)鏈接共享庫內(nèi)容

4. 設(shè)置程序計(jì)數(shù)器

設(shè)置上下文的PC捡遍,使其指向當(dāng)前的入口。

[圖片上傳失敗...(image-fec8ea-1546768649789)]

圖7-9 加載器的用戶地址空間映射

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

如7.2節(jié)所述竹握,Linux維護(hù)著進(jìn)程的虛擬內(nèi)存画株,假設(shè)MMU在試圖翻譯某個(gè)虛擬地址A時(shí),觸發(fā)了一個(gè)缺頁。這個(gè)異常導(dǎo)致控制轉(zhuǎn)移到內(nèi)核的缺頁處理程序谓传。程序會執(zhí)行以下幾個(gè)步驟

1. A地址是否合法蜈项,如果地址不合法,訪問到了段之外的地址续挟,那么就會觸發(fā)一個(gè)段錯誤紧卒,從而終止進(jìn)程。

2. 試圖進(jìn)行的內(nèi)存訪問是否合法诗祸,即進(jìn)程是否有讀跑芳、寫或執(zhí)行這個(gè)段內(nèi)頁面的權(quán)限,如果不合法直颅,就會觸發(fā)一個(gè)保護(hù)異常博个,終止程序。

3. 如果上述情況都沒有發(fā)生功偿,那就是一個(gè)正常的缺頁盆佣,選擇一個(gè)犧牲頁面,如果頁面沒有修改過械荷,就正常覆蓋共耍,如果修改過,換出在覆蓋养葵。當(dāng)缺頁處理程序返回時(shí)征堪,CPU重新啟動引起缺頁的指令,再次查找关拒。

[圖片上傳失敗...(image-5e1317-1546768649789)]

圖7-10 缺頁處理方式

7.9動態(tài)存儲分配管理

原理:

動態(tài)內(nèi)存分配器維護(hù)著一個(gè)進(jìn)程的虛擬內(nèi)存區(qū)域佃蚜,稱為“堆”。對于每個(gè)進(jìn)程着绊,內(nèi)核維護(hù)著一個(gè)變量brk谐算,指向堆頂。

分配器將堆視為一組不同大小塊的集合來維護(hù)洲脂。每個(gè)塊是一個(gè)連續(xù)的虛擬內(nèi)存片(chunk)剧包,這個(gè)塊要么是已分配的,要么是空閑的疆液。已分配的塊顯式的保留為應(yīng)用程序使用堕油,空閑塊可用來分配潘飘“怪空閑塊保持空閑戈擒,直到它被應(yīng)用程序顯式的分配筐高。已分配塊保持分配凯傲,直到被應(yīng)用程序顯式的釋放或者被分配器隱式的釋放冰单。

由于Linux中使用的是分離適配方法,因此在這里介紹顯示空閑鏈表荒叼。

[圖片上傳失敗...(image-44f771-1546768649789)]

 [圖片上傳失敗...(image-96088f-1546768649789)] 

圖7-11 分配塊 圖7-12 空閑塊

在這里被廓,堆被組織成一個(gè)雙向空閑鏈表嫁乘,在每個(gè)空閑塊中蜓斧,包含著一個(gè)pred前驅(qū)指針和succ后繼指針挎春。這使得首次適配時(shí)間從塊總數(shù)的線性時(shí)間減少到了空閑塊的線性時(shí)間直奋。空閑鏈表的排序策略有兩種

  1. 后進(jìn)先出LIFO的順序維護(hù)鏈表

將新釋放的塊放置在鏈表的開始處殉挽,使用首次適配和LIFO排序策略斯碌,分配器會檢查先檢查最近使用的塊傻唾。

  1. 地址順序維護(hù)鏈表

鏈表中的每一個(gè)塊的地址都小于它的后繼地址,釋放一個(gè)塊需要線性時(shí)間來搜索定位適合的前驅(qū)。

地址排序的首次排序要比LIFO有更高的內(nèi)存利用率迫卢。

當(dāng)一個(gè)應(yīng)用請求k字節(jié)的塊蝗敢,分配器搜索空閑鏈表寿谴,找到適合大小的塊讶泰,這里有三種搜索放置策略

  1. 首次適配

  2. 下一次適配

  3. 最佳適配

一旦分配器找到合適的空閑塊,它通常將空閑塊分割為兩部分惠桃,一個(gè)是已分

配塊辜王,另一個(gè)是空閑塊呐馆⌒牛或者坟岔,如果空閑塊與請求匹配較好,會將整個(gè)空閑塊分配給它鸥咖,盡管會產(chǎn)生內(nèi)部碎片啼辣。

如果空閑鏈表中沒有足夠的空間,分配器會申請新的堆內(nèi)存或者合并空閑塊住涉。分配器通過檢查當(dāng)前塊的前一個(gè)塊的腳部舆声,獲取前一個(gè)塊的位置和狀態(tài),決定是否合并蛾找。一般有四種情況

  1. 前后都分配

  2. 前分配打毛,后空閑

  3. 前空閑,后分配

  4. 前后空閑

不同情況對應(yīng)不同處理方式熬甫,都是把空閑中的多出來的頭部腳部去掉椿肩。

7.10本章小結(jié)

本章介紹了在存儲層面hello是如何被存儲訪問的和Linux是如何管理虛擬內(nèi)存的蘸朋。

Linux將磁盤文件抽象為虛擬內(nèi)存用來管理,為了管理這些虛擬頁噪沙,又建立了頁表PTE,為了更加高效的讀取PTE局义,又建立了TLB。通過MMU另萤,將VA轉(zhuǎn)換為PA四敞,實(shí)現(xiàn)對數(shù)據(jù)的訪問。最后為了實(shí)現(xiàn)主動對內(nèi)存的管理铺厨,簡單介紹了動態(tài)內(nèi)存管理的辦法。

第8章 hello的IO管理

8.1 Linux的IO設(shè)備管理方法

設(shè)備的模型化:

文件是C語言和Linux管理的思想伐蒂,所有的IO設(shè)備都被抽象為文件恩沛,所有的輸入輸出都作為對文件的操作雷客。這個(gè)設(shè)備映射為文件的方式,允許Linux內(nèi)核引出一個(gè)簡單部逮、低級的應(yīng)用接口,稱為Unix I/O颅和,這使得輸入和輸出都能以一種統(tǒng)一且一致的方式的來執(zhí)行。

8.2 簡述Unix IO接口及其函數(shù)

Linux以文件的方式對I/O設(shè)備進(jìn)行讀寫有额,將設(shè)備均視為文件巍佑。對文件的操作,內(nèi)核提供了Unix I/O接口脆栋。

打開文件:int open(char *filename, int flags, mode_t mode);

關(guān)閉文件:int close(int fd);

讀文件:ssize_t read(int fd, void *buf, size_t n);

寫文件:ssize_t write(int fd, const void *buf, size_t n);

8.3 printf的實(shí)現(xiàn)分析

printf實(shí)現(xiàn)代碼如下:

  1. static int printf(const char *fmt, ...)

  2. {

  3. **va_list** args;  
    
  4. **int** i;  
    
  5. va_start(args, fmt);  
    
  6. write(1,printbuf,i=vsprintf(printbuf, fmt, args));  
    
  7. va_end(args);  
    
  8. **return** i;  
    
  9. }

其中*fmt是格式字符串熟嫩,后面是變量,也就是打印用的變量。va_list型變量args表示參數(shù)板壮,va-_start函數(shù)是取到fmt中的第一個(gè)參數(shù)的地址,write為系統(tǒng)級函數(shù)茬底,vsprintf函數(shù)用來格式化殿如,該函數(shù)返回值打印出字符串的長度涉馁,同時(shí)將printbuf根據(jù)格式串進(jìn)行格式化寒随,產(chǎn)生格式化輸出。write函數(shù)調(diào)用syscall實(shí)現(xiàn)系統(tǒng)調(diào)用陷入內(nèi)核讯泣,使內(nèi)核執(zhí)行打印操作。

從vsprintf生成顯示信息,到write系統(tǒng)函數(shù)霍掺,到陷阱-系統(tǒng)調(diào)用 int 0x80或syscall.棋凳。字符顯示驅(qū)動子程序:從ASCII到字模庫到顯示vram(存儲每一個(gè)點(diǎn)的RGB顏色信息)。顯示芯片按照刷新頻率逐行讀取vram拍棕,并通過信號線向液晶顯示器傳輸每一個(gè)點(diǎn)(RGB分量)。

8.4 getchar的實(shí)現(xiàn)分析

getchar函數(shù)的實(shí)現(xiàn)如下

  1. int getchar(void)

  2. {

  3. **char** c;  
    
  4. **return** (read(0,&c,1)==1)?(unsigned **char**)c:EOF  
    
  5. }

getchar函數(shù)通過使用read這個(gè)系統(tǒng)級函數(shù)返回字符蠢箩。read第一個(gè)參數(shù)表示標(biāo)準(zhǔn)輸入(0),最后參數(shù)表示讀入字符的數(shù)量。

read函數(shù)也是使用了syscall陷入系統(tǒng)內(nèi)核贱鼻,鍵盤中斷處理程序會等待鍵盤輸入,read從緩沖區(qū)讀取拘悦。

異步異常-鍵盤中斷的處理:鍵盤中斷處理子程序。接受按鍵掃描碼轉(zhuǎn)成ascii碼,保存到系統(tǒng)的鍵盤緩沖區(qū)蘑斧。

getchar等調(diào)用read系統(tǒng)函數(shù)沟突,通過系統(tǒng)調(diào)用讀取按鍵ascii碼,直到接受到回車鍵才返回。

8.5本章小結(jié)

Linux將IO設(shè)備抽象為文件域携,提供了IO接口。通過接口锋边,程序能夠簡單的對設(shè)備進(jìn)行調(diào)用灵巧,這就是Linux系統(tǒng)的思想之一。printf函數(shù)通過系統(tǒng)級函數(shù)write函數(shù)對標(biāo)準(zhǔn)輸出進(jìn)行輸出敏弃,getchar使用read來對標(biāo)準(zhǔn)輸入進(jìn)行接收。

結(jié)論

hello從誕生到毀滅經(jīng)過了一系列過程瓶颠,它的一生的過程有:

1. 通過IDE輸入代碼形成了c格式文件hello.c

2. 通過預(yù)處理器,編譯器屋匕,匯編器,鏈接器最終生成可執(zhí)行文件hello

3. 通過shell的fork函數(shù)為hello創(chuàng)造了能夠運(yùn)行的上下文,通過execve函數(shù)將hello加載入進(jìn)程之中廓推,使得hello真正的運(yùn)行了起來

4. 通過Linux的虛擬內(nèi)存管理翩隧,操作系統(tǒng)的時(shí)間片分配樊展,信號接受處理機(jī)制,IO輸入輸出等堆生,hello程序在計(jì)算機(jī)中游刃有余的輸出到屏幕专缠,讀取共享庫中的代碼并使用淑仆。

5. 通過Linux的進(jìn)程回收機(jī)制涝婉,hello運(yùn)行完后,結(jié)束了它短暫的一生蔗怠。

一個(gè)簡單的hello程序如果要成功運(yùn)行墩弯,需要經(jīng)過很多很多的步驟,操作系統(tǒng)與硬件相互配合寞射,精密耦合渔工,才能保證程序成功的運(yùn)行。

操作系統(tǒng)是一個(gè)高度復(fù)雜的精密系統(tǒng)桥温,每一步都不可或缺引矩,用戶模式和內(nèi)核模式保證了工作處理的互不干擾,系統(tǒng)調(diào)用保證了系統(tǒng)安全的同時(shí)也允許用戶能夠使用簡答的系統(tǒng)功能侵浸。高度抽象的思想貫徹到了系統(tǒng)的每一處旺韭。

硬件系統(tǒng)上,磁盤與CPU之間巨大的速度差距使得緩存的誕生掏觉,多級緩存保證了速度的逐漸穩(wěn)定過度茂翔。同樣也是緩存的思想,TLB提高了頁表的訪問速度履腋,緩沖區(qū)使得輸入可以不用與讀取的速度匹配珊燎,寄存器作為最高級別L0的緩存惭嚣,對于CPU的計(jì)算,匯編的運(yùn)行做出了巨大貢獻(xiàn)悔政。

包裝的思想無處不在晚吞,從最基本二進(jìn)制抽象為十六進(jìn)制,到代碼包裝為函數(shù)谋国,到函數(shù)被包裝為簡單的接口槽地,在層層包裝為模塊芦瘾,組成了系統(tǒng)捌蚊,包裝也可以說為抽象,隨意啦近弟。

系統(tǒng)協(xié)調(diào)一切缅糟,系統(tǒng)領(lǐng)導(dǎo)一切,每個(gè)穩(wěn)定運(yùn)行的程序背后祷愉,都是操作系統(tǒng)在背后的默默付出窗宦。系統(tǒng)協(xié)調(diào)軟硬件無縫合作,使得二進(jìn)制10在總線和芯片上翻涌著浪花二鳄。

誰能想到赴涵,簡單的hello卻蘊(yùn)含著這么多呢。

附件

  1. hello.c

hello程序的源程序订讼,用來進(jìn)行接下來的處理

  1. hello.i

hello.c文件經(jīng)過預(yù)處理器處理后的文件髓窜,替換了c文件中的#開頭語句, 該文件用于接下來的編譯

  1. hello.s

hello.i文件經(jīng)過編譯后生成的文件欺殿,將源代碼換為匯編代碼寄纵,該文件用于接下來匯編器的處理

  1. hello.o

hello.s文件經(jīng)過匯編器處理生成的文件,生成可重定位文件祈餐,為經(jīng)過鏈接處理擂啥,該文件用于接下來和其他動態(tài)庫或靜態(tài)庫鏈接生成可執(zhí)行文件

  1. hello

經(jīng)過gcc命令通過hello.c生成

  1. hello

經(jīng)過ld命令鏈接器最終生成的文件哄陶,可以用來運(yùn)行和研究帆阳。

參考文獻(xiàn)

[1] 蘭德爾E, 布萊恩特. 深入理解計(jì)算機(jī)系統(tǒng)[M]. 北京:機(jī)械工業(yè)出版社, 2016..

[2] rabbit_in_android.虛擬地址、邏輯地址屋吨、線性地址蜒谤、物理地址[EB/OL]. https://blog.csdn.net/rabbit_in_android/article/details/49976101.2015-2-22-2018-12-31.

[3] wang_xya.[EB/OL]. https://blog.csdn.net/wang_xya/article/details/43985241.2015-2-28-2018-12-31.

[4] Pianistx.[EB/OL]. https://www.cnblogs.com/pianist/p/3315801.html.2013-9-11-2018-12-31.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市至扰,隨后出現(xiàn)的幾起案子鳍徽,更是在濱河造成了極大的恐慌,老刑警劉巖敢课,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阶祭,死亡現(xiàn)場離奇詭異绷杜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)濒募,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門鞭盟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瑰剃,你說我怎么就攤上這事齿诉。” “怎么了晌姚?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵粤剧,是天一觀的道長。 經(jīng)常有香客問我挥唠,道長抵恋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任猛遍,我火速辦了婚禮馋记,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘懊烤。我一直安慰自己梯醒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布腌紧。 她就那樣靜靜地躺著茸习,像睡著了一般。 火紅的嫁衣襯著肌膚如雪壁肋。 梳的紋絲不亂的頭發(fā)上号胚,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機(jī)與錄音浸遗,去河邊找鬼猫胁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛跛锌,可吹牛的內(nèi)容都是我干的弃秆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼髓帽,長吁一口氣:“原來是場噩夢啊……” “哼菠赚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起郑藏,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤衡查,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后必盖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拌牲,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俱饿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了塌忽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稍途。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖砚婆,靈堂內(nèi)的尸體忽然破棺而出械拍,到底是詐尸還是另有隱情,我是刑警寧澤装盯,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布坷虑,位于F島的核電站,受9級特大地震影響埂奈,放射性物質(zhì)發(fā)生泄漏迄损。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一账磺、第九天 我趴在偏房一處隱蔽的房頂上張望芹敌。 院中可真熱鬧,春花似錦垮抗、人聲如沸氏捞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽液茎。三九已至,卻和暖如春辞嗡,著一層夾襖步出監(jiān)牢的瞬間捆等,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工续室, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留栋烤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓挺狰,卻偏偏與公主長得像明郭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子她渴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容