【版权所有,转载请注明出处。出处:http://www.cnblogs.com/joey-hua/p/5597705.html 】

Linux内核因为使用了内存分页机制,所以相对来说好理解些。因为内存分页就是为了方便管理内存。

说到内存分页,最根部的要属页目录表了,head.h中:

extern unsigned long pg_dir[1024];	// 内存页目录数组。每个目录项为4 字节。从物理地址0 开始。

然后再看head.s:

/*
* head.s 含有32 位启动代码。
* 注意!!! 32 位启动代码是从绝对地址0x00000000 开始的,这里也同样是页目录将存在的地方,
* 因此这里的启动代码将被页目录覆盖掉。
*/
.text
.globl _idt,_gdt,_pg_dir,_tmp_floppy_area
_pg_dir: # 页目录将会存放在这里。
...

页目录存放的地方是从绝对地址0开始的,接下来分配页表:

/* Linus 将内核的内存页表直接放在页目录之后,使用了4 个表来寻址16 Mb 的物理内存。
* 如果你有多于16 Mb 的内存,就需要在这里进行扩充修改。
*/
# 每个页表长为4 Kb 字节(1 页内存页面),而每个页表项需要4 个字节,因此一个页表共可以存放
# 1024 个表项。如果一个页表项寻址4 Kb 的地址空间,则一个页表就可以寻址4 Mb 的物理内存。
# 页表项的格式为:项的前0-11 位存放一些标志,例如是否在内存中(P 位0)、读写许可(R/W 位1)、
# 普通用户还是超级用户使用(U/S 位2)、是否修改过(是否脏了)(D 位6)等;表项的位12-31 是
# 页框地址,用于指出一页内存的物理起始地址。
.org 0x1000 # 从偏移0x1000 处开始是第1 个页表(偏移0 开始处将存放页表目录)。
pg0: .org 0x2000
pg1: .org 0x3000
pg2: .org 0x4000
pg3: ...

分配了4个页表空间,因为一个页表项对应的是一个页也就是4K,所以一个页表就是4K*1024=4M,这里有4个页表所以就是16M。并且注意这里是从.org 0x1000开始的,前面4K的空间是留给整个页目录的。

下面是设置四个页目录项的属性,因为只有4个页表,所以相应的就是4个页目录项:

# 下面4 句设置页目录表中的项,因为我们(内核)共有4 个页表所以只需设置4 项。
# 页目录项的结构与页表中项的结构一样,4 个字节为1 项。参见上面113 行下的说明。
# "$pg0+7"表示:0x00001007,是页目录表中的第1 项。
# 则第1 个页表所在的地址 = 0x00001007 & 0xfffff000 = 0x1000;
# 第1 个页表的属性标志 = 0x00001007 & 0x00000fff = 0x07,表示该页存在、用户可读写。
movl $pg0+7,_pg_dir /* set present bit/user r/w */
movl $pg1+7,_pg_dir+4 /* --------- " " --------- */
movl $pg2+7,_pg_dir+8 /* --------- " " --------- */
movl $pg3+7,_pg_dir+12 /* --------- " " --------- */

注意这里+7的意思是,先看PDE 页目录项格式 可知,每个页目录项的0-11位是属性,12-31位才是基址,7对应的二进制是111,也就是P、R/W、U/S位都为1.

好,现在页目录和4个页表都分配好了。接下来进入初始化程序mem_init(main_memory_start, memory_end);在main.c和memory.c中:

static long memory_end = 0;	// 机器具有的物理内存(字节数)。
static long buffer_memory_end = 0; // 高速缓冲区末端地址。
static long main_memory_start = 0; // 主内存(将用于分页)开始的位置。 memory_end = (1 << 20) + (EXT_MEM_K << 10); // 内存大小=1Mb 字节+扩展内存(k)*1024 字节。
memory_end &= 0xfffff000; // 忽略不到4Kb(1 页)的内存数。
if (memory_end > 16 * 1024 * 1024) // 如果内存超过16Mb,则按16Mb 计。
memory_end = 16 * 1024 * 1024;
if (memory_end > 12 * 1024 * 1024) // 如果内存>12Mb,则设置缓冲区末端=4Mb
buffer_memory_end = 4 * 1024 * 1024;
else if (memory_end > 6 * 1024 * 1024) // 否则如果内存>6Mb,则设置缓冲区末端=2Mb
buffer_memory_end = 2 * 1024 * 1024;
else
buffer_memory_end = 1 * 1024 * 1024; // 否则则设置缓冲区末端=1Mb
main_memory_start = buffer_memory_end; // 主内存起始位置=缓冲区末端; // 如果定义了内存虚拟盘,则初始化虚拟盘。此时主内存将减少。参见kernel/blk_drv/ramdisk.c。
#ifdef RAMDISK // 如果定义了内存虚拟盘,则主内存将减少。
main_memory_start += rd_init (main_memory_start, RAMDISK * 1024);
#endif mem_init (main_memory_start, memory_end);

这里结合下图参考:

如果定义了内存虚拟盘,主内存区开始位置main_memory_start就从虚拟盘末端开始,否则就从高速缓冲区末端开始;而主内存区结尾memory_end则为16M。

/* 下面定义若需要改动,则需要与head.s 等文件中的相关信息一起改变 */
// linux 0.11 内核默认支持的最大内存容量是16M,可以修改这些定义以适合更多的内存。
#define LOW_MEM 0x100000 // 内存低端(1MB)。
#define PAGING_MEMORY (15*1024*1024) // 分页内存15MB。主内存区最多15M。
#define PAGING_PAGES (PAGING_MEMORY>>12) // 分页后的物理内存页数。相当于除以4096
#define MAP_NR(addr) (((addr)-LOW_MEM)>>12) // 指定内存地址映射为页号。
#define USED 100 // 页面被占用标志,参见405 行。 static long HIGH_MEMORY = 0; // 全局变量,存放实际物理内存最高端地址。 // 内存映射字节图(1 字节代表1 页内存),每个页面对应的字节用于标志页面当前被引用(占用)次数。
static unsigned char mem_map[PAGING_PAGES] = { 0, }; //// 物理内存初始化。
// 参数:start_mem - 可用作分页处理的物理内存起始位置(已去除RAMDISK 所占内存空间等)。
// end_mem - 实际物理内存最大地址。
// 在该版的linux 内核中,最多能使用16Mb 的内存,大于16Mb 的内存将不于考虑,弃置不用。
// 0 - 1Mb 内存空间用于内核系统(其实是0-640Kb)。
void
mem_init (long start_mem, long end_mem)
{
int i; HIGH_MEMORY = end_mem; // 设置内存最高端。
for (i = 0; i < PAGING_PAGES; i++) // 首先置所有页面为已占用(USED=100)状态,
mem_map[i] = USED; // 即将页面映射数组全置成USED。
i = MAP_NR (start_mem); // 然后计算可使用起始内存的页面号。
end_mem -= start_mem; // 再计算可分页处理的内存块大小。
end_mem >>= 12; // 从而计算出可用于分页处理的页面数。
while (end_mem-- > 0) // 最后将这些可用页面对应的页面映射数组清零。
mem_map[i++] = 0;
}

首先注意PAGING_MEMORY,因为1M以下的内存空间是内核所用,不参与分页,所以主内存大小最多为15M。所以PAGING_PAGES就是主内存大小除以4096(一页就是4096字节)就是内存页面总数。

所以mem_map的作用就是内存页面映射。

这里注意一下MAP_NR(start_mem)这个宏,用主内存区开始地址减去不参与分页的1M空间,得到从可用于分页的内存地址开始的容量,再除以4096(一页就是4096字节)就可以得到页号。

然后计算主内存区的大小,并把mem_map中从主内存区start_mem开始的页号置为0.

到这里为止,内存管理的初始化就算结束了!

Linux0.11内核--内存管理之1.初始化的更多相关文章

  1. Linux0.11内核--内存管理之2.配合fork

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5598451.html ] 在上一篇的fork函数中,首先一上来就调用get_free_page ...

  2. Linux0.11内核--进程调度分析之1.初始化

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5596746.html ] 首先看main.c里的初始化函数main函数里面有个函数是对进程调度 ...

  3. Linux-0.11内核源代码分析系列:内存管理get_free_page()函数分析

    Linux-0.11内存管理模块是源码中比較难以理解的部分,如今把笔者个人的理解发表 先发Linux-0.11内核内存管理get_free_page()函数分析 有时间再写其它函数或者文件的:) /* ...

  4. linux0.11内核源码剖析:第一篇 内存管理、memory.c【转】

    转自:http://www.cnblogs.com/v-July-v/archive/2011/01/06/1983695.html linux0.11内核源码剖析第一篇:memory.c July  ...

  5. linux内核--内核内存管理

    如题目所示,为什么要称作“内核内存管理”,因为内核所需要的内存和用户态所需要的内存,这两者在管理上是不一样的. 这篇文章描述内核的内存管理,用户态的内存管理在以后的文章中讲述. 首先简单的说明一下下面 ...

  6. 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)

    1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ...

  7. Linux内核内存管理算法Buddy和Slab: /proc/meminfo、/proc/buddyinfo、/proc/slabinfo

    slabtop cat /proc/slabinfo # name <active_objs> <num_objs> <objsize> <objpersla ...

  8. Linux内核内存管理架构

    内存管理子系统可能是linux内核中最为复杂的一个子系统,其支持的功能需求众多,如页面映射.页面分配.页面回收.页面交换.冷热页面.紧急页面.页面碎片管理.页面缓存.页面统计等,而且对性能也有很高的要 ...

  9. Linux0.11内核剖析--初始化程序(init)

    1.概述 在内核源代码的 init/目录中只有一个 main.c 文件. 系统在执行完 boot/目录中的 head.s 程序后就会将执行权交给 main.c.该程序虽然不长,但却包括了内核初始化的所 ...

随机推荐

  1. 【转】WPF 给DataGridTextColumn统一加上ToolTip

    源地址:http://dongguojun.iteye.com/blog/1671963 我发现WPF中DataGridTextColumn直接设置它的ToolTipService.Tooltip并不 ...

  2. SharePoint 2013 托管导航 无法被开启的解决办法

    在阅读了园子中霖雨的一片博文<SharePoint 2013 托管导航及相关配置>之后,非常想尝试一下SharePoint 2013 中的这个新功能,但是我的网站集包括样式是从2010升级 ...

  3. grape动态PHP结构(一)——目录结构与配置文件

    一.结构介绍 结构的名字grape,中文名叫葡萄,因为最近一个同事经常带葡萄到公司给我们吃,受到启发想到了这个名字. 1)本结构需要在PHP5.5中运行,如果要在5.4中运行,有些地方就要做些修改 2 ...

  4. 1Z0-053 争议题目解析700

    1Z0-053 争议题目解析700 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 700.Which two statements are true about a duplica ...

  5. WebGIS中基于AGS的画圆查询简析以及通过Polygon来构造圆的算法

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 某个项目需求中需要在前端进行画圆查询,将圆范围上的多边形要素 ...

  6. 20 个看起来很棒的 Web UI 工具包

    程序员们比设计师更需要这些 UI 方面的内容: 1. Mini Reminders Mini Reminders 2. Transluscent UI elements Transluscent UI ...

  7. SpringData —— HelloWorld

    SpringData 简介 优点 简化数据库访问,减少数据访问层的开发量. 支持的数据库类型 支持 NoSQL 存储,如 MongoDB,Redis. 支持关系型数据存储技术,如 jdbc,jpa. ...

  8. null和undefined的一些区别

    读了阮一峰的博客,自己总结一下,便记录一篇博客 在javacript的基本类型中,有2种基本类型,只有1个值,便是null和undefined,都表示的是"无".在一定程度上是相等 ...

  9. Nancy之Forms authentication的简单使用

    一.前言 想必大家或多或少都听过微软推出的ASP.NET Identity技术,可以简单的认为就是一种授权的实现 很巧的是,Nancy中也有与之相类似的技术Authentication,这两者之间都用 ...

  10. 创建javaScript对象的方法

    一.工厂模式 function person (name,age) { var p=new Object(); p.name=name; p.age=age; p.showMessage=functi ...