我知道copy_to_user
/copy_from_user
,get_user
/put_user
功能是爲了這個目的。如何從Linux內核訪問用戶空間內存?
我的問題是,給定一個用戶空間地址/指針,我怎樣才能訪問內核地址指向的數據?
我可以想象,首先我必須確保包含頁面應該在物理內存中(而不是在磁盤中)。
下一步是什麼?我能否使用*p
,其中p
是指向某些用戶空間數據的指針,直接引用數據?
或者我必須首先調用kmap
來將包含物理頁面框架映射到內核虛擬地址空間嗎?爲什麼?
我知道copy_to_user
/copy_from_user
,get_user
/put_user
功能是爲了這個目的。如何從Linux內核訪問用戶空間內存?
我的問題是,給定一個用戶空間地址/指針,我怎樣才能訪問內核地址指向的數據?
我可以想象,首先我必須確保包含頁面應該在物理內存中(而不是在磁盤中)。
下一步是什麼?我能否使用*p
,其中p
是指向某些用戶空間數據的指針,直接引用數據?
或者我必須首先調用kmap
來將包含物理頁面框架映射到內核虛擬地址空間嗎?爲什麼?
指針本身還不夠!您需要知道指針「屬於」哪個進程。
當進程被搶佔時,指針指向另一個進程的地址空間。地址可能不再映射,yadda yadda,
如果該進程在您訪問數據時是當前進程,那麼您應該使用copy_to_user/copy_from_user函數。
如果可以調度進程,可以嘗試mlock()RAM中的頁面,找出頁面的物理RAM地址。每當你想訪問它時,你將該物理頁面映射到內核虛擬地址。
注:
不同的用戶空間應用程序有不同的頁面表。 1)你需要獲得用戶空間程序pid。 2)在pid的頁表中搜索addree。
下面是將用戶空間虛擬地址轉換爲物理地址的示例代碼。 它適用於x86平臺。
taskpid = find_get_pid(curpid);
task = pid_task(taskpid, PIDTYPE_PID);
mm = get_task_mm(task);
down_read(&mm->mmap_sem);
start_vaddr = vaddr;
end_vaddr = 0xC0000000;
while(start_vaddr < end_vaddr){
u32 end;
end = ((start_vaddr + PMD_SIZE) & PMD_MASK);
if(end < start_vaddr || end > end_vaddr)
end = end_vaddr;
ret = walk_pgd(start_vaddr, end, mm);
if(ret != 0){
printk("ret: %08x \n", ret);
break;
}
start_vaddr = end;
}
up_read(&mm->mmap_sem);
paddr = ret;
kaddr = __va(paddr);
mmput(mm);
你需要follow
的地址就能獲取相應的page
結構(見follow_page的例子)。接下來,獲取page
結構,您需要通過kmap
或kmap_atomic
將其映射到內核地址空間。
您可能會覺得這很有用。
讓我們重複的buff參數的讀取和寫入方法是 用戶空間指針。因此,它不能直接由 內核代碼解除引用。有此限制的幾個原因:
根據其架構您的驅動器上運行,並 內核是如何配置的,而在內核模式下運行 用戶空間指針可能無效所有。該地址可能沒有映射,也可能指向其他一些隨機數據。
即使指針確實意味着在內核空間同樣的事情, 用戶空間內存分頁,系統調用時有問題的內存可能不 駐留在RAM中。嘗試引用 用戶空間內存直接可能會產生頁面錯誤,這是內核代碼不允許執行的內容錯誤,即 。結果將是 「oops」,這將導致系統調用 的進程死亡。
有問題的指針由用戶程序提供,其中 可能有問題或惡意。如果你的驅動永遠盲目取消引用 用戶提供的指針,它提供了一個開放的門道允許 用戶空間程序來訪問或 系統的任何地方覆蓋內存。如果您不希望對用戶系統的安全性產生影響,則不能直接取消引用 用戶空間指針。
來源:http://www.makelinux.net/ldd3/chp-3-sect-7
這麼說,我是我自己很想知道,如果用戶空間地址確實是有效的,並沒有上述報考條件會發生什麼......
好點,代碼邏輯很好。但我想有一些哈希表或類似的數據結構,給定一個虛擬地址,可以幫助您快速找到物理頁面。有一個缺陷:kaddr = __va(paddr);這條線只有在paddr駐留在低內存時纔有效,對吧? – Infinite
paddr表示物理地址,所以始終存在於內存中。 kaddr表示內核地址。在Linux內核中,define是'#define __va(x)((void *)((unsigned long)(x)+ PAGE_OFFSET))'。內核地址內存映射並不複雜,只是一個PAGE_OFFSET。 (在x86模式下應該是0xC0000000)。 還有其他方法可以獲取地址。用戶空間應用程序可以通過/ proc//pagemap訪問內核地址來獲取頁面信息。如果能得到PFN,它也可以得到內核地址。 –
richliu