1.用户空间内存之缺页分析

在linux中使用了分页机制,分页机制把线性地址空间分成固定大小的页面,如果包含线性地址的页面当前不在物理内存中,处理器就会产生一个页错误异常。然后交给内核处理。

在init/main.c中,内核调用了trap_init方法初始化中断处理,其中就设置了缺页异常,page_fault是一个用汇编实现的方法,它只是把实际处理方法do_page_fault入栈,然后跳转到统一的错误中断入口,所以我们可以直接跳到do_page_fault去看看

void trap_init(void)
{
    int i;

    // ......
    set_trap_gate(13,&general_protection);
    set_trap_gate(14,&page_fault);      /*缺页中断*/
    // ......
}

// sys_call.S 中的page_fault方法,只是把do_page_fault入栈
.align 4
_page_fault:
    pushl $_do_page_fault
    jmp error_code

do_page_fault方法中,我们现在仅仅需要看用户空间的,内核空间的缺页暂时不管

asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
    unsigned long address;
    unsigned long user_esp = 0;
    unsigned int bit;

    /*从cr2中读取引起页错误的地址*/
    __asm__("movl %%cr2,%0":"=r" (address));
    /*如果是用户空间*/
    if (address < TASK_SIZE) {
        if (error_code & 4) {   /* user mode access? */
            if (regs->eflags & VM_MASK) {
                bit = (address - 0xA0000) >> PAGE_SHIFT;
                if (bit < 32)
                    current->screen_bitmap |= 1 << bit;
            } else 
                user_esp = regs->esp;
        }
        // 错误码如果是1代表访问了非法的物理地址
        if (error_code & 1)
            do_wp_page(error_code, address, current, user_esp);
        // 错误码为0代表访问了不存在的物理地址
        else
            do_no_page(error_code, address, current, user_esp);
        return;
    }
}
页表项

先看页不存在吧,也就是do_no_page,该版本的linux还是使用二级目录,也就是把32位线性地址分成3部分(目录,页面,页内偏移)。get_empty_pgtable方法就是取前10位取出目录页

page是目录页,接下来就从中间10位取出页表项,如果页表项存在,直接返回该页

/* 要访问的地址不在内存当中
 */
void do_no_page(unsigned long error_code, unsigned long address,
    struct task_struct *tsk, unsigned long user_esp)
{
    unsigned long tmp;
    unsigned long page;
    struct vm_area_struct * mpnt;

    /* 
     * 根据前10位目录标记取出目录页
     */
    page = get_empty_pgtable(tsk,address);
    if (!page)
        return;
    page &= PAGE_MASK;

    // 根据中间10位从目录页中取地址页
    page += PAGE_PTR(address);
    tmp = *(unsigned long *) page;
    // 如果地址页存在直接返回
    if (tmp & PAGE_PRESENT)
        return;

    /*增加进程在内核中占用的物理页的数量*/
    ++tsk->rss;
    /*如果缺页的内存在交换区中则将交换区中的内存交换到内存*/
    if (tmp) {
        ++tsk->maj_flt;
        /*注意此处的page是页表项的地址*/
        swap_in((unsigned long *) page);
        return;
    }
    
    /* vm_area_struct中的地址是和4KB对齐的
     */
    address &= 0xfffff000;
    tmp = 0;
    /* 此处注意虚拟地址链表是按照地址大小顺序来排列的,目前版本内核是链表的,
     * 在高版本内核中是二叉树结构(AVL),
     */
    for (mpnt = tsk->mmap; mpnt != NULL; mpnt = mpnt->vm_next) {
        if (address < mpnt->vm_start)
            break;
        if (address >= mpnt->vm_end) {
            tmp = mpnt->vm_end;
            continue;
        }
        // 执行到该处说明找到了一个地址处于mmap区间内的虚拟地址段

        // vm_ops是该内存段的操作属性,类似file的op
        // 里面涵盖了该内存的一些方法执行的动作,其中就有缺页nopage
        if (!mpnt->vm_ops || !mpnt->vm_ops->nopage) {
            ++tsk->min_flt;
            get_empty_page(tsk,address);
            return;
        }
        // 如果指定了nopage方法则调用
        mpnt->vm_ops->nopage(error_code, mpnt, address);
        return;
    }
    
    // 执行到再最终检查
    // 如果进程不是当前进程
    // 或者地址大于进程end_data又小于brk
    // 其他情况都杀死进程
    if (tsk != current)
        goto ok_no_page;
    if (address >= tsk->end_data && address < tsk->brk)
        goto ok_no_page;
    if (mpnt && mpnt == tsk->stk_vma &&
        address - tmp > mpnt->vm_start - address &&
        tsk->rlim[RLIMIT_STACK].rlim_cur > mpnt->vm_end - address) {
        mpnt->vm_start = address;
        goto ok_no_page;
    }
    /*cr2记录缺页地址*/
    tsk->tss.cr2 = address;
    current->tss.error_code = error_code;
    current->tss.trap_no = 14;
    /*发送段错误信号,杀死进程*/
    send_sig(SIGSEGV,tsk,1);
    if (error_code & 4) /* user level access? */
        return;
ok_no_page:
    ++tsk->min_flt;
    get_empty_page(tsk,address);
}

交换区,上面代码中,从目录页取出对应的页表项的时候,如果页表项的P位存在则直接返回对应的页,如果P不存在但页表项又不全为0,则从交换区内换到内存。在swap_in方法中,entry是页表项指针对应的内容,也就是下图中32位的数据,用一个long型结构保存着。


image.png
void swap_in(unsigned long *table_ptr)
{
    unsigned long entry;
    unsigned long page;

    entry = *table_ptr;
    // 如果该页的P位为1直接返回
    if (PAGE_PRESENT & entry) {
        printk("trying to swap in present page\n");
        return;
    }
    // 如果entry全0,也直接返回
    if (!entry) {
        printk("No swap page in swap_in\n");
        return;
    }

    // SWP_TYPE是宏,取后8位并右移1位(也就是取跳过P位的后7位)
    // 如果type=SHM_SWP_TYPE代表属于共享内存
    if (SWP_TYPE(entry) == SHM_SWP_TYPE) {
        shm_no_page ((unsigned long *) table_ptr);
        return;
    }
    
    // 申请一页内存,如果申请失败则oom退出进程ß
    if (!(page = get_free_page(GFP_KERNEL))) {
        oom(current);
        page = BAD_PAGE;
    } else  
        read_swap_page(entry, (char *) page);
    if (*table_ptr != entry) {
        free_page(page);
        return;
    }
    /* 设置表项和内存页的映射关系*/
    *table_ptr = page | (PAGE_DIRTY | PAGE_PRIVATE);
    swap_free(entry);
}

参考文章:
swap机制概述

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

推荐阅读更多精彩内容

  • 前言 内存管理一向是所有操作系统书籍不惜笔墨重点讨论的内容,无论市面上或是网上都充斥着大量涉及内存管理的教材和资料...
    木有sky阅读 907评论 0 1
  • 1 内存寻址 1.1 物理地址、虚拟地址以及线性地址 物理地址: 物理内存的内存单元地址 虚拟地址: 程序员看到的...
    疯狂小王子阅读 2,804评论 3 21
  • >计算机系统中有几类存储设备:cache、内存、外存。cache的存取速度最高,可以和CPU匹配,因此其代价最高,...
    一生信仰阅读 1,136评论 0 0
  • 最近开始想稍微深入一点地学习Linux内核,主要参考内容是《深入理解Linux内核》和《深入理解Linux内核架构...
    ice_camel阅读 1,785评论 0 2
  • Introduction 该 lab 主要需要编写操作系统的内存管理部分。内存管理分为两个部分: 内核的物理内存分...
    找不到工作阅读 12,203评论 0 12