蛻變成蝶:Linux裝置驅動之CPU與記憶體和I/O

李輝發表於2016-04-29

由於Linux系統提供了複雜的記憶體管理功能,本節將講解的是記憶體和I/O的訪問程式設計。

在X86中,I/O空間是相對於記憶體空間而言的,通過特定的in、out來訪問,in、out指令格式如下:

下面說說MMU(記憶體管理單元),作業系統藉助MMU可以讓使用者感覺到好像程式可以使用非常大的核心空間,實際上就是我們平時瞭解的虛擬地址一樣的。為了好好了解一下MMU,先看兩個概念

TLB:MMU的核心部件,快取少量的虛擬地址與實體地址的轉換關係,是轉換表的Cache

TTW:當TLB中沒有緩衝對應的地址轉換關係時候,需要通過對記憶體中轉換表的訪問來獲取虛擬地址和實體地址的對應關係,TTW成功後,結果應該寫入TLB。

為了說明MMU在記憶體中使用的關係,下圖可以說明如下關係

對於提供了 MMU(儲存管理器,輔助作業系統進行記憶體管理,提供虛實地址轉換等硬體支援)的處理器而言,Linux 提供了複雜的儲存管理系統,使得程式所能訪問的記憶體達到4GB。程式的 4GB 記憶體空間被人為的分為兩個部分——使用者空間與核心空間。使用者空間地址分佈從0 到3GB(PAGE_OFFSET,在0x86 中它等於0xC0000000),3GB 到4GB 為核心空間,

核心空間中,從3G 到vmalloc_start 這段地址是實體記憶體對映區域(該區域中包含了核心映象、物理頁框表mem_map 等等)kmalloc 和get_free_page 申請的記憶體位於實體記憶體對映區域,而且在物理上也是連續的,它們與真實的實體地址只有一個固定的偏移,因此存在較簡單的轉換關係,virt_to_phys()可以實現核心虛擬地址轉化為實體地址:

上面轉換過程是將虛擬地址減去3G(PAGE_OFFSET=0XC000000)。與之對應的函式為phys_to_virt(),將核心實體地址轉化為虛擬地址:

virt_to_phys()和phys_to_virt()都定義在includeasm-i386io.h中。而vmalloc申請的記憶體則位於vmalloc_start~vmalloc_end之間,與實體地址沒有簡單的轉換關係,雖然在邏輯上它們也是連續的,但是在物理上它們不要求連續。我們用下面的程式來演示kmalloc、get_free_page和vmalloc的區別:

裝置通常會提供一組暫存器來用於控制裝置、讀寫裝置、獲取裝置狀態,那麼Linux裝置驅動究竟怎樣訪問裝置的I/O埠(暫存器)和I/O記憶體的訪問的呢

1.操作I/O口

(1)申請I/O 埠:

在驅動還沒獨佔裝置之前,不應對埠進行操作。核心提供了一個註冊介面,以允許驅動宣告其需要的埠:

(2)訪問IO埠:

在驅動成功請求到I/O 埠後,就可以讀寫這些埠了。大部分硬體會將8位、16位和32位埠區分開,無法像訪問記憶體那樣混淆使用。驅動程式必須呼叫不同的函式來訪問不同大小的埠。

Linux 核心標頭檔案(體系依賴的標頭檔案<asm/io.h>) 定義了下列行內函數來存取I/O埠:

(3)釋放IO埠:

2 操作IO記憶體

(1)申請I/O 記憶體:
I/O 記憶體區在使用前必須先分配。分配記憶體區的函式介面在<linux/ioport.h>定義中:

(2)對映:
在訪問I/O記憶體之前,分配I/O記憶體並不是唯一要求的步驟,你還必須保證核心可存取該I/O記憶體。訪問I/O記憶體並不只是簡單解引用指標,在許多體系中,I/O 記憶體無法以這種方式直接存取。因此,還必須通過ioremap 函式設定一個對映。

(3)訪問IO記憶體:
經過 ioremap之後,裝置驅動就可以存取任何I/O記憶體地址。注意,ioremap返回的地址不可以直接解引用;相反,應當使用核心提供的訪問函式。訪問I/O記憶體的正確方式是通過一系列專門用於實現此目的的函式:

(4)釋放IO記憶體步驟:

幾乎每一種外設都是通過讀寫裝置上的暫存器來進行的,通常包括控制暫存器、狀態暫存器和資料暫存器三大類,外設的暫存器通常被連續地編址。根據CPU體系結構的不同,CPU對IO埠的編址方式有兩種:

(1)I/O對映方式(I/O-mapped)

典型地,如X86處理器為外設專門實現了一個單獨的地址空間,稱為”I/O地址空間”或者”I/O埠空間”,CPU通過專門的I/O指令(如X86的IN和OUT指令)來訪問這一空間中的地址單元。

(2)記憶體對映方式(Memory-mapped)

RISC指令系統的CPU(如ARM、PowerPC等)通常只實現一個實體地址空間,外設I/O埠成為記憶體的一部分。此時,CPU可以象訪問一個記憶體單元那樣訪問外設I/O埠,而不需要設立專門的外設I/O指令。 但是,這兩者在硬體實現上的差異對於軟體來說是完全透明的,驅動程式開發人員可以將記憶體對映方式的I/O埠和外設記憶體統一看作是”I/O記憶體”資源。

一般來說,在系統執行時,外設的I/O記憶體資源的實體地址是已知的,由硬體的設計決定。但是CPU通常並沒有為這些已知的外設I/O記憶體資源的實體地址預定義虛擬地址範圍,驅動程式並不能直接通過實體地址訪問I/O記憶體資源,而必須將它們對映到核心虛地址空間內(通過頁表),然後才能根據對映所得到的核心虛地址範圍,通過訪內指令訪問這些I/O記憶體資源。Linux在io.h標頭檔案中宣告瞭函式ioremap(),用來將I/O記憶體資源的實體地址對映到核心虛地址空間(3GB-4GB)中,原型如下:

iounmap函式用於取消ioremap()所做的對映,原型如下:

在將I/O記憶體資源的實體地址對映成核心虛地址後,理論上講我們就可以象讀寫RAM那樣直接讀寫I/O記憶體資源了。為了保證驅動程式的跨平臺的可移植性,我們應該使用Linux中特定的函式來訪問I/O記憶體資源,而不應該通過指向核心虛地址的指標來訪問。

最後,我們要特別強調驅動程式中mmap函式的實現方法。用mmap對映一個裝置,意味著使使用者空間的一段地址關聯到裝置記憶體上,這使得只要程式在分配的地址範圍內進行讀取或者寫入,實際上就是對裝置的訪問。≠

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

蛻變成蝶:Linux裝置驅動之CPU與記憶體和I/O 蛻變成蝶:Linux裝置驅動之CPU與記憶體和I/O

相關文章