注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

vmalloc返回的常规页面的双重地址验证  

2013-09-11 00:09:37|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、高端内存
我们通常所说的高端内存是指它们的内核逻辑地址和物理地址没有简单的3G偏移映射的物理内存,而内核中从3G--4G地址空间中除去常规内存之外,这些逻辑地址空间就可以被内核的vmalloc使用。
假设系统的物理内存为128M,那么在默认配置下,系统没有高端内存,并且从3G+128M开始的内核逻辑地址空间就可以被vmalloc使用,在内核的vmalloc函数族中,对于这个变量的使用在逻辑地址空间的分配过程中
struct vm_struct *get_vm_area_node(unsigned long size, unsigned long flags,
                   int node, gfp_t gfp_mask)
{
    return __get_vm_area_node(size, flags, VMALLOC_START, VMALLOC_END, node,
                  gfp_mask);
}
/* Just any arbitrary offset to the start of the vmalloc VM area: the
 * current 8MB value just means that there will be a 8MB "hole" after the
 * physical memory until the kernel virtual memory starts.  That means that
 * any out-of-bounds memory accesses will hopefully be caught.
 * The vmalloc() routines leaves a hole of 4kB between each vmalloced
 * area for the same reason. ;)
 */
#define VMALLOC_OFFSET    (8*1024*1024)
#define VMALLOC_START    (((unsigned long) high_memory + vmalloc_earlyreserve + \
            2*VMALLOC_OFFSET-1) & ~(VMALLOC_OFFSET-1))
这里的high_memory就是由高端内存确定的,高端内存页面的其实地址就是这个值。
这个值的初始化在linux-2.6.21\arch\i386\kernel\setup.c文件的static unsigned long __init setup_memory(void)函数中实现
#ifdef CONFIG_HIGHMEM
    highstart_pfn = highend_pfn = max_pfn;
    if (max_pfn > max_low_pfn) {
        highstart_pfn = max_low_pfn;
    }
    printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
        pages_to_mb(highend_pfn - highstart_pfn));
    num_physpages = highend_pfn;
    high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;
#else
    num_physpages = max_low_pfn;
    high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1;
#endif
由于内核启动时接受参数highmem来制定保留高端内存的数量,所以即使是物理内存数量比较拮据,同样可以体验高端内存的“高端大气上档次”的感觉。
二、一般情况下
就像一般情况下大家都是屌丝一样,我们这里看一下通常情况下的情况。假设说系统只有128M的物理内存,此时我们执行vmalloc的时候,在vmalloc的页面属性中依然是指定了__GFP_HIGHMEM属性,这个属性是不管系统是否配置了高端内存选项,是否有多余的物理内存。
对于只有128M的系统来说,此时系统根本不存在高端物理内存,所以此时内存退而求其次(fallback)的到normal区申请页面,之前的一片日志已经说过,normal区的页面在系统启动之后已经和内核的逻辑地址建立了简单的线性映射关系,此时再被vmalloc分配,并且映射到内核中的高端逻辑地址空间,此时就意味着一个页面在内核中有两个逻辑地址可以访问到。下面使用gdb验证一下这个说法:
(gdb) p pages
$1 = (struct page **) 0xc130d3a4
(gdb) p *pages
$2 = (struct page *) 0x0
(gdb) p area->pages@10
$3 = {0xc130d3a0, 0x1, 0x0, 0x0, 0xcfffbc80, 0xcfffa4a8, 0x0, 0xc131e000, 0x1,
  0xffffffff}
(gdb) p area->addr  该地址是页面被映射到非常规逻辑空间的地址
$4 = (void *) 0xd080a000
(gdb) p (area->pages[0] -mem_map)*1024*4 + 0xc000000
$5 = 221347840
(gdb) p/x  (area->pages[0] -mem_map)*1024*4 + 0xc000000
$6 = 0xd318000
(gdb) p/x  (area->pages[0] -mem_map)*1024*4 + 0xc0000000 通过直接映射算出该页面的常规内核逻辑地址
$7 = 0xc1318000
(gdb) p $7
$8 = 3241246720
(gdb) p/x $7
$9 = 0xc1318000
(gdb) x/10x $7
0xc1318000:    0x00000000    0x00000000    0x00000000    0x00000000
0xc1318010:    0x00000000    0x00000000    0x00000000    0x00000000
0xc1318020:    0x00000000    0x00000000
(gdb) x/10x $4
0xd080a000:    0x00000000    0x00000000    0x00000000    0x00000000
0xd080a010:    0x00000000    0x00000000    0x00000000    0x00000000
0xd080a020:    0x00000000    0x00000000
(gdb) set *(int*)$4=0x12345678  可以看到修改一个地方,两个逻辑地址的内容均发生了变化
(gdb) x/10x $4
0xd080a000:    0x12345678    0x00000000    0x00000000    0x00000000
0xd080a010:    0x00000000    0x00000000    0x00000000    0x00000000
0xd080a020:    0x00000000    0x00000000
(gdb) x/10x $7
0xc1318000:    0x12345678    0x00000000    0x00000000    0x00000000
0xc1318010:    0x00000000    0x00000000    0x00000000    0x00000000
0xc1318020:    0x00000000    0x00000000
(gdb) bt
#0  __vmalloc_area_node (area=0xc130d3c0, gfp_mask=210, prot=..., node=-1)
    at mm/vmalloc.c:459
#1  0xc01aae08 in __vmalloc_node (size=4096, gfp_mask=210, prot=..., node=-1)
    at mm/vmalloc.c:495
#2  0xc01aae34 in __vmalloc (size=40, gfp_mask=210, prot=...)
    at mm/vmalloc.c:500
#3  0xc01aae60 in vmalloc (size=40) at mm/vmalloc.c:515
#4  0xc06264ba in csr1212_new_keyval (type=0 '\000', key=12 '\f')
    at drivers/ieee1394/csr1212.c:232
#5  0xc0626545 in csr1212_new_immediate (key=12 '\f', value=33728)
    at drivers/ieee1394/csr1212.c:251
#6  0xc061fbfd in init_csr () at drivers/ieee1394/csr.c:829
#7  0xc0abfabc in ieee1394_init () at drivers/ieee1394/ieee1394_core.c:1145
#8  0xc0a8634d in do_initcalls () at init/main.c:672
#9  0xc0a86474 in do_basic_setup () at init/main.c:712
#10 0xc0a86502 in init (unused=0x0) at init/main.c:803
#11 0xc01086d3 in ?? ()
(gdb) list
454            }
455        }
456   
457        if (map_vm_area(area, prot, &pages))
458            goto fail;
459        return area->addr;
460   
461    fail:
462        vfree(area->addr);
463        return NULL;
(gdb)
三、回顾一下kmalloc
这个函数返回的也是逻辑地址空间,但是大家搜索一下内核的代码就可以看到,这个函数调用的时候通常是不会在页面标志中传入高端内存标志的,因为如果系统物理内存较少,根本没有高端物理内存供申请,如果系统物理内存较多,那么此时系统的空间逻辑地址空间就较少,申请高端高端地址容易失败。并且kmalloc函数并没有提供将返回的高端内存映射到逻辑地址的功能(同样未验证),但是对于常规内存,这个地址直接转换即可。
  评论这张
 
阅读(621)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017