下面兩個函數(shù)返回page指針:
alloc_pages(gfp_mask,order); ??
alloc_page(gfp_mask);
下面幾個函數(shù)返回線性地址:
__get_free_pages(gfp_mask,order);
__get_free_page(gfp_mask);
get_zeroed_page(gfp_mask);
__get_dma_pages(gfp_mask, order);
高端內(nèi)存頁框的映射:
永久映射:kmap ? ? ? ? ? ? ? ? ? ? 可能會休眠
臨時映射:kmap_atomic ? ? ? ? 不會休眠
非連續(xù)內(nèi)存分配
永久內(nèi)核映射
永久內(nèi)核映射允許內(nèi)核建立高端頁框到內(nèi)核線性地址空間(128M)的長期映射。
主要使用主內(nèi)核的一個專門頁表:
pkmap_page_table
頁表中的項數(shù):
LAST_PKMAP
該頁表映射的線性地址(開始):
PKMAP_BASE
pkmap_count:
這個數(shù)組包含LAST_PKMAP個計數(shù)器
計數(shù)器含義:
計數(shù)器為0:對應(yīng)的頁表沒有映射高端內(nèi)存也框,而且可用
計數(shù)器為1:對應(yīng)的頁表沒有映射任何高端內(nèi)存也框,但是它不可用,因為對應(yīng)的TLB還沒有刷新
計數(shù)器為N:共有N-1個內(nèi)核成分在使用該頁框
kmap函數(shù)分析
arch/i386/mm/highmem.h ? ? ? ? ? ? ? ? ? ? ? ?void *kmap(struct page *)
void *kmap(struct page *page){
? ? ? ? might_sleep();
? ? ? ? if (!PageHighMem(page)) ? ? ? ? ? ? /* ? 如果不是高端內(nèi)存 ? 則直接返回它的線性地址 ? ?*/
? ? ? ? ? ? ? ? return page_address(page);
? ? ? ? return kmap_high(page); ? ? ? ? ? ? ?/* ? ?否則調(diào)用kmap_high(page) 函數(shù)*/
mm/highmem.c ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void fastcall ?*kmap_high(struct page *page)
void fastcall *kmap_high(struct page *page)
{
? ? ? ? ? unsigned long vaddr; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? spin_lock(&kmap_lock); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* ? 獲取自旋鎖 */ ? ?
? ? ? ? ? vaddr = (unsigned long)page_address(page); ? ? ? ? ?/* ?嘗試獲取該page的線性地址 */
? ? ? ? ? if (!vaddr) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* ?已經(jīng)建立映射,則直接返回線性地址 */
? ? ? ? ? ? ? ? ? vaddr = map_new_virtual(page); ? ? ? ? ? ? ? ? ? /* ?否則調(diào)用map_new_virtual函數(shù) */
? ? ? ? ?pkmap_count[PKMAP_NR(vaddr)]++; ? ? ? ? ? ? ? ? ?/* ?在pkmap_page_table空閑的頁表項*/ ??
? ? ? ? ?if (pkmap_count[PKMAP_NR(vaddr)] < 2)
? ? ? ? ? ? ? ? ? BUG();
? ? ? ? spin_unlock(&kmap_lock);
? ? ? ? return (void*) vaddr;
}
mm/highmem.h ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? static inline unsigned long ?map_new_virtual(struct page *)
static inline unsigned long map_new_virtual(struct page *page)
{
? ? ? ? unsigned long vaddr;
? ? ? ? int count;
start:
? ? ? ? count = LAST_PKMAP;
? ? ? ? /* Find an empty entry */
? ? ? ? for (;;) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* ?大的for循環(huán)為了找出空閑頁表項 */
? ? ? ? ? ? ? ? last_pkmap_nr = (last_pkmap_nr + 1) & LAST_PKMAP_MASK;
? ? ? ? ? ? ? ? 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;
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? DECLARE_WAITQUEUE(wait, current); ? ? ? ? ? ? ? ? ? ? ? /* 如果找不到則休眠*/
? ? ? ? ? ? ? ? ? ? ? ? __set_current_state(TASK_UNINTERRUPTIBLE);
? ? ? ? ? ? ? ? ? ? ? ? add_wait_queue(&pkmap_map_wait, &wait);
? ? ? ? ? ? ? ? ? ? ? ? spin_unlock(&kmap_lock);
? ? ? ? ? ? ? ? ? ? ? ?schedule();
remove_wait_queue(&pkmap_map_wait, &wait);
? ? ? ? ? ? ? ? ? ? ? ?spin_lock(&kmap_lock);
? ? ? ? ? ? ? ? ? ? ? ? if (page_address(page)) ? ? ? ? ? ? ? ? ? ? ? ?/* ?在休眠時 可能有的內(nèi)核成分 完成了該頁框*/
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return (unsigned long)page_address(page); ? ? ? ? ?/*映射*/
? ? ? ? ? ? ? ? ? ? ? ? goto start;
? ? ? ? ? ? ? ? }
? ? ? ? }
? ? ? ? vaddr = PKMAP_ADDR(last_pkmap_nr); ? ? ? ? ? ? ? ? ? /* 獲取PKMAP_BASE開始的空閑線性地址*/
? ? ? ? set_pte(&(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot)); /*設(shè)置頁表*/
? ? ? ? pkmap_count[last_pkmap_nr] = 1;
? ? ? ? set_page_address(page, (void *)vaddr); ? ? ? ? /* 向page結(jié)構(gòu)填入關(guān)聯(lián)的線性地址*/
? ? ? ?return vaddr;
}
kunmap函數(shù)
arch/i386/mm/highmem.h? ? ? ? ? ? ? ? ? ? ? ? void *kunmap(struct page *)
void kunmap(struct page *page)
{
? ? ? ? if (in_interrupt()) ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? BUG();
? ? ? ? if (!PageHighMem(page)) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* ?如果是常規(guī)內(nèi)存范圍,直接返回*/
? ? ? ? ? ? ? ? return;
? ? ? ? kunmap_high(page); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* 釋放映射的內(nèi)存為高端內(nèi)存頁,調(diào)用*/
} ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/*kunmap_high()*/
mm/highmem.c ? ? ? ? ? ? ? ? ? ? ? ? ? ?void fastcall kunmap_high(struct page *)
void fastcall kunmap_high(struct page *page)
{
? ? ? ? unsigned long vaddr;
? ? ? ? unsigned long nr;
? ? ? ? int need_wakeup;
? ? ? ? spin_lock(&kmap_lock);
? ? ? ? vaddr = (unsigned long)page_address(page); ? ?/* 獲取線性地址 */
? ? ? ? if (!vaddr)
? ? ? ? ? ? ? ? BUG(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* 。。。。*/
? ? ? ? nr = PKMAP_NR(vaddr); ? ? ? ? ? ? ? ? ? ? ? ? ? /* 將線性地址轉(zhuǎn)換成pkmap_page_table和*/
need_wakeup = 0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* pkmap_count (計數(shù)數(shù)組)的下標(biāo)*/
? ? ? ? switch (--pkmap_count[nr]) {
? ? ? ? case 0:
? ? ? ? ? ? ? ? BUG(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* bug*/
? ? ? ? case 1:
? ? ? ? ? ? ? ? need_wakeup = waitqueue_active(&pkmap_map_wait); ?/* 有空心的頁表項,喚醒因此休眠代*/
? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* 碼*/
? ? ? ? spin_unlock(&kmap_lock);
? ? ? ? if (need_wakeup)
? ? ? ? ? ? ? ? wake_up(&pkmap_map_wait);
}
可以看出來,kmap休眠的部分就在于對空閑頁表項的等待,如果一直沒有,則一直休眠。
臨時映射(fix-mapping):
臨時映射比內(nèi)核映射簡單,而且不會休眠。
每個CPU都有13個候選的線性地址(頁框)可以使用,
這13個窗口用km_type來表示:
include/asm-i386/kmap_types.h ? ? ? ? ? ? km_type
enum km_type {
D(0)? ? KM_BOUNCE_READ,
D(1)? ? KM_SKB_SUNRPC_DATA,
D(2)? ? KM_SKB_DATA_SOFTIRQ,
D(3)? ? KM_USER0,
D(4)? ? KM_USER1,
D(5)? ? KM_BIO_SRC_IRQ,
D(6)? ? KM_BIO_DST_IRQ,
D(7)? ? KM_PTE0,
D(8)? ? KM_PTE1,
D(9)? ? KM_IRQ0,
D(10)? KM_IRQ1,
D(11)? KM_SOFTIRQ0,
D(12)? KM_SOFTIRQ1,
D(13)? KM_TYPE_NR
2};
kmap_atomic函數(shù):
arch/i386/mm/highmem.h? ? ? ? ? ? ? ? ? ? ? ? void *kmap_atomic(struct page *page, enum km_type type)
void *kmap_atomic(struct page *page, enum km_type type)
{
? ? ? ? enum fixed_addresses idx;
? ? ? ? unsigned long vaddr;
? ? ? ? inc_preempt_count();
? ? ? ? if (!PageHighMem(page)) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* 不是高端內(nèi)存直接返回 */
? ? ? ? ? ? ? ? return page_address(page);
? ? ? ? idx = type + KM_TYPE_NR*smp_processor_id(); ? ? ?/* 計算出線性地址的位置*/
? ? ? ? vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); ? ? /* 轉(zhuǎn)換成線性地址*/
? ? ? ? set_pte(kmap_pte-idx, mk_pte(page, kmap_prot)); ? ? ?/*建立頁表項*/ ?
? ? ? ? __flush_tlb_one(vaddr); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/* 刷新線性地址對應(yīng)的塊表*/
? ? ? ? return (void*) vaddr;
}
kunmap_atomic函數(shù)
arch/i386/mm/highmem.h? ? ? ? ? ? ? ? ? ? ? ? void *kunmap_atomic(struct page *page, enum km_type type)
void kunmap_atomic(void *kvaddr, enum km_type type)
{
? ? ? ? unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; ?/* 清空低12位*/
? ? ? ? enum fixed_addresses idx = type + KM_TYPE_NR*smp_processor_id(); /* 計算出對應(yīng)下標(biāo)*/
? ? ? ? if (vaddr < FIXADDR_START) { // FIXME ? ?/* 給出的線性地址不符合要求*/
? ? ? ? ? ? ? ? dec_preempt_count(); ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? preempt_check_resched();
? ? ? ? ? ? ? ?return;
? ? ? ? }
? ? ? ? if (vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx))? /* 給出的線性地址與type對應(yīng)的不符合*/
? ? ? ? ? ? ? ? BUG();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* BUG*/
? ? ? ?pte_clear(kmap_pte-idx); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*將該頁表項置空*/
? ? ? ? __flush_tlb_one(vaddr); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*刷新該快表*/
? ? ? ? dec_preempt_count();
? ? ? ? preempt_check_resched();
}