我們希望能通過訪問使用者空間的記憶體達到讀取核心資料的目的,這樣便可進行核心空間到使用者空間的大規模資訊傳輸。
具體的講,我們要利用記憶體對映功能,將系統核心中的一部分虛擬記憶體對映到使用者空間,從而使得使用者空間位址等同與被對映的核心記憶體位址。
核心空間記憶體分配介紹
因此我們將試圖寫乙個虛擬字元裝置驅動程式,通過它將系統核心空間對映到使用者空間——將核心虛擬記憶體對映到使用者虛擬位址。當然對映位址時少不了定位核心空間對應的實體地址,並且還要建立新的使用者頁表項,以便使用者程序定址時能找到對應的物理記憶體。
從中應該看出,需要我完成既定目標,我們需要獲得:被對映核心空間實體地址
和建立對應的使用者程序頁表。
在核心空間中主要存在
kmalloc
分配的物理連續空間和
vmalloc
分配的非物理連續空間。
kmalloc
分配的空間往往被稱為核心邏輯位址
page_offset
(0xc0000000
)(核心有對應例程
virt_to_phys
)即等於實體地址,而且其對應的頁表屬於核心頁表(
而vmalloc
分配的空間被稱為核心虛擬位址,它的問題相對要複雜些,這是因為其分配的核心虛擬記憶體空間並非直接操作頁框,而是分配的是
vm_struct
結構。該結構邏輯上連續但對應的物理記憶體並非連續,也就是說它
vamlloc
分配的核心空間位址所對應的實體地址並非可通過簡單線性運算獲得。從這個意義上講,它的實體地址在分配前是不確定的,因此雖然
vmalloc
分配的空間與
kmalloc
一樣都是由核心頁表來對映的,但
vmalloc分配核心虛擬位址時必須更新核心頁表
注釋:vmalloc
分配的核心虛擬記憶體與
kmalloc/get_free_page
分配的核心邏輯記憶體位於不同的區間,不會重疊。因為核心空間被分割槽管理,各司其職。
程序空間位址分布從0到3g
(其實是到
page_offset,
在0x86
中它等於
0xc0000000)
,從3g
到vmalloc_start
這段位址是物理記憶體對映區域(該區域中包含了核心映象、物理頁框表
mem_map
等等)比如我使用的系統記憶體是
64m(
可以用free看到)
,那麼(
3g——3g
+
64m)
這片記憶體就應該對映物理記憶體,而
vmalloc_start
位置應在3g+
64m附近(說附近因為是在物理記憶體對映區與
vmalloc_start
期間還回存在乙個
8m 大小的
gap來防止躍界)
,vmalloc_end
的位置接近4g(
說接近是因為最後位置系統會保留一片
128k
大小的區域用於專用頁面對映,還由可能會由高階記憶體對映區,這些都是細節,這裡我們不做糾纏)。
另乙個需要澄清的是,
vmalloc
分配的核心空間,其結構是
vm_area
,可千萬別與使用者空間
malloc
分配的vm_area_struct
結構混淆。前者由核心頁表對映,而後者則由使用者頁表對映。
程序位址空間
物理記憶體對映區kmalloc分配
vmalloc 分配區
03g
(page_offset
)
核心虛擬空間
vmalloc_start
vmalloc_end
上圖是記憶體分布的模糊輪廓
為了近可能豐富我們的例子程式的場景,我們選擇對映vmalloc分配的核心虛擬空間(下面我們簡稱為vk位址)到使用者空間。
要知道使用者程序操作的是虛擬記憶體區域vm_area_struct
,我們此刻需要將使用者
vma區間利用使用者頁表對映到
vk對應的物理記憶體上去(如下圖所示)。這裡主要工作便是建立使用者也表項完成對映工作,而這個工作完全落在了
vma->nopage
[3]操作上,該方法會幫助我們在發生「缺頁」時,動態構造對映所需物理記憶體的頁表項。
使用者虛擬空間vm_area_struct
vk空間vm_struct
物理記憶體
vma->nopage
我們需要實現nopage方法,動態建立對應頁表,而在該方法中核心任務是尋找到vk位址對應的核心邏輯位址
[4]。這必然需要我們做以下工作:
a)找到vmalloc虛擬記憶體對應的核心頁表,並尋找到對應的核心頁表項。
b)獲取核心頁表項對應的物理頁框指標。
c)通過頁框得到對應的核心邏輯位址。
[3]構建使用者也表項,除了使用nopage一次一頁的動態構造,還又一種方法remap_page_range可以一次構造一段記憶體範圍的也表項,但顯然這個方法時針對物理記憶體連續被分配時使用的,而我們vk對應的物理記憶體並非連續,所以這裡使用nopage。
[4]很多人一定會問,為什麼不直接找到實體地址那,而要找核心邏輯位址呢?沒錯,我們本意應該是獲得實體地址,但是為了利用核心提供的一些現成的例程,如virt_to_page
等(它們都是針對核心邏輯位址而言的),我們不妨轉化成核心邏輯位址來做,別忘了核心邏輯位址與理位址僅僅相差乙個偏移量。
基本函式
我們例項將利用乙個虛擬字元驅動程式,驅動負責將一定長的核心虛擬位址(vmalloc分配的)對映到裝置檔案上,以便可以通過訪問檔案內容來達到訪問記憶體的目的。這樣做的最大好處是提高了記憶體訪問速度,並且可以利用檔案系統的介面程式設計(裝置在linux中作為特殊檔案處理)訪問記憶體,降低了開發難度。
map_driver.c
就是我們的虛擬字元驅動程式,不用說它要實現檔案操作表(
file_operations
——字元驅動程式主要做的工作便是實現該結構)中的
,為了要完成記憶體對映
,除了常規的
open/release
操作外,
必須自己實現
mmap操作,
該函式將給定的檔案對映到指定的位址空間上,也就是說它將負責把
vmalloc
分配的核心位址對映到我們的裝置檔案上。
我們下面就談談
mmap
操作的實現細節:
檔案操作的
mmap
操作是在使用者進行系統呼叫
mmap
[5]時被執行的,而且在呼叫前核心已經給使用者找到並分配了合適的虛擬記憶體區域
vm_area_struct
,這個區域將代表檔案內容,所以剩下要做的便是如何把虛擬區域和物理記憶體掛接到一起了,即構造頁表。由於我門前面所說的原因,我們系統中頁表需要動態分配,因此不可使用
remap_page_range
函式一次分配完成,而必須使用
虛擬記憶體區域
自帶的nopage
方法,在現場構造頁表。這樣以來,檔案操作的
mmap
的方法只要完成「為它得到的虛擬記憶體區域繫結對應的操作表
vm_operations」
即可。於是主要的構造工作就落在了
vm_operations
中的nopage
方法上了。
nopage
方法中核心內容上面已經提到了是「尋找到vk位址對應的核心邏輯位址」,這個解析核心頁表的工作是需要自己編寫輔助函式vaddr_to_kaddr來完成的,它所作的工作概括來講就是上文提到的a/b/c三條。
有關整個任務執行路徑請看下圖。
[5]系統呼叫mmap原形是void *mmap2(void *start, size_t length, int prot, int flags, int fd, off_t pgoff)。
編譯map_driver.c為map_driver.o模組,具體引數見makefile
載入模組:in**od map_driver.o
生成對應的裝置檔案
1 在/proc/devices下找到map_driver對應的裝置命和裝置號:grep mapdrv /proc/devices
2 建立裝置檔案mknodmapfile c 254 0
(在我系統裡裝置號為254)
利用maptest讀取mapfile檔案,將取自核心的資訊(」ok」——我們在核心中在vmalloc分配的空間中填放的資訊)列印到使用者螢幕
記憶體管理 3
記憶體對映檔案例項 dll 動態庫的好處在前面說過,可以動態呼叫,節約記憶體空間,以及動態的改變dll而不需要重新編譯我們呼叫dll 的客戶端程式。動態的庫的呼叫有2中方式,一種是顯式的一種隱式的,這裡我只考慮顯式的,隱式的呼叫跟靜態庫的呼叫類似,需要提供宣告檔案和庫檔案 lib 所以我們在隱式呼叫...
3記憶體管理
3.2.2 交換技術 3.2.3 空閒記憶體管理 3.3 虛擬記憶體 帕金森定律 不管儲存器多大,程式都可填滿它 這章討論os怎樣對儲存器建立抽象模型 os的工作是將這個儲存體系抽象為乙個有用的模型並管理這個抽象模型。至於永久性儲存器 磁碟 的抽象和管理,是下章主題 從最簡單的管理方案討論,逐步深入...
Linux記憶體管理
本文首先介紹一下linux記憶體管理方式,著重說明一下使用者空間的記憶體管理,包括linux虛擬對映以及glibc中malloc的實現 然後簡要介紹單程序多執行緒的記憶體管理方式,主要涉及各執行緒堆疊空間的分配 linux 採用兩級保護機制,隔離核心空間和使用者程式空間,使使用者程式無法直接訪問核心...