/*高端内存映射,运用数组进行操作分配情况 分配好后需要加入哈希表中;*/ void *kmap(struct page *page) { might_sleep(); if (!PageHighMem(page))/*如果页框不属于高端内存*/ return page_address(page); return kmap_high(page);/*页框确实属于高端内存*/ } /** * kmap_high - map a highmem page into memory * @page: &struct page to map * * Returns the page's virtual memory address. * * We cannot call this from interrupts, as it may block. */ void *kmap_high(struct page *page) { unsigned long vaddr; /* * For highmem pages, we can't trust "virtual" until * after we have the lock. */ lock_kmap();/*保护页表免受多处理器系统上的 并发访问*/ /*检查是否已经被映射*/ vaddr = (unsigned long)page_address(page); if (!vaddr)/*如果没有*/ /*把页框的物理地址插入到pkmap_page_table的 一个项中并在page_address_htable散列表中加入一个 元素*/ vaddr = map_new_virtual(page); pkmap_count[PKMAP_NR(vaddr)]++;/*分配计数加一,此时流程都正确应该是2了*/ BUG_ON(pkmap_count[PKMAP_NR(vaddr)] < 2); unlock_kmap(); return (void*) vaddr;/*返回地址*/ } static inline unsigned long map_new_virtual(struct page *page) { unsigned long vaddr; int count; start: count = LAST_PKMAP; /* Find an empty entry */ for (;;) { /*加1,防止越界*/ last_pkmap_nr = (last_pkmap_nr + 1) & LAST_PKMAP_MASK; /* 接下来判断什么时候last_pkmap_nr等于0,等于0就表示1023(LAST_PKMAP(1024)-1)个页表项已经被分配了 ,这时候就需要调用flush_all_zero_pkmaps()函数,把所有pkmap_count[] 计数为1的页表项在TLB里面的entry给flush掉 ,并重置为0,这就表示该页表项又可以用了,可能会有疑惑为什么不在把pkmap_count置为1的时候也 就是解除映射的同时把TLB也flush呢? 个人感觉有可能是为了效率的问题吧,毕竟等到不够的时候再刷新,效率要好点吧。 */ if (!last_pkmap_nr) { flush_all_zero_pkmaps(); count = LAST_PKMAP; } if (!pkmap_count[last_pkmap_nr]) break; /* Found a usable entry */ if (--count) continue; /* * Sleep for somebody else to unmap their entries */ { DECLARE_WAITQUEUE(wait, current); __set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&pkmap_map_wait, &wait); unlock_kmap(); schedule(); remove_wait_queue(&pkmap_map_wait, &wait); lock_kmap(); /* Somebody else might have mapped it while we slept */ if (page_address(page)) return (unsigned long)page_address(page); /* Re-start */ goto start; } } /*返回这个页表项对应的线性地址vaddr.*/ vaddr = PKMAP_ADDR(last_pkmap_nr); /* v set_pte_at(mm, addr, ptep, pte)函数在NON-PAE i386上的实现其实很简单,其实就等同于下面的代码: static inline void native_set_pte(pte_t *ptep , pte_t pte) { *ptep = pte; } */ set_pte_at(&init_mm, vaddr,/*设置页表项*/ &(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot)); /*接下来把pkmap_count[last_pkmap_nr]置为1,1不是表示不可用吗, 既然映射已经建立好了,应该赋值为2呀,其实这个操作 是在他的上层函数kmap_high里面完成的(pkmap_count[PKMAP_NR(vaddr)]++).*/ pkmap_count[last_pkmap_nr] = 1; /*到此为止,整个映射就完成了,再把page和对应的线性地址 加入到page_address_htable哈希链表里面就可以了*/ set_page_address(page, (void *)vaddr); return vaddr; } |