内存管理

进程与内存

进程的5种不同的数据区

代码段

代码段是用来存放可执行文件的操作指令,也就是说它是可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只允许读取操作,儿不允许写入(修改)操作--它是不可写的。

数据段

数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。

BSS段

BSS段包含了程序未初始化的全局变量,在内存中bss段全部置零。

堆(heap)

堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)

栈是用户存放程序临时创建的局部变量,也就是说我们函数括弧'{ }'中定义的变量,(但不包括static声明的变量,static意味着在数据段中存放变量),除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数返回值也会被存放回栈中。由于栈的先进后出的特点,所以栈特别方便用来保存/恢复现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

进程内存的分配与回收

创建进程fork()、程序载入execve()、映射文件mmap()、动态内存分配malloc()/brk()等进程相关操作都需要分配内存给进程。不过这时进程申请和获得的还不是实际内存,而是虚拟内存准确的说是"内存区域".进程对内存区域的分配最终都会归结到do_map()函数上来(brk调用被单独一系统调用实现,不用do_map()),内核使用do_map()函数创建一个新的线性地址区间。但是说该函数创建了一个新VMA并不准确,因为如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限的话,那么两个区间将合并为一个。如果不能合并,那么就确实需要创建一个新的VMA了。但无论哪种情况,do_map()函数都会将一个地址区间加入到进程的地址区间中--无论是扩展已存在的内存区域还是创建一个新的区域。同样,释放一个内存区域应使用函数do_ummap(),它会销毁对应的内存区域。

如何由虚变实

从上面已经看到进程所能直接操作的地址都为虚地址。当进程需要内存时,从内核获得的仅仅是虚拟的内存区域,而不是实际的物理地址,进程并没有获得物理内存(物理页面),获得的仅仅是对一个新的线性地址区间的使用权。实际物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请求页机制”产生“缺页”异常,从而进入分配实际页面的例程。该异常时虚拟内存机制赖以存在的基本保证--它会告诉内核去真正为进程分配物理页面,并建立对应的页表,这之后虚拟地址才实实在在的映射到了系统的物理内存上。(当然,如果页被换出到磁盘,也会产生缺页异常,不过这时不用再建立页表了)。这种请求页机制把页面的分配推迟到不能再推迟为止,并不急于把所有的事情都一次做完。之所以能这么做是利用了内存访问的"局部性原理",请求页带来的好处是节约了空闲内存,提高了系统的吞吐率。

物理内存管理

Linux内核管理物理内存是通过分页机制实现的,它将整个内存划分成无数个4k(在i386体系结构中)大小的页,从而分配和回收内存的基本单位便是内存页了。利用分页管理有助于灵活分配内存地址,因为分配时不必要求有大块的连续内存,系统可以东一页、西一页的凑出所需要的内存供进程使用。虽然如此,但是实际上系统使用内存时还是倾向于分配连续的内存块,因为分配连续内存时,页表不需要更改,因此能降低TLB的刷新率(频繁刷新会在很大程度上降低访问速度)。

鉴于上述需求,内核分配物理页面是为了尽量减少不连续的情况,采用了"伙伴"关系来管理空闲页面。Linux中空闲页面的组织和管理利用了伙伴关系,因此空闲页面分配时页需要遵循伙伴关系,最小单位只能是2的幂倍页面大小。内核中分配空闲页面的基本函数是get_free_page/get_free_pages,它们或是分配单页或是分配指定的页面(2,4,8,...,512页)。注意:get_free_page是在内核中分配内存,不同于malloc在用户空间中分配,malloc利用堆动态分配,实际上是调用brk()系统调用,该调用的作用是扩大或缩小进程堆空间。如果现有的内存区域不够容纳堆空间,则会以页面大小的倍数为单位,扩张或收缩对应的内存区域,但brk值并非以页面大小为倍数修改,而是按实际请求修改。因此malloc在用户空间分配内存可以以字节为单位分配,但内核在内部仍然是以页为单位分配的。

内核内存使用

slab:所谓尺有所长,寸有所短。以页为最小单位分配内存对于内核管理系统中的物理内存来说的确比较方便,但内核自身最常使用的内存却往往很小(远远小于一页)的内存块--比如存放文件描述符、进程描述符、虚拟内存区域描述符等行为所需的内存都不足一页。这些用来存放描述符的内存相比页面而言,就好比是面包屑和面包。一个整页中可以聚集多个这些小块内存;而且这些小块内存也和面包屑一样频繁的生成/销毁。

为了满足内核对这种小内存块的需要,Linux系统采用了一种被称为slab分配器的技术。slab分配器的实现相当复杂,但原理不难,其核心思想就是"存储池"的运用。内存片段(小内存块)被看作对象,当被使用完后,并不是直接释放而是被缓存到“存储池”里,留做下次使用,这无疑避免了频繁创建与销毁对象所带来的额外的负载。

slab技术不但避免了内存内部分片带来的不便(引入slab分配器的主要目的是为了减少对伙伴系统分配算法的调用次数--频繁分配和回收必然会导致内存碎片--难以找到大块连续的可用内存),而且可以很好的利用硬件缓存提高访问速度。

slab并非是脱离伙伴关系而独立存在的一种内存分配方式,slab仍然是建立在页面基础之上,换句话说,slab将页面(来自于伙伴关系管理的空闲页面链表)撕碎成众多小内存块以供分配,slab中的对象分配和销毁使用kmem_cache_alloc与kmem_cache_free。

Kmalloc

slab分配器不仅仅只用来存放内核专用的结构体,它还被用来处理内核对小块内存的请求。当然鉴于slab分配器的特点,一般来说内核程序中对小于一页的小块内存的请求才通过slab分配器提供的接口kmalloc来完成(虽然它可分配32到131072字节的内存)。从内核内存分配的角度来讲,kmalloc可被看成是get_free_page(s)的一个有效补充,内存分配粒度更灵活了。

内核非连续内存分配(Vmalloc)

伙伴关系也好、slab技术也好,从内存管理理论角度而言目的基本是一致的,它们都是为了防止"分片",不过分片又分为外部分片和内部分片之说,所谓内部分片是说系统为了满足一小段内存区(连续)的需要,不得不分配一大区域连续内存给他,从而造成了空间浪费;外部分片是指系统虽有足够的内存,但却是分散的碎片,无法满足对大块"连续内存"的需求。无论何种分片都是系统有效利用内存的障碍。slab分配器使得一个页面包含的众多小块内存可独立被分配使用,避免了内部分片,节约了空闲内存。伙伴关系把内存块按大小分组管理,一定程度上减轻了外部分片的危害,因为页框分配不再盲目,而是按照大小依次有序进行,不过伙伴关系只是减轻了外部分片,但并未彻底消除。

所以避免外部分片的最终思路还是落到了如何利用不连续的内存块组合成“看起来很大的内存块”--这里的情况很类似于用户空间分配虚拟内存,内存逻辑上连续,其实映射到并不一定连续的物理内存上,Linux内核借用了这个技术,允许内核程序在内核地址空间中分配虚拟地址,同样也利用页表(内核页表)将虚拟地址映射到分散的内存页上。以此完美的解决了内核内存中使用外部分片问题。内核提供vmalloc函数分配内核虚拟内存,该函数不同于kmalloc,它可以分配较Kmalloc大得多的内存空间(可远大于128K,但必须是页大小的倍数),但相比kmalloc来说,vmalloc需要对内核虚拟地址进行重映射,必须更新内核页表,因此分配效率上要低一些。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,719评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,337评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,887评论 0 324
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,488评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,313评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,284评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,672评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,346评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,644评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,700评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,457评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,316评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,706评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,261评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,648评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,859评论 2 335

推荐阅读更多精彩内容

  • 1. 用户空间 通常 32 位 Linux 虚拟地址空间划分, 0-3GB为用户空间,3GB-4GB为内核空间。每...
    Fly_Li阅读 1,671评论 0 5
  • Linux 内存管理 1 页的概念 linux 内核中把物理页作为内存分配的最小单位,32位CPU 页的大小通常为...
    赤兔欢阅读 3,251评论 0 5
  • 内存管理 在内核中分配内存不像在其他地方分配内存那么容易。造成这种局面的因素很多,根本原因是内核本身不能像用户空间...
    大雄good阅读 398评论 0 1
  • >计算机系统中有几类存储设备:cache、内存、外存。cache的存取速度最高,可以和CPU匹配,因此其代价最高,...
    一生信仰阅读 1,125评论 0 0
  • android 为了高效的 IPC 通信做了很多工作,内存管理就属于其中之一。传统的 IPC 传递数据,至少需要2...
    zjfclimin阅读 4,021评论 0 4