linux的物理内存

来源:互联网 发布:新sat写作 知乎 编辑:程序博客网 时间:2024/06/12 01:06

linux系统对于物理内存管理主要有下面3次探测的过程: 

1:第一次探测 Arch/i386/boot/setup.S

在这里使用了三种方法,通过调用0x15 中断:
E820H 得到memory map。
E801H 得到大小。
88H 0-64M.

 

2:第二次探测:
start_kernel() -> setup_arch()
这里主要是对物理内存的初始化,建立zone区。并且初始化page。
start_kernel() -> setup_arch() -> setup_memory_region()
把bios 里的memory map 拷贝到 e820 这个全局变量里。会在屏幕上显示
BIOS-provided physical RAM map:
BIOS-e820: 0000000000000000 – 000000000009FC00 (usable)
BIOS-e820: 0000000000100000-0000000002000000 (usable)

上面参数的含义:BIOS-e820 说的是通过e820 来读取成功的数据,第一个代表起始地址,后面接着的是大小。usable 说明内存可用。还有 reserved, ACPI data, ACPI NVS 等。第二条说明起始地址为0x100000 = 1M,大小是0x2000000 = 32M 内存。
start_kernel() -> setup_arch() -> setup_memory ()


start_kernel() -> setup_arch() -> setup_memory ()->find_max_pfn()
通过e820计算出最大可用页面。
e820.map[1].addr = 0x100000 e820.map[1].size = 0x1f00000,那么start =PFN_UP(0x100000) = (0x100000+0x1000(4096 = 1>>12)-1)>>12 =0x100FFF>>12=0x100, end = PFN_DOWN(0x2000000) = 0x2000000 >> 12 =0x2000 = 8192,也就是max_pfn = 0x2000 就是最大的页面。


start_kernel() -> setup_arch() -> setup_memory ()->find_max_low_pfn(),主要是高端内存的限制。max_low_pfn = max_pfn 不能大于896M

PFN_DOWN((-0xC0000000 –0x8000000(128<<20)) = (0x38000000 = 896M限制)) = 0x38000


start_kernel() -> setup_arch() -> setup_memory ()-> init_bootmem()
引导内存分配只使用在引导过程中,为内核提供保留分配页,并且建立内存的位图。比如_end 为0xc02e5f18,start_pfn = PFN_UP(__pa(&end)) = 0x2e6,所以
start_pfn 指的是内核结束的下一个页面((_pa(&_end) >> 12 =0x2e5 ,那么
init_bootmen(start_pfn,max_low_pfn) = init_bootmen(0x2e6,0x2000)
start_kernel() -> setup_arch() -> setup_memory ()->init_bootmem()->init_bootmem_core()


init_bootmen(0x2e6,0x2000) = init_bootmem_core(&contig_page_data,0x2e6,0,0x2000),mapsize就是建立内存位图大小需要多少字节 1024=(8192-0+7)/8,1个位代表着4K的一个页面。
bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT) =
phys_to_virt(0x2e6<<12) = phys_to_virt(0x2e6000) = 0xc02e6000
bdata->node_boot_start = (start << PAGE_SHIFT) = 0
bdata->node_low_pfn = end = 8192
把0xc02e6000 开始0xff 填充1024 个字节 0xc02e6400,这样就代表着所有内
存不可用。
start_kernel() -> setup_arch() -> setup_memory ()-> register_bootmem_low_pages
e()
根据e820和内存位图来标识位图那些内存可用。
start_kernel() -> setup_arch() -> setup_memory ()-> register_bootmem_low_pages
e()->free_bootmem()->free_bootmem_core()
第一次调用 free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)) =
free_bootmem(PFN_PHYS( 0), PFN_PHYS(159))= free_bootmem(0, 0x9F000))
free_bootmem_core(contig_page_data.bdata, 0, 0x9f000)
eidx代表这块内存共占用多少页面
eidx = (addr + size - bdata->node_boot_start)/PAGE_SIZE = (0+0x9f000-0)/0x1000=0x9f
end = (addr + size)/PAGE_SIZE=(0+0x9f000)/0x1000=0x9f
start = (addr + PAGE_SIZE-1) / PAGE_SIZE = (0+0x1000-1)/0x1000=0
sidx = start - (bdata->node_boot_start/PAGE_SIZE)=0-(0/0x1000)=0
每一次循环清1 位,第一次循环0x9f次清到0x9f /8= 0x13,所以第一次清到
了0xc02e6013的第7位159%8 = 7,所以0xc02e6013的数据应该是0x10000000,
第二次free_bootmem_core(contig_page_data.bdata, 0x100000, 0x 1F00000)
eidx = (addr + size - bdata->node_boot_start)/PAGE_SIZE = 0x2000
end = (addr + size)/PAGE_SIZE = 0x2000
start = (addr + PAGE_SIZE-1) / PAGE_SIZE = 0x100
sidx = start - (bdata->node_boot_start/PAGE_SIZE) = 0x100
这次从0xc02e6020 ( 0x100/8 ) 开始清0x2000-0x100 次, 清到
0xc02e6400(0xc02e6020+0x1f00/8)
通过free_bootmem的操作,我们已经把内存的位图标识出来。
start_kernel() -> setup_arch() -> setup_memory ()->reserve_bootmem()
保留内存,说明这部分不能用于动态分配。
reserve_bootmem(HIGH_MEMORY, (PFN_PHYS(start_pfn) +
bootmap_size + PAGE_SIZE-1) - (HIGH_MEMORY)) =
reserve_bootmem(0x100000, (PFN_PHYS(2e6) +1024 + 4096-1) - (0x100000))=
reserve_bootmem(0x100000, 1E73FF)
保留内核开始1M 到建立的内存位图的结束地方,因为位图是接着内核的下
一个页面存放的,所以一起保留,对于+ PAGE_SIZE-1 操作就是位图的结束
位置也与内存边界对齐。重新改写内存位图之后对于我们的情况是
0xc02e6020(1M)到0xc02e605C 都改成了1(保留)0xc02e605D还是0
reserve_bootmem(0,PAGE_SIZE)= reserve_bootmem(0,4096)
保留物理内存的第0 页内存。0x0-0x4096
start_kernel() -> setup_arch() -> paging_init()
当我们上面的物理内存都完成时,我们就要对所有内存进行管理,需要重新
建立页面映射。
start_kernel() -> setup_arch() -> paging_init()->pagetable_init()
我们根据已有的内存信息,重新修改页面表
end = (unsigned long)__va(max_low_pfn*PAGE_SIZE) = 0xC2000000
pgd_base = swapper_pg_dir = 0xC0101000
i = __pgd_offset(PAGE_OFFSET) = (0xC0000000>>22)&(1024-1)=768
pgd = pgd_base + i= 0xC0101C00
pgd就是内核模式的第一个页目录
第一个for循环就是说从768开始到1024结束,也就是swapper_pg_dir结束,
这部分都是内核的页目录,也就是我们要修改的页目录。
第二个for循环用于中间页表,对我们2 层i386 体系无效。
alloc_bootmem_low_pages(PAGE_SIZE) = __alloc_bootmem(0x1000,0x1000,0)
->__alloc_bootmem_core(pgdat->bdata, 0x1000,0x1000,0)
eidx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT) =
8192-(0>>12) = 8192 代表着这个区域可用的页面,实际上就是整个内存。
我们以前保留过第0 页面的内存,所以我们分配内存就是从第一页面开始,
重新写内存位图将已分配的页面做保留标记。这次运行后我们分配了一个页
面(0x4096),开始地址为0xc0001000,与此同时,0xc02e6000 = 0x3 = 0011。
第三个for循环用于写页表,写满这个我们新分配的页表。
第一次循环的时候pte = 0xc0001000;vaddr = 0xc0000000;
*pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL) =
mk_pte_phys(0, MAKE_GLOBAL(_PAGE_PRESENT | _PAGE_RW |
_PAGE_DIRTY | _PAGE_ACCESSED) =
mk_pte_phys(0, MAKE_GLOBAL(0x001 | 0x002 | 0x040 | 0x020) =
mk_pte_phys(0, ( MAKE_GLOBAL(0x001 | 0x002 | 0x040 | 0x020) = 0x63)) =
mk_pte_phys(0, pgprot_t __ret = __pgprot(0x63)) = mk_pte_phys(0, __ret) =
__mk_pte((0) >> 12, __ret) = __pte(((0)<<12)| pgprot_val(__ret)) = __pte(0x63)
= 0x00000063
第二次运行时 vaddr = 0xc0001000;
*pte = 0x00001063
当pmd = 0xc0101c00 时
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte_base))) =
(pmd,__pmd((_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED |
_PAGE_DIRTY) + 0x00001000) = (pmd,__pmd(0x001 | 0x002 | 0x020 | 0x040
+0x00001000) = set_pmd(pmd,__pmd( 0x00001063)
这就是结果0xc0101c00=0x00001063下一次pmd是0xc0101c04=0x00002063
对于我们现在的状态,我们是32M 内存,end = 0xC2000000,所以只映射了8
项,0xC0101C1C=0x00008063
现在要建立专用区的页表了,专用区是位于虚拟地址为0xfffff000 往回的一
个区域。Enum fixed_addresses 描述了专用区的功能
vaddr=__fix_to_virt(__end_of_fixed_addresses-1)&PMD_MASK= 0xFFC00000
这就是专用区起始地址所在的中级页目录边界地址
fixrange_init(vaddr, 0, pgd_base) = fixrange_init(0xffc00000, 0, 0xc0101000)
我们要在里面建立专用区的页面。
0xffc00000 处于页目录的1023 映射的位置(最后一项),所以又申请了一个
页面0xc0009000,(没有作为页表使用)。因为这个页面是接着我们以前的目
录页面申请的,所以这个页面与目录页面在物理上是连续的,但在虚拟地址
却差很大。0xC0101FFC = 0x00009067,这就是目录映射了。
当我们运行完时我们就把所有的页表及页目录都建立好,通过load_cr3 与
__flush_tlb_all重新刷新分页机制。
start_kernel() -> setup_arch() -> paging_init()->zone_sizes_init()
根据内存位图开始建立分区。
max_dma = virt_to_phys((char*) MAX_DMA_ADDRESS) >> PAGE_SHIFT =
virt_to_phys(0xC1000000) >> 12 = 0x1000
对于我们32M内存来说,zones_size = { 4096, 4096, 0 }
start_kernel() -> setup_arch() -> paging_init()->zone_sizes_init()->free_area_init()
建立分区数据结构:标记所有页面保留,标记所有内存队列空,清除内存位
图。(需要详细)
map_size = (totalpages + 1)*sizeof(struct page) = 0xE000 根据page结构看
8192 个页面需要多大空间。
lmem_map =alloc_bootmem_node(pgdat, 0xE000) =
__alloc_bootmem_node(pgdat, 0xE000, 0x10, 0x1000000) = 0xc1000000
lmem_map = (struct page *)(PAGE_OFFSET +
MAP_ALIGN((unsigned long)lmem_map - PAGE_OFFSET))
= 0xC0000000+MAP_ALIGN(0x1000000) = 0xC0000000+
((((0x1000000) % sizeof(mem_map_t)) == 0) ? (0x1000000) : ((0x1000000) +
sizeof(mem_map_t) - ((0x1000000) % sizeof(mem_map_t))))=
0xC0000000+((8==0)?0x1000000: 0x1000038-8) = 0xC1000030
因为cache需要对齐
接下来循环初始化每个区
zone->zone_mem_map = mem_map + offset; 这个区的mem_map 结构位置
zone->zone_start_paddr = zone_start_paddr; 这个区的开始虚拟地址。

for (i = 0; i < size; i++) {
struct page *page = mem_map + offset + i;

}
初始化这个区的所有page 结构。在page 里所有的页面都是保留的,不能投
入使用。

for (i = 0; ; i++) {
unsigned long bitmap_size;
INIT_LIST_HEAD(&zone->free_area[i].free_list);

}

这是伙伴分配系统的初始化
这就说明了每个区有每个区自己的管理系统。

 

3:第三次探测
start_kernel() ->mem_init()
start_kernel() ->mem_init()->free_pages_init()
start_kernel() ->mem_init()->free_pages_init()->free_all_bootmem_core()
struct page *page = pgdat->node_mem_map; 这就是第一个page.
idx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT) =8192-0>>12 = 8192
第一个循环就是根据我们以前建立的内存位图来初始化Page。
start_kernel() ->mem_init()->free_pages_init()->free_all_bootmem_core()->__free_page()->__free_pages()->__free_pages_ok()
释放内存页。

第二个循环就是释放内存位图本身的内存。
执行完后以后的内存操作就可以使用Page 了,第三阶段的内存初始化完毕,还
有一部分零散的内存操作会在别的部分讨论,至此,物理内存管理结束。

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 网易邮箱帐号忘了怎么办 网易邮箱密码忘记了怎么办 网易邮箱密码忘了怎么办 网易邮箱忘记邮箱账号怎么办 崩坏3三无号被盗怎么办 qq邮箱给58占用怎么办 支付宝邮箱被占用怎么办 苹果手机下载不了qq怎么办 附件预览时发生错误怎么办 手机下载的压缩包打不开怎么办 邮箱提示中转站剩余容量不足怎么办 手机邮箱密码忘记了怎么办 垃圾邮件被系统删除了怎么办 邮箱里的邮件下载不了怎么办 qq邮箱下载不了文件怎么办 苹果手机忘记邮箱密码怎么办 oppo手机安装包损坏怎么办 手机安装包损坏无法安装怎么办 手机qq安装包损坏了怎么办 安装包损坏无法安装怎么办 方舟生存进化安装包损坏怎么办 如果安装包坏了怎么办 酷狗下载音乐收费怎么办 酷狗下载歌曲收费怎么办 酷狗音乐下载歌曲要钱怎么办 酷狗音乐下载要钱怎么办 手机酷狗音乐下载收费怎么办 咪咕视频静音了怎么办 卡西欧自拍神器死机了怎么办 手机一自拍就死机怎么办 微信怎么黑屏了怎么办 宝宝睡袋买大了怎么办 社保中间断了一年怎么办 京东阅读换手机怎么办 酷狗自动续费怎么办 qq音乐下载需要钱怎么办 电脑播放音乐没声音怎么办 酷狗账号注销了怎么办 电脑酷狗音乐没有声音怎么办 百度云的视频被净网怎么办 v380用户名或密码错误怎么办