ULK3 內(nèi)存分配( 高端內(nèi)存映射)

下面兩個函數(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();

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容