`
xitong
  • 浏览: 6203278 次
文章分类
社区版块
存档分类
最新评论

linux内存布局的内核实现--用户空间的映射方式

 
阅读更多

引用牛人的一个表格(本人绘制能力实在有限,引自:http://blog.csdn.net/absurd/archive/2006/06/19/814268.aspx)

Linux的内存模型,一般为:

地址

作用

说明

>=0xc000 0000

内核虚拟存储器

用户代码不可见区域

Stack(用户栈)

ESP指向栈顶

空闲内存

>=0x4000 0000

文件映射区

空闲内存

Heap(运行时堆)

通过brk/sbrk系统调用扩大堆,向上增长。

.data、.bss(读写段)

从可执行文件中加载

>=0x0804 8000

.init、.text、.rodata(只读段)

从可执行文件中加载

保留区域

这 里的数据是怎么得到的呢?我们一般用到的malloc是从哪里分配的内存呢?为什么从这里分配呢?实际上malloc是c库的内存分配管理函数,其种种弊 端使得人们不太喜欢它,从而使人们写出很多自己的内存分配函数实现,不管哪种实现在linux下都是调用系统调用brk实现的,在内核当中就是 sys_brk。
我们知道,一个进程的所有的地址空间是按照区域组织的,每个区域都是一个vm_area_struct的结构体,每个结构体内部的地址是连续的,而不同的 结构体之间可能留有空洞,既然这样,不管brk出来的空间还是其它,比如说映射得到的空间都是vm_area_struct的表现,那么为何0x4000 0000是个分界点呢?之上的就是映射空间而之下的就是brk空间即堆空间呢?我们只有看一下这两种实现的内核源代码,分别是:sys_mmap2和 sys_brk。在可能具体代码之前首先要明白一件事就是:为了管理方便,所有的vm_area_struct的首地址和末地址都是页对齐的。好了,现在 可以看相关的代码了(我省略了很多不相关的代码,那些很重要,但不是这篇文章要说的):
asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
unsigned long fd, unsigned long pgoff)
{
...
down_write(&mm->mmap_sem);
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
... return error;
}
unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
unsigned long len, unsigned long prot,
unsigned long flags, unsigned long pgoff)
{
len = PAGE_ALIGN(len); //对齐了长度,使得首地址加上长度也是页对齐
...
addr = get_unmapped_area(file, addr, len, pgoff, flags);//此函数内部对齐了addr
if (addr & ~PAGE_MASK) //如果现在都不是页对齐的,那么返回的肯定是错误码,返回之
return addr;
...
}
unsigned long get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
unsigned long (*get_area)(struct file *, unsigned long,
unsigned long, unsigned long, unsigned long);
get_area = current->mm->get_unmapped_area;
if (file && file->f_op && file->f_op->get_unmapped_area)
get_area = file->f_op->get_unmapped_area;
addr = get_area(file, addr, len, pgoff, flags);
if (IS_ERR_VALUE(addr)) //这个宏很有意思,值得研究,如果返回真,那么addr就是一个错误码了,返回之
return addr;
if (addr > TASK_SIZE - len)//跨到了内核空间,返回错误码
return -ENOMEM;
if (addr & ~PAGE_MASK) //到此还是非页对齐,返回错误码
return -EINVAL;
return arch_rebalance_pgtables(addr, len);
}
在很多平台get_unmapped_area就是mm/mmap.c中定义的arch_get_unmapped_area
unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
unsigned long len, unsigned long pgoff, unsigned long flags)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
unsigned long start_addr;
if (len > TASK_SIZE) //跨越内核空间边界
return -ENOMEM;
if (flags & MAP_FIXED) //如果有MAP_FIXED标志那么就直接返回addr,其实在MAP_FIXED
设置的前提下,用户传入的addr必须是页对齐的,若不是则出错,这里将addr返回,如果addr不是页对齐,那么get_unmapped_area中的if (addr & ~PAGE_MASK)验证将无法通过。
return addr;
if (addr) { //如果用户提供了addr则优先考虑用户提供的addr,但是不保证这个addr就是最后返回的addr。下面还是要说一下这里的要点。
addr = PAGE_ALIGN(addr);
vma = find_vma(mm, addr);
if (TASK_SIZE - len >= addr &&
(!vma || addr + len vm_start))//addr的地址没有被映射,而且空洞足够我们这次的映射,那么返回addr以准备这次的映射
return addr;
}
if (len > mm->cached_hole_size) { //我们需要的长度大于当前的vm区域间的空洞
start_addr = addr = mm->free_area_cache;//从上次的查询位置开始
} else { //需要的长度小于当前空洞,为了不至于时间浪费,那么从0开始搜寻,这里的
搜寻基地址TASK_UNMAPPED_BASE很重要,用户mmap的地址的基地址必须在TASK_UNMAPPED_BASE之上,但是一定这样严格 吗?看上面的if (addr)判断,如果用户给了一个地址在TASK_UNMAPPED_BASE之下,映射实际上还是会发生的。
start_addr = addr = TASK_UNMAPPED_BASE;
mm->cached_hole_size = 0;//空洞大小从0开始,以后逐渐增加
}
full_search:
for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
if (TASK_SIZE - len free_area_cache,因此下面的if判断当然可以通过,所以从新开始,从TASK_UNMAPPED_BASE开始搜索,若开 始地址就是TASK_UNMAPPED_BASE,那么肯定出错了
if (start_addr != TASK_UNMAPPED_BASE) {
addr = TASK_UNMAPPED_BASE;
start_addr = addr;
mm->cached_hole_size = 0;
goto full_search;
}
return -ENOMEM;
}
if (!vma || addr + len vm_start) {
mm->free_area_cache = addr + len;
return addr;
}
if (addr + mm->cached_hole_size vm_start)
mm->cached_hole_size = vma->vm_start - addr;//更新当前空洞长度
addr = vma->vm_end;
}
}
TASK_UNMAPPED_BASE就是mmap时的地址限制,我们看一眼TASK_UNMAPPED_BASE的定义:
#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3))
就是1g的位置,即0x40000000的位置。可是这种限制并不是强制的,如果我做以下程序:
#include <sys><br>int main() <br>{ <br> char * buf; <br> buf = (char*)mmap(0x30000000,1024,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,-1,0); <br>} <br>用gdb调试可以看出buf的地址确实是0x30000000,也就是0x40000000以下的地址,这么说,linux的内存布局并不是强制用户执行 的,把策略完全留给用户,内核真是惯坏了用户,仅仅提供给用户一个建议而已。说完了sys_mmap2的逻辑简单谈谈sys_brk就可以了,每个 mm_struct都有一个brk字段,这个字段记录着用户堆的位置,每次用户调用brk的时候sys_brk都回扩展或者缩减堆空间,本质上也是映射 vm_area_struct结构,而brk的限制是在elf文件里面确定的,elf文件格式规定了堆,bss变量,栈在哪里,但是这些都不是强制的。 <br>通过这里的分析,我们看到,linux的内存布局是非常灵活的,看到很多人问怎么把内核空间扩展成2g,正如windows一样,这个其实是很简单的,将 sys_mmap和sys_brk的检测条件一改就可以,实际不用改动任何这些代码,仅仅改TASK_SIZE宏就可以了,用户空间分配内存的唯一限制就 是不能超越内核边界,别的限制就不是那么重要了,那只是用户程序自己的事情,而分配用户内存就是映射vm_area_struct结构,进一步仅仅在 sys_mmap2和sys_brk里会有如此动作,因此扩展内核空间的任务就会很简单,那么内核的内存怎么映射呢?这就完全是内核的事情了,无非有两种 方式,一种是一一映射,另一种是非线性映射(包括类似高端内存的临时映射方式)。</sys>

分享到:
评论

相关推荐

    疯狂内核之——Linux虚拟内存

    1.7 高端内存内核映射 50 1.8.1 永久内存映射 50 1.8.2 临时内核映射 55 第二章 内核级内存管理系统 58 2.1 Linux页面管理 58 2.1.1 NUMA架构 61 2.1.2 内存管理区 62 2.2 伙伴系统算法 65 2.2.1 数据结构 66 2.2.2 ...

    深入分析Linux内核源码.chm

    1.3走进Linux内核 1.4 分析Linux内核的意义 1.5 Linux内核结构 1.6 Linux内核源代码 1.7 Linux内核源代码分析工具 第二章 Linux运行的硬件基础 2.1 i386的寄存器 2.2 内存地址 2.3 段机制和描述符 2.4 分页机制 2.5 ...

    深入分析Linux内核源码

    6.1.2 内核空间和用户空间 6.1.3 虚拟内存实现机制间的关系 6.2 Linux内存管理的初始化 6.2.1 启用分页机制 6.2.2 物理内存的探测 6.2.3 物理内存的描述 6.2.4 页面管理机制的初步建立 6.2.5页表的建立 ...

    Linux虚拟地址空间布局

    在Linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3,而Windows系统为2:2(通过设置Large-Address-Aware Executables标志也可为1:3)。这并不意味着内核使用那么多物理内存,仅表示它可支配这部分地址空间,...

    基于ARM CPU的Linux物理内存管理

    第一部分介绍内存布局的演进。这样方便理解为什么内存管理中需要虚拟地址,物理内存和访问保护。 第二部分介绍在ARMC CPU上是如何支持内存管理的。...第四部分在源代码中介绍Linux内核是如何实现物理内存管理的。

    C语言内存精讲,让你彻底明白C语言的运行机制!

    8. Linux下C语言程序的内存布局(内存模型) 9. Windows下C语言程序的内存布局(内存模型) 10. 用户模式和内核模式 11. 栈(Stack)是什么?栈溢出又是怎么回事? 12. 一个函数在栈上到底是怎样的? 13. 函数调用...

    清华大学Linux操作系统原理与应用

    4.1.2 虚拟内存实现机制间的关系 69 4.2 进程用户空间的管理 70 4.2.1 进程用户空间的描述 71 4.2.2 进程用户空间的创建 74 4.2.3 虚存映射 76 4.2.4 进程的虚存区示例 76 4.2.5 与用户空间相关的系统调用 78 4.3 请...

    新版Android开发教程.rar

    � 采用了对有限内存、电池和 CPU 优化过的虚拟机 Dalvik , Android 的运行速度比想象的要快很多。 � 运营商(中国移动等)的大力支持,产业链条的热捧。 � 良好的盈利模式( 3/7 开),产业链条的各方:运营商、...

    《精通Linux 设备驱动程序开发》.(Sreekrishnan).pdf

    第19章 用户空间的驱动程序386 19.1 进程调度和响应时间387 19.1.1 原先的调度器387 19.1.2 o(1)调度器387 19.1.3 cfs388 19.1.4 响应时间388 19.2 访问i/o区域390 19.3 访问内存区域393 19.4 用户...

    精通LINUX设备驱动程序开发

    367 第18章 嵌入式linux 369 18.1 挑战 369 18.2 元器件选择 370 18.3 工具链 371 18.4 bootloader 372 18.5 内存布局 374 18.6 内核移植 375 18.7 嵌入式驱动程序 376 18.7.1 闪存 377 18.7.2 uart 377 ...

    LINUX系统管理白皮书

    本书同时收录了Linux领域两位领导人物的作品—相当于“Linux 文档项目”的一个印刷版本,展示了Linux 核心概念及其基本结构。对于面向所有主流Linux子系统的支持与管理任务,本书都进行了恰到好处的讲解。涵盖的主题...

    linux_kernel_cves:跟踪linux内核的CVE

    这是一个用于跟踪上游linux内核中CVE的简单项目。 单个发行版(RHEL,Debian,Ubuntu等)通常会很好地跟踪自己内核的CVE,但上游内核缺少此信息。 该项目旨在帮助解决这一空白。 输出是通过一组尚未经过全面测试或...

    [小e笔记]之10gR2+redhat5.3+32bit+RAC安装

    1.2 虚拟机磁盘布局 3 1.3 RAC数据库环境 3 2 配置第一个虚拟机 4 2.1虚拟机的配置 4 2.2 配置共享存储 16 2.3 安装Redhat5.3 20 2.4 安装Vmware工具 35 2.5 同步客户OS与主机OS的时间 35 2.6创建oracle用户...

Global site tag (gtag.js) - Google Analytics