1. 程式人生 > >Linux驅動虛擬地址和物理地址的映射

Linux驅動虛擬地址和物理地址的映射

沒有 映射 跟著 申請 不能 物理地址 技術 存在 ngs

一般情況下,Linux系統中,進程的4GB內存空間被劃分成為兩個部分------用戶空間和內核空間,大小分別為0~3G,3~4G

用戶進程通常情況下,只能訪問用戶空間的虛擬地址,不能訪問到內核空間。

每個進程的用戶空間都是完全獨立、互不相幹的,用戶進程各自有不同的頁表。而內核空間是由內核負責映射,它並不會跟著進程改變,是固定的。內核空間地址有自己對應的頁表,內核的虛擬空間獨立於其他程序。

3~4G之間的內核空間中,從低地址到高地址依次為:物理內存映射區—隔離帶—vmalloc虛擬內存分配區—隔離帶—高端內存映射區—專用頁面映射區—保留區。

技術分享

【內核空間內存動態申請】

主要包括三個函數:kmalloc(), __get_free_pages, vmalloc

kmalloc(), __get_free_pages申請的內存位於物理地址映射區,而且在物理上也是連續的,它們與真實的物理地址只有一個固定的偏移,因此存在較簡單的轉換關系。而vmalloc申請的內存位於vmalloc虛擬內存分配區(這些區都是以線性地址為度量),它在虛擬內存空間給出一塊連續的內存區,實質上,這片連續的虛擬內存在物理內存中並不一定連續,而vmalloc申請的虛擬內存和物理內存之間也沒有簡單的換算關系。

因為vmalloc申請的在虛擬內存空間連續的內存區在物理內存中並不一定連續,可以想象為了完成vmalloc,新的頁表需要被建立,因此,知識調用vmalloc來分配少量內存是不妥的。

一般來講,kmalloc用來分配小於128K的內存,而更大的內存塊需要用vmalloc來實現。

【虛擬地址與物理地址關系】

對於內核物理內存映射區的虛擬內存(用kmalloc(), __get_free_pages申請的),使用virt_to_phys()和phys_to_virt()來實現物理地址和內核虛擬地址之間的互相轉換。它實際上,僅僅做了3G的地址移位。

上述方法適用於常規內存(內核物理內存映射區),高端內存的虛擬地址與物理地址之間不存在如此簡單的換算關系。因為它涉及到了分離物理頁的頁表控制機制。

【ioremap

在ARM中,設備的寄存器或者存儲塊的這部分空間屬於內存空間的一部分,我們稱之為IO內存。

在內核中訪問IO內存之前,我們只有IO內存的物理地址,這樣是無法通過軟件直接訪問的,需要首先用ioremap()函數將設備所處的物理地址映射到內核虛擬地址空間(3GB~4GB)。然後,才能根據映射所得到的內核虛擬地址範圍,通過訪問指令訪問這些IO內存資源。

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

【mmap

用mmap映射一個設備,意味著使用戶空間的一段地址關聯到設備內存上,這使得只要程序在分配的地址範圍內進行讀取或者寫入,實際上就是對設備的訪問。這種數據傳輸是直接的,不需要用到內核空間作為數據轉移的中間站。

remap_page_range函數的功能是構造用於映射一段物理地址的新頁表,實現了內核空間與用戶空間的映射。

在內核驅動程序的初始化階段,通過ioremap()將物理地址映射到內核虛擬空間;在驅動程序的mmap系統調用中,使用remap_page_range()將該塊ROM映射到用戶虛擬空間。這樣內核空間和用戶空間都能訪問這段被映射後的虛擬地址。

Ioremap

進程空間?內核空間?IO內存

其中,後面兩個指的是同一段物理內存區域,只是一個為虛擬地址,一個為物理地址。進程空間和內核空間對應著不同的物理地址,它們之間的數據傳遞,是實際的數據的拷貝。

Mmap

進程空間?IO內存

其中,進程空間mmap得到的那段虛擬地址跟IO內存對應著同一段物理地址。這個過程沒有額外的數據中轉,讀寫都直接針對硬件的物理地址進行。

一般來講,小數據量的傳輸用ioremap()就足夠了,

【IO內存的一般訪問方法】

1. 首先是調用request_mem_region()申請資源,即告訴內核,本驅動正在使用這段物理內存,其他驅動不得訪問它們。在設備驅動模塊加載或open()函數中進行。

2. 接著講寄存器地址通過ioremap()映射到內核空間虛擬地址,之後就可以通過Linux設備訪問編程接口訪問這些設備的寄存器了。在設備驅動初始化、write(),read(),ioctl()函數中進行。

3. 訪問完成之後,應對ioremap()申請的虛擬地址進行釋放,並釋放release_mem_region()申請的IO內存資源。在設備驅動模塊卸載或release()函數中進行。

Linux驅動虛擬地址和物理地址的映射