博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux 0.11 源码学习(十一)
阅读量:7097 次
发布时间:2019-06-28

本文共 4364 字,大约阅读时间需要 14 分钟。

memory.c

在X86的保护模式中,线性地址由页目录表(10位)+页表(10位)+ 偏移(12位)组成,因此对线性地址而言可以寻址4G的地址空间。而实际中linux支持16M的内存,因此在memory.c或者说linux的内存管理模块中,维护了线性地址和实际物理地址的映射。本篇博客主要记录内存管理的几个主要函数学习。

下面几个宏定义可以看出物理页面的分配数:

#define USED 100    //mem_map中的映射值,UNUSED是初始化值0,在mem_init中完成 #define PAGING_MEMORY (15*1024*1024) //实际的主内存15M,最低端的1M分配给内核#define PAGING_PAGES (PAGING_MEMORY>>12) //实际的物理页,每页表项是12位偏移地址,因此长度为4096
  • get_free_page(void)代码,完成功能是获取一个可用的页:
/* * Get physical address of first (actually last :-) free page, and mark it * used. If no free pages left, return 0. */unsigned long get_free_page(void){register unsigned long __res asm("ax"); //返回值是寄存器ax__asm__("std ; repne ; scasb\n\t"//edi指向的值与al(0)比较    "jne 1f\n\t"    "movb $1,1(%%edi)\n\t" //edi+1的值=1    "sall $12,%%ecx\n\t" //ecx的值左移12位,即ecx * 4KB    "addl %2,%%ecx\n\t" //ecx的值加2    "movl %%ecx,%%edx\n\t" //赋值给edx    "movl $1024,%%ecx\n\t" //ecx = 1024    "leal 4092(%%edx),%%edi\n\t" //edx + 4092的值读进edi    "rep ; stosl\n\t" //将eax的值拷贝到edi中,重复ecx次,完成页表项清零    "movl %%edx,%%eax\n" //edx赋值给eax,返回eax    "1:"    :"=a" (__res)     :"0" (0),"i" (LOW_MEM),"c" (PAGING_PAGES), //ax = 0, ecx = PAGIN_PAGES 物理内存页数    "D" (mem_map+PAGING_PAGES-1)//edi = mem_map + PAGING_PAGES - 1    );return __res;}
  •  free_page完成释放指定物理地址对应的物理页,代码如下:
addr -= LOW_MEM;    addr >>= 12; //上述两行代码,将addr由实际物理地址转换为mem_map中的索引    if (mem_map[addr]--) return;    mem_map[addr]=0;//对应的addr的mem_map设置为未使用
  • copy_page_tables在进程fork时被调用,如下:
if (copy_page_tables(old_data_base,new_data_base,data_limit)) {        printk("free_page_tables: from copy_mem\n");        free_page_tables(new_data_base,data_limit);        return -ENOMEM;    }

注: 此处的from/to都是线性地址,如上述描述由10+10+12表示,但要注意的是这里的10\10\都是在页目录表和页表中的索引,因此对于实际的物理地址要*4B。

int copy_page_tables(unsigned long from,unsigned long to,long size){    unsigned long * from_page_table;    unsigned long * to_page_table;    unsigned long this_page;    unsigned long * from_dir, * to_dir;    unsigned long nr;    if ((from&0x3fffff) || (to&0x3fffff))        panic("copy_page_tables called with wrong alignment");    from_dir = (unsigned long *) ((from>>20) & 0xffc); //左移24位 * 4B,即from线性地址对应的目录表实际地址    to_dir = (unsigned long *) ((to>>20) & 0xffc); //同上      size = ((unsigned) (size+0x3fffff)) >> 22;         for( ; size-->0 ; from_dir++,to_dir++) {        if (1 & *to_dir)            panic("copy_page_tables: already exist");        if (!(1 & *from_dir))            continue;        from_page_table = (unsigned long *) (0xfffff000 & *from_dir);        if (!(to_page_table = (unsigned long *) get_free_page()))//获取一个物理页,存放其页目录表            return -1;    /* Out of memory, see freeing */        *to_dir = ((unsigned long) to_page_table) | 7;        nr = (from==0)?0xA0:1024;        for ( ; nr-- > 0 ; from_page_table++,to_page_table++) {
//每个页目录表有1024个页表项 this_page = *from_page_table; if (!(1 & this_page)) continue; this_page &= ~2; *to_page_table = this_page; if (this_page > LOW_MEM) { *from_page_table = this_page; this_page -= LOW_MEM; this_page >>= 12; mem_map[this_page]++;//该物理页(即是1024*4大小) } } } invalidate(); return 0;}

在linux中采用了写时复制技术,也就是当某个线性地址被写时,触发相应的缺页错误。该缺页错误会导致分配物理页面,实现的代码是page.s,

  • 具体的业务逻辑在函数do_no_page中,如下:
void do_no_page(unsigned long error_code,unsigned long address)//address是产生异常页面的线性地址{    int nr[4];    unsigned long tmp;    unsigned long page;    int block,i;    address &= 0xfffff000;//低12位是页面内的偏移,此处要复制的是整个页的起始地址,因此低12位取0    tmp = address - current->start_code;//current->start_code是进程线性地址的起始地址    if (!current->executable || tmp >= current->end_data) {        get_empty_page(address);        return;    }    if (share_page(tmp))//申请共享内存,如果有其他进程已执行了一样的文件        return;    if (!(page = get_free_page()))//获取一个page        oom();/* remember that 1 block is used for header */    block = 1 + tmp/BLOCK_SIZE;    for (i=0 ; i<4 ; block++,i++)        nr[i] = bmap(current->executable,block);//寻求进程相应地址在文件系统中的逻辑号    bread_page(page,current->executable->i_dev,nr);//将文件中的内容读入到分配的内存页中    i = tmp + 4096 - current->end_data;    tmp = page + 4096;    while (i-- > 0) {        tmp--;        *(char *)tmp = 0;    }    if (put_page(page,address))        return;    free_page(page);    oom();}

转载于:https://www.cnblogs.com/Fredric-2013/archive/2013/05/09/3060839.html

你可能感兴趣的文章
ActionBar的简单应用
查看>>
IE11下不能引入外部css的解决方法
查看>>
java web 答辩总结
查看>>
GUI测试含义
查看>>
javabean使用技巧
查看>>
JS/JQ综合总结
查看>>
CGAffineTransform相关函数
查看>>
字符编码与字符集区别与联系(网页/PHP文件/MYSQL数据库乱码问题)
查看>>
黑马程序员-----const和readonly的区别
查看>>
转载:基于MapXtreme的鹰眼功能
查看>>
黄聪:远程序桌面登录的.NET(C#)开发
查看>>
JMeter聚合报告(Aggregate Report)理解
查看>>
C# 多线程Thread.IsBackground=True的作用
查看>>
Oracle数据库安装问题记录
查看>>
Error:flask_sqlalchemy
查看>>
算法3-排序-简单选择排序
查看>>
poj 1743 Musical Theme (后缀数组)
查看>>
XACML学习
查看>>
Java中文乱码问题研究(二)
查看>>
easyui图标大全
查看>>