1. 程式人生 > >一次外場宕機引發對linux記憶體管理的進一步思考--Linux虛擬地址空間如何分佈

一次外場宕機引發對linux記憶體管理的進一步思考--Linux虛擬地址空間如何分佈

0x01 緣由

        外場一次伺服器宕機,一群人baba的上去圍觀,分析問題,大部分是猜測,通過回退版本後只解決了問題表象,內在的真實原因沒確定。伺服器上執行著JAVA程式和C程式,到底是什麼導致這次宕機事故。通過分析日誌發現有類似如下錯誤:
test_mem invoked oom-killer: gfp_mask=0x280da, order=0, oom_adj=0, oom_score_adj=0
Pid: 10021, comm: test_mem Tainted: G           --------------- H  2.6.32-431.el6.i686 #1
Call Trace:
 [<c04ec6c4>] ? dump_header+0x84/0x190
 [<c04eca68>] ? oom_kill_process+0x68/0x280
 [<c04ec9a2>] ? oom_badness+0x92/0xf0
 [<c04ecfe8>] ? out_of_memory+0xc8/0x1e0
 [<c04f773d>] ? __alloc_pages_nodemask+0x7fd/0x810
 [<c050df91>] ? handle_pte_fault+0x9d1/0xdd0
 [<c0444e82>] ? __wake_up+0x42/0x60
 [<c043fb80>] ? kmap_atomic_prot+0x120/0x150
 [<c050e4c1>] ? handle_mm_fault+0x131/0x1d0
 [<c04398fb>] ? __do_page_fault+0xfb/0x410
 [<c0536df1>] ? vfs_write+0x121/0x190
 [<c086279a>] ? do_page_fault+0x2a/0x90
 [<c0862770>] ? do_page_fault+0x0/0x90
 [<c0860247>] ? error_code+0x73/0x78
Mem-Info:
DMA per-cpu:
CPU    0: hi:    0, btch:   1 usd:   0
CPU    1: hi:    0, btch:   1 usd:   0
CPU    2: hi:    0, btch:   1 usd:   0
CPU    3: hi:    0, btch:   1 usd:   0
Normal per-cpu:
CPU    0: hi:  186, btch:  31 usd:   0
CPU    1: hi:  186, btch:  31 usd:  30
CPU    2: hi:  186, btch:  31 usd:   0
CPU    3: hi:  186, btch:  31 usd:   0
HighMem per-cpu:
CPU    0: hi:  186, btch:  31 usd:   0
CPU    1: hi:  186, btch:  31 usd:   0
CPU    2: hi:  186, btch:  31 usd:   0
CPU    3: hi:  186, btch:  31 usd:   0
active_anon:693728 inactive_anon:221684 isolated_anon:64
 active_file:26 inactive_file:51 isolated_file:0
 unevictable:0 dirty:0 writeback:3072 unstable:0
 free:27528 slab_reclaimable:1391 slab_unreclaimable:9790
 mapped:131 shmem:872 pagetables:4219 bounce:0
DMA free:7576kB min:64kB low:80kB high:96kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15796kB 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
lowmem_reserve[]: 0 863 3938 3938
Normal free:102148kB min:3724kB low:4652kB high:5584kB active_anon:260712kB inactive_anon:259352kB active_file:156kB inactive_file:88kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:883912kB mlocked:0kB dirty:0kB writeback:4380kB mapped:68kB shmem:0kB slab_reclaimable:5564kB slab_unreclaimable:39160kB kernel_stack:1312kB pagetables:1916kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:32 all_unreclaimable? no
lowmem_reserve[]: 0 0 24605 24605
HighMem free:388kB min:512kB low:3828kB high:7148kB active_anon:2514200kB inactive_anon:627384kB active_file:0kB inactive_file:116kB unevictable:0kB isolated(anon):256kB isolated(file):0kB present:3149484kB mlocked:0kB dirty:0kB writeback:7908kB mapped:456kB shmem:3488kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:14960kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:53 all_unreclaimable? no
lowmem_reserve[]: 0 0 0 0
DMA: 4*4kB 3*8kB 3*16kB 4*32kB 3*64kB 2*128kB 3*256kB 0*512kB 2*1024kB 2*2048kB 0*4096kB = 7576kB
Normal: 623*4kB 395*8kB 236*16kB 129*32kB 89*64kB 54*128kB 33*256kB 18*512kB 15*1024kB 7*2048kB 7*4096kB = 102196kB
HighMem: 23*4kB 14*8kB 11*16kB 2*32kB 0*64kB 2*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 700kB
4464 total pagecache pages
3404 pages in swap cache
Swap cache stats: add 2804401, delete 2800997, find 71974/72729
Free swap  = 0kB
Total swap = 4046840kB
1046000 pages RAM
819202 pages HighMem
76971 pages reserved
1403 pages shared
939149 pages non-shared
[ pid ]   uid  tgid total_vm      rss cpu oom_adj oom_score_adj name
[  485]     0   485      696        2   0     -17         -1000 udevd
[ 1211]     0  1211     3233       21   3     -17         -1000 auditd
[ 1227]     0  1227     9296       19   2       0             0 rsyslogd
[ 1273]     0  1273     3102       22   0       0             0 usbdaemon
[ 1287]     0  1287     2237       13   2     -17         -1000 sshd
[ 1363]     0  1363     3242       12   0       0             0 master
[ 1372]    89  1372     3278        9   2       0             0 qmgr
[ 1373]     0  1373     1501       33   2       0             0 crond
[ 1405]     0  1405     1376        2   0       0             0 su
[ 1413]     0  1413      502        2   0       0             0 mingetty
[ 1415]     0  1415      502        2   3       0             0 mingetty
[ 1417]     0  1417      502        2   0       0             0 mingetty
[ 1419]     0  1419      502        2   1       0             0 mingetty
[ 1421]     0  1421      502        2   3       0             0 mingetty
[ 1423]     0  1423      502        2   1       0             0 mingetty
[ 1430]     0  1430      860        2   0     -17         -1000 udevd
[ 1431]     0  1431      860        2   2     -17         -1000 udevd
[ 1433]     0  1433     2525     1286   2       0             0 startup.sh
[ 1452]     0  1452     2973       33   2       0             0 sshd
[ 1507]     0  1507     1314        2   0       0             0 bash
[ 2305]     0  2305     3073       13   2       0             0 sshd
[ 2309]     0  2309     1314       17   0       0             0 bash
[30012]     0 30012     3307        2   2       0             0 sshd
[30024]     0 30024     2280        2   0       0             0 sftp-server
[30075]     0 30075     2280        2   0       0             0 sftp-server
[30094]     0 30094     2280        2   0       0             0 sftp-server
[ 6973]     0  6973     2973       11   2       0             0 sshd
[ 6985]     0  6985     1314        2   2       0             0 bash
[12591]     0 12591     3065       11   2       0             0 sshd
[12611]     0 12611     1314        2   0       0             0 bash
[19446]     0 19446     3196       41   0       0             0 sshd
[19458]     0 19458     1314       17   0       0             0 bash
[21347]     0 21347     2973       38   2       0             0 sshd
[21351]     0 21351     1314       17   0       0             0 bash
[21483]     0 21483     3008       44   0       0             0 sshd
[21495]     0 21495     1314       17   0       0             0 bash
[21583]     0 21583     3008       41   3       0             0 sshd
[21587]     0 21587     1314       27   2       0             0 bash
[ 5391]    89  5391     3261       18   2       0             0 pickup
[ 8906]     0  8906      642       46   2       0             0 top
[ 9064]     0  9064      675       74   2       0             0 top
[ 9289]     0  9289     2973       40   2       0             0 sshd
[ 9301]     0  9301     1314       17   0       0             0 bash
[10020]     0 10020   785602     2722   3       0             0 test_mem1
[10021]     0 10021   636028   457164   0       0             0 test_mem
[10022]     0 10022   495963   449718   1       0             0 test_mem
[10118]     0 10118     1016       23   1       0             0 sleep
Out of memory: Kill process 10020 (test_mem1) score 365 or sacrifice child

0x02 日誌現象分析

        系統觸發了invoked oom-killer機制,下面為相關簡介,但不是導致宕機的原因,只能導致程式退出重啟; oom kiiler會在記憶體緊張的時候,會依次kill記憶體佔用較高的程序,傳送Signal 15(SIGTERM)。並在/var/log/message中進行記錄。裡面會記錄一些如pid,process name,cpu mask,trace等資訊,通過監控可以發現類似問題。

0x03 記憶體佈局

x86_32:      《深入理解計算機系統》圖
     

x86_64:

虛擬記憶體的佈局說明,Linux使用虛擬地址空間,大大增加了程序的定址空間,由低地址到高地址分別為:
a.只讀段:該部分空間只能讀,不可寫,包括程式碼段、rodata 段(C常量字串和#define定義的常量); b.資料段:儲存全域性變數、靜態變數的空間; c.堆:就是平時所說的動態記憶體,malloc/new大部分都來源於此。其中堆頂的位置可通過函式brk()和 sbrk()進行動態調整; d.檔案對映區域:如動態庫、共享記憶體等對映物理空間的記憶體,一般是mmap函式所分配的虛擬地址空間; e.棧:用於維護函式呼叫的上下文空間,一般為8M ,可通過ulimit -s檢視; f.核心虛擬空間:使用者程式碼不可見的記憶體區域,由核心管理; 利用測試程式檢視下各自情況:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int   global_num = 0;
char  global_str_arr [65536] = {'a'};
int main(int argc, char** argv)
{
    char* heap_var = NULL;
    int local_var = 0;
    printf("Address of function main 0x%lx\n", main);
    printf("Address of global_num 0x%lx\n", &global_num);
    printf("Address of global_str_arr 0x%lx ~ 0x%lx\n", &global_str_arr[0], &global_str_arr[65535]);
    printf("Bot of stack is 0x%lx\n", &heap_var);
    printf("Top of stack is 0x%lx\n", &local_var);
    printf("Top of heap is 0x%lx\n", sbrk(0));
    heap_var = malloc(sizeof(char) * 127 * 1024);
    printf("Address of heap_var is 0x%lx\n", heap_var);
    printf("Top of heap after malloc is 0x%lx\n", sbrk(0));
    free(heap_var);
    heap_var = NULL;
    printf("Top of heap after free is 0x%lx\n", sbrk(0));
    return 1;
}
執行結果:
gcc -o mem_layout mem_layout.c
[email protected] test]# ./mem_layout
Address of function main 0x8048454
Address of global_num 0x8059928
Address of global_str_arr 0x8049920 ~ 0x805991f
Bot of stack is 0xbfbb8b9c
Top of stack is 0xbfbb8b98
Top of heap is 0x8486000
Address of heap_var is 0x8486008
Top of heap after malloc is 0x84c6000
Top of heap after free is 0x84a7000
可以檢視彙編:  
objdump -d mem_layout -M intel > mem_layout.asm 找對應的關係。
虛擬地址位數:      x86_32:  32位表示地址,可定址空間為4G;  x86_64:
[[email protected] test]# ./mem_layout
Address of function main 0x400594
Address of global_num 0x610bd0
Address of global_str_arr 0x600bc0 ~ 0x610bbf
Bot of stack is 0x7fff3d81ce38
Top of stack is 0x7fff3d81ce34
Top of heap is 0x1984000
Address of heap_var is 0x1984010
Top of heap after malloc is 0x19c4000
Top of heap after free is 0x19a5000
虛擬地址位數:      x86_64:  48位表示地址,可虛擬空間為128TB;具體系統具體檢視:cat /proc/cpuinfo | grep address

0x04 模擬記憶體分配耗盡場景

模擬目的:      1、觸發oom-killer機制,看是否導致宕機;      2、malloc 記憶體申請和分配機制的原理;      3、多個程式對記憶體資源的搶佔問題; 測試程式碼1:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BLOCK_SIZE 1024*1024

int main(void)
{
        char *block = NULL;
        int mem_total = 0;
        while(1)
        {
                usleep(200);
                block = malloc(BLOCK_SIZE);
                if(block == NULL)
                {
                        printf("Malloc Fail\n");
                        sleep(360);
                }
                mem_total++;
                printf("Mem Total %d Kb\n", mem_total);
                //memset(block, 'a', BLOCK_SIZE);
        }
        return EXIT_SUCCESS;
}
編譯執行:
     gcc -o test_mem test_mem.c
     ./test_mem
執行結果:
Mem Total 3052 Kb
Mem Total 3053 Kb
Mem Total 3054 Kb
Mem Total 3055 Kb
Mem Total 3056 Kb
Malloc Fail

ps -ef | grep test_mem
top -p xxxx
 截圖如下:
虛擬地址 3G 實體地址才12M,為什麼?        因為不是 malloc 後就馬上佔用實際記憶體,而是第一次使用時發現虛存對應的物理頁面未分配,產生缺頁中斷,才真正分配物理頁面,同時更新程序頁面的對映關係。這也是 Linux 虛擬記憶體管理的核心概念之一。 測試程式2(將上述註釋去掉):

A.執行上述程式後,使用者態可訪問的記憶體空間已經佔完,結果還是未觸發 oom-killer機制,為什麼?
執行另一個程式處理過程:
     ./test_mem2
B.top觀察,出現的現象時,test_mem佔用的記憶體在減少,test_mem2記憶體在不斷上升,為什麼?
同時執行兩個程式後,也未觸發 oom-killer,為什麼?
觀察下記憶體情況:
[[email protected] ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3819       3693        126          0          0          8
-/+ buffers/cache:       3684        135
Swap:         3951       2654       1297
./test_mem3
執行後,系統相當卡頓,說明資源不足。此時觸發了oom-killer,殺掉了test_mem;

A問題的解答:當實體記憶體使用完或者達到一定比例之後,我們可以使用swap做臨時的記憶體使用。當實體記憶體和swap都被使用完那麼就會出錯,out of memory。對於使用多大比例記憶體之後開始使用swap,在系統的配置檔案中可以通過調整引數進行修改。cat  /proc/sys/vm/swappiness
B問題的解答:當記憶體沒有可用的,就必須要把記憶體中不經常執行的程式給踢出去。但是踢到哪裡去,這時候swap就出現了。swap全稱為swap place,,即交換區,當記憶體不夠的時候,被踢出的程序被暫時儲存到交換區。當需要這條被踢出的程序的時候,就從交換區重新載入到記憶體,否則它不會主動交換到真是記憶體中。

0x05 結論

       當某個程序記憶體洩露嚴重時,會觸發oom-killer和系統卡頓。宕機的原因還是不明。後期繼續分析!!


相關推薦

外場引發linux記憶體管理進一步思考--Linux虛擬地址空間如何分佈

0x01 緣由         外場一次伺服器宕機,一群人baba的上去圍觀,分析問題,大部分是猜測,通過回退版本後只解決了問題表象,內在的真實原因沒確定。伺服器上執行著JAVA程式和C程式,到底是什麼導致這次宕機事故。通過分析日誌發現有類似如下錯誤: test_me

MongoDB節點引發思考(原始碼剖析)

目錄 簡介 日誌分析 副本集 如何實現 Failover 心跳的實現 electionTimeout 定時器 業務影響評估 參考連結

由Redis的hGetAll函式所引發服務事件

昨晚通宵生產壓測,終於算是將生產服務宕機的原因定位到了,心累。這篇部落格,算作一個覆盤和記錄吧。。。   先來看看Redis的快取淘汰演算法思維導圖: 說明:當實際佔用的記憶體超過Redis配置的maxmemory時,Redis就會根據使用者選擇淘汰策略清除被選中的key。  

Linux虛擬地址空間分佈

在多工作業系統中,每個程序都執行在屬於自己的記憶體沙盤中。這個沙盤就是虛擬地址空間(Virtual Address Space),在32位模式下它是一個4GB的記憶體地址塊。在Linux系統中, 核心程序和使用者程序所佔的虛擬記憶體比例是1:3,而Windo

編譯後的程式是如何在作業系統(linux)中執行的,虛擬地址空間到實際實體記憶體的訪問

Linux中,每個程序通過一個task_struct結構體描述,每個程序地址虛擬空間通過一個mm_struct描述,c語言中每個段空間通過vm_area_struct描述,關係如下, 當執行

“淘寶購物”引發出來產品的思考

b)告訴使用者,哪些關鍵點必須注意,否則會影響安裝質量,有點東西安裝錯了,會導致木板損壞,很難恢復(例如:塑料螺帽安裝到木板的小孔中的時候,不能砸得太狠,必須保證跟木板齊平,突出來會讓木板銜接不密封,太深了會讓螺絲無法卡在旋轉螺帽中) (adsbygoogle = window.a

給女朋友轉賬引發分散式事務的思考

本文在個人技術部落格不同步釋出,詳情可用力戳 亦可掃描螢幕右側二維碼關注個人公眾號,公眾號內有個人聯絡方式,等你來撩...    前兩天發了工資,第一反應是想著要給遠方的女朋友一點驚喜!於是打開了平安銀行的APP給女朋友轉點錢!填寫上對方招商銀行卡的卡號、開戶名,一鍵轉賬!搞定!在我點選的那瞬間,就收到了

ORACLE sql調優之記錄trim函數引發的大表全表掃描

oracle trim 全表掃描 sql 調優 2017年8月14日,一地市oracle相關的調度程序ETL抽取速度奇慢,sql語句每次執行平均時間要9秒左右,如果所示:該調度過程涉及的sql語句如下:select count(*) from (SELECT rtrim(

事務配置失敗引發的總結

第一個 過程 service logs 代理 生成 bsp time log 環境 :   springboot + mybatis + shiro + mysql 需求:    service 層配置事務過程 :   1. springboot 啟動類加上 @Enabl

Nginx+Tomcat做負載均衡時臺伺服器實現自動切換

思路:有兩種方式。 第一種:設定一臺伺服器為備機,只有當訪問的伺服器異常時才會訪問它; 第二種:設定伺服器轉發請求超時時間。 一、設定備機: 在安裝目錄下(例E:\nginx-1.14.0\conf)開啟nginx.conf修改 upstream netitcast.com {

bochs磁碟問題引發的除錯慘案

最近用bochs模擬一個簡單的作業系統,將自己建立的硬碟作為啟動盤,建立硬碟的命令如下: 建立的flat模式的硬碟,命令列已經提示瞭如何在bochs配置檔案中加入該硬碟,自己卻作死寫了下面這句: ata0-master: type=disk, mo

mybatis中ognl引發的bug排查

現象   專案組一妹子程式設計師求助,說mybatis有bug,有一個值明明設定的是A.prop1=XXX,但是存到資料庫裡面卻會自動變成A.prop1=true,嘗試了各種調整也找不原因,都快急瘋了!我以前確實沒有研究過mybatis原始碼,本著專(ba)研(me

全站升級https引發的一系列問題

中秋假期,閒來無事。花了一下午折騰了下https,說實話這年頭還有網站不上https顯然是折騰精神不夠啊~ 1、SSL證書評估 看了市面上各種型別的證書,有收費的也有免費的,但是最終還是選擇了騰訊雲提供的TrustAsia一年免費期的證書,沒有次數限制,可以過期後再次申請。最主要的原因還是我懶,哈哈~~

Spring refresh context引發Data source is closed異常的坑

背景:Spring的profile寫在了自定義配置檔案中,需要手動讀取配置檔案,手動啟用profile,關於啟用profile的方法參考Spring啟用profile的幾種方式,我們採用了方案2。程式是首先初始化Spring上下文,建立連線池和事務管理器等等,然後讀取配置檔案,獲

裝mysql服務引發的血案

上午閒玩dos命令,因為dos命令無法啟動mysql服務,於是百度查詢原因,然後從裝了服務。從這裡開始一場沒有硝煙血案就開始了。 從裝mysql服務之後,按原來的方式(原來的使用者名稱密碼)連線資料庫,可是不管怎樣都連線不上 這是什麼原因呢?我想了想,試試不填寫密碼看能不

排查mbstowcs誤用引發的bug

問題程式碼如下: #define MAX_LINE_LEN (10240*2) char tieziLine[MAX_LINE_LEN]; wchar_t oneTieziLine_wchar[MAX_LINE_LEN];

synchronized鎖字串引發的坑兼再談Java字串

問題描述業務有一個需求,我把問題描述一下:通過代理IP訪問國外某網站N,每個IP對應一個固定的網站N的COOKIE,COOKIE有失效時間。併發下,取IP是有一定策略的,取到IP之後拿IP對應的COOK

MongoDB效能問題+Linux記憶體管理學習筆記--實體記憶體分配

最近忙著把一個專案從MySQL遷移到MongoDB,在匯入舊資料的過程中,遇到了些許波折,犯了不少錯誤,但同時也學到了不少知識,遂記錄下來。 公司為這個專案專門配備了幾臺高效能務器,清一色的雙路四核超執行緒CPU,外加32G記憶體,運維人員安裝好MongoDB後,就交我手裡了,我習慣於在使用新伺服器前先看

害你加班的bug就是我寫的,記升級Jenkins外掛引發的加班

## 主旨 本文主要記錄了下Jenkins升級外掛過程中出現的場景,一次加班經歷,事發時沒有截圖,有興趣可以看看。 ## 起因 ### 需求 最近有個需求:在Jenkins流水線中完成下載Git上的檔案簡單修改並提交的功能 起初找到了相關的外掛用法,即使用 `SSH Agent Plugin` 來完成這個

依賴注入不慎引發的一連串事故

一次依賴注入不慎引發的一連串事故 ## 起因和現象 偶爾會看到線上服務啟動的時候第一波流量進來之後, 遲遲沒有任何的響應,同時服務的監控檢查介面正常, 所以 K8S 叢集認為服務正常,繼續放入流量。 檢視日誌基本如下: ```log [2020-06-05T13:00:30.7080743+00