這兩天一直爲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之間).