1. 程式人生 > >Linux核心開發之記憶體與I/O訪問(一)

Linux核心開發之記憶體與I/O訪問(一)

“小王,今天咱們開始講有關記憶體和I/O訪問的內容,心裡先要有點低,這部分內容還是有點煩,有點難的哦”說著話,我心裡都沒底,怕嚇著小王,不瞞你說,當時看這部分,我可是沒少費勁。

“哦,那咋辦,不能不學是不?沒事,有小濤哥在,俺就不怕”小王信心十足的說。

“哦!看不出來,還讓你來安慰我了”看著小王這充滿信心的樣子,我也沒啥顧慮了。好了,深吸一口氣,開始今天的課程。

   我們知道,在X86中,有I/O空間的概念,I/O空間是相對於記憶體空間的概念,它通過特定的指令in,out來訪問。埠號標識了外設的暫存器地址。而巧的是Arm等嵌入式控制器中並不提供I/O空間,所以我們就不考慮了,我們重點放在記憶體空間。

記憶體空間可以通過地址,指標來訪問,在C語言中的表現就是通過指標來操作,如在186處理器中:

   unsigned char *p = (unsigned char *)0xF000FF00;

   *P=11;

   這段程式碼的意思就是:在絕對地址0xF000+0xFF00(186處理器使用16位段地址和16位偏移地址)寫入11。在ARM, PPC等未採用段地址的處理器中,P指向的記憶體

空間就是0xF000FF00,而*p=11就是在該地址寫入11。

   說完記憶體空間,就不得不說說MMU(記憶體管理單元),它輔助作業系統進行記憶體管理,提供虛擬地址和實體地址的對映,記憶體訪問許可權保護和Cache快取控制等硬體支援,作業系統核心藉助MMU,可以讓使用者感覺好像程式可以使用非常大的記憶體空間,從而使得程式設計人員在寫程式時不用考慮計算機中實體記憶體的實際容量。

   為了理解基本的MMU操作原理,先介紹幾個概念:

   1)TLB:Translation Lookaside Buffer,即轉換旁路快取。也稱快表,是轉換表的cache,快取少量的虛擬地址與實體地址的對應關係。

   2)TTW:Translation Table walk,即轉換表漫遊。當TLB中沒有緩衝對應的地址轉換關係時,需要通過對記憶體中轉換表的訪問來獲得虛擬地址和實體地址的對應關係。TTW成功後,結果應寫入TLB.

   為了說明MMU在訪問記憶體中的使用,我特意畫了一個流程圖。如下:

   123

   說了MMU,現在就來和linux聯絡一下,對於包含MMU的處理而言,Linux系統提供了複雜的儲存管理系統,使得程序所能訪問的記憶體達到4GB,這4GB是分為2個部

分---使用者空間和核心空間,前者一般分佈為0~3GB(即PAGE_OFFSET,在0x86中等於0xC0000000),剩下的3~4GB是核心空間。通常情況下,使用者程序只有通過系統呼叫等方式才可以訪問到核心空間。

   小王你學過C語言,知道在使用者空間動態申請記憶體用malloc()函式,釋放用free,這在各種作業系統上的使用是一致的。這方面的內容,比如記憶體洩漏啦,具體使用等就不細講了,我們的重點放在核心空間,核心空間怎麼做呢?

   前邊有個知識點沒說,後面要用到,就提一下:linux核心空間3~4GB是還可以在分的,從低到高依次是:實體記憶體對映區->隔離帶->vmalloc虛擬記憶體分配器->隔

離帶->高階記憶體對映區->專用頁面影視區->保留區

   在Linux核心空間中申請記憶體涉及的函式主要包括kmalloc(),__get_free_pages()【這兩個申請的記憶體位於實體記憶體對映區在實體記憶體上也是連續的,和實體記憶體有簡單的轉換關係】和vmalloc【它是在虛擬記憶體空間給出一塊連續的記憶體區,在實體記憶體中不一定是聯絡的,和實體記憶體也沒有簡單的換算關係】等。有關這三個函式的使用網上一大堆,我就我細說了,小王,你的我都給你列印好了,你直接看就行了。

  我們說虛擬地址和實體地址是有一定的轉換關係,具體是使用virt_to_phys()可以實現虛擬地址轉換為實體地址,程式碼清單如下:

  #define _ _pa(x)  ((unsignedlong)(x)-PAGE_OFFSET)
  extern inline unsignedlong virt_to_phys(volatilevoid * address)
  {
    return _ _pa(address);
  }

   與之對應的函式是phys_to_virt(),用於將實體地址轉化為虛擬地址。具體程式碼如下:

   #define  _ _pa(x)      ((unsignedlong)(x)+PAGE_OFFSET)

extern inline unsignedlong virt_to_phys(volatilevoid * address)

   {

return _ _pa (address);

   } 

   值得說明的是上述方法僅適用與常規記憶體,高階記憶體的虛擬地址與實體地址之間不存在這樣簡單的換算關係。

“小王不知道,你聽的怎麼樣,我說的時候心裡都沒底啊,這一章確實很難,挺那個的..”

“小濤哥,沒事,你放心講吧,經過那麼多,我覺得自己應該有一定能力了,理解起來應該能跟上,如果跟不上再找你哈”小王又給了我繼續講下去的信心。