文章目錄
- 1. 用戶虛擬地址轉換成物理地址
- 2. 內核虛擬地址轉換成物理地址
- 3. 物理地址轉換成內核虛擬地址
- 4 內核虛擬地址和對應頁
- 5 根據進程號獲取進程描述符
1. 用戶虛擬地址轉換成物理地址
static void get_pgtable_macro(void)
{printk("PAGE_OFFSET = 0x%lx\n", PAGE_OFFSET);printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);printk("PUD_SHIFT = %d\n", PUD_SHIFT);printk("PMD_SHIFT = %d\n", PMD_SHIFT);printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);printk("PAGE_MASK = 0x%lx\n", PAGE_MASK);
}
static unsigned long vaddr2paddr(unsigned long vaddr)
{pgd_t *pgd;pud_t *pud;pmd_t *pmd;pte_t *pte;unsigned long paddr = 0;unsigned long page_addr = 0;unsigned long page_offset = 0;pgd = pgd_offset(current->mm, vaddr);printk("pgd_val = 0x%lx\n", pgd_val(*pgd));printk("pgd_index = %lu\n", pgd_index(vaddr));if (pgd_none(*pgd)) {printk("not mapped in pgd\n");return -1;}pud = pud_offset(pgd, vaddr);printk("pud_val = 0x%lx\n", pud_val(*pud));if (pud_none(*pud)) {printk("not mapped in pud\n");return -1;}pmd = pmd_offset(pud, vaddr);printk("pmd_val = 0x%lx\n", pmd_val(*pmd));printk("pmd_index = %lu\n", pmd_index(vaddr));if (pmd_none(*pmd)) {printk("not mapped in pmd\n");return -1;}pte = pte_offset_kernel(pmd, vaddr);printk("pte_val = 0x%lx\n", pte_val(*pte));printk("pte_index = %lu\n", pte_index(vaddr));if (pte_none(*pte)) {printk("not mapped in pte\n");return -1;}//頁框物理地址機制 | 偏移量page_addr = pte_val(*pte) & PAGE_MASK;page_offset = vaddr & ~PAGE_MASK;paddr = page_addr | page_offset;printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);return paddr;
}
static int __init v2p_init(void)
{unsigned long vaddr = 0;printk("vaddr to paddr module is running..\n");get_pgtable_macro();printk("\n");vaddr = (unsigned long)vmalloc(1000 * sizeof(char));if (vaddr == 0) {printk("vmalloc failed..\n");return 0;}printk("vmalloc_vaddr=0x%lx\n", vaddr);vaddr2paddr(vaddr);printk("\n\n");vaddr = __get_free_page(GFP_KERNEL);if (vaddr == 0) {printk("__get_free_page failed..\n");return 0;}printk("get_page_vaddr=0x%lx\n", vaddr);vaddr2paddr(vaddr);return 0;
}
static void __exit v2p_exit(void)
{printk("vaddr to paddr module is leaving..\n");vfree((void *)vaddr);free_page(vaddr);
}
整個程序的結構如下:
get_pgtable_macro()打印當前系統分頁機制中的一些宏。
通過vmalloc()在內核空間中分配內存,調用vaddr2paddr()將虛擬地址轉化成物理地址。
通過__get_free_pages()在內核空間中分配頁框,調用vaddr2paddr()將虛擬地址轉化成物理地址。
分別通過vfree()和free_page()釋放申請的內存空間。
vaddr2paddr()的執行過程如下:
-
通過pgd_offset計算頁全局目錄項的線性地址pgd,傳入的參數為內存描述符mm和線性地址vaddr。接著打印pgd所指的頁全局目錄項。
-
通過pud_offset計算頁上級目錄項的線性地址pud,傳入的參數為頁全局目錄項的線性地址pgd和線性地址vaddr。接著打印pud所指的頁上級目錄項。
-
通過pmd_offset計算頁中間目錄項的線性地址pmd,傳入的參數為頁上級目錄項的線性地址pud和線性地址vaddr。接著打印pmd所指的頁中間目錄項。
-
通過pte_offset_kernel計算頁表項的線性地址pte,傳入的參數為頁中間目錄項的線性地址pmd和線性地址vaddr。接著打印pte所指的頁表項。
-
pte_val(*pte)先取出頁表項,與PAGE_MASK相與的結果是得到要訪問頁的物理地址;vaddr&~PAGE_MASK用來得到線性地址offset字段;兩者或運算得到最終的物理地址。
2. 內核虛擬地址轉換成物理地址
__pa():將物理內核虛擬地址轉換成物理虛擬地址
#include <asm/page.h>
#define __pa(x) __phys_addr((unsigned long)(x))
3. 物理地址轉換成內核虛擬地址
__va()將物理地址轉換成內核虛擬地址:
#include <asm/page.h>
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
4 內核虛擬地址和對應頁
struct page *virt_to_page(void *kaddr);
//將內核邏輯地址轉換為響應的page結構指針。
struct page *pfn_to_page(int pfn);
//針對給定的頁幀號,返回page結構指針。
void *page_address(struct page *page);
//如果地址存在的話,則返回頁的內核虛擬地址
5 根據進程號獲取進程描述符
struct task_struct * task = pid_task(find_vpid(target_pid), PIDTYPE_PID);
find_vpid:此函數根據提供的局部進程號獲取對應的進程描述符
struct pid *find_vpid(int nr)
pid_task:此函數獲取任務的任務描述符信息,此任務在進程pid的使用鏈表中,并且搜索的鏈表的起始元素的下標為參數type的值。
struct task_struct *pid_task(struct pid *pid, enum pid_type)enum pid_type
{PIDTYPE_PID, //進程的進程號PIDTYPE_PGID, //進程組領頭進程的進程號PIDTYPE_SID, //會話領頭進程的進程號PIDTYPE_MAX
};