【版权所有,转载请注明出处。出处: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. android ANR产生原因和解决办法

    转自http://blog.sina.com.cn/s/blog_618199e60101kvbl.html ANR (Application Not Responding) ANR定义:在Andro ...

  2. Android实现不重复启动APP的方法

    转载博客:http://blog.sina.cn/dpool/blog/s/blog_5de73d0b0102vpai.html?utm_source=bshare&utm_campaign= ...

  3. android标题栏上面弹出提示框(二) PopupWindow实现,带动画效果

    需求:上次用TextView写了一个从标题栏下面弹出的提示框.android标题栏下面弹出提示框(一) TextView实现,带动画效果,  总在找事情做的产品经理又提出了奇葩的需求.之前在通知栏显示 ...

  4. 记录一则ORA-00054,ORA-00031解决过程

    生产环境:AIX 5.3 + Oracle 10.2.0.5 任务要求:普通表改造分区表,历史数据不要   这个需求很简单: pl/sql导出建表语句,依次修改成分区的建表语句,注意将索引修改成本地索 ...

  5. struts2学习笔记--动手搭建环境+第一个helloworld项目

    在Myeclipse中已经内置好了struts2的环境,但是为了更好的理解,这里自己从头搭建一下: 前期准备:下载struts2的完整包,下载地址:https://struts.apache.org/ ...

  6. 实用的SQL语句

    行列互转 ) select * from test2 --列转行 select id,name,quarter,profile from test2 unpivot ( profile for qua ...

  7. (一)FlexViewer之整体框架解析

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.FlexViewer简介 FlexViewer框架为Esri提供的 ...

  8. IO多路复用之select总结

    1.基本概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程.IO多路复用适用如下场合: (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/ ...

  9. SQL Server基础之《视图的概述和基本操作》

     数据库中的视图是一个虚拟表.同真实的表一样,视图包含一系列带有名称的列和行数据,行和列数据用来自由定义视图和查询所引用的表,并且在引用视图时动态产生.本篇将通过一些实例来介绍视图的概念,视图的作用, ...

  10. Win10计算器在哪里?三种可以打开Win10计算器的方法图文介绍

    全新的windows10系统带来了不少新的特性和改变,其中win10的计算器位置就发生了很多的变化,导致很多网友们都以为win10计算器不见了,那么,win10计算器在哪里?如何打开?针对此问题,本文 ...