Chinaunix首页 | 论坛 | 博客
  • 博客访问: 222028
  • 博文数量: 253
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 3
  • 用 户 组: 普通用户
  • 注册时间: 2014-09-21 12:29
文章分类

全部博文(253)

文章存档

2014年(253)

我的朋友

分类: LINUX

2014-09-21 12:52:33

原文地址:编程感悟之Linux 作者:andyhzw

1,Linux 内存管理    

在 linux 系统中,进程的 4GB 内存空间被分为两个部分------用户空间与内核空间。用户空间地址一般分布为 0 ~ 3GB,这样剩下的 3 ~ 4GB为内核空间,用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间虚拟地址。用户进程只有通过系统调用(代表用户进程在内核态执行)等方式才可以访问到内核空间。


            

每个进程的用户空间都是完全独立、互不相关的,用户进程各自有不同的页表。而内核空间是由内核负责映射,它并不会跟着进程改变,是固定的。内核空间地址有自己对应的页表,内核的虚拟空间独立于其他程序。

            

linux 中 1GB的内核地址空间又被划分为物理内存映射区、虚拟内存分配区、高端页面映射区、专用页面映射区和系统保留映射区这几个区域。

    

 一般情况下,物理内存映射区最大长度为 896MB,系统的物理内存被顺序映射在内核空间的这个区域中。当系统物理内存大于 896MB时,超过物理内存映射区的那部分内存被称为高端内存(而为超过物理内存映射的内存通过被称为常规内存),内核在存取高端内存时必须将它们映射到高端页面映射区。

           

 linux保留内核空间最顶部FIXADDR TOP ~ 4GB的区域作为保留区。

紧接着最顶端的保留区以下的一段区域为专用页面映射区(FIXADDR_START ~ FIXADDR TOP),它的总尺寸和每一页的用途有 fixed_address 枚举结构在编译时预定义,用 __fix_to_virt(index)可获取专用区内预定义页面的逻辑地址。其开始地址和结束地址宏定义如下;

#define    FIXADDR_START                     (FIXADDR_TOP - __FIXADDR_SIZE)

#define    FIXADDR_TOP                        ((unsigned long)__FIXADDR_TOP)

#define    __FIXADDR_TOP                    0xfffff000


接下来,如果系统配置了高端内存,则位于专用页面映射区之下的就是一段高端内存映射区,其起始地址为 PKMAP_BASE,定义如下:

#define    PKMAP_BASE                          ((FIXADDR_BOOT_START - PAGE_SIZE*(LAST_PKMAP + 1)) & PMD_MASK)

其中涉及的宏定义如下:

#define    FIXADDR_BOOT_START          (FIXADDR_TOP - __FIXADDR_BOOT_SIZE)

#define    LAST_PKMAP                           PTRS_PER_PTE

#define    PTRS_PER_PTE                       512

#define    PMD_MASK                              (~(PMD_SIZE - 1))

#define    PMD_SIZE                               (1UL << PMD_SHIFT)

#define    PMD_SHIFT                             21


在物理区和高端映射区之间为虚拟内存分配区(VMALLOC_START ~ VMALLOC_END),用于 vmalloc() 函数,它的前部与物理内存映射区有一个隔离带,后部与高端映射区也有一个隔离带。


当系统物理内存超过 4GB 时,必须使用CPU 的扩张分页(PAE)模式所提供的64位页目录项才能取到 4GB以上的物理内存,这需要 CPU 的支持。加入了 PAE 功能的 Intel Pentium Pro 及其后的 CPU 允许内存最大可配置到 64 GB,具备 36 位物理地址空间寻址能力。


由此可见,在 3 ~ 4 GB 之间的内核空间中,从低地址到高地址依次为:物理内存映射区----- 隔离带----- vmalloc虚拟内存分配器----- 隔离带----- 高端内存映射区----- 专用页面映射区----- 保留区。



2,kmalloc()

void   *kmalloc(size_t size,int flags);

给 kmalloc() 的第一个参数是要分配的块的大小,第二个参数为分配标志,用于控制kmalloc() 的行为。


最常用的分配标志是 GFP_KERNEL,其含义是在内核空间的进程中申请内存。kmalloc()的底层依赖 __get_free_pages()实现,分配标志的前缀 GFP 正好是这个底层函数的缩写。使用 GFP_KERNEL 标志申请内存时,若暂时不能满足,则进程会睡眠等待页,即会引起阻塞,因此不能再中断上下文或持有自旋锁的时候使用 GFP_KERNEL 申请内存


3,slab

slab不是要代替 __get_free_pages(),其在最底层仍然依赖于 __get_free_pages(),slab在底层每次申请 1 页或多页,之后再分隔这些页为更小的单元进行管理,从而节省了内存,也提高了 slab 缓冲对象的访问效率。
阅读(313) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~