1.摘要
最近小伙伴們在排查一個(gè)線(xiàn)上關(guān)于linux內存oom的問(wèn)題,前些天來(lái)問(wèn)我某篇文章里的一句話(huà)是什么含義,問(wèn)題比較難用幾句話(huà)說(shuō)明,在這里梳理一下。
2.背景
最近小伙伴們在排查一個(gè)線(xiàn)上關(guān)于內存oom的問(wèn)題,前些天來(lái)問(wèn)我某篇文章里的一句話(huà)是什么含義:
每次申請的block大小比較有講究,Linux內核分為L(cháng)owMemroy和HighMemroy,LowMemory為內存緊張資源,LowMemroy有個(gè)閥值,通過(guò)free -lm和/proc/sys/vm/lowmem_reserve_ratio來(lái)查看當前l(fā)ow大小和閥值low大小。低于閥值時(shí)候才會(huì )觸發(fā)oom killer,所以這里block的分配小雨默認的256M,否則如果每次申請512M(大于128M),malloc可能會(huì )被底層的brk這個(gè)syscall阻塞住,內核觸發(fā)page cache回寫(xiě)或slab回收。
出自:
http://blog.csdn.net/gugemichael/article/details/24017515
感覺(jué)這句話(huà)里混淆了好幾個(gè)概念,并且一些概念跟自己的理解有沖突,在此說(shuō)一下自己的理解。對內核研究不深,一些概念可能理解有誤,因此文章里給出了比較可靠的參考文檔,有興趣可以進(jìn)一步了解詳情。
3.lowmem與highmem
關(guān)于lowmem和highmem的定義在這里就不詳細展開(kāi)了,推薦兩篇文章:
鏈接內講的比較清楚,這里只說(shuō)結論:
- 當系統的物理內存 > 內核的地址空間范圍時(shí),才需要引入highmem概念。
- x86架構下,linux默認會(huì )把進(jìn)程的虛擬地址空間(4G)按3:1拆分,0~3G user space通過(guò)頁(yè)表映射,3G-4G kernel space線(xiàn)性映射到進(jìn)程高地址。就是說(shuō),x86機器的物理內存超過(guò)1G時(shí),需要引入highmem概念。
- 內核不能直接訪(fǎng)問(wèn)1G以上的物理內存(因為這部分內存沒(méi)法映射到內核的地址空間),當內核需要訪(fǎng)問(wèn)1G以上的物理內存時(shí),需要通過(guò)臨時(shí)映射的方式,把高地址的物理內存映射到內核可以訪(fǎng)問(wèn)的地址空間里。
- 當lowmem被占滿(mǎn)之后,就算剩余的物理內存很大,還是會(huì )出現oom的情況。對于linux2.6來(lái)說(shuō),oom之后會(huì )根據score殺掉一個(gè)進(jìn)程(oom的話(huà)題這里不展開(kāi)了)。
- x86_64架構下,內核可用的地址空間遠大于實(shí)際物理內存空間,所以目前沒(méi)有上面討論的highmem的問(wèn)題。
4.linux的物理內存管理
接下來(lái)的問(wèn)題是,linux是如何實(shí)現highmem的概念的?
Linux把物理內存劃分為三個(gè)層次來(lái)管理:存儲節點(diǎn)(Node)、管理區(Zone)和頁(yè)面(Page)。
在NUMA架構下,系統根據CPU的物理顆數,將物理內存分成對應的Node,這里也不展開(kāi)了,可以參考NUMA (Non-Uniform Memory Access): An Overview。
每一個(gè)Node,系統又將其分為多個(gè)Zone,x86架構下,node被分為ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM,而64位x86架構下有ZONE_DMA(ZONE_DMA32)和ZONE_NORMAL。它們之間的是樹(shù)狀的包含關(guān)系:
可以通過(guò)以下命令查看numa node信息:
$ numactl --hardwareavailable: 2 nodes (0-1)node 0 cpus: 0 2 4 6 8 10 12 14 16 18 20 22node 0 size: 8114 MBnode 0 free: 2724 MBnode 1 cpus: 1 3 5 7 9 11 13 15 17 19 21 23node 1 size: 8192 MBnode 1 free: 818 MBnode distances:node 0 1 0: 10 20 1: 20 10
可以通過(guò)以下命令查看zone信息,注意單位是page(4k):
$ cat /proc/zoneinfoNode 0, zone DMA pages free 3933 min 20 low 25 high 30 scanned 0 spanned 4095 present 3834……………………
結合之前關(guān)于highmem的說(shuō)明,對于x86架構的系統來(lái)說(shuō),物理內存被劃分為:
| 類(lèi)型 | 地址范圍 |
|---|---|
| ZONE_DMA | 前16MB內存 |
| ZONE_NORMAL | 16MB – 896MB |
| ZONE_HIGHMEM | 896 MB以上 |
ZONE_DMA位于低端的內存空間,用于某些舊的ISA設備。ZONE_NORMAL的內存直接映射到Linux內核線(xiàn)性地址空間的高端部分。
對于x86_64架構的系統來(lái)說(shuō):
| 類(lèi)型 | 地址范圍 |
|---|---|
| ZONE_DMA | 前16MB內存 |
| ZONE_DMA32 | 16MB – 4G |
| ZONE_NORMAL | 4G以上 |
和x86相比,多了ZONE_DMA32,少了ZONE_HIGHMEM.
5.linux如何分配內存
這里也不詳細展開(kāi)了,推薦兩篇文章:
結論:
- malloc屬于glic的庫函數,分配的是虛擬地址。
- linux的malloc分配時(shí),如果申請內存小于MMAP_THRESHOLD(默認128K),使用brk分配,否則使用mmap分配。
- 通過(guò)brk分配的地址空間,當堆尾的空閑內存超過(guò)M_TRIM_THRESHOLD(默認128K)時(shí),執行內存縮緊操作,這里指的也是虛擬地址。
- 讀寫(xiě)內存時(shí),觸發(fā)缺頁(yè)中斷,此時(shí)才會(huì )分配物理內存。
- 分配物理內存時(shí),kernel由high zone到low zone依次查找是否有足夠的內存可以分配,找到可用的內存后映射到虛擬地址上。
關(guān)于系統分配內存的詳細介紹,可以參考:Memory Mapping and DMA。
6.lowmem_reserve_ratio
6.1.為什么要調整lowmem_reserve_ratio
在有高端內存的機器上,從低端內存域給應用層進(jìn)程分配內存是很危險的,因為這些內存可以通過(guò)mlock()系統鎖定,或者變成不可用的swap空間。
在有大量高端內存的機器上,缺少可以回收的低端內存是致命的。因此如果可以使用高端內存,Linux頁(yè)面分配器不會(huì )使用低端內存。這意味著(zhù),內核會(huì )保護一定數量的低端內存,避免被用戶(hù)空間鎖定?!發(fā)owmem_reserve_ratio”參數可以調整內核對于lower zone的保護力度。
6.2.lowmem_reserve_ratio參數的含義
lowmem_reserve_ratio是一個(gè)數組,可以通過(guò)以下命令查看:
% cat /proc/sys/vm/lowmem_reserve_ratio256 256 32
數組的長(cháng)度=內存zone數量-1,其中每個(gè)數并不是絕對值,而是一個(gè)比例,代表1/256或1/32。
再次搬出zoneinfo,這里以zone_dma和zone_dma32舉例:
$ cat /proc/zoneinfoNode 0, zone DMA pages free 3933 min 20 low 25 high 30 scanned 0 spanned 4095 present 3834 protection: (0, 3179, 7976, 7976)Node 0, zone DMA32 pages free 639908 min 4456 low 5570 high 6684 scanned 0 spanned 1044480 present 813848 protection: (0, 0, 4797, 4797)……………………
linux嘗試在zone中分配page時(shí),會(huì )判斷當前zone的page_free與高位zone的page_present的關(guān)系。
例如在dma中嘗試申請dma32的page時(shí),會(huì )計算一個(gè)protection值:
protection[dma,dma32] = zone_dma32.present/lowmem_reserve_ratio[dma(1)] = 813848/256 = 3179,這個(gè)結果對應上面DMA段中,protection數組的第二個(gè)元素。
然后需要比較zone_dma.free的值(3933) 與 protectiondma,dma32 + zone_dma.watermarkhigh的大?。喝绻鹒ree>protection+watermark[high],則可以分配page;否則不能分配,內核繼續查找下一個(gè)lower zone。也就是說(shuō)只有在higher zone內存不足時(shí)才會(huì )嘗試從lower zone繼續申請。
更詳細的文檔可以參考:Documentation/sysctl/vm.txt
根據公式可以看出:
- lowmem_reserve_ratio越大,低級的zone中被保護的內存就越??;
- lowmem_reserve_ratio越小,低級的zone中被保護的內存就越大;
- 當lowmem_reserve_ratio=1(100%)時(shí)代表對low zone的最大保護強度。


