日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

來源:微信公眾號:cpp軟件架構(gòu)獅

要想回答這個問題,就得刨根問底,內(nèi)存到底是怎樣分配的?

在內(nèi)核態(tài)的角度來看,進(jìn)程需要分配內(nèi)存的方式有兩種:brk和mmap這兩個系統(tǒng)調(diào)用。

  1. brk是數(shù)據(jù)段的最高地址指針_edata往高地址增長;
  2. mmap是建立了頁到用戶進(jìn)程的虛擬空間映射,在進(jìn)程的虛擬地址空間中,堆和棧之間的大空間中找一塊空閑的地址。

這兩種方式分配到的都是虛擬內(nèi)存,并還沒有分配真正的物理地址。會在第一次訪問的時候,內(nèi)核判斷有沒有物理地址,如果沒有回發(fā)生缺頁中斷,然后分配物理地址,建立虛擬地址和物理地址之間的映射關(guān)系。

但在用戶態(tài)申請動態(tài)內(nèi)存,是不會直接調(diào)用這兩個系統(tǒng)調(diào)用接口。一般情況下是通過C庫的malloc/free來申請和釋放。而C庫的實(shí)現(xiàn)也正是基于這兩個系統(tǒng)調(diào)用接口,進(jìn)行上層的封裝和管理。

當(dāng)內(nèi)核發(fā)生缺頁中斷的時候,到底會做哪些事情呢?

首先進(jìn)程會從用戶態(tài)陷入到內(nèi)核態(tài)運(yùn)行,會執(zhí)行以下步驟:

  1. 檢查需要訪問的虛擬地址是否合法;
  2. 查找,分配物理頁;
  3. 填充物理頁的內(nèi)容;
  4. 建立映射關(guān)系(虛擬地址->物理地址);

重新執(zhí)行發(fā)生缺頁中斷的那條指令。

如果第3步,需要讀取磁盤,那么這次缺頁中斷就是majflt,否則就是minflt

注意:

用ps -o majflt,minflt -C program命令查看。

majflt代表major fault,中文名叫主錯誤,minflt代表minor fault,中文名叫次錯誤。

這兩個數(shù)值表示一個進(jìn)程自啟動以來所發(fā)生的缺頁中斷的次數(shù)。

下面我們用實(shí)例來解釋下內(nèi)存申請的原理

情況一、malloc小于128k的內(nèi)存,使用brk分配內(nèi)存,將_edata往高地址推(只分配虛擬空間,不對應(yīng)物理內(nèi)存(因此沒有初始化),第一次讀/寫數(shù)據(jù)時,引起內(nèi)核缺頁中斷,內(nèi)核才分配對應(yīng)的物理內(nèi)存,然后虛擬地址空間建立映射關(guān)系),如下圖:

 

為什么會產(chǎn)生內(nèi)存碎片?

 

 

1、進(jìn)程啟動的時候,其(虛擬)內(nèi)存空間的初始布局如第一幅圖所示。其中,mmap內(nèi)存映射文件是在堆和棧的中間(例如libc-2.2.93.so,其它數(shù)據(jù)文件等),為了簡單起見,省略了內(nèi)存映射文件。_edata指針(glibc里面定義)指向數(shù)據(jù)段的最高地址。

2、進(jìn)程調(diào)用A=malloc(30K)以后,內(nèi)存空間如第二幅圖:malloc函數(shù)會調(diào)用brk系統(tǒng)調(diào)用,將_edata指針往高地址推30K,就完成虛擬內(nèi)存分配。你可能會問:只要把_edata+30K就完成內(nèi)存分配了?

事實(shí)是這樣的,_edata+30K只是完成虛擬地址的分配,A這塊內(nèi)存現(xiàn)在還是沒有物理頁與之對應(yīng)的,等到進(jìn)程第一次讀寫A這塊內(nèi)存的時候,發(fā)生缺頁中斷,這個時候,內(nèi)核才分配A這塊內(nèi)存對應(yīng)的物理頁。也就是說,如果用malloc分配了A這塊內(nèi)容,然后從來不訪問它,那么,A對應(yīng)的物理頁是不會被分配的。

3、進(jìn)程調(diào)用B=malloc(40K)以后,內(nèi)存空間如第三幅圖。

情況二、malloc大于128k的內(nèi)存,使用mmap分配內(nèi)存,在堆和棧之間找一塊空閑內(nèi)存分配(對應(yīng)獨(dú)立內(nèi)存,而且初始化為0),如下圖:

 

為什么會產(chǎn)生內(nèi)存碎片?

 

 

4、進(jìn)程調(diào)用C=malloc(200K)以后,內(nèi)存空間第一幅圖:默認(rèn)情況下,malloc函數(shù)分配內(nèi)存,如果請求內(nèi)存大于128K(可由M_MMAP_THRESHOLD選項調(diào)節(jié)),那就不是去推_edata指針了,而是利用mmap系統(tǒng)調(diào)用,從堆和棧的中間分配一塊虛擬內(nèi)存。

這樣子做主要是因?yàn)?brk分配的內(nèi)存需要等到高地址內(nèi)存釋放以后才能釋放(例如,在B釋放之前,A是不可能釋放的,這就是內(nèi)存碎片產(chǎn)生的原因,什么時候緊縮看下面),而mmap分配的內(nèi)存可以單獨(dú)釋放。

當(dāng)然,還有其它的好處,也有壞處,再具體下去,有興趣的同學(xué)可以去看glibc里面malloc的代碼了。

5、進(jìn)程調(diào)用D=malloc(100K)以后,內(nèi)存空間如圖5;

6、進(jìn)程調(diào)用free(C)以后,C對應(yīng)的虛擬內(nèi)存和物理內(nèi)存一起釋放。

 

為什么會產(chǎn)生內(nèi)存碎片?

 

 

7、進(jìn)程調(diào)用free(B)以后,如第一幅圖所示:B對應(yīng)的虛擬內(nèi)存和物理內(nèi)存都沒有釋放,因?yàn)橹挥幸粋€_edata指針,如果往回推,那么D這塊內(nèi)存怎么辦呢

當(dāng)然,B這塊內(nèi)存,是可以重用的,如果這個時候再來一個40K的請求,那么malloc很可能就把B這塊內(nèi)存返回回去了。

8、進(jìn)程調(diào)用free(D)以后,如第二幅圖所示:B和D連接起來,變成一塊140K的空閑內(nèi)存。

9、默認(rèn)情況下:當(dāng)最高地址空間的空閑內(nèi)存超過128K(可由M_TRIM_THRESHOLD選項調(diào)節(jié))時,執(zhí)行內(nèi)存緊縮操作(trim)。在上一個步驟free的時候,發(fā)現(xiàn)最高地址空閑內(nèi)存超過128K,于是內(nèi)存緊縮,變成第三幅圖所示。

如果這個時候我們想要申請一塊50K的內(nèi)存E,就會出現(xiàn)下圖的情況。

 

為什么會產(chǎn)生內(nèi)存碎片?

 

有沒有發(fā)現(xiàn),并沒有從中間40K的內(nèi)存分配,因?yàn)椴粷M足我們申請50K大小的要求,而是向下偏移指針_edata分配50K,到這里就能發(fā)現(xiàn)就產(chǎn)生了內(nèi)存碎片,這塊碎片正是那40K。

系統(tǒng)運(yùn)行的越久,出現(xiàn)上述這樣的情況越多,整個系統(tǒng)的小內(nèi)存就會很多,導(dǎo)致的結(jié)果是:查看系統(tǒng)的剩余內(nèi)存比較可觀,但申請大塊內(nèi)存會返回失敗。

分享到:
標(biāo)簽:碎片 內(nèi)存
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定