美文网首页
ULK3 内存分配( 高端内存映射)

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

作者: rlkbk | 来源:发表于2016-04-22 00:35 被阅读0次

    下面两个函数返回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();

    }

    相关文章

      网友评论

          本文标题:ULK3 内存分配( 高端内存映射)

          本文链接:https://www.haomeiwen.com/subject/czotrttx.html