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

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

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容