如果物理記憶體是分布式的,由多個cell組成(比如每個核有自己的本地記憶體),那麼cpu在訪問靠近它的本地記憶體的時候就比較快,訪問其他cpu的記憶體或者全域性記憶體的時候就比較慢,這種體系結構被稱為non-uniform memory access(numa)。
以上是硬體層面上的numa,而作為軟體層面的linux,則對numa的概念進行了抽象。即便硬體上是一整塊連續記憶體的uma,linux也可將其劃分為若干的node。同樣,即便硬體上是物理記憶體不連續的numa,linux也可將其視作uma。
在numa系統中,當linux核心收到記憶體分配的請求時,它會優先從發出請求的cpu本地或鄰近的記憶體node中尋找空閒記憶體,這種方式被稱作local allocation,local allocation能讓接下來的記憶體訪問相對底層的物理資源是local的。
每個node由乙個或多個zone組成(我們可能經常在各種對虛擬記憶體和物理記憶體的描述中迷失,但以後你見到zone,就知道指的是物理記憶體),每個zone又由若干page frames組成(一般page frame都是指物理頁面)。
因為硬體的限制,核心不能對所有的page frames採用同樣的處理方法,因此它將屬性相同的page frames歸到乙個zone中。對zone的劃分與硬體相關,對不同的處理器架構是可能不一樣的。
在uma系統上只有乙個pg_data_t,但在numa系統上卻又多個pg_data_t。
typedef
struct pglist_data pg_data_t;
zone雖然是用於管理物理記憶體的,但zone與zone之間並沒有任何的物理分割,它只是linux為了便於管理進行的一種邏輯意義上的劃分。zone在linux中用struct zone表示:
struct zone
核心把物理頁作為記憶體管理的基本單位。大多數32位體系結構支援4kb的頁,64位體系結構支援8kb的頁。核心用struct page結構來表示系統中的每個物理頁。
struct page
出於節省記憶體的考慮,struct page中使用了大量的聯合體union。下面僅對常用的一些的字段做說明:
這裡考慮這種情況,當程序開始執行時,執行到某一條指令,此時他的頁面不在記憶體,程序出現頁錯誤。進行如下操作:
1.設定中斷(作業系統)
trap_init用於初始化硬體異常處理中斷向量(陷阱門),並設定允許中斷請求訊號的到來。
void
trap_init
(void
)#define set_trap_gate(n,addr) \
_set_gate(&idt[n],15,0,addr)
trap_init設定page_fault的中斷向量值為14。set_trap_gate設定陷阱門函式。 引數:n是中斷號,addr為中斷程式偏移位址。&idt[n]是中斷描述符表中中斷號 n 對應項的偏移值;中斷描述符的型別是 15,特權級是 0。
2.mm/page.s 程式包含底層頁異常處理**。實際工作在 memory.c 中完成。
.globl _page_fault # 宣告為全域性變數。將在 traps.c 中用於設定頁異常描述符。
_page_fault:
xchgl %eax,
(%esp) # 取出錯碼到 eax。
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10
,%edx
mov %dx,
%ds mov %dx,
%es mov %dx,
%fs movl %cr2,
%edx # 取引起頁面異常的線性位址。
pushl %edx # 將該線性位址和出錯碼壓入棧中,作為將呼叫函式的引數。
pushl %eax
testl $1
,%eax # 測試頁存在標誌 p(位 0),如果不是缺頁引起的異常則跳轉。
jne 1f
call _do_no_page # 呼叫缺頁處理函式(mm/memory.c)。
jmp 2f
1: call _do_wp_page
2: addl $8
,%esp
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
3.do_no_page執行缺頁處理。訪問不存在頁面的處理函式,頁異常中斷處理過程中呼叫此函式。在 page.s 程式中被呼叫。函式引數 error_code 和 address 是程序在訪問頁面時因缺頁產生異常而由 cpu 自動生成。error_code 指出出錯型別,address 是產生異常的頁面線性位址。該函式首先檢視所缺頁是否在交換裝置中,若是則交換進來。否則嘗試與已載入的相同檔案進行頁面共享,或者只是由於程序動態申請記憶體頁面而只需對映一頁物理記憶體頁即可。若共享操作不成功,那麼只能從相應檔案中讀入所缺的資料頁面到指定線性位址處
void
do_no_page
(unsigned
long error_code,
unsigned
long address)if(
share_page
(tmp)
)//找乙個程序,看是否可以與當前程序共享頁面
return;if
(!(page =
get_free_page()
))//申請一頁物理記憶體。
oom();
/* remember that 1 block is used for header */
block =
1+ tmp/block_size;
for(i=
0; i<
4; block++
,i++
) nr[i]
=bmap
(current->executable,block)
;bread_page
(page,current->executable->i_dev,nr)
;//根據這個頁面去磁碟上讀,把i_dev中的東西讀到剛才申請的page頁上
i = tmp +
4096
- current->end_data;
tmp = page +
4096
;while
(i--
>0)
// 最後把引起缺頁異常的一頁物理頁面對映到指定線性位址 address 處。若操作成功就返回,
// 否則就釋放記憶體頁,顯示記憶體不夠。if(
put_page
(page,address)
)return
;free_page
(page)
;oom()
;}
4.put_page這個函式將乙個頁面放在記憶體中需要的位址。它返回獲得的頁面的實體地址,如果記憶體不足,則返回0。
引數 page 是分配的主記憶體區中某一頁面的指標;address 是線性位址。
unsigned
long
put_page
(unsigned
long page,
unsigned
long address)
page_table[
(address>>12)
&0x3ff
]= page |7;
//把page物理頁放到page_table表項的頁內偏移後12位,建立對映關係
/* no need for invalidate */
return page;
}
Linux的記憶體管理的一些總結
1.kmalloc 在arm平台上,kmalloc是以cache line的位元組數對齊的,比如cortex a8,a15是64bytes 對齊,而cortex a9則是32bytes 對齊。2.如何知道系統中可用的memory the free memory is memfree buffers ...
一些記憶體管理API
釋放頁面塊,釋放的頁面塊從 page 開始,個數等於2的order次方個 void free pages struct page page,unsigned int order 以 gfp mask 分配方式,分配2的order次方個連續的物理頁 unsigned long get free pag...
變數以及一些記憶體的管理
c語言筆記1 為什麼嵌入式需要學習c 因為作業系統的核心大多數都是用c寫的 而且c語言的三大特點 移植性好 就是在不同的平台上進行移植,需要修改的 小 訪問硬體 能夠直接的訪問硬體,雖然彙編也能訪問硬體,但是它是低階語言,但是c 語言設計高階語言的低階語言,一些複雜的操作彙編完成起來比較的複雜。執行...