linux 內存管理

      這兩天一直爲linux內存中的4G的邏輯空間的3:1的用戶空間:內核空間的分配糾結,在網上看了不少文檔也把那些書都搬出來啦,具體沒有看到哪個地方直接解除我心中的疑惑,但從中間也澄清了很多概念。記錄如下,以便以後複習。

     在華爲筆試的時候有這樣一個題,局部變量,全局變量,靜態變量分別分別在什麼空間中,當時應該是憑感覺寫了,結構是很糟糕。在網上看到有關於這點的,轉一下,http://blog.csdn.net/kanghua/archive/2007/10/22/1837872.aspx

 

    進程與內存
進程如何使用內存?
毫無疑問所有進程(執行的程序)都必須佔用一定數量的內存,它或是用來存放從磁盤載入的程序代碼,或是存放取自用戶輸入的數據等等。不過進程對這些內存的管理方式因內存用途不一而不盡相同,有些內存是事先靜態分配和統一回收的,而有些卻是按需要動態分配和回收的。

對任何一個普通進程來講,它都會涉及到5種不同的數據段。稍有編程知識的朋友都該能想到這幾個數據段種包含有“程序代碼段”、“程序數據段”、“程序堆棧段”等。不錯,這幾種數據段都在其中,但除了以上幾種數據段之外,進程還另外包含兩種數據段。下面我們來簡單歸納一下進程對應的內存空間中所包含的5種不同的數據區。

代碼段:代碼段是用來存放可執行文件的操作指令,也就是說是它是可執行程序在內存種的鏡像。代碼段需要防止在運行時被非法修改,所以只准許讀取操作,而不允許寫入(修改)操作——它是不可寫的

數據段:數據段用來存放可執行文件中已初始化全局變量,換句話說就是存放程序靜態分配的變量全局變量

BSS段:BSS段包含了程序中未初始化全局變量,在內存中 bss段全部置零。

堆(heap):堆是用於存放進程運行中被動態分配的內存段,它大小並不固定,可動態擴張或縮減。當進程調用malloc等函數分配內存時,新分配的內存就被動態添加到堆上(堆被擴張);當利用free等函數釋放內存時,被釋放的內存從堆中被剔除(堆被縮減)

棧是用戶存放程序臨時創建的局部變量,也就是說我們函數括弧“{}”中定義的變量(但不包括static聲明的變量,static意味這在數據段中存放變量)。除此以外在函數被調用時,其參數也會被壓入發起調用的進程棧中,並且待到調用結束後,函數的返回值也回被存放回棧中。由於棧的先進先出特點,所以棧特別方便用來保存/恢復調用現場。從這個意義上將我們可以把堆棧看成一個臨時數據寄存、交換的內存區。

進程如何組織這些區域?
上述幾種內存區域中數據段、BSS和堆通常是被連續存儲的——內存位置上是連續的,而代碼段和棧往往會被獨立存放。有趣的是堆和棧兩個區域關係很“曖昧”,他們一個向下“長”(i386體系結構中棧向下、堆向上),一個向上“長”,相對而生。但你不必擔心他們會碰頭,因爲他們之間間隔很大(到底大到多少,你可以從下面的例子程序計算一下),絕少有機會能碰到一起。

下圖簡要描述了進程內存區域的分佈:(圖沒有cp過來)

 


BSS
數據段 


代碼段


“事實勝於雄辯”,我們用一個小例子(原形取自《User-Level Memory Management》)來展示上面所講的各種內存區的差別與位置。

#include<stdio.h>

#include<malloc.h>

#include<unistd.h>

int bss_var;

int data_var0=1;

int main(int argc,char **argv)

{

  printf("below are addresses of types of process's mem/n");

  printf("Text location:/n");                                                                     //代碼段地方

  printf("/tAddress of main(Code Segment):%p/n",main);

  printf("____________________________/n");

  int stack_var0=2;

  printf("Stack Location:/n");                                                                   //棧段的局部變量的地址

  printf("/tInitial end of stack:%p/n",&stack_var0);

  int stack_var1=3;

  printf("/tnew end of stack:%p/n",&stack_var1);

  printf("____________________________/n");

  printf("Data Location:/n");

  printf("/tAddress of data_var(Data Segment):%p/n",&data_var0);      //數據段變量的地址(初始化的全局變量)

  static int data_var1=4;

  printf("/tNew end of data_var(Data Segment):%p/n",&data_var1);   // 靜態數據變量

  printf("____________________________/n");

  printf("BSS Location:/n");

  printf("/tAddress of bss_var:%p/n",&bss_var);                                  // bbs段的未被初始化的全局變量的地址

  printf("____________________________/n");

  char *b = sbrk((ptrdiff_t)0);                                                                // 在堆段分配的局部存儲區的地址

  printf("Heap Location:/n");

  printf("/tInitial end of heap:%p/n",b);

  brk(b+4);

  b=sbrk((ptrdiff_t)0);

  printf("/tNew end of heap:%p/n",b);

return 0;

 }

它的結果如下

below are addresses of types of process's mem

Text location:

   Address of main(Code Segment):0x8048388

____________________________

Stack Location:

   Initial end of stack:0xbffffab4

   new end of stack:0xbffffab0     (棧是向下長的)

____________________________

Data Location:

   Address of data_var(Data Segment):0x8049758

   New end of data_var(Data Segment):0x804975c

____________________________

BSS Location:

   Address of bss_var:0x8049864

____________________________

Heap Location:

   Initial end of heap:0x8049868

   New end of heap:0x804986c (堆是向上長的)

 

注意:不管什麼樣的數據的地址都小於0xC0000000,因爲用戶區的虛擬空間的地址爲0~3G

利用size命令也可以看到程序的各段大小,比如執行size example會得到

text data bss dec hex filename

1654 280   8 1942 796 example

但這些數據是程序編譯的靜態統計,而上面顯示的是進程運行時動態值,但兩者是對應的。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/kanghua/archive/2007/10/22/1837872.aspx

 

寫到此再來看一個kernel態的程序,看看他的地址是什麼樣的。

#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int __init hello_init(void)
{
 int stack_val0 = 0;
 printk("stack val0's address %p/n",&stack_val0);
 printk("address of the code program %p/n", hello_init);

 printk(KERN_ALERT "hello world!");
 return 0;
}

static void __exit hello_exit(void)
{
 printk(KERN_ALERT "Goodbye!");
}

module_init(hello_init);
module_exit(hello_exit);

 

打印的結果爲:

                          stack val0's address d26cdf58
[ 1064.630088] address of the code program e08ef000
[ 1064.630124] hello world!

可以看到kernel中的地址都是大於0xC0000000.

這也許能證明經常看到的0~3G爲用戶空間,3G~4G爲內核空間的說法啦!(這裏的空間是邏輯地址,而並非物理地址。上面我的程序打印的地址都是邏輯地址)。

分析如下:

首先在分析linux內存管理的時候要搞清楚3個地址的關係,邏輯地址,線性地址,物理地址。不想說的太複雜,簡單地說,

邏輯地址是相對於用人說的,不論是用戶空間的人還是kernel空間的人。

線性地址是對cpu來說的,cpu看到的是線性地址。

物理地址是物理內存來說的,是真正的物理地址。

但具體這三個地址之間如何轉換的,有內存分頁機制和內存分段機制的區別。網上很多分析這點的文檔的。

現在系統中有一個1G的內存,那麼這個1G的內存和上面的4G的空間是什麼的關係,用戶空間和kernel空間在這1G的內存上又是如何分配的呢,這也是一直困擾我的問題。

在具體的1G內存上,kernel放在什麼地方,用戶空間有分配在什麼地方呢,可能需要結合一個具體的系統來分析(arm/x86/mips),沒有具體研究是由體系決定還是有bootloader決定的。這和邏輯空間的分配沒有任何關係(3:1).

3:1的關係是用在邏輯地址空間,例如在用戶空間的程序分配的內存的地址是小於0xC0000000(0~3G之間),假如這個地址爲xxxx,具體這個地址對應於哪個物理地址,是要經過地址轉換後才知道的,但這並不需要我們用戶空間的用戶知道。例如在kernel空間運行的程序分配的內存空間的地址肯定大於0xC0000000(3G~4G之間).

 

 

發佈了21 篇原創文章 · 獲贊 5 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章