淺談linux中的內(nèi)存管理

1 物理內(nèi)存

1.1 NUMA節(jié)點(diǎn)

在多個(gè)CPU(指的是物理CPU[1])通過總線來訪問系統(tǒng)中的內(nèi)存,這種方式稱之為多對(duì)稱處理器(如圖1左)复旬,由于每塊CPU都需要通過總線訪問內(nèi)存唠雕,導(dǎo)致總線數(shù)據(jù)繁忙浑此,降低了從內(nèi)存讀取的速率西乖。因此提出了一種叫做非統(tǒng)一內(nèi)存訪問(NUMA)的方式(如圖1右)侣集,這種方式將每塊CPU(物理CPU)和內(nèi)存綁定在一起允跑,形成一個(gè)NUMA節(jié)點(diǎn)王凑,這樣就可以通過CPU之間訪問內(nèi)存,而不用通過總線去訪問了聋丝,加快了對(duì)內(nèi)存的訪問速度索烹。

圖1 非一致性內(nèi)存訪問

小實(shí)驗(yàn)

通過如下命令可以查看系統(tǒng)numa節(jié)點(diǎn)的個(gè)數(shù)
1. lscpu
2. numactl --hardware (需要進(jìn)行安裝yum install numactl)
# lscpu的演示
[root@compute3-trust ~]# lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
...
CPU(s):                40   # 邏輯CPU的個(gè)數(shù)
...
NUMA node(s):          2
...
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              10240K
NUMA node0 CPU(s):     0-9,20-29  // 不同numa節(jié)點(diǎn)上,邏輯cpu的分布情況
NUMA node1 CPU(s):     10-19,30-39

# numactl 的演示
[root@compute]# numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 20 21 22 23 24 25 26 27 28 29
node 0 size: 32365 MB
node 0 free: 29546 MB
node 1 cpus: 10 11 12 13 14 15 16 17 18 19 30 31 32 33 34 35 36 37 38 39
node 1 size: 32768 MB
node 1 free: 30287 MB
node distances:
node   0   1 
  0:  10  21 
  1:  21  10 
[root@compute]# free -h  // free命令顯示的內(nèi)存是系統(tǒng)中總內(nèi)存的大小弱睦,而不是每個(gè)numa節(jié)點(diǎn)中內(nèi)存的大小
              total        used        free      shared  buff/cache   available
Mem:            62G        2.0G         58G         25M        1.9G         59G
Swap:           31G          0B         31G

1.2 NUMA節(jié)點(diǎn)中的內(nèi)存

圖2 NUMA節(jié)點(diǎn)中的內(nèi)存

從圖2中百姓,我們得知NUMA節(jié)點(diǎn)由一個(gè)叫做pg_data_t的結(jié)構(gòu)體來描述從中我們大概可以得知:

  1. 該節(jié)點(diǎn)所包含的物理頁(yè)面數(shù)等信息
  2. node_zones數(shù)組表示NUMA節(jié)點(diǎn)中的物理內(nèi)存又細(xì)分為不同的區(qū)域,如(DMA, NORMAL, HIGHMEM, MOVAABLE)况木。(每個(gè)區(qū)域的含義自行百度)垒拢。
  3. node_mem_map數(shù)組,該節(jié)點(diǎn)所有的page對(duì)象

針對(duì)每個(gè)區(qū)域火惊,linux用一個(gè)名為zone的結(jié)構(gòu)體來進(jìn)行描述求类,從該結(jié)構(gòu)體中,我們大概可以得知:

  1. 該區(qū)域的起始物理頁(yè)號(hào)
  2. 該區(qū)域可用的物理頁(yè)數(shù)
  3. free_area后面講
  4. 真正的物理頁(yè)面就存放了這些區(qū)域中

接下來我們就終于看到真正的物理頁(yè)面了屹耐,根據(jù)頁(yè)的大小及作用仑嗅,此時(shí)將頁(yè)分為三類:

  1. 分配完整的一頁(yè),并且該物理頁(yè)直接和虛擬空間建立映射關(guān)系张症。則稱該頁(yè)為匿名頁(yè)(anonymous page)仓技。
  2. 分配完整的一頁(yè),該物理頁(yè)不僅與虛擬空間建立映射關(guān)系而且還關(guān)聯(lián)一個(gè)文件俗他。則稱該頁(yè)為文件映射頁(yè)(自己取的名字)脖捻。
  3. 通過申請(qǐng)完整的一頁(yè),然后再通過slub兆衅,slab等方式將完整的一頁(yè)再切分成更小的頁(yè)地沮,將這些頁(yè)分配出去,稱之為小頁(yè)羡亩。

匿名頁(yè)和文件映射頁(yè)的區(qū)別見圖3

圖3 匿名頁(yè)和文件映射頁(yè)的區(qū)別

1.2.1 伙伴系統(tǒng)分配物理頁(yè)

上面在將zone結(jié)構(gòu)體的時(shí)候摩疑,其中的free_area變量我們沒講,現(xiàn)在我們來簡(jiǎn)述下它的作用畏铆。

如圖4所示雷袋,free_area變量負(fù)責(zé)組織當(dāng)前區(qū)域可用的物理頁(yè)面,當(dāng)通過伙伴系統(tǒng)分配物理頁(yè)面的時(shí)候就是根據(jù)該變量的值進(jìn)行分配的辞居。

一般情況下楷怒,該free_area變量是一個(gè)長(zhǎng)度為11的數(shù)組蛋勺。當(dāng)數(shù)組下標(biāo)為0時(shí),其只能一個(gè)連續(xù)的頁(yè)面鸠删,當(dāng)數(shù)組下標(biāo)為1時(shí)抱完,其只能分配兩個(gè)連續(xù)的頁(yè)面,當(dāng)數(shù)組下標(biāo)為2時(shí)刃泡,其只能分配4個(gè)連續(xù)的頁(yè)面巧娱,當(dāng)數(shù)組下標(biāo)為10時(shí),其能分配1024個(gè)連續(xù)的頁(yè)面烘贴。即數(shù)組下標(biāo)對(duì)應(yīng)能連續(xù)分配物理頁(yè)面的關(guān)系是:2數(shù)組下標(biāo)家卖。圖中數(shù)組下標(biāo)所對(duì)應(yīng)連續(xù)的物理頁(yè)稱之為頁(yè)塊鏈表。

比如當(dāng)需要分配513個(gè)連續(xù)的物理頁(yè)的時(shí)候(29 = 512)庙楚,此時(shí)數(shù)組下標(biāo)為9的頁(yè)塊鏈表不能分配這么多連續(xù)的物理頁(yè),則去數(shù)組下標(biāo)為10的頁(yè)塊鏈表進(jìn)行申請(qǐng)趴樱。當(dāng)分配完畢后馒闷,伙伴系統(tǒng)將會(huì)把剩下的物理頁(yè)插入的對(duì)應(yīng)的頁(yè)塊鏈表中去。

圖4 伙伴系統(tǒng)分配物理頁(yè)

1.2.2 物理頁(yè)面的回收

當(dāng)物理頁(yè)面不夠叁征,而又需要分配新的物理頁(yè)面的時(shí)候或者線程kswapd發(fā)現(xiàn)當(dāng)前系統(tǒng)物理內(nèi)存很少的時(shí)候纳账,就會(huì)觸發(fā)物理頁(yè)面回收的機(jī)制了。{還有一種叫做out of memory機(jī)制了捺疼,簡(jiǎn)單來說就是直接通過kill進(jìn)程的方式來釋放內(nèi)存}

如圖5所示疏虫,不管是分配新的物理內(nèi)存還是kswapd線程,最終都用調(diào)用shrink_node_memcg函數(shù)來進(jìn)行物理頁(yè)面的回收啤呼。

從圖5中卧秘,我們發(fā)現(xiàn)系統(tǒng)維護(hù)了4個(gè)物理頁(yè)面的鏈表(實(shí)際上不是4個(gè),還有其他的)官扣,分別是匿名頁(yè)(active)翅敌,匿名頁(yè)(inactive),文件映射頁(yè)(active)惕蹄,文件映射頁(yè)(inactive)蚯涮。其通過LRU算法對(duì)inactive的頁(yè)的鏈表進(jìn)行回收,如果是匿名頁(yè)(inactive)卖陵,則系統(tǒng)將物理頁(yè)中的數(shù)據(jù)寫入到swap分區(qū)中遭顶,然后釋放對(duì)應(yīng)的物理內(nèi)存,最后將部分匿名頁(yè)(acitve)鏈表中的頁(yè)插入到匿名頁(yè)(inacitve)鏈表中泪蔫。如果是文件映射頁(yè)(inactive)則將物理內(nèi)存中的數(shù)據(jù)寫回到文件中棒旗,然后釋放物理內(nèi)存,最后將部分文件映射頁(yè)(active)鏈表中的頁(yè)插入到文件映射頁(yè)(inactive)鏈表中撩荣。

圖5 物理頁(yè)面的回收

2 虛擬內(nèi)存

2.1 地址

在linux中嗦哆,每個(gè)進(jìn)程都有邏輯上屬于自己的內(nèi)存空間谤祖,稱之為虛擬內(nèi)存,從而使得不同進(jìn)程的內(nèi)存空間可以相互進(jìn)行隔離老速。每個(gè)進(jìn)程虛擬空間的大小與操作系統(tǒng)的位數(shù)有關(guān)粥喜。對(duì)于32位操作系統(tǒng)而言,虛擬內(nèi)存的邏輯地址范圍為0-232橘券,所以虛擬內(nèi)存空間的大小為4GB额湘。

在談到內(nèi)存的時(shí)候,地址是最為重要的概念旁舰。因?yàn)橛辛说刂贩婊涂梢詫ふ业綄?duì)應(yīng)的內(nèi)存空間,就可以往里面存儲(chǔ)數(shù)據(jù)和讀取數(shù)據(jù)了箭窜。所以理解內(nèi)存就需要對(duì)地址有清晰的認(rèn)識(shí)毯焕,下面就圍繞邏輯地址展開介紹。

在linux中磺樱,一個(gè)32位的邏輯地址被分為頁(yè)號(hào)和頁(yè)偏移兩個(gè)部分纳猫。其中頁(yè)偏移的尋址范圍了0 - 212,每個(gè)一個(gè)邏輯地址(如圖中0,1,2)對(duì)應(yīng)的內(nèi)存空間為1Byte竹捉,所以其最大的存儲(chǔ)空間為4Kb = 212 * 1Byte芜辕,即頁(yè)(linux中內(nèi)存分配的基本單位,一次最少分配一個(gè)頁(yè))块差。如圖6-<邏輯地址和內(nèi)存的關(guān)系>所示(圖中例子假設(shè)邏輯地址對(duì)應(yīng)的前20位頁(yè)號(hào)全位0)侵续。

接下來只需要將邏輯地址轉(zhuǎn)化為物理地址就可以真正的存儲(chǔ)和讀取數(shù)據(jù)了,其過程如圖6-<邏輯地址轉(zhuǎn)化為物理地址>所示憨闰,從頁(yè)表中找到邏輯地址的頁(yè)號(hào)P所對(duì)應(yīng)的物理頁(yè)號(hào)b状蜗,然后將得到的物理頁(yè)號(hào)b與頁(yè)偏移進(jìn)行相加即得到物理地址。

圖6-<數(shù)組在內(nèi)存中的存放>鹉动,顯示了java定義了長(zhǎng)度為2的int數(shù)據(jù)在內(nèi)存中是如何存放诗舰,可以與圖6-<邏輯地址和內(nèi)存的關(guān)系>對(duì)應(yīng)起來。

圖6 邏輯地址

2.2 虛擬內(nèi)存空間的劃分

對(duì)于整個(gè)虛擬內(nèi)存空間训裆,linux將其劃分為兩個(gè)部分眶根,分別是用戶內(nèi)存空間,用來存放用戶態(tài)的數(shù)據(jù)和內(nèi)核態(tài)內(nèi)存空間边琉,用來內(nèi)核態(tài)的數(shù)據(jù)属百。

結(jié)構(gòu)體struct mm_struct *mm 就是用來描述虛擬內(nèi)存空間的。

在struct mm_struct中的TASK_SIZE_MAX變量的值為用戶空間和內(nèi)核空間的分界線变姨。其值如圖7所示

注:對(duì)于64位系統(tǒng)而言族扰,用戶空間地址的尋址范圍只用到了前48位。從1左移了47位就能看出

圖7 虛擬內(nèi)存空間的劃分

2.3 用戶內(nèi)存空間

用戶態(tài)內(nèi)存空間的分布如圖8所示。圖中描述了有哪些區(qū)域渔呵,這些區(qū)域用于存放哪種類型的數(shù)據(jù)等信息怒竿。

圖8 用戶內(nèi)存空間

現(xiàn)在我們以32位系統(tǒng)例,看看linux如何用代碼來組織用戶空間的

在結(jié)構(gòu)體struct mm_struct *mm定義了很多描述內(nèi)存的相關(guān)變量扩氢,如圖9所示牺陶。變量太多了场刑,沒法全部記住晕翠,但從中我們大概能知道其描述了該進(jìn)程總共映射了多少頁(yè)盖文,存儲(chǔ)數(shù)據(jù)的頁(yè)面數(shù)是多少,存儲(chǔ)的可執(zhí)行代碼的頁(yè)數(shù)是多少双饥,還有各個(gè)區(qū)域(數(shù)據(jù)段媒抠,代碼段等)起始地址和終點(diǎn)地址分別是多少等信息。

圖9 用戶內(nèi)存空間

結(jié)構(gòu)體struct mm_struct中還有一個(gè)比較關(guān)鍵的結(jié)構(gòu)體變量struct vm_area_struct *mmp咏花,其主要作用是用于描述前面所說的段(代碼段趴生,數(shù)據(jù)段,堆昏翰,棧)等信息苍匆。如圖10所示,我們大概可以知道其指定了某一段的開始和結(jié)束矩父,即某一段所能存儲(chǔ)的范圍以及其通過鏈表的方式將段與段之間進(jìn)行連接

圖10 vm_area_struct

物理內(nèi)存的分配以及頁(yè)表的建立

linux并不會(huì)滿足每個(gè)進(jìn)程虛擬內(nèi)存(32位4G)的大小,如果有50個(gè)進(jìn)程那不就要分配200G內(nèi)存排霉,很明顯這是不可能的窍株。而是當(dāng)進(jìn)程通過邏輯地址讀取(存儲(chǔ))內(nèi)存中的數(shù)據(jù)的時(shí)候攻柠,發(fā)現(xiàn)沒有對(duì)應(yīng)的物理內(nèi)存時(shí)球订,產(chǎn)生缺頁(yè)中斷進(jìn)行物理內(nèi)存的分配。簡(jiǎn)而言之瑰钮,就是當(dāng)程序真正要訪問物理內(nèi)存的時(shí)候冒滩,linux在開始分配物理內(nèi)存。

那么在缺頁(yè)中斷函數(shù)中大概做了些什么呢浪谴?

如下圖10所示开睡,發(fā)生缺頁(yè)中斷后,經(jīng)過一系列調(diào)用苟耻,進(jìn)入_do_page_fault函數(shù)篇恒,在該函數(shù)中,判斷缺頁(yè)是發(fā)生在內(nèi)核還是在用戶空間凶杖,若發(fā)生在內(nèi)核胁艰,則進(jìn)入內(nèi)核的處理邏輯,若發(fā)生在用戶態(tài),則調(diào)用handle_mm_fault函數(shù)腾么,在函數(shù)中奈梳,分配創(chuàng)建了pud,pmd目錄項(xiàng)(頂級(jí)目錄項(xiàng)不用創(chuàng)建解虱,其存儲(chǔ)在CR3寄存器中)攘须。然后調(diào)用handle_pte_fault函數(shù),此時(shí)又分為三種情況分別是:
1))頁(yè)表不存在饭寺,并且是匿名頁(yè)缺頁(yè)異常阻课,則調(diào)用do_anoymous_page函數(shù)
2)頁(yè)表不存在,并且是文件映射頁(yè)缺頁(yè)異常艰匙,則調(diào)用do_fault函數(shù)
3)頁(yè)表之前存在限煞,則調(diào)用do_swap_page

三種函數(shù)具體的做了些什么請(qǐng)看圖11。


圖10 缺頁(yè)中斷

3 相關(guān)概念

[1] 物理cpu
  • 物理cpu: 物理cpu指的是主板上cpu芯片的數(shù)量(通過physical id 來查看有幾個(gè)物理cpu)
  • 邏輯cpu:邏輯cpu一般指的是各個(gè)物理cpu中的core的數(shù)量(通過processor可以查看系統(tǒng)共有多少個(gè)邏輯cpu)

命令:cat /proc/cpuinfo 可查看系統(tǒng)cpu的情況

參考
zone參考
numa參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末员凝,一起剝皮案震驚了整個(gè)濱河市署驻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌健霹,老刑警劉巖旺上,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異糖埋,居然都是意外死亡宣吱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門瞳别,熙熙樓的掌柜王于貴愁眉苦臉地迎上來征候,“玉大人,你說我怎么就攤上這事祟敛“贪樱” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵馆铁,是天一觀的道長(zhǎng)跑揉。 經(jīng)常有香客問我,道長(zhǎng)埠巨,這世上最難降的妖魔是什么历谍? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮辣垒,結(jié)果婚禮上扮饶,老公的妹妹穿的比我還像新娘。我一直安慰自己乍构,他們只是感情好甜无,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布扛点。 她就那樣靜靜地躺著,像睡著了一般岂丘。 火紅的嫁衣襯著肌膚如雪陵究。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天奥帘,我揣著相機(jī)與錄音铜邮,去河邊找鬼。 笑死寨蹋,一個(gè)胖子當(dāng)著我的面吹牛松蒜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播已旧,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼秸苗,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了运褪?” 一聲冷哼從身側(cè)響起惊楼,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秸讹,沒想到半個(gè)月后檀咙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡璃诀,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年弧可,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劣欢。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棕诵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出氧秘,到底是詐尸還是另有隱情年鸳,我是刑警寧澤趴久,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布丸相,位于F島的核電站,受9級(jí)特大地震影響彼棍,放射性物質(zhì)發(fā)生泄漏灭忠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一座硕、第九天 我趴在偏房一處隱蔽的房頂上張望弛作。 院中可真熱鬧,春花似錦华匾、人聲如沸映琳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)萨西。三九已至有鹿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谎脯,已是汗流浹背葱跋。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留源梭,地道東北人娱俺。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像废麻,于是被迫代替她去往敵國(guó)和親荠卷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348