首頁 > 軟體

深入理解作業系統記憶體

2020-06-16 17:09:50

圖片來源:http://www.tomshardware.com/

序言:

  Memory(記憶體)是一台計算機組成的重要部分,也是最基礎的一部分。其它基礎元件有主機板、CPU、磁碟、顯示卡(可獨立可整合)等。寫這篇文章源自後面的一個案例,出於想搞明白,以及分享以前關於記憶體方面的一些記錄的知識點。

  本文概要主要講了記憶體的介紹;如何正確檢視系統記憶體使用率;對Swap分割區進行介紹;如何將記憶體當作硬碟來加速資料讀寫,以及分享關於記憶體異常的案例分析;介紹了oom-killer的機制。

一、記憶體的介紹

1.何為記憶體

記憶體 是一種利用半導體技術製成的儲存資料的電子裝置。其電子電路中的資料以二進位制方式儲存,記憶體的每一個儲存單元稱做記憶元。記憶體根據儲存能力及電源關係又可分為易失性記憶體(斷電後丟失),非易失性記憶體(斷電後持久)。

  • 易失性記憶體:是指當電源供應中斷後,記憶體所儲存的資料將會丟失。有兩類:動態隨機存取記憶體(DRAM),靜態隨機存取記憶體(SRAM)。後者速度更快。
  • 非易失性記憶體:是指當電源供應中斷後,記憶體說儲存的資料並不會小時。供電恢復後,資料可用正常讀取(有很小的幾率會出現資料損壞的情況)。有三類,唯讀記憶體、快閃記憶體、磁碟(磁碟就是我們最常見的非易失性記憶體的記憶體)
  • 後文所出現‘記憶體’字樣,均指易失性記憶體。

2.記憶體、CPU、磁碟三者的關係

  • 記憶體與cpu的關係:cpu(中央處理器)就像人類的大腦一樣,負責解釋計算機指令以及處理計算機軟體中的資料。由於cpu需要處理資料,如果直接從硬碟中讀取寫入資料,以現在的機械定址的硬碟和固態SSD硬碟的速度來比較,兩者差距太大。所以記憶體作為CPU和硬碟 兩者的橋樑存在,另外CPU與記憶體之間為了縮小讀寫差距,CPU技術加入了快取記憶體概念(L1、L2、L3,三種級別的Cache),一般在CPU產品型號技術參數列中都會具體的介紹。
  • 記憶體與硬碟的關係:程式是在記憶體上執行的,為了加快讀速度,記憶體會將讀取過的資料在自身中進行快取。直到cache已滿,會釋放最先快取的資料,將其寫入磁碟,並騰出一定空間來滿足當前正在讀取的新資料。硬碟是為了持久化儲存程序執行的資料,保證資料在機器斷電後不被丟失。

二、如何正確檢視記憶體的使用率

1.free 命令檢視記憶體使用

[root@docker ~]# free
              total        used        free      shared  buff/cache   available
Mem:        1883740      440288      896080       31024      547372     1256984
Swap:             0           0           0

註:本機已關閉swap分割區。

  • 物理總記憶體total:1883740KB / 1024 =1.8G(這裡不包括核心在啟動時為其自身保留的一小部分,所以這裡是1.8G,而其實是2G記憶體)
  • 使用記憶體:1883740KB - 896080KB -547372KB = 440288KB(計算公式:total - free - buffers - cache = used)
  • free 空閒記憶體:1883740KB - 440288KB -547372KB = 440288KB(同used計算公式)
  • shared共用記憶體:被tmpfs使用的(大部分)記憶體
  • buffers緩衝區:核心緩衝區使用的記憶體(寫資料時的緩衝)
  • cache快取:頁面快取和slab 使用的記憶體 (註:slab記憶體分配機制 參考頁面點此)(讀資料時的快取,設定drop_cache=3,使用time分別計算 讀檔案開啟時間,動態watch cache的大小)
  • available有效記憶體:估計有多少記憶體可用於啟動新的應用程式,沒有交換。 與快取或free欄位提供的資料不同,此欄位考慮到頁面快取,並且由於專案正在使用,並不是所有可回收記憶體板都將被回收。(由系統動態的估算,此值相當於6.x系統裡 free+cache+buffers的可用記憶體)

2.top 檢視進程使用的記憶體

可指定進程檢視,不加引數顯示所有的進程,以及概覽。

[root@docker ~]# top -p `pgrep nginx|head -n 1`
...省略
  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                               
26826 root      20   0   45816   1000      0 S  0.0  0.1   0:00.00 nginx                                 

注釋:

第一行,顯示了預設的欄位,另top狀態下,可輸入 f 鍵,進行選擇其它欄位,如DATA、SWAP等

第二行,顯示欄位的值。這裡說下進程RES (駐留記憶體空間)和%MEM的關係,%MEM 是該進程佔用系統物理total記憶體的百分比。RES計算方式:total*%MEM=RES;VIRT包括所需要的程式碼、資料和共用庫。

3.vmstat 檢視虛擬記憶體使用以及swap分割區 和系統、cpu的io 報告

[root@docker ~]# vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 4  0      0 858708  15756 571772    0    0     6    49   16    0  8  0 92  0  0

 

三、Swap——與記憶體緊密相關的交換空間

1.Swap 用途介紹

當實體記憶體(RAM)的數量已滿時,會使用Linux中的swap交換空間。如果系統需要更多記憶體資源並且RAM已滿,則記憶體中的非活動頁面將移動到交換空間中。雖然交換空間可以幫助機器使用少量的RAM,但不應該被認為是更多RAM的替代品。因為交換空間位於硬碟機上,存取時間比實體記憶體慢的很多。發生的交換越多,系統越慢。

Linux有兩種形式的交換空間:交換分割區和交換檔案。交換分割區是僅用於交換的硬碟的獨立部分,沒有其他檔案可以存放在那裡。交換檔案是位於系統和資料檔案之間在檔案系統中的一個特殊檔案。另外,swap 空間一般設定實體記憶體的2倍。

2.啟用或禁止交換空間

  • 啟用:如果覺得伺服器應用對記憶體的讀寫不是特別在意,但伺服器記憶體又很小的話,可設定開啟swap 交換空間。
  • 禁止:一種情況是當主機記憶體夠大時,如果開啟swap空間,當發生實體記憶體使用到達核心設定的一個臨界值時(稍後會講),記憶體中的資料會移動到swap交換空間內,而這時,實體記憶體還是有很多的。由於swap分割區是走磁碟IO,所以我們有必要修改這個臨界值或者禁止使用swap分割區。還有一種情況就是我們的客戶機作業系統是虛擬機器(如kvm虛擬化上的),這個時候是要一定禁止swap分割區的。

3.如何啟用或禁用交換空間

檢視當前swap空間狀態

root@docker ~]# swapon -s
檔名                型別        大小    已用    許可權
/mnt/os_swap                               file    4194300    0    -1

禁用swap空間

[root@docker ~]# swapoff -a

啟用swap空間(這裡使用的檔案形式作為swap分割區)

[root@docker ~]# swapon /mnt/os_swap 

4.建立swap分割區大小

  • 用檔案形式來建立swap

在當前的檔案系統裡,用dd或者fallocate(更快更簡單)建立一個用於swap分割區的檔案:

[root@docker ~]# dd if=/dev/zero of=/mnt/os_swap bs=1M count=4096

[root@docker ~]# fallocate -l 4G /mnt/os_swap 

修改該檔案許可權為600

[root@docker ~]# chmod 600 /mnt/os_swap 

使用mkswap命令將檔案建成一個交換空間(也可在最後用 -p size 指定劃分給swap空間的大小,預設使用整個file 大小)

[root@docker ~]# mkswap /mnt/os_swap
mkswap: /mnt/os_swap: warning: wiping old swap signature.
正在設定交換空間版本 1,大小 = 4194300 KiB
無標籤,UUID=fc870bd5-c823-4e70-9d14-966543a52db2

使用swap分割區,並檢查swap空間大小

[root@docker ~]# swapon /mnt/os_swap 
[root@docker ~]# swapon -s
檔名                型別        大小    已用    許可權
/mnt/os_swap                               file    4194300    0    -1
  • 用磁碟分割區的方式建立swap

首先找到一個大小合適的磁碟分割區,分割區型別選擇82-Linux Swap。然後之後和上述類似,用mkswap命令建立啟用swap分割區。並用swapon /dev/vdb1(vdb1為分割區裝置)命令啟用swap分割區。

 5.swappiness-系統什麼時候開始使用交換空間

當我們設定啟用swap分割區後,系統會在什麼情況下使用交換空間?這要從物理RAM記憶體使用情況來看,系統核心引數定義了這個界限:vm.swappiness,該引數取值範圍在0-100之間,它定義了當系統剩餘記憶體是總記憶體的多少百分比後,即開始使用交換空間。(暫時對這句話保持質疑,經過多方記憶體使用的測試,系統在由RAM轉向Swap空間時並沒有一個固定的剩餘記憶體值!如你有相關計算方式,還望不吝賜教,謝謝!)

 vm.swappiness 預設值是60。swappiness引數的值越高,表示核心將會更積極地從記憶體到交換空間中移動資料。經測試

當值為0時,系統可用記憶體在使用完後再使用swap進行交換。

當值為1時,和0的區別不大。

推薦在資料庫讀寫資料密集的應用中禁用swap交換空間或者設定更低的vm.swappiness值,避免更多的硬碟IO操作,以此作為提高讀寫速度的一個方式。

 

四、如何將記憶體當作磁碟使用來加速資料讀寫

1.記憶體檔案系統介紹

圖片來源:www.thomas-krenn.com

如上圖所示,利用記憶體作為特殊檔案系統,目前知道的方式有3種,RAMdisk、ramfs、tmpfs。

ramdisk:RAM disk是使用主系統記憶體作為塊裝置的一種方式。它也可用於臨時檔案系統的加密工作,因為內容在重新啟動時將被擦除。由於這個塊裝置大小是固定的,因此安裝存放在其上面的檔案系統是固定的大小。

ramfs:Ramfs是一個非常簡單的檔案系統,用於匯出Linux的磁碟快取 機制(頁面快取和dentry快取)基於RAM的檔案系統,可以動態的根據需要調整大小(前提不超過總RAM的情況)。通常,檔案都被Linux快取在記憶體中,資料頁從後備儲存(通常是安裝檔案系統的塊裝置)中讀取並被儲存 防止它再次需要,但標記為乾淨(可自由使用的),以防萬一 虛擬記憶體系統需要其他記憶體。但ramfs 是沒有後備儲存的,像往常一樣,寫入ramfs的檔案分配 dentry和頁面快取,但是沒有地方可寫。這意味著頁面從未被標記為乾淨,因此當VM正在尋找回收記憶體時,它們不能被釋放 。除非關機斷電。

tmpfs:將所有檔案儲存在虛擬記憶體中的檔案系統。tmpfs中的所有內容都是臨時的,因為在你的硬碟上不會有檔案被建立。如果解除安裝tmpfs範例, 儲存在其中的一切都丟失。如果你umount tmpfs檔案系統,儲存在裡面的內容將丟失。tmpfs可以將所有內容放入核心內部快取並增長收縮以容納它包含的檔案,並能夠交換不需要的頁面到swap交換空間。由於tmpfs 完全位於頁面快取和交換中,所有tmpfs頁面將在/proc/meminfo 和‘shared’ 中顯示為 ”Shmem“

2.它們三的簡單比較

ramfs與ramdisk比較:使用ram disk還需要不必要地將偽造塊裝置中的記憶體複製到頁面快取(並複製更改),以及建立和銷毀dentries。此外,它還需要一個檔案系統驅動程式(如ext2)來格式化和解釋這些資料。與ramfs相比,這浪費了記憶體(和記憶體匯流排頻寬),為CPU創造了不必要的工作,並汙染了CPU快取記憶體。更重要的一點,ramfs的所有的工作都發生在_anyway_,因為所有檔案存取都通過頁面和dentry快取。 RAM disk是不必要的; ramfs在內部更簡單,使用起來更靈活,大小可隨著需要的空間而動態增加或減少。

ramfs與tmpfs比較:ramfs的一個缺點是你可以持續的往裡面寫入資料,直到填滿所有的記憶體,並且VM無法釋放它,因為VM認為檔案應該寫入後備儲存(而不是交換空間),但是ramfs 它沒有任何後備儲存。因此,只有root(或受信任的使用者)才允許對ramfs mount進行寫存取。建立一個名為tmpfs的ramfs派生物,以增加大小限制和能力,將資料寫入swap交換空間。普通使用者也可以允許寫入許可權 tmpfs掛載。

3.如何設定ramfs

建立一個目錄,用於掛載ramfs

[root@docker ~]# mkdir /ramfs_test

 掛載ramfs到上一步建立的目錄中

[root@docker ~]# mount -t ramfs ramfs /ramfs_test/

檢查
[root@docker ~]# mount |grep ramfs_test
ramfs on /ramfs_test type ramfs (rw,relatime)

測試一下ramfs與磁碟io讀寫的比較,一目了然。

[root@docker ~]# dd if=/dev/zero of=/ramfs_test/testfile.ramfs bs=1M count=1000
記錄了1000+0 的讀入
記錄了1000+0 的寫出
1048576000位元組(1.0 GB)已複製,0.60369 秒,1.7 GB/秒
[root@docker ~]# dd if=/dev/zero of=/tmp/testfile.ramfs bs=1M count=1000
記錄了1000+0 的讀入
記錄了1000+0 的寫出
1048576000位元組(1.0 GB)已複製,13.3286 秒,78.7 MB/秒

另外一個需要說明,網上大部分文章說掛載ramfs時,可以指定maxsize,即使用的最大的記憶體,經過測試,size(maxsize)引數沒有生效,我使用以下命令進行掛載:

[root@docker ~]# mount -t ramfs ramfs /ramfs_test -o size=1024M && mount | grep ramfs
ramfs on /ramfs_test type ramfs (rw,relatime,size=1024M)

然後放入一個大於1G的檔案,並檢查大小

[root@docker ~]# dd if=/dev/zero of=/ramfs_test/testramfs.file bs=1M count=1200
記錄了1200+0 的讀入
記錄了1200+0 的寫出
1258291200位元組(1.3 GB)已複製,0.78763 秒,1.6 GB/秒
[root@docker ~]# ll -h /ramfs_test/testramfs.file 
-rw-r--r-- 1 root root 1.2G 6月   2 09:04 /ramfs_test/testramfs.file

從上面可以看出,使用ramfs作檔案系統,並沒有受到限制,所以它有可能佔用系統全部的RAM,並導致系統死鎖,無法進行操作,系統核心將崩潰。所以這裡在使用ramfs時,要慎重考慮使用場景,避免程式故障或記憶體溢位導致系統崩潰,必須重新啟動才能解決!另外檢視了mount的man 手冊,掛載ramfs內容時只有以下內容:

Mount options for ramfs
       Ramfs is a memory based filesystem. Mount it and you have  it.  Unmount
       it  and it is gone. Present since Linux 2.3.99pre4.  There are no mount
       options.

而,size引數只適用於tmpfs!

Mount options for tmpfs
       size=nbytes
              Override default maximum size of the filesystem.   The  size  is
              given  in bytes, and rounded up to entire pages.  The default is
              half of the memory. The size parameter also accepts a  suffix  %
              to limit this tmpfs instance to that percentage of your physical
              RAM: the default, when neither size nor nr_blocks is  specified,
              is size=50

4.如何設定tmpfs

和ramfs有點類似,先建立一個目錄,用於掛載tmpfs

[root@docker ~]# mkdir /tmpfs_test

使用mount命令掛載到tmpfs_test目錄中,並檢查掛載情況

[root@docker ~]# mount -t tmpfs -o size=1G tmpfs /tmpfs_test && mount |grep tmpfs_test
tmpfs on /tmpfs_test type tmpfs (rw,relatime,size=1048576k)

現在我們使用dd來測試一下速度,並檢查下size 限制空間的效果

[root@docker ~]# dd if=/dev/zero of=/tmpfs_test/testtmpfs.file bs=1M count=1100
dd: error writing ‘/tmpfs_test/testtmpfs.file’: No space left on device
1025+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 0.497443 s, 2.2 GB/s

從上面的提示可以看出,空間已經不夠,現在看下實際存入的檔案大小

[root@docker ~]# ls -l --block-size=K /tmpfs_test/testtmpfs.file
-rw-r--r-- 1 root root 1048576K Jun  2 12:16 /tmpfs_test/testtmpfs.file

從ls的輸出可以看到,實際存入的檔案大小,剛好是size限制的大小。很明顯,size起到了避免系統RAM/SWAP記憶體被tmpfs 全部填滿的情況。也體現了tmpfs 比ramfs的優勢所在。所以推薦使用tmpfs。 

下面是之前tmpfs介紹中所說的tmpfs在/proc/meminfo 以及shared 顯示。

[root@docker ~]# free -m && echo '-------------------------/proc/meminfo' && cat /proc/meminfo |grep Shmem
              total        used        free      shared  buff/cache   available
Mem:           1839         268         174        1024        1397         393
Swap:             0           0           0
-------------------------/proc/meminfo
Shmem:           1048964 kB

 

五、常見記憶體不足或記憶體溢位導致的故障分析

1.一次記憶體異常導致應用被kill的案例

某台應用無法提供服務,主機sshd無法存取,通訊異常。使用vnc連線到本地終端後,發現終端介面上報以下錯誤紀錄檔,也無法進行操作:

INFO: task sh:12628 blocked for more than 120 seconds.Not tainted 2.6.32-431.el6.x86 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.

上述錯誤,只是警告提示,跟系統核心引數vm.dirty_ratio有關係,後面會專門的介紹。 由於該主機無法響應任何操作,系統也登陸不進去,只有斷電重新啟動了(這裡也可看出系統紀錄檔收集到某個中心節點的好處Y(^_^)Y)。重新啟動後觀察系統messages紀錄檔,下面是紀錄檔的一部分,這裡有完整的紀錄檔,有興趣的可以下載看下:

May 19 00:31:07 robot-web kernel: VFS: file-max limit 65535 reached
May 19 00:31:30 robot-web kernel: VFS: file-max limit 65535 reached
May 19 00:32:24 robot-web kernel: sh invoked oom-killer: gfp_mask=0x84d0, order=0, oom_adj=0, oom_score_adj=0
May 19 00:32:24 robot-web kernel: sh cpuset=/ mems_allowed=0
May 19 00:32:24 robot-web kernel: Pid: 20387, comm: sh Not tainted 2.6.32-431.el6.x86_64 #1
May 19 00:32:24 robot-web kernel: Call Trace:
May 19 00:32:24 robot-web kernel: [<ffffffff810d05b1>] ? cpuset_print_task_mems_allowed+0x91/0xb0
May 19 00:32:24 robot-web kernel: [<ffffffff81122960>] ? dump_header+0x90/0x1b0
May 19 00:32:24 robot-web kernel: [<ffffffff8122798c>] ? security_real_capable_noaudit+0x3c/0x70
May 19 00:32:24 robot-web kernel: [<ffffffff81122de2>] ? oom_kill_process+0x82/0x2a0
May 19 00:32:24 robot-web kernel: [<ffffffff81122d21>] ? select_bad_process+0xe1/0x120
May 19 00:32:24 robot-web kernel: [<ffffffff81123220>] ? out_of_memory+0x220/0x3c0
May 19 00:32:24 robot-web kernel: [<ffffffff8112fb3c>] ? __alloc_pages_nodemask+0x8ac/0x8d0
May 19 00:32:24 robot-web kernel: [<ffffffff81167a9a>] ? alloc_pages_current+0xaa/0x110
May 19 00:32:24 robot-web kernel: [<ffffffff8104ee9b>] ? pte_alloc_one+0x1b/0x50
May 19 00:32:24 robot-web kernel: [<ffffffff81146412>] ? __pte_alloc+0x32/0x160
May 19 00:32:24 robot-web kernel: [<ffffffff8114b220>] ? handle_mm_fault+0x1c0/0x300
May 19 00:32:24 robot-web kernel: [<ffffffff8104a8d8>] ? __do_page_fault+0x138/0x480
May 19 00:32:24 robot-web kernel: [<ffffffff8152d45e>] ? do_page_fault+0x3e/0xa0
May 19 00:32:24 robot-web kernel: [<ffffffff8152a815>] ? page_fault+0x25/0x30
May 19 00:32:24 robot-web kernel: [<ffffffff8152d45e>] ? do_page_fault+0x3e/0xa0
May 19 00:32:24 robot-web kernel: [<ffffffff8152a815>] ? page_fault+0x25/0x30
May 19 00:32:24 robot-web kernel: Mem-Info:
May 19 00:32:24 robot-web kernel: Node 0 DMA per-cpu:
May 19 00:32:24 robot-web kernel: CPU    0: hi:    0, btch:   1 usd:   0
May 19 00:32:24 robot-web kernel: CPU    1: hi:    0, btch:   1 usd:   0
May 19 00:32:24 robot-web kernel: CPU    2: hi:    0, btch:   1 usd:   0
May 19 00:32:24 robot-web kernel: CPU    3: hi:    0, btch:   1 usd:   0
May 19 00:32:24 robot-web kernel: Node 0 DMA32 per-cpu:
May 19 00:32:24 robot-web kernel: CPU    0: hi:  186, btch:  31 usd:   0
May 19 00:32:24 robot-web kernel: CPU    1: hi:  186, btch:  31 usd:   0
May 19 00:32:24 robot-web kernel: CPU    2: hi:  186, btch:  31 usd:   0
May 19 00:32:24 robot-web kernel: CPU    3: hi:  186, btch:  31 usd:   0
May 19 00:32:24 robot-web kernel: Node 0 Normal per-cpu:
May 19 00:32:24 robot-web kernel: CPU    0: hi:  186, btch:  31 usd:   0
May 19 00:32:24 robot-web kernel: CPU    1: hi:  186, btch:  31 usd:   0
May 19 00:32:24 robot-web kernel: CPU    2: hi:  186, btch:  31 usd:   0
May 19 00:32:24 robot-web kernel: CPU    3: hi:  186, btch:  31 usd:   0
May 19 00:32:24 robot-web kernel: active_anon:1459499 inactive_anon:284686 isolated_anon:0
May 19 00:32:24 robot-web kernel: active_file:54 inactive_file:45 isolated_file:0
May 19 00:32:24 robot-web kernel: unevictable:0 dirty:0 writeback:0 unstable:0
May 19 00:32:24 robot-web kernel: free:26212 slab_reclaimable:6599 slab_unreclaimable:53001
May 19 00:32:24 robot-web kernel: mapped:697 shmem:793 pagetables:131666 bounce:0
May 19 00:32:24 robot-web kernel: Node 0 DMA free:15728kB min:124kB low:152kB high:184kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15340kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
May 19 00:32:24 robot-web kernel: lowmem_reserve[]: 0 3000 8050 8050
May 19 00:32:24 robot-web kernel: Node 0 DMA32 free:45816kB min:25140kB low:31424kB high:37708kB active_anon:1983180kB inactive_anon:496216kB active_file:28kB inactive_file:72kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:3072092kB mlocked:0kB dirty:0kB writeback:0kB mapped:2000kB shmem:1912kB slab_reclaimable:3200kB slab_unreclaimable:82768kB kernel_stack:10296kB pagetables:147684kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:1 all_unreclaimable? no
May 19 00:32:24 robot-web kernel: lowmem_reserve[]: 0 0 5050 5050
May 19 00:32:24 robot-web kernel: Node 0 Normal free:43304kB min:42316kB low:52892kB high:63472kB active_anon:3854816kB inactive_anon:642528kB active_file:188kB inactive_file:108kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:5171200kB mlocked:0kB dirty:0kB writeback:0kB mapped:788kB shmem:1260kB slab_reclaimable:23196kB slab_unreclaimable:129236kB kernel_stack:26800kB pagetables:378980kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:59 all_unreclaimable? no
May 19 00:32:24 robot-web kernel: lowmem_reserve[]: 0 0 0 0
May 19 00:32:24 robot-web kernel: Node 0 DMA: 2*4kB 1*8kB 2*16kB 2*32kB 2*64kB 1*128kB 0*256kB 0*512kB 1*1024kB 1*2048kB 3*4096kB = 15728kB
May 19 00:32:24 robot-web kernel: Node 0 DMA32: 340*4kB 76*8kB 461*16kB 264*32kB 132*64kB 62*128kB 12*256kB 5*512kB 0*1024kB 1*2048kB 1*4096kB = 45952kB
May 19 00:32:24 robot-web kernel: Node 0 Normal: 9334*4kB 326*8kB 2*16kB 8*32kB 4*64kB 2*128kB 2*256kB 0*512kB 0*1024kB 1*2048kB 0*4096kB = 43304kB
May 19 00:32:24 robot-web kernel: 30346 total pagecache pages
May 19 00:32:24 robot-web kernel: 29453 pages in swap cache
May 19 00:32:24 robot-web kernel: Swap cache stats: add 691531, delete 662078, find 1720814/1738009
May 19 00:32:24 robot-web kernel: Free swap  = 0kB
May 19 00:32:24 robot-web kernel: Total swap = 2064376kB
May 19 00:32:24 robot-web kernel:  0  8384    26517       56   1       0             0 sh
May 19 00:32:24 robot-web kernel: [ 8385]     0  8385    64694     2309   2       0             0 php
May 19 00:32:24 robot-web kernel: [ 8386]     0  8386    26517       56   2       0             0 sh
May 19 00:32:24 robot-web kernel: [ 8387]     0  8387    26517       57   0       0             0 sh
May 19 00:32:24 robot-web kernel: [ 8388]     0  8388    26517       55   1       0             0 sh
May 19 00:32:24 robot-web kernel: [ 8402]     0  8402    26517       56   0       0             0 sh
May 19 00:32:24 robot-web kernel: [ 8887]    93  8887    23967      272   1       0             0 sendmail
May 19 00:32:24 robot-web kernel: [15917]     0 15917    64760     2347   3       0             0 php
May 19 00:32:24 robot-web kernel: [15921]     0 15921    64760     2351   3       0             0 php
May 19 00:32:24 robot-web kernel: [15923]     0 15923    64760     2349   2       0             0 php
May 19 00:32:24 robot-web kernel: [15925]     0 15925    64696     2343   3       0             0 php
May 19 00:32:24 robot-web kernel: [15950]     0 15950    64760     2346   3       0             0 php
我篩選了幾條,主要內容有以下部分:
 1 May 19 00:31:30 robot-web kernel: VFS: file-max limit 65535 reached
 2 ...
 3 
 4 May 19 00:32:24 robot-web kernel: Free swap  = 0kB
 5 May 19 00:32:24 robot-web kernel: Total swap = 2064376kB
 6 ...
 7  
 8 May 19 00:32:24 robot-web kernel: [20477]     0 20477    26518       55   3       0             0 sh
 9 May 19 00:32:24 robot-web kernel: Out of memory: Kill process 1572 (mysqld) score 3 or sacrifice child
10 May 19 00:32:24 robot-web kernel: Killed process 1572, UID 500, (mysqld) total-vm:452564kB, anon-rss:5400kB, file-rss:40kB
11 May 19 00:32:24 robot-web kernel: sh invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
從紀錄檔中可以看到兩個問題,1是系統開啟檔案控制代碼數到達設定上限;2是記憶體不足,swap交換空間都用完了,觸發了記憶體Oom-Killer,由oom-killer根據演算法選擇了mysqld,將其殺死(稍後將會說下oom-killer是怎麼選擇的)。按理應該系統這時就可以恢復的,但是當時系統內的crontabd 裡有每10秒執行的sh任務,且這些命令執行完沒有正常釋放,導致進程連線數太多,佔用的記憶體超過了系統的可用記憶體。所以猜測這是系統無法響應任何操作的根本原因,只能重新啟動。

重新啟動系統恢復正常後,根據以上出現的問題,對系統進行了調優工作。主要包括以下幾個部分:

  • 按照提示修改 /proc/sys/kernel/hung_task_timeout_secs 值為 0
    臨時:
    echo 0 > /proc/sys/kernel/hung_task_timeout_secs
    或
    sysctl -w hung_task_timeout_secs=0
    
    永久
    echo “kernel.hung_task_timeout_secs = 0” >>/etc/sysctl.conf
    sysctl -p
     
  • 增加系統實體記憶體到16G,並修改使用者open files限制數為1048576 計算是每4M記憶體開啟256個檔案數,即:(16G*1024)M/4M *256。
    臨時:
    ulimit -n 1048576echo "ulimit -n 1048576" >> /etc/profile
    
    永久:編寫/etc/security/limits.conf,根據提示在末行新增

  • 增加file-max值為1048576
     
    臨時:
    echo 1048576 > /proc/sys/fs/file-max
    或
    sysctl -w fs.file-max=1048576
    
    永久
    echo “fs.file-max=1048576” >> /etc/sysctl.conf
    sysctl -p
     
  • 優化修改系統對臟資料的重新整理策略
     
    臨時:
    sysctl -w vm.dirty_background_ratio=5
    sysctl -w vm.dirty_ratio=10
    
    永久:
    echo "vm.dirty_background_ratio=5" >> /etc/sysctl.conf
    echo "vm.dirty_ratio=10" >> /etc/sysctl.conf
    sysctl -p 
  • 優化crontab裡的任務計劃,檢查,確保執行後釋放
  • 由於該系統是虛擬機器,關閉swap分割區。(虛擬機器裡使用磁碟做swap分割區,IO效能會更低)

做了以上修改優化後,系統到目前為止,沒有出現過異常。

2.對以上引數修改進行分析

"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message

先說下這個吧。關於核心為什麼提示我們要“修改這個時間為0,來禁用這條訊息”的提示。先看下hung_task_timeout_secs的介紹,參考核心文件:

hung_task_timeout_secs:

Check interval. When a task in D state did not get scheduled
for more than this value report a warning.
This file shows up if CONFIG_DETECT_HUNG_TASK is enabled.

0: means infinite timeout - no checking done.
Possible values to set are in range {0..LONG_MAX/HZ}.

核心定時檢測系統中處於D狀態的進程,如果其處於D狀態的時間超過了指定時間(預設120s,可以設定),則列印相關堆疊資訊,也可以通過proc引數設定使其直接panic。

預設情況下,Linux使用高達40%的可用記憶體將進行檔案系統快取。達到此標記後,檔案系統將所有未完成的資料重新整理到磁碟,導致所有的IO進行同步。要將此資料重新整理到磁碟,預設情況下有120秒的時間限制。在這種情況下,IO子系統速度不夠快,可以有120秒的時間重新整理資料。由於IO子系統響應緩慢,並且使用者進程又提出了更多的請求,系統記憶體將被填滿,從而導致上述截圖中的錯誤提示。

將其值修改為 0,意味著表示可以無限超時--不檢查完成情況。但此並不能真正的解決問題根源。

與hung_task其相關的幾個引數

[root@docker ~]# sysctl -a|grep vm.dirty
vm.dirty_background_bytes
= 0
vm.dirty_background_ratio
= 10
vm.dirty_bytes
= 0
vm.dirty_expire_centisecs
= 3000
vm.dirty_ratio
= 30
vm.dirty_writeback_centisecs
= 500

這裡只說下其中兩個相關的引數:vm.dirty_ratio 、vm.dirty_background_ratio,以下內容出自Linux核心文件介紹:

dirty_background_ratio

Contains, as a percentage of total available memory that contains free pages
and reclaimable pages, the number of pages at which the background kernel
flusher threads will start writing out dirty data.

The total available memory is not equal to total system memory.
==============================================================
dirty_ratio

Contains, as a percentage of total available memory that contains free pages
and reclaimable pages, the number of pages at which a process which is
generating disk writes will itself start writing out dirty data.

The total available memory is not equal to total system memory.

dirty_background_ratio:是系統總記憶體的占用百分比,其中包括空閒頁面和可回收頁面。後台核心重新整理執行緒的頁面開始寫入臟資料。即dirty data如果達到了dirty_background_ratio,則核心將在後台執行回寫磁碟,但是應用程式仍然可以在不阻塞的情況下寫入頁面快取。

dirty_ratio:包含可用頁面和可回收頁面的佔 總可用記憶體的百分比,產生磁碟寫入的進程將自動開始寫入臟資料。這時應用程式將阻塞並將髒頁面寫入磁碟。直到系統內的dirty page低於該值。

即先達到dirty_background_ratio,這是程式不會阻塞,等到達dirty_ratio時,程式將阻塞請求,直到將資料寫入磁碟中。這些引數的值取決於系統的執行的什麼程式,如果執行大型資料庫,建議將這些值保持小數值 background_ratio < dirty_ratio,以避免I/O瓶頸以及增加系統負載。這也是作為系統VM優化的一個點。

 

說下file-max

sysctl -w fs.file-max=1048576

file-max中的值表示Linux核心將要分配的檔案控制代碼的最大數量。 每當應用程式請求檔案控制代碼時,核心會動態分配檔案控制代碼,但核心在應用程式關閉時並不會釋放銷毀這些檔案控制代碼。核心會迴圈使用這些檔案控制代碼。也意味著隨著時間的推移,分配的檔案控制代碼的總數將增加,即使當前使用的檔案控制代碼的數量可能較低。所以當您收到大量關於執行檔案控制代碼的錯誤訊息時,可能需要增加file-max值的大小。

 

3.什麼是oom-killer ?

介紹

OOM(Out Of Memory) Management:它有一個簡單的任務->檢查系統是否有足夠的可用記憶體來滿足應用程式,驗證系統是否真的是記憶體不足,如果是這樣,請‘選擇’一個進程來殺死它。


IT145.com E-mail:sddin#qq.com