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)存的訪問速度索烹。
小實(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)由一個(gè)叫做pg_data_t的結(jié)構(gòu)體來描述從中我們大概可以得知:
- 該節(jié)點(diǎn)所包含的物理頁(yè)面數(shù)等信息
- node_zones數(shù)組表示NUMA節(jié)點(diǎn)中的物理內(nèi)存又細(xì)分為不同的區(qū)域,如(DMA, NORMAL, HIGHMEM, MOVAABLE)况木。(每個(gè)區(qū)域的含義自行百度)垒拢。
- node_mem_map數(shù)組,該節(jié)點(diǎn)所有的page對(duì)象
針對(duì)每個(gè)區(qū)域火惊,linux用一個(gè)名為zone的結(jié)構(gòu)體來進(jìn)行描述求类,從該結(jié)構(gòu)體中,我們大概可以得知:
- 該區(qū)域的起始物理頁(yè)號(hào)
- 該區(qū)域可用的物理頁(yè)數(shù)
- free_area后面講
- 真正的物理頁(yè)面就存放了這些區(qū)域中
接下來我們就終于看到真正的物理頁(yè)面了屹耐,根據(jù)頁(yè)的大小及作用仑嗅,此時(shí)將頁(yè)分為三類:
- 分配完整的一頁(yè),并且該物理頁(yè)直接和虛擬空間建立映射關(guān)系张症。則稱該頁(yè)為匿名頁(yè)(anonymous page)仓技。
- 分配完整的一頁(yè),該物理頁(yè)不僅與虛擬空間建立映射關(guān)系而且還關(guān)聯(lián)一個(gè)文件俗他。則稱該頁(yè)為文件映射頁(yè)(自己取的名字)脖捻。
- 通過申請(qǐng)完整的一頁(yè),然后再通過slub兆衅,slab等方式將完整的一頁(yè)再切分成更小的頁(yè)地沮,將這些頁(yè)分配出去,稱之為小頁(yè)羡亩。
匿名頁(yè)和文件映射頁(yè)的區(qū)別見圖3
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è)塊鏈表中去。
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)鏈表中撩荣。
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)起來。
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位就能看出
2.3 用戶內(nèi)存空間
用戶態(tài)內(nèi)存空間的分布如圖8所示。圖中描述了有哪些區(qū)域渔呵,這些區(qū)域用于存放哪種類型的數(shù)據(jù)等信息怒竿。
現(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)地址分別是多少等信息。
結(jié)構(gòu)體struct mm_struct中還有一個(gè)比較關(guān)鍵的結(jié)構(gòu)體變量struct vm_area_struct *mmp咏花,其主要作用是用于描述前面所說的段(代碼段趴生,數(shù)據(jù)段,堆昏翰,棧)等信息苍匆。如圖10所示,我們大概可以知道其指定了某一段的開始和結(jié)束矩父,即某一段所能存儲(chǔ)的范圍以及其通過鏈表的方式將段與段之間進(jìn)行連接
物理內(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。
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的情況