惠州+網(wǎng)站建設(shè)公司南昌seo管理
謝謝閱讀,如有錯(cuò)誤請(qǐng)大佬留言!!
目錄
謝謝閱讀,如有錯(cuò)誤請(qǐng)大佬留言!!
拋出總結(jié)
開(kāi)始介紹
發(fā)現(xiàn)問(wèn)題
進(jìn)程地址空間(虛擬地址)
頁(yè)表
物理內(nèi)存與進(jìn)程地址空間映射
缺頁(yè)中斷基本概念
寫(xiě)時(shí)拷貝的原理(基于缺頁(yè)中斷)
拋出總結(jié)
進(jìn)程:何為進(jìn)程進(jìn)程 = 內(nèi)核數(shù)據(jù)結(jié)構(gòu)(PCB+mm_struct+頁(yè)表(MMU))+ 代碼和數(shù)據(jù)
開(kāi)始介紹
發(fā)現(xiàn)問(wèn)題
看一份代碼
?我們知道,當(dāng)子進(jìn)程出現(xiàn)寫(xiě)實(shí)拷貝的時(shí)候,將共享的數(shù)據(jù)拷貝一份,為子進(jìn)程獨(dú)立存儲(chǔ)。
讓我們運(yùn)行該代碼。
分析結(jié)果:1、寫(xiě)實(shí)拷貝前:在寫(xiě)實(shí)拷貝前子進(jìn)程與父進(jìn)程的flag數(shù)據(jù)相同,數(shù)據(jù)地址相同,無(wú)可厚非為寫(xiě)時(shí)拷貝前,父子進(jìn)程共享一塊數(shù)據(jù)空間
? ? ? ? ? ? ? ? ?2、寫(xiě)實(shí)拷貝:子進(jìn)程準(zhǔn)備修改flag數(shù)據(jù),先發(fā)生寫(xiě)實(shí)拷貝-父進(jìn)程的flag數(shù)據(jù)拷貝一份,然后拷貝的空間給子進(jìn)程形成獨(dú)立,然后子進(jìn)程的數(shù)據(jù)flag改變?yōu)?22。
? ? ? ? ? ? ? ? ?3、寫(xiě)實(shí)拷貝后,我們觀察結(jié)果:子進(jìn)程與父進(jìn)程的flag值確實(shí)發(fā)生了不一樣了,但是!!我們驚奇的發(fā)現(xiàn)他們的地址居然一模一樣!同一個(gè)地址怎么能保存不一樣的值呢??
????????????????得出結(jié)論,我們看見(jiàn)的地址,其實(shí)并不是直接物理內(nèi)存上面的地址,這里我們看見(jiàn)的地址其實(shí)是進(jìn)程進(jìn)程地址空間(虛擬地址)。
進(jìn)程地址空間(虛擬地址)
我們常見(jiàn)這個(gè)表其實(shí)是不是內(nèi)存上面的數(shù)據(jù)區(qū)分布,其實(shí)是進(jìn)程空間分布圖。它其實(shí)一種結(jié)構(gòu)體類(lèi)型。
讓我們講個(gè)小故事更加了解虛擬內(nèi)存:
????????有一個(gè)有錢(qián)的富翁他對(duì)他的朋友非常的好,但是他的朋友們都不知道富翁有其他朋友,以為富翁只有他一個(gè)朋友,富翁有一千萬(wàn)元,他對(duì)所有朋友說(shuō)哎呀我的錢(qián)都可以借給你,但是你不能一下子借太多,要經(jīng)過(guò)我的同意才行。富翁所有的朋友都認(rèn)為自己可以向富翁借款一千萬(wàn),所有他們就先規(guī)劃了這一千萬(wàn)怎么用。富翁也可以同時(shí)放貸給許多朋友。故事先暫停一下。
? ? ? ? 這里的富翁換成物理內(nèi)存,而朋友們換成進(jìn)程,所有的進(jìn)程都是獨(dú)立的其實(shí),但是進(jìn)程都認(rèn)為一個(gè)人獨(dú)占了整個(gè)物理內(nèi)存資源,所以就事先規(guī)劃了內(nèi)存使用的分布。劃分了并不代表?yè)碛?#xff0c;只是劃分了而已
????????這里我們介紹一下inux的進(jìn)程地址空間:struct mm_struct{}? 這個(gè)結(jié)構(gòu)體就是進(jìn)程地址空間結(jié)構(gòu)體
????????這里的每對(duì)數(shù)據(jù)其實(shí)就是對(duì)應(yīng)著每個(gè)段的開(kāi)始和結(jié)束。
但是單單只有進(jìn)程地址空間也沒(méi)辦法,畢竟所有的進(jìn)程都認(rèn)為自己獨(dú)占了物理內(nèi)存,所以必須加上一些東西-->頁(yè)表+查葉表。
頁(yè)表
查頁(yè)表:也就是在頁(yè)表上查詢(xún)數(shù)據(jù)一個(gè)硬件設(shè)施(這里我們不做過(guò)度說(shuō)明)
頁(yè)表:Linux在啟動(dòng)過(guò)程中,要首先進(jìn)行內(nèi)存的初始化,那么就一定要首先創(chuàng)建頁(yè)表。我們知道每個(gè)進(jìn)程都擁有各自的進(jìn)程空間,而每個(gè)進(jìn)程空間又分為內(nèi)核空間和用戶(hù)空間。
以32位計(jì)算機(jī)為例,每個(gè)進(jìn)程有4G的虛擬空間,其中0-3G屬于用戶(hù)地址空間,3G-4G屬于內(nèi)核地址空間,內(nèi)核地址空間是所有進(jìn)程共享的,因此內(nèi)核地址空間的頁(yè)表也是所有進(jìn)程共享的。
Linux內(nèi)核中用戶(hù)進(jìn)程內(nèi)存頁(yè)表的管理是通過(guò)一個(gè)結(jié)構(gòu)體mm_struct來(lái)描述的??
讓我們抽象的描述頁(yè)表
?左邊是進(jìn)程地址空間,而右邊是物理地址空間,頁(yè)表是承接進(jìn)程地址空間與物理地址空間的橋梁。
接下來(lái)我們將物理內(nèi)存、頁(yè)表、進(jìn)程地址空間建立一個(gè)初步的了解:
物理內(nèi)存與進(jìn)程地址空間映射
舉個(gè)例子:現(xiàn)在我們進(jìn)程中main函數(shù)地址我們需要存在物理內(nèi)存中先將main虛擬地址傳入頁(yè)表進(jìn)程空間列
?然后在操作系統(tǒng)將該進(jìn)程main函數(shù)真實(shí)地址與對(duì)應(yīng)虛擬地址對(duì)應(yīng)。
?這樣當(dāng)我們需要訪(fǎng)問(wèn)main時(shí)的時(shí)候我們的操作系統(tǒng)就會(huì)讓cpu根據(jù)該進(jìn)程的頁(yè)表映射關(guān)系找到實(shí)際的函數(shù)代碼入口。
小知識(shí)點(diǎn),為了讓cpu快速在虛擬地址訪(fǎng)問(wèn)到進(jìn)程入口處,我們無(wú)論是哪個(gè)進(jìn)程的main函數(shù)地址都是一樣的。
觀察進(jìn)程pid:兩個(gè)不同進(jìn)程加載到了內(nèi)存,但是他們的main函數(shù)地址居然時(shí)相同的,發(fā)現(xiàn)雖然進(jìn)程不同當(dāng)時(shí)main函數(shù)入口是相同的,然后再?gòu)呐c對(duì)應(yīng)的物理內(nèi)存實(shí)際映射找到物理內(nèi)存上該進(jìn)程main實(shí)際的地址:我們的
繼續(xù)觀察
?
讓我們同時(shí)運(yùn)行程序?
兩份代碼同時(shí)加載在進(jìn)程,同時(shí)為R狀態(tài),但是他們的main地址居然相同,有一次的告訴我們我們?nèi)〉胢ain函數(shù)地址為虛擬地址。這些進(jìn)程都有自己的PCB,mm_struct、頁(yè)表,所有他們?cè)L問(wèn)內(nèi)存實(shí)際其實(shí)是,通過(guò)映射關(guān)系訪(fǎng)問(wèn),而不是直接去內(nèi)存訪(fǎng)問(wèn)數(shù)據(jù)
?各訪(fǎng)問(wèn)各的。
?缺頁(yè)中斷基本概念
個(gè)人理解:缺頁(yè)中斷就是操作系統(tǒng)先暫停對(duì)進(jìn)程通過(guò)頁(yè)表訪(fǎng)問(wèn)物理內(nèi)存,然后操作系統(tǒng)對(duì)物理內(nèi)存進(jìn)行操作(拷貝父進(jìn)程數(shù)據(jù)(寫(xiě)實(shí)拷貝),申請(qǐng)動(dòng)態(tài)內(nèi)存空間),然后再讓進(jìn)程操作該空間數(shù)據(jù)
我們先寫(xiě)份代碼:
#include<iostream>
#include<unistd,h>
int main()
{int*p=new int[10];*p=10086;*(p+1)=10087;*(p+2)=10088;return 0;
}
我們向內(nèi)存申請(qǐng)40各字節(jié)的空間。確實(shí)現(xiàn)在空間的使用全給我了。但是我并沒(méi)有立刻使用空間,而是過(guò)了10秒才使用。如果這個(gè)空間一直等待我我寫(xiě)入數(shù)據(jù)的話(huà),大大的浪費(fèi)了內(nèi)存的使用效率。所有操作系統(tǒng)會(huì)先讓急需內(nèi)存的進(jìn)程先使用空間,當(dāng)我需要寫(xiě)入的時(shí)候,再去給我開(kāi)辟空間。
畫(huà)圖理解:
第一步先去申請(qǐng)空間:我們?cè)谶M(jìn)程地址空間查看是可以開(kāi)辟40各字節(jié)的空間,允許開(kāi)辟,然后返回空間地址值,這里我們這里申請(qǐng)的是虛擬地址空間,然后反饋告訴進(jìn)程,申請(qǐng)空間成功。(其實(shí)并沒(méi)有在物理內(nèi)存中申請(qǐng))。
?第二,我們進(jìn)程進(jìn)入休眠狀態(tài),如果我們申請(qǐng)的是物理內(nèi)存,那么這10秒我們申請(qǐng)的物理內(nèi)存就要一直等待被當(dāng)前進(jìn)程使用,現(xiàn)在我們申請(qǐng)的只是虛擬內(nèi)存,這并不占用物理內(nèi)存40個(gè)字節(jié),這樣這40個(gè)字節(jié)空間可以被其他的進(jìn)程先使用
?當(dāng)10秒過(guò)去后,我們cpu運(yùn)行當(dāng)前進(jìn)程,需要寫(xiě)入數(shù)據(jù),這時(shí)不會(huì)立刻寫(xiě)入數(shù)據(jù)而是,先發(fā)生中斷,也叫做缺頁(yè)中斷。操作系統(tǒng)先去物理內(nèi)存申請(qǐng)40個(gè)字節(jié)空間然后與該進(jìn)程建立映射關(guān)系,然后才將數(shù)據(jù)寫(xiě)入空間(這里寫(xiě)一份可能就開(kāi)辟4個(gè)字節(jié)空間,還有36字節(jié)空間不會(huì)開(kāi)辟,未學(xué)習(xí)地方,以后回來(lái)補(bǔ)充)。
看步驟
?休眠結(jié)束當(dāng)我們需要在這塊空間寫(xiě)入空間,先暫停寫(xiě)入
?I:操作系統(tǒng)先在物理內(nèi)存開(kāi)辟空間。
?
II:將開(kāi)辟的空間與進(jìn)程地址空間建立映射關(guān)系(操作會(huì)進(jìn)行到訪(fǎng)問(wèn)進(jìn)程)
?
?III、最后通過(guò)映射關(guān)系,在物理地址上寫(xiě)入數(shù)據(jù)
?全圖:
并不是所有的地址都映射在頁(yè)表上:?
?如果是這樣,4G物理內(nèi)存只能跑一個(gè)進(jìn)程甚至一個(gè)都跑不了。一頁(yè)項(xiàng)有物理內(nèi)存與虛擬內(nèi)存,不止要4g空間。
所有頁(yè)表也是按需申請(qǐng)頁(yè)表項(xiàng)的。
寫(xiě)時(shí)拷貝的原理(基于缺頁(yè)中斷)
運(yùn)行這段代碼得到結(jié)果,我們知道發(fā)生了寫(xiě)實(shí)拷貝
讓我們看看怎么回事
?首先:這是父進(jìn)程的進(jìn)程信息,val存放在虛擬地址數(shù)據(jù)區(qū),映射在物理內(nèi)存上為0x00afcd。
發(fā)生創(chuàng)建子進(jìn)程,其實(shí)就是將PCB、mm_struct、頁(yè)表拷貝一份給子進(jìn)程,那么還沒(méi)寫(xiě)實(shí)拷貝前,其實(shí)所有的數(shù)據(jù)都是和父進(jìn)程一模一樣的。就是直接拷貝一份父進(jìn)程數(shù)據(jù)給子進(jìn)程。創(chuàng)建子進(jìn)程時(shí),將父進(jìn)程的?虛擬內(nèi)存
?與?物理內(nèi)存
?映射關(guān)系復(fù)制到子進(jìn)程中,并將內(nèi)存設(shè)置為只讀(設(shè)置為只讀是為了當(dāng)對(duì)內(nèi)存進(jìn)行寫(xiě)操作時(shí)觸發(fā)?缺頁(yè)異常
)。
他們所有數(shù)據(jù)都是一樣的,映射關(guān)系也是相同的。
????????當(dāng)我們想要改變子進(jìn)程的val值會(huì)發(fā)生寫(xiě)實(shí)拷貝。發(fā)現(xiàn)該數(shù)據(jù)為在頁(yè)表項(xiàng)為只讀發(fā)生缺頁(yè)中斷,拷貝該物理空間數(shù)據(jù),將映射關(guān)系改為映射拷貝的物理內(nèi)存數(shù)據(jù)。
?
?這里改變子進(jìn)程頁(yè)表的val權(quán)限,不會(huì)影響父進(jìn)程的val權(quán)限,防止父進(jìn)程創(chuàng)建多個(gè)子進(jìn)程而導(dǎo)致無(wú)法發(fā)生缺頁(yè)中斷。
如果父進(jìn)程發(fā)生寫(xiě)實(shí)拷貝呢?那么就是父進(jìn)程改變映射關(guān)系,映射到拷貝的空間上,權(quán)限為可讀可寫(xiě),而原空間可能依舊有多個(gè)進(jìn)程正在使用,所以原空間權(quán)限不變,不受到父進(jìn)程的影響
謝謝閱讀,如有錯(cuò)誤請(qǐng)大佬留言!!