1. 程式人生 > >淺析Linux下的堆記憶體管理

淺析Linux下的堆記憶體管理

  最近在看一本叫《程式設計師的自我修養-連結.裝載域庫》(俞甲子,石凡,潘愛民 著)這本書,不得不像大家安利這本書,從最基本的EFL檔案到連結,再到裝載,雖然現在還沒看完,但是對程式的可執行檔案和虛擬記憶體的佈局有了進一步的瞭解,不得不說是一本好書。言歸正傳,本文討論的話題是堆記憶體的管理,首先讓我們來了解一下程序的虛擬地址空間的組成部分(如下圖):

首先來討論一下這個program break,手冊上說brk和sbrk會改變program break的位置,program break被定義為程式data segment的結束位置。感覺program break被定義成程式data segment的結束位置理解有點模糊,下面我們就用最基本的程式來驗證。

1.程式程式碼如下:

#include <stdio.h>   
#include <unistd.h>
#include <apue.h>   
int main(){  
     void* p = sbrk(0);  //獲取當前program break的當前位置(此時是heap的起始地址)
     int* p1 = (int *)p; // 將當前空指標轉化成int型指標
     brk(p1+4);   //分配了16個位元組的空間
     p1[0] = 10;  
     p1[1] = 20;  
     p1[2] = 30;  
     p1[3] = 40;  
     p1[4] = 50;     
     printf("%p\n",p1);
     printf("%p\n",p1+1);
     printf("%p\n",p1+2);
     printf("%p\n",p1+3); 
     printf("%p\n",sbrk(0));//這個時候program break的值比較大,因為一次性分配了較大的空間,而不必每次都呼叫sbrk()來分配堆空間
     while(1){
	sleep(1);	
	}
}  

首先我們將程式在後臺執行(./a.out &),得到的結果是:


然後,執行命令cat  /proc/18092/maps就可以得到程序堆的地址範圍,如下圖:

可以看到我們程序堆的範圍是09c2b000-09c4d000 rw-p 00000000 00:00 0  [heap],堆的起始地址和p1的地址一樣,說明程式brk()在堆上申請空間,也就是說剛開始的時候program break和堆的起始地址是一樣的。

2.我們對上面的程式進行修改,在這個基礎上繼續分配空間,如下:

#include <stdio.h>   
#include <unistd.h>
#include <apue.h>   
int main(){  
     void* p = sbrk(0);  //獲取當前program break的當前位置(此時是heap的起始地址)
     int* p1 = (int *)p; // 將當前空指標轉化成int型指標
     brk(p1+4);  
     printf("program break is :%p\n",sbrk(0));//這個時候program break的值比較大,因為一次性分配了較大的空間,而不必每次都呼叫sbrk()來分配堆空間 
     p1[0] = 10;
     printf("program break is %p\n",sbrk(0));  
     p1[1] = 20;  
     p1[2] = 30;  
     printf("program break is %p\n",sbrk(0));
     p1[3] = 40;  
     p1[4] = 50;     
     printf("%d,%p\n",*p1,p1);
     printf("%d,%p\n",*(p1+1),p1+1);
     printf("%d,%p\n",*(p1+2),p1+2);
     printf("%d,%p\n",*(p1+3),p1+3); 
     printf("program break is %p\n",sbrk(0));//這個時候program break的值比較大,因為一次性分配了較大的空間,而不必每次都呼叫sbrk()來分配堆空間 
}  

執行結果為:


那麼問題來了,我們在呼叫brk()之後,program break的值為0x95f6010,剛好是從0x95f6000地址開始的16個位元組,但當我們對空間進行賦值之後,program break的值就變成0x9618000,一下子就變大了很多,這是疑問?

3.我們繼續修改程式:

#include <stdio.h>   
#include <unistd.h>
#include <apue.h>   
int main(){  
     void* p = sbrk(0);  //獲取當前program break的當前位置(此時是heap的起始地址)
     int* p1 = (int *)p; // 將當前空指標轉化成int型指標
     int *p2,*temp;
     brk(p1+4);  
     printf("program break is :%p\n",sbrk(0)); 
     p1[0] = 10;  
     printf("program break is :%p\n",sbrk(0)); 
     p1[1] = 20;  
     p1[2] = 30;  
     p1[3] = 40;  
     p1[4] = 50;     
     printf("%p\n",&p1[0]);
     printf("%p\n",&p1[1]);
     printf("%p\n",&p1[2]);
     printf("%p\n",&p1[3]); 
     temp=p1+4;
     brk(temp+2);
     temp[0]=60;
     temp[1]=70;
     printf("%p\n",&temp[0]);
     printf("%p\n",&temp[1]);
     printf("program break is :%p\n",sbrk(0)); 
}  

輸出結果為:

這個時候我們看到program break的位置本來已經在一個足夠大的位置上(0x962f000),但是在追加申請空間之後又變回到0x960d018(這個偏移量剛好為24個位元組,是6個int型變數的大小),為什麼program break從0x962f000回退到了0x960d018,這又是個疑問?

4.下面來看看第二個程式:

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>
int main(){
	void* ptr, *ptr1,*ptr2;
	ptr = sbrk(0);
	printf("sbrk:%p\n", ptr);
	ptr1 = malloc(100);
	ptr = sbrk(0);
	printf("sbrk:%p, ptr1:%p\n", ptr, ptr1);
	free(ptr1);
	ptr2=malloc(50);
	ptr = sbrk(0);
	printf("sbrk:%p,ptr2:%p\n",ptr,ptr2);
}
輸出結果為:

我們可以看到ptr的當前地址為0x991f000,這個地址就是堆空間的起始地址,也是program break的位置,後來我們呼叫malloc()在堆上分配100位元組的空間,這100位元組空間起始地址為0x991f410(至於為什麼和0x991f000相差這麼多還沒搞明白),如果是正真分配100位元組,那麼program break的位置應該是(0x991f410+100位元組=0x991f474),但是我們顯示program break當前位置(0x9940000)的時候卻比0x991f474大很多,說明系統一次性就分配了比100位元組大很多的堆空間,至於為什麼?我想如果每次分配幾字節或幾十位元組的空間都呼叫sbrk()函式的話,系統開銷太大,所以索性一次分配足夠大的空間。當程序需要另外申請空間時,系統可能的工作就是在這個一次性分配足夠大的剩餘空間裡再分配。至於為什麼不從0x991f000直接開始分配,初步設想是0x991f000-0x991f410這段堆空間肯能用作記錄目前堆的分配情況。

針對以上三個疑問,目前還在探索中,希望已經深入理解這一塊的大牛指點!!!!!!

相關推薦

Linux程序記憶體管理之malloc和sbrk

之前自己突發興趣想寫一下malloc函式,順便了解一下程序的記憶體管理。在寫的過程中發現其實malloc只不過是通過呼叫Linux下的sbrk函式來實現記憶體的分配,只是在sbrk之上加了一層對所分配的記憶體的管理罷了,而sbrk以及brk是實現從虛擬記憶體到記憶體的對映的

Linuxglibc記憶體管理

整理的參考文獻,記不清了 1 背景簡介 出現疑似”記憶體洩露”問題: malloc申請的記憶體,free釋放以後沒有歸還作業系統,比如記憶體模組佔用的記憶體為10GB,釋放記憶體以後,通過TOP命令或者/proc/pid/status檢視佔用的記憶體有時仍然為10G,

淺析Linux記憶體管理

  最近在看一本叫《程式設計師的自我修養-連結.裝載域庫》(俞甲子,石凡,潘愛民 著)這本書,不得不像大家安利這本書,從最基本的EFL檔案到連結,再到裝載,雖然現在還沒看完,但是對程式的可執行檔案和虛擬記憶體的佈局有了進一步的瞭解,不得不說是一本好書。言歸正傳,本文討論的話

Linux記憶體管理深入分析(

Linux堆記憶體管理深入分析 (下半部) 作者@走位,阿里聚安全 0 前言回顧 在上一篇文章中,詳細介紹了堆記憶體管理中涉及到的基本概念以及相互關係,同時也著重介紹了堆中chunk分配和釋放策略中使用到的隱式連結串列技術。通過前面的介紹,我們知道使用隱式連結串

Linux記憶體管理深入分析(上)

Linux堆記憶體管理深入分析 (上半部) 作者:走位@阿里聚安全 ​ 0 前言 近年來,漏洞挖掘越來越火,各種漏洞挖掘、利用的分析文章層出不窮。從大方向來看,主要有基於棧溢位的漏洞利用和基於堆溢位的漏洞利用兩種。國內關於棧溢位的資料相對較多,這裡就不累述了,

奪命雷公狗---linux NO:27 linux的密碼管理和用戶切換以及sudo的使用

意思 發現 which window 明顯 能開 操作 賬戶 修改 我們在windows下用戶名是可以設置空密碼登錄的,但是在linux 裏面必須要設置一個密碼才可以登錄,否則會失敗的。。。 在linux 下設置密碼可以使用 passwd 這個命令來進行設置。。。 但是

linux安裝NPM管理工具

body 目的 amp 下載 。。 3.6 src 目錄 8.0 轉自: http://www.cnblogs.com/lovelylife/p/3503980.html 根據”挖一下“開發需要,選擇nodejs實現異步IO,目的是為了解決

Linux進程管理簡概

linux進程Linux下進程管理簡概目錄Linux內核功能簡述進程相關概念進程管理查看工具一.Linux內核功能簡述1.進程管理內核負責創建和銷毀進程, 並處理它們與外部世界的聯系(輸入和輸出). 不同進程間通訊(通過信號, 管道, 或者進程間通訊原語)對整個系統功能來說是基本的, 也由內核處理. 另外,

linux使用git管理代碼

文件夾 clas oba stat remote body -m 提交 IT 環境:centos7.2 ; 工具:xshell 1、安裝git,命令如下: #yum install git 2、設置用戶名和郵箱,命令如下: #git config --glo

linux權限管理

文本文 usermod 之間 數字 size flag file 不可用 ... 一、linux下用戶和組管理 1.1、何為linux下用戶 在談論用戶之前,我們簡單的來說明一下單用戶單任務操作系統以及多用戶多任務操作系統。簡單而言,單用戶單任務就是可以登錄操作系統的用戶一

智慧指標與記憶體管理

  目錄 shared_ptr std::weak_ptr std::unique_ptr 使用時注意事項: ①.new的普通指標與shared_ptr轉換: ②.指向棧的指標與shared_ptr轉換: 3.智慧指標向常規指標的轉換 自從c++

LINUX的日誌管理

系統日誌 • 程序和作業系統核心需要能夠為發生的事件記錄日誌 , 這些日誌 可用於系統稽核和問題的故障排除 , 一般這些日誌永久存 儲 /var/log 目錄中。系統檔案記錄資訊如下表 :     日誌檔案           用途  /var/log/messages    大多數系統

Linux檢視記憶體型號、主機板、硬碟等等

首先檢視是否安裝了dmidecode工具 [[email protected] ~]# dmidecode Linux 檢視記憶體的插槽數,已經使用多少插槽.每條記憶體多大: [[email protected] ~]# dmidecode|grep -

Linux使用者許可權管理和防火牆配置

1、Linux使用者許可權管理 1.1、修改密碼 (1)如果是root超級使用者: passwd 使用者名稱 //修改該使用者密碼 passwd -l 使用者名稱 //鎖定該使用者,-l:lock passwd -u 使用者名稱 //解禁該使用者,-u:unlock (2)如果是

JVM記憶體管理與自定義分配引數詳解

堆記憶體模型:   在Java中,堆被劃分成兩個不同的區域:新生代(Young),老年代(Old)。而Permanent屬於永久代(方法區),不屬於堆記憶體。新生代又被分為了三個區域:Eden,from  survivor,to survivor。這樣劃分的目的

Linux 共享記憶體實現

c程式實現 寫記憶體 讀記憶體 執行結果 總結 c程式實現 寫記憶體 /* * rShareM.c * * Created on: 2011-11-20 * Author: snape */ #include &

Linux的賬號管理(3)-chage/usermod/userdel

chage命令: 可以更好的展示和修改密碼的屬性,直接修改配置檔案也是可以的 -l:檢視一個賬戶和密碼有關的時間引數 -d:YYYY-MM-DD,修改最近一次更改密碼的時間 -E: YYYY-MM-DD,修改賬號的失效時間 -I:天數,設定密碼幾天後失效 -m:天數,設定密碼至少保留

Linux的賬戶管理(1)--passwd和shadow

Linux中的使用者分為兩大類 超級使用者:root(預設) 普通使用者:其餘都是普通使用者 超級使用者登入是“#”的表示,普通使用者登入是“$”的標誌 [[email protected] ~]# [[email protected] ~]$ 方括號中的文

part_03 : linux的網路管理

基礎知識 ipv4: 2進位制32位-----10進位制 172.25.0.10/255.255.255.0 172.25.0.10:ip地址 255.255.255.0:子網掩碼(實現子網的劃分) 子網掩碼:用來劃分網路區域,255位對應的ip位為網路位,網路

Linux檔案許可權管理

檔案許可權 1.檔案屬性的檢視 ls -l filename 2.檔案所有人所有組的管理 chown username file|dir chgrp groupname file|dir chown -R username dir /更改目錄本身及裡