Linux0.11内核--内存管理之1.初始化
【版权所有,转载请注明出处。出处: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.初始化的更多相关文章
- Linux0.11内核--内存管理之2.配合fork
[版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5598451.html ] 在上一篇的fork函数中,首先一上来就调用get_free_page ...
- Linux0.11内核--进程调度分析之1.初始化
[版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5596746.html ] 首先看main.c里的初始化函数main函数里面有个函数是对进程调度 ...
- Linux-0.11内核源代码分析系列:内存管理get_free_page()函数分析
Linux-0.11内存管理模块是源码中比較难以理解的部分,如今把笔者个人的理解发表 先发Linux-0.11内核内存管理get_free_page()函数分析 有时间再写其它函数或者文件的:) /* ...
- linux0.11内核源码剖析:第一篇 内存管理、memory.c【转】
转自:http://www.cnblogs.com/v-July-v/archive/2011/01/06/1983695.html linux0.11内核源码剖析第一篇:memory.c July ...
- linux内核--内核内存管理
如题目所示,为什么要称作“内核内存管理”,因为内核所需要的内存和用户态所需要的内存,这两者在管理上是不一样的. 这篇文章描述内核的内存管理,用户态的内存管理在以后的文章中讲述. 首先简单的说明一下下面 ...
- 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)
1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ...
- Linux内核内存管理算法Buddy和Slab: /proc/meminfo、/proc/buddyinfo、/proc/slabinfo
slabtop cat /proc/slabinfo # name <active_objs> <num_objs> <objsize> <objpersla ...
- Linux内核内存管理架构
内存管理子系统可能是linux内核中最为复杂的一个子系统,其支持的功能需求众多,如页面映射.页面分配.页面回收.页面交换.冷热页面.紧急页面.页面碎片管理.页面缓存.页面统计等,而且对性能也有很高的要 ...
- Linux0.11内核剖析--初始化程序(init)
1.概述 在内核源代码的 init/目录中只有一个 main.c 文件. 系统在执行完 boot/目录中的 head.s 程序后就会将执行权交给 main.c.该程序虽然不长,但却包括了内核初始化的所 ...
随机推荐
- php的mysql\mysqli\PDO(二)mysqli
原文链接:http://www.orlion.ga/1147/ mysqli有面向对象风格和面向过程风格,个人感觉还是用面向对象风格比较好(毕竟是面向对象) 1.mysqli::_construct( ...
- vue小总结
以下是我在使用vue过程中自己对vue的一些小总结,希望对学习vue的亲们能有所帮助: 1. http的post请求: this.$http({url: '/someUrl', method: ' ...
- c++堆
c++ reference: http://www.cplusplus.com/reference/algorithm/make_heap/ heap并不属于STL容器组件,它分为 max heap ...
- .Net 转战 Android 4.4 日常笔记(9)--常用组件的使用方法[附源码]
经过两天的学习,把常用的组件都学习了一遍,并做成了App 学习可能真没有捷径,跟学习html有点类似,都是一个控件一个控件学习并使用,最后拼凑成一个系统 链接:http://pan.baidu.com ...
- Android音视频之MediaPlayer音视频播放
前言: 昨天总结了视频录制,今天来学习一下视频的播放,Android的视频播放主要采用MediaPlayer类. MediaPlayer介绍 MediaPlayer类可用于控制音频/视频文件或流的播放 ...
- TextView+Fragment实现底部导航栏
前言:项目第二版刚上线没多久,产品又对需求进行了大改动,以前用的是左滑菜单,现在又要换成底部导航栏,于是今天又苦逼加班了.花了几个小时实现了一个底部导航栏的demo,然后总结一下.写一篇博客.供自己以 ...
- Android调用Jni,非常简单的一个Demo
step1:创建一个android项目 Project Name:jnitest Build Target: Android 1.6 Application Nam ...
- 改用C++生成自动化数据表
改用C++生成自动化数据表 前面的文章中,我们讨论了使用一个基于.NET的第三方程序库来从程序中来生成数据表.在我看来,这整个思路是非常有用的,例如为显示测试结果.我经常会自己在博客中尝试各种像这样的 ...
- VNC的安装和配置
服务端环境:CentOS 6.7 客户端环境:Windows 7 1.服务器安装VNC服务端 2.编辑vnc配置文件 3.设定VNC的密码 4.查看vnc的会话信息 5.客户端测试vnc连接 Refe ...
- java操作数据库增删改查的小工具2--TxQueryRunner
当涉及到多表查询时,如数据库中有两张表分别为t_person和t_address,表结构如下: 其中t_person的外键为t-address的主键aid, 新建两个javaBean类,Person ...