前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux copy on write源码分析(基于linux0.11)

linux copy on write源码分析(基于linux0.11)

作者头像
theanarkh
发布2019-09-05 17:48:00
2.2K0
发布2019-09-05 17:48:00
举报
文章被收录于专栏:原创分享原创分享

写时复制是有一块内存,由多个进程共享,属性是只读的,当有一个进程对这块内存进行写的时候,系统会先申请一块新的内存给他写。比如进程fork的时候,父子进程对应的物理地址都一样,这时候会在页表项中记录该物理地址是只读的,有一个进程写的时候,就会触发写保护异常。执行写时复制。

在触发写保护异常的时候,处理器会给系统提供两个信息。一个在系统栈中的错误码,一个在cr2寄存器中保存的引起异常的线性地址。错误码一般会告诉系统这些信息。

代码语言:javascript
复制
——P 标志表明异常是由于一个不存在页(0)还是访问权限违例或是使用了保留位(1)。
——W/R 标志表明引起异常的内存访问是读(0)还是写(1)。
——U/S 标志表明异常发生时处理器是在用户态(1)执行还是在管理态(0)执行。

下面我们分析这个过程,首先,在系统初始化的时候,注册了14号中断异常处理程序为page_fault。

代码语言:javascript
复制
// 缺页和写保护异常处理函数
set_trap_gate(14,&page_fault)

page_fault是汇编实现的

代码语言:javascript
复制
_page_fault:
    // 交换两个寄存器的值,esp指向的位置保存了错误码
    xchgl %eax,(%esp)
    // 压栈寄存器
    pushl %ecx
    pushl %edx
    push %ds
    push %es
    push %fs
    // 内核数据段描述符
    movl $0x10,%edx
    mov %dx,%ds
    mov %dx,%es
    mov %dx,%fs
    // 如果是缺页异常,cr2保存了引起缺页的线性地址
    movl %cr2,%edx
    // 线性地址(有的话)和错误码入参
    pushl %edx
    pushl %eax
    // 1和eax与,结果放到ZF中
    testl $1,%eax
    // zf=0则跳转,即0是写异常,1是缺页异常
    jne 1f
    call _do_no_page
    // 跳到标签2
    jmp 2f
1:    call _do_wp_page
// 出栈,返回中断,会重新执行异常指令
2:    addl $8,%esp
    pop %fs
    pop %es
    pop %ds
    popl %edx
    popl %ecx
    popl %eax
    ire

处理程序是do_wp_page

代码语言:javascript
复制
/*
 * This routine handles present pages, when users try to write
 * to a shared page. It is done by copying the page to a new address
 * and decrementing the shared-page counter for the old page.
 *
 * If it's in code space we exit with a segment error.
 */

void do_wp_page(unsigned long error_code,unsigned long address)
{
#if 0
/* we cannot do this yet: the estdio library writes to code space */
/* stupid, stupid. I really want the libc.a from GNU */
    if (CODE_SPACE(address))
        do_exit(SIGSEGV);
#endif
    /*
        address为线性地址,
        address>>10 = address>>12<<2,得到页表项的地址,
        address>>20 = address>>22<<2,得到页目录项地址,
        页目录项里存着页表地址+页表偏移得到页表项地址
    */
    un_wp_page((unsigned long *)
        (((address>>10) & 0xffc) + (0xfffff000 &
        *((unsigned long *) ((address>>20) &0xffc)))));

}
代码语言:javascript
复制
// 共享的页面被写入的时候会执行该函数。该函数申请新的一页物理地址,解除共享状态
void un_wp_page(unsigned long * table_entry)
{
    unsigned long old_page,new_page;
    // table_entry是页表项地址,算出该页的物理首地址
    old_page = 0xfffff000 & *table_entry;
    // LOW_MEM以下是内核使用的内存。old_page对应的物理页引用数为1,可以直接修改内容,置可写标记位(第二位)
    if (old_page >= LOW_MEM && mem_map[MAP_NR(old_page)]==1) {
        *table_entry |= 2;
        invalidate();
        return;
    }
    // 分配一个新的物理页
    if (!(new_page=get_free_page()))
        oom();
    // 页的引用数减一,因为有一个进程不使用这块内存了
    if (old_page >= LOW_MEM)
        mem_map[MAP_NR(old_page)]--;
    // 修改页表项的内容,使其指向新分配的内存页,置用户级、有效、可读写、可执行标记位
    *table_entry = new_page | 7;
    // 刷新tlb
    invalidate();
    // 把数据赋值到新分配的页上
    copy_page(old_page,new_page);
}
代码语言:javascript
复制
// 把一页的内容从from复制到to
#define copy_page(from,to) \
__asm__("cld ; rep ; movsl"::"S" (from),"D" (to),"c" (1024):"cx","di","si")
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档