ULK3 内存分配( 高端内存映射)

2016-04-22  本文已影响0人  rlkbk

下面两个函数返回page指针:

alloc_pages(gfp_mask,order);   

alloc_page(gfp_mask);

下面几个函数返回线性地址:

__get_free_pages(gfp_mask,order);

__get_free_page(gfp_mask);

get_zeroed_page(gfp_mask);

__get_dma_pages(gfp_mask, order);


高端内存页框的映射:

永久映射:kmap                     可能会休眠

临时映射:kmap_atomic         不会休眠

非连续内存分配


永久内核映射

永久内核映射允许内核建立高端页框到内核线性地址空间(128M)的长期映射。

主要使用主内核的一个专门页表:

pkmap_page_table

页表中的项数:

LAST_PKMAP

该页表映射的线性地址(开始):

PKMAP_BASE

pkmap_count:

这个数组包含LAST_PKMAP个计数器

计数器含义:

计数器为0:对应的页表没有映射高端内存也框,而且可用

计数器为1:对应的页表没有映射任何高端内存也框,但是它不可用,因为对应的TLB还没有刷新

计数器为N:共有N-1个内核成分在使用该页框

kmap函数分析

arch/i386/mm/highmem.h                        void *kmap(struct page *)

void *kmap(struct page *page){

        might_sleep();

        if (!PageHighMem(page))             /*   如果不是高端内存   则直接返回它的线性地址    */

                return page_address(page);

        return kmap_high(page);              /*    否则调用kmap_high(page) 函数*/

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)                                                            /*  已经建立映射,则直接返回线性地址 */

                  vaddr = map_new_virtual(page);                   /*  否则调用map_new_virtual函数 */

         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循环为了找出空闲页表项 */

                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))                        /*  在休眠时 可能有的内核成分 完成了该页框*/

                                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)); /*设置页表*/

        pkmap_count[last_pkmap_nr] = 1;

        set_page_address(page, (void *)vaddr);         /* 向page结构填入关联的线性地址*/

       return vaddr;

}

kunmap函数

arch/i386/mm/highmem.h                        void *kunmap(struct page *)

void kunmap(struct page *page)

{

        if (in_interrupt())                         

                BUG();

        if (!PageHighMem(page))                                 /*  如果是常规内存范围,直接返回*/

                return;

        kunmap_high(page);                                          /* 释放映射的内存为高端内存页,调用*/

}                                                                              /*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);                           /* 将线性地址转换成pkmap_page_table和*/

need_wakeup = 0;                                               /* pkmap_count (计数数组)的下标*/

        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):

临时映射比内核映射简单,而且不会休眠。

每个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函数:

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))                                        /* 不是高端内存直接返回 */

                return page_address(page);

        idx = type + KM_TYPE_NR*smp_processor_id();      /* 计算出线性地址的位置*/

        vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);     /* 转换成线性地址*/

        set_pte(kmap_pte-idx, mk_pte(page, kmap_prot));      /*建立页表项*/  

        __flush_tlb_one(vaddr);                                            /* 刷新线性地址对应的块表*/

        return (void*) vaddr;

}

kunmap_atomic函数

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(); /* 计算出对应下标*/

        if (vaddr < FIXADDR_START) { // FIXME    /* 给出的线性地址不符合要求*/

                dec_preempt_count();                            

                preempt_check_resched();

               return;

        }

        if (vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx))  /* 给出的线性地址与type对应的不符合*/

                BUG();                                                              /* BUG*/

       pte_clear(kmap_pte-idx);                                           /*将该页表项置空*/

        __flush_tlb_one(vaddr);                                           /*刷新该快表*/

        dec_preempt_count();

        preempt_check_resched();

}

上一篇下一篇

猜你喜欢

热点阅读