當一個任務(進程)執行系統調用而陷入內核代碼中執行時,我們就稱進程處于內核運行態(內核態)。在內核態下,CPU可執行任何指令。當進程在執行用戶自己的代碼時,則稱其處于用戶運行態(用戶態)。用戶態不能訪問內核空間,包括代碼和數據。
進程處于用戶態時能訪問的是用戶空間,處于內核態時能訪問的稱為內核空間。
CPU執行程序所訪問的地址都是虛擬地址,MMU 必須通過讀取控制寄存器CR3中的值作為當前頁面目錄的指針,進而根據分頁內存映射機制(參看相關文檔)將該虛擬地址轉換為真正的物理地址才能讓CPU真正的訪問到物理地址。
進程有4G的尋址空間,其中第一部分為“用戶空間”,用來映射其整個進程空間(0x0000 0000-0xBFFF FFFF)即3G字節的虛擬地址;第二部分為“系統空間”,用來映射(0xC000 0000-0xFFFF FFFF)1G字節的虛擬地址。可以看出Linux系統中每個進程的頁面目錄的第二部分是相同的,所以從進程的角度來看,每個進程有4G字節的虛擬空間,較低的3G字節是自己的用戶空間,最高的1G字節則為與所有進程以及內核共享的系統空間。
if(數據在物理內存中)
????????{ ?????? 虛擬地址轉換成物理地址
?????????????????讀數據 }
????????else
????????{ ??? if(數據在磁盤中)
??????????????{
?????????????????????if(物理內存還有空閑)
?????????????????????{??????????把數據從磁盤中讀到物理內存
?????????????????????????????????虛擬地址轉換成物理地址
?????????????????????????????????讀數據
?????????????????????}
?????????????????????else
?????????????????????{ ???????? 把物理內存中某頁的數據存入磁盤
?????????????????????????????????把要讀的數據從磁盤讀到該頁的物理內存中
?????????????????????????????????虛擬地址轉換成物理地址
?????????????????????????????????讀數據
?????????????????????}
??????????????}
??????????????else
??????????????{ ??? 報錯 ?????? } }
?
請問如何在驅動程序里邊通過用戶空間的虛擬地址得到它對應的物理地址呢?
一般的驅動程序里邊都要實現read函數 read函數里邊有一個參數char* buf是用戶空間傳遞過來的指針,是用戶空間的虛擬地址,請問我怎么通過這個虛擬地址得到它對應的物理地址呢? (因為我想實現通過dma把內核空間的數據傳送到用戶空間去,所以一定要知道buf對應的物理地址) 請問有可能嗎? 謝謝 |
?
?
我覺得有三種解決方案.第一種性能差些,但是簡單,即先從內核到內核dma,然后再調用現成的copy_to_usr等函數.第二種就是搜索該用戶進程的頁表,(找到該進程的task結構,task->pgd...),從頁表中搜索虛擬地址對應的物理地址.第三種就是在內核中開辟一塊內存,用于存放dma后的數據,然后將它映射到用戶空間去.這種方法我以前采用過,依稀的一些印象是.mknod一個設備文件,然后對該設備文件進行一些ioctl請求(ioctl的各個操作函數當然是自己寫),必需實現的一個函數是該文件的***_mmap.(這樣mmap系統調用->do_mmap()->do_mmap_pgoff()然后再找到你的***_mmap()),在這個map函數中,一個關鍵可用的內核函數是remap_page_range().該函數則可用來生成一個用戶空間的vma段,然后將其和給定的物理內存(設備文件片斷)聯系起來,將返回的虛擬地址傳回用戶空間(ioctl)后,就可以在用戶空間對那塊內存操作了