现在的位置: 首页 > 综合 > 正文

Linux上的内存管理

2017年12月25日 ⁄ 综合 ⁄ 共 5279字 ⁄ 字号 评论关闭
 Linux上的内存管理 [ FAQ ]

Q:什么是mm?
A:这可不是meimei哦!指的是内存管理(memory management)。是内核中的一套软件机制,用于有效管理内存的使用。

Q:内存管理的软件实现了什么功能?
A:1) 进程的保护,即进程向内存管理请求的物理页,由内存管理系统负责,不会将同一物理页(共享页面除外)映射到不同进程的虚地址空间。
?? 2) 虚存的实现,允许所有进程使用的内存总量大于实际物理内存总量。

Q:MMU与TLB的功能?
A:TLB是MMU的核心部件,用于实现页表转换,将虚拟地址转换成实地址。
?? 对i386而言,MMU会根据CR3中的地址(注意,这个地址是物理地址)指定的页表目录一级一级去查询,在下面情况会抛出缺页中断:
* 页目录为空(NULL)
* 页表项为空,或者对应物理页不再内存中。
* 权限不满足,比如写带只读属性的页;用户态的代码访问特权属性的页。

Q:什么是cache?
A: 对于硬件,指的是一块SRAM,由于它的访问速度比较快,所以将经常访问的数据放在这块SRAM中,必要时才写回到主存储器中,这样可以提高CPU的使用 效率;对于软件,指的是主存储器中的一块空间(即缓冲区),当CPU与慢速IO进行交换数据时,先将数据放在这块内存空间中,等系统空闲时,才真正与IO 进行数据交换。比如说,当用户请求将一块数据写入磁盘时,内核会先将数据放在缓冲区中,等到系统空闲或真正有必要马上就写数据时,内核才会执行磁盘操作, 将数据写到磁盘上。在读内核源代码时,经常会看到"xx_cache_xx"之类的字眼,其实基本上都是内存缓冲区。

Q:什么是COW?
A: 我第一次看它,还以为是“母牛”呢!它实际上指的是“写时拷贝(Copy On Write)”,其主要目的是提高内存管理的效率。当内核创建进程时,为其分配的虚拟页面并没有被真正映射到物理内存上,而是被映射到一块不可写(页表项 上的标志为“只读”)的内存上(对于子进程,映射的是其父进程的物理空间),当该进程要往里面写东东时,就会产生一个页面异常。在异常处理函数中会将这块 虚拟页面映射到它应该映射的物理页面上。

Q:什么是伙伴(buddy)系统?
A:用伙伴算法(一般的数据结构书中都有讲解)管理物理内存的系统。它分配的物理内存大小都是页(4K)的整数倍。

Q:什么是slab分配器?
A: 指的是内存管理单元先向伙伴系统申请一块较大的物理内存(批发),然后再将其肢解成许多小碎片分配给需要的进程(零售)。当有的进程需要分配一小块内存 (一般都小于一个页面,或不是页的整数倍),用来存放某个数据结构(如vm_area_struct),都可以向slab申请零售。

Q:什么是足印(footprint)?
A:足印(footprint)是指要完成某个工作,需要访问的缓存(一般指L1 DATA CACHE 或TLB)项的个数。
例 如,完成的工作是:访问B. 但要访问B对象首先必须从A对象中获得B的指针,如果A,B在一个页面中,那么只需要在TLB踏上一个"足印"就可以了,否则需要踏上两个“足印”;进一 步的,如果A,B对象在同一个16或者32个字节对齐的空间里,那么在L1 DATA CACHE里只需要踏上一个足印就可以了。一般完成某项工作在各种缓存上的足印越少,引起缓存失去命中的可能性就越小,缓存和内存之间的交互就越小,因此 访问速度就越快。

Q:什么是active_mm?
A:active_mm表示当前与CPU关联的分页结构,对于普通进程,它等于mm,对于内核线程,它等于上一次用户进程的mm。

Q:什么是e820?
A:INT15的0xe820(=AX)是BIOS调用获取系统物理内存的配置表(e820map)。

Q:源代码中的gfp_xxx,gfp是什么的缩写?
A:是Get Free Page。

Q:cache_cache里的slab的大小是不是一个page?
A: cache_cache是专用slab管理器队列的头,在 kmem_cache_sizes_init() 的过程中cache_cache->gfporder应该一直都是0,所以是一个page;而这个队列中的各个slab管理器 (kmem_cache_t)的gfporder都不一定等于0,所以不一定是一个page。

Q:kmalloc(size)返回的是物理地址,还是虚拟地址,为什么?
A:是虚拟地址,因为该地址是经过映射后的地址。

Q:怎样得到kmalloc分配的内存地址的实际物理地址?
A: kmalloc分配了一段物理上连续的内存空间,可以通过virt_to_phys()将返回的虚地址转换为一个物理地址,或者通过 virt_to_bus() 转换为一个IO总线地址。在2.6的内核中,直接采用addr-PAGE_OFFSET就可以了。在采用此类方式时,请查阅你的内核源码中的实际实现(此方式同样适合__get_free_pages)。

Q:malloc()在内核中对应的函数是什么?
A:用户程序中使用的malloc是Glibc包装过的函数,它通过brk系统调用分配虚存给用户。

Q:分配虚存空间有了mmap和munmap为什么还需要sys_brk?
A:其实,brk调用的系统调用是sys_brk,他通过mmap进行实现(可以这样认为)。但是,对于用户调用的malloc,free等函数,是由 glibc实现的。即:库首先通过brk向系统申请一块大的虚存块,然后,这块内存由库进行管理,就是堆的管理由库实现。实际上,调用brk的目的无非是告诉内核这块内存的引用是合法,只有这样,缺页中断才能进行下去。如果没有这种机制,则可能导致应用程序对应的虚存块太多,使系统性能不佳。如果堆的管理在应用层进行,则没有这种坏处。

Q:请教一个术语--“堆”。
A:堆和栈合起来才是堆栈,堆是从低到高扩展的,栈是从高到低扩展的。局部变量都在栈里,通过malloc分配的内存都是在堆里的。

Q:malloc在程序结束不free会怎样?
A:进程退出后,与进程相关的所有资源都被内核无条件释放,进程一般不会在内核中造成资源溢漏。
malloc()基于进程的虚存映射区域,进程退出后,随着虚存映射的撤消,它所占用的物理页面也随之释放。

A:page->age与__PAGE_OLD标志位有何关系?
Q:和交换有关。
PAGE_OLD实际上是cpu自己设定的,每次被访问的页面会被cpu设为~PAGE_OLD,age是linux设定的,初始根据PAGE_OLD,然后不断改动。

A:我想阅读mm部分的源代码,从哪个函数入手?
Q:page_alloc.c和slab.c是内核用于分配内存的,可以先看。

Q:
 1.linux在刚刚启动后,允许使用的最大内存连续空间是多少?
 2./proc/buddyinfo里后面的这些数字怎么解释
 node 0 ,zone normal 72 31 16 19 25 22 10 3 1 0 5

 当我加载一个模块,包含语句 addr = get_free_pages(GFP_KERNEL,5);
 
 结果如下:
 node 0 ,zone normal 83 45 21 21 24 23 10 2 1 0 5
 
 当卸载该模块后,free_pages(addr,5);

 结果如下:
 node 0 ,zone normal 55 43 22 22 24 24 10 2 1 0 5

A:

83 45 21 21 24 23 10 2 1 0 5

是不是这样理解:
一个页面一组: 有83组
二个页面一组: 有45组

...

10个页面一组: 有5组

从83->5是按照 order从0->10排列的?

Q: vmalloc()函数分配内存的虚拟地址从3G+high_memory+hole_8M开始其中high_memory为实际物理内存, hole_8M为8M的隔离带为什么要有8M的隔离带? 这样岂不是很浪费虚拟地址空间吗?另外分配的内存块之间有一个4K的隔离页,这样是不是也很浪费虚拟地址空间?

A: Linux用这些空洞来检测存储器读写越界故障,当然空洞越大出现破坏性故障的可能性就越小,8M的空洞正好用两个页目录项来标记,4K的空洞用一个页表项来标记,由于一般物理内存远小于线性地址空间,因此这种浪费是微不足到的。

Q: 那为何用两个页目录表,用一个不行吗?甚至用一个4k的页表不就够了吗?加一个空洞,是不是利用了页保护的属性?如果越界的话,不论空洞的大小都应该产生 异常,对吗?不明白空洞越大越安全的原因另外3G+phymem+8M给vmaloc剩下的空间不多了,如果实际物理内存接近1G的话,是不是 vmaloc函数就不能使用了?我感觉空间并不充裕。

A: 隔离带的大小是任意的,但如果隔离带不够大的话,有可能会被故障代码跨过引起破坏。当内核有代码引用到这些隔离带的地址时,这些地址对应的页目录项或页表 项由于被标记为”不存在”,就会产生页故障,这样就可以准确定位故障所在。如果物理内存非常大,造成内核虚拟空间不足时,可以减小内核的起始线性地址,通 过减小用户程序的虚拟地址空间来增大内核的虚拟空间。如果物理内存超过2G,可通过一个内核补丁big Physical Memory for IA-32将应用程序与内核的页目录分开,尽管这样还是只能管理3.8G,如果物理内存还要大,就要使用64位的体系了。

==============================================================================================================
a. 要注意kmalloc最大只能开辟128k-16,16个字节是被页描述符结构占用了。
b. 内存映射的I/O口,寄存器或者是硬件设备的RAM(如显存)一般占用F0000000以上的地址空间。在驱动程序中不能直接访问,要通过kernel函数vremap获得重新映射以后的地址。
c. 另外,很多硬件需要一块比较大的连续内存用作DMA传送。这块内存需要一直驻留在内存,不能被交换到文件中去。但是kmalloc最多只能开辟128k的内存。
  这可以通过牺牲一些系统内存的方法来解决。
  具体做法是:比如说你的机器由32M的内存,在lilo.conf/grub的启动参数中加上mem=30M,这样linux就认为你的机器只有30M的内存,剩下的2M内存在vremap之后就可以为DMA所用了。
  请记住,用vremap映射后的内存,不用时应用unremap释放,否则会浪费页表。

Paging model in general

paging unit转换linear address为physical address,一组linear address聚集为page,linear addresses本质上是连续的,paging unit将这些连续的内存映射为物理地址称为page frames。page unit将内存划分为大小固定的page frame成为可见, 亦即page。

1) Linux将整个4G线性地址空间分为用户空间和内核空间两部分,而内核地址空间又被划分为“物理内存区”,“虚拟内存分配区”, “高端页面映射区”,“专用页面映射区”, “系统保留映射区”几个区域。

2) 在标准配置下,物理区最大长度为896M。系统的物理内存被顺序映射在物理区中,在支持扩展页长(PSE)和全局页面(PGE)的机器上,物理区使用4M 页面并作为全局页面来处理(呵呵,没有白白计算)。当系统物理内存大于896M时,超过物理区的那部分内存称为高端内存,低端内存和高端内存用 highmem_start_page变量来定界,内核在存取高端内存时必须将它们映射到“高端页面映射区”。

3) Linux保留内核空间最顶部128K区域作为保留区,紧接保留区以下的一段区域为专用页面映射区,它的总尺寸和每一页的用途由 fixed_address枚举结构在编绎时预定义,用__fix_to_virt(index)可获取专用区内预定义页面的逻辑地址。在专用页面区内为 每个CPU预定义了一张高端内存映射页,用于在中断处理中高端页面的映射操作。

4) 距离内核空间顶部32M,长度为4M的一段区域为高端内存映射区,它正好占用1个页帧表所表示的物理内存总量, 它可以缓冲1024个高端页面的映射。在物理区和高端映射区之间为虚存内存分配区, 用于vmalloc()函数,它的前部与物理区有8M隔离带, 后部与高端映射区有8K(2.4为4k?)的隔离带。

5) 当系统物理内存超过4G时,必须使用CPU的扩展分页(PAE)模式所提供的64位页目录项才能存取到4G以上的物理内。在PAE模式下, 线性地址到物理地址的转换使用3级页表,第1级页目录由线性地址的最高2位索引, 每一目录项对应1G的寻址空间,第2级页目录项以9位索引, 每一目录项对应2M的寻址空间, 第3级页目录项以9位索引,每一目录项对应4K的页帧。除了页目录项所描述的物理地址扩展为36位外,64位和32位页目录项结构没有什么区别。在PAE 模式下,包含PSE位的中级页目录项所对应的页面从4M减少为2M。

抱歉!评论已关闭.