91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么分析Curve中的內存管理

發布時間:2022-01-12 16:30:48 來源:億速云 閱讀:132 作者:柒染 欄目:云計算

本篇文章給大家分享的是有關怎么分析Curve中的內存管理,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

前言

Curve 實踐過程中遇到過幾次內存相關的問題,與操作系統內存管理相關的是以下兩次:

  1. chunkserver上內存無法釋放

  2. mds出現內存緩慢增長的現象

內存問題在開發階段大多很難發現,測試階段大壓力穩定性測試(持續跑7*24小時以上)、異常測試往往比較容易出問題,當然這還需要我們在測試階段足夠仔細,除了關注io相關指標外,還要關注服務端內存/CPU/網卡等資源使用情況以及采集的metric是否符合預期。比如上述問題mds 內存緩慢增長,如果只關注io是否正常,在測試階段是無法發現的。內存問題出現后定位也不容易,尤其在軟件規模較大的情況下。

下面主要是從開發者的角度來談 Curve 中的內存管理,不會過度強調內存管理理論,目的是把我們在軟件開發過程中對 Linux 內存管理的認知、內存問題分析的一些方法分享給大家。本文會從以下幾個方面展開:

  • 內存布局。結合 Curve 軟件說明內存布局。

  • 內存分配策略。說明內存分配器的必要性,以及需要解決的問題和具有的特點,然后通過舉例說明其中一個內存分配器的內存管理方法。

  • Curve 的內存管理。介紹當前 Curve 軟件內存分配器的選擇及原因。

內存布局

在說內存管理之前,首先簡要介紹下內存布局相關知識。

軟件在運行時需要占用一定量的內存用來存放一些數據,但進程并不直接與存放數據的物理內存打交道,而是直接操作虛擬內存。物理內存是真實的存在,就是內存條;虛擬內存為進程隱藏了物理內存這一概念,為進程提供了簡潔易用的接口和更加復雜的功能。本文說的內存管理是指虛擬內存管理。為什么需要抽象一層虛擬內存?虛擬內存和物理內存是如何映射管理的?物理尋址是怎么的?這些虛擬內存更下層的問題不在本文討論范圍。

Linux 為每個進程維護了一個單獨的虛擬地址空間,包括兩個部分進程虛擬存儲器(用戶空間)和內核虛擬存儲器(內核空間),小編主要討論進程可操作的用戶空間,形式如下圖。

怎么分析Curve中的內存管理

現在我們使用pmap查看運行中的 curve-mds 虛擬空間的分布。pmap用于查看進程的內存映像信息,該命令讀取的是/proc/[pid]/maps中的信息。

// pmap -X {進程id} 查看進程內存分布
sudo pmap -X 2804620
 
// pmap 獲取的 curve-mds 內存分布有很多項
Address Perm   Offset Device    Inode    Size   Rss   Pss Referenced Anonymous ShmemPmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Locked Mapping
 
// 為了方便展示這里把從 Pss 后面的數值刪除了, 中間部分地址做了省略
2804620:   /usr/bin/curve-mds -confPath=/etc/curve/mds.conf -mdsAddr=127.0.0.1:6666 -log_dir=/data/log/curve/mds -graceful_quit_on_sigterm=true -stderrthreshold=3
         Address Perm   Offset Device    Inode    Size   Rss   Pss   Mapping
      c000000000 rw-p 00000000  00:00        0   65536  1852  1852  
    559f0e2b9000 r-xp 00000000  41:42 37763836    9112  6296  6296   curve-mds
    559f0eb9f000 r--p 008e5000  41:42 37763836     136   136   136   curve-mds
    559f0ebc1000 rw-p 00907000  41:42 37763836       4     4     4   curve-mds
    559f0ebc2000 rw-p 00000000  00:00        0   10040  4244  4244
    559f1110a000 rw-p 00000000  00:00        0    2912  2596  2596   [heap]
    7f6124000000 rw-p 00000000  00:00        0     156   156   156
    7f6124027000 ---p 00000000  00:00        0   65380     0     0
    7f612b7ff000 ---p 00000000  00:00        0       4     0     0
    7f612b800000 rw-p 00000000  00:00        0    8192     8     8
    7f612c000000 rw-p 00000000  00:00        0     132     4     4
    7f612c021000 ---p 00000000  00:00        0   65404     0     0
    .....
    7f6188cff000 ---p 0026c000  41:42 37750237    2044     0     0
    7f61895b7000 r-xp 00000000  41:42 50201214      96    96     0    libpthread-2.24.so
    7f61895cf000 ---p 00018000  41:42 50201214    2044     0     0    libpthread-2.24.so
    7f61897ce000 r--p 00017000  41:42 50201214       4     4     4    libpthread-2.24.so
    7f61897cf000 rw-p 00018000  41:42 50201214       4     4     4    libpthread-2.24.so
    7f61897d0000 rw-p 00000000  00:00        0      16     4     4    
    7f61897d4000 r-xp 00000000  41:42 50200647      16    16     0    libuuid.so.1.3.0
    7f61897d8000 ---p 00004000  41:42 50200647    2044     0     0    libuuid.so.1.3.0
    7f61899d7000 r--p 00003000  41:42 50200647       4     4     4    libuuid.so.1.3.0
    7f61899d8000 rw-p 00004000  41:42 50200647       4     4     4    libuuid.so.1.3.0
    7f61899d9000 r-xp 00000000  41:42 37617895    9672  8904  8904    libetcdclient.so
    7f618a34b000 ---p 00972000  41:42 37617895    2048     0     0    libetcdclient.so
    7f618a54b000 r--p 00972000  41:42 37617895    6556  5664  5664    libetcdclient.so
    7f618abb2000 rw-p 00fd9000  41:42 37617895     292   252   252    libetcdclient.so
    7f618abfb000 rw-p 00000000  00:00        0     140    60    60    
    7f618ac1e000 r-xp 00000000  41:42 50201195     140   136     0    ld-2.24.so
    7f618ac4a000 rw-p 00000000  00:00        0    1964  1236  1236    
    7f618ae41000 r--p 00023000  41:42 50201195       4     4     4    ld-2.24.so
    7f618ae42000 rw-p 00024000  41:42 50201195       4     4     4    ld-2.24.so
    7f618ae43000 rw-p 00000000  00:00        0       4     4     4    
    7fffffd19000 rw-p 00000000  00:00        0     132    24    24    [stack]
    7fffffdec000 r--p 00000000  00:00        0       8     0     0    [vvar]
    7fffffdee000 r-xp 00000000  00:00        0       8     4     0    [vdso]
ffffffffff600000 r-xp 00000000  00:00        0       4     0     0    [vsyscall]
                                               ======= ===== ===== 
                                               1709344 42800 37113
  1. 上面輸出中進程實際占用的空間是從 0x559f0e2b9000 開始,不是內存分布圖上畫的 0x40000000。這是因為地址空間分布隨機化(ASLR),它的作用是隨機生成進程地址空間(例如棧、庫或者堆)的關鍵部分的起始地址,目的是增強系統安全性、避免惡意程序對已知地址攻擊。Linux 中/proc/sys/kernel/randomize_va_space的值為 1 或 2 表示地址空間隨機化已開啟,數值1、2的區別在于隨機化的關鍵部分不同;0表示關閉。

  2. 接下來 0x559f0e2b9000 0x559f0eb9f000 0x559f0ebc1000 三個地址起始對應的文件都是curve-mds ,但是對該文件的擁有不同的權限,各字母代表的權限r-讀 w-寫 x-可執行 p-私有 s-共享。curve-mds 是elf類型文件,從內容的角度看,它包含代碼段、數據段、BSS段等;從裝載到內存角度看,操作系統不關心各段所包含的內容,只關心跟裝載相關的問題,主要是權限,所以操作系統會把相同權限的段合并在一起去加載,就是我們這里看到的以代碼段為代表的權限為可讀可執行的段、以只讀數據為代表的權限為只讀的段、以數據段和 BSS 段為代表的權限為可讀可寫的段。

  3. 再往下 0x559f1110a000 開始,對應上圖的運行時堆,運行時動態分配的內存會在這上面進行 。我們發現也是在.bss段的結束位置進行了隨機偏移。

  4. 接著 0x7f6124000000 開始,對應的是上圖 mmap 內存映射區域,這一區域包含動態庫、用戶申請的大片內存等。到這里我們可以看到HeapMemory Mapping Region都可以用于程序中使用malloc動態分配的內存,在下一節內存分配策略中會有展開,也是本文關注重點。

  5. 接著 0x7fffffd19000 開始是棧空間,一般有數兆字節。

  6. 最后 vvar、vdso、vsyscall 區域是為了實現虛擬函數調用以加速部分系統調用,使得程序可以不進入內核態1直接調用系統調用。這里不具體展開。

內存分配策略

我們平時使用malloc分配出來的內存是在HeapMemory Mapping Region這兩個區域。mallloc 實際上由兩個系統調用完成:brkmmap

  • brk 分配的區域對應堆 heap

  • mmap 分配的區域對應 Memory Mapping Region

如果讓每個開發者在軟件開發時都直接使用系統調 brk 和 mmap 用去分配釋放內存,那開發效率將會變得很低,而且也很容易出錯。一般來說我們在開發中都會直接使用內存管理庫,當前主流的內存管理器有三種:ptmalloc``tcmalloc``jemalloc, 都提供malloc, free接口,glibc 默認使用ptmalloc。這些庫的作用是管理它通過系統調用獲得的內存區域,一般來說一個優秀的通用內存分配器應該具有以下特征:

  1. 額外的空間損耗量盡量少。比如應用程序只需要5k內存,結果分配器給他分配了10k,會造成空間的浪費。

  2. 分配的速度盡可能快。

  3. 盡量避免內存碎片。下面我們結合圖來直觀的感受下內存碎片。

  4. 通用性、兼容性、可移植性、易調試。

我們通過下面一幅圖直觀說明下 glibc 默認的內存管理器 ptmalloc 在單線程情況下堆內存的回收和分配:

怎么分析Curve中的內存管理

  1. malloc(30k)通過系統調用 brk 擴展堆頂的方式分配內存。

  2. malloc(20k)通過系統調用 brk 繼續擴展堆頂。

  3. malloc(200k)默認情況下請求內存大于 128K (由M_MMAP_THRESHOLD確定,默認大小為128K,可以調整),就利用系統調用 mmap分配內存。

  4. free(30k)這部分空間并沒有歸還給系統,而是 ptmalloc 管理著。由1、2兩步的 malloc 可以看出,我們分配空間的時候調用 brk 進行堆頂擴展,那歸還空間給系統是相反操作即收縮堆頂。這里由于第二步 malloc(20k) 的空間并未釋放,所以此時堆頂無法收縮。這部分空間是可以被再分配的,比如此時 malloc(10k),那可以從這里分配 10k 空間,而不需要通過 brk 去申請。考慮這樣一種情況,堆頂的空間一直被占用,堆頂向下的空間有部分被應用程序釋放但由于空間不夠沒有再被使用,就會形成內存碎片

  5. free(20k)這部分空間應用程序釋放后,ptmalloc 會把剛才的 20k 和 30k 的區域合并,如果堆頂空閑超過M_TRIM_THREASHOLD,會把這塊區域收縮歸還給操作系統。

  6. free(200k)mmap分配出來的空間會直接歸還給系統。

那對于多線程程序,ptmalloc 又是怎么區分配的?多線程情況下需要處理各線程間的競爭,如果還是按照之前的方式,小于HEAP_MAX_SIZE( 64 位系統默認大小為 64M )的空間使用 brk 擴展堆頂, 大于HEAP_MAX_SIZE的空間使用 mmap 申請,那對于線程數量較多的程序,如果每個線程上存在比較頻繁的內存分配操作,競爭會很激烈。ptmalloc 的方法是使用多個分配區域,包含兩種類型分配區:主分配區 和 動態分配區。

  • 主分配區:會在HeapMemory Mapping Region這兩個區域分配內存

  • 動態分配區:在Memory Mapping Region區域分配內存,在 64 位系統中默認每次申請的大小位。Main 線程和先執行 malloc 的線程使用不同的動態分配區,動態分配區的數量一旦增加就不會減少了。動態分配區的數量對于 32 位系統最多是 ( 2 number of cores + 1 ) 個,對于 64 位系統最多是( 8 number of cores + 1 )個。

舉個多線程的例子來看下這種情況下的空間分配:

// 共有三個線程
// 主線程:分配一次 4k 空間
// 線程1: 分配 100 次 4k 空間
// 線程2: 分配 100 次 4k 空間
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
 
void* threadFunc(void* id) {
		std::vector<char *> malloclist;
		for (int i = 0; i < 100; i++) {
		  malloclist.emplace_back((char*) malloc(1024 * 4));
		}
 
		sleep(300); // 這里等待是為查看內存分布
}
int main() {
    pthread_t t1,t2;
		int id1 = 1;
		int id2 = 2;
    void* s;
    int ret;
    char* addr;
 
    addr = (char*) malloc(4 * 1024);
    pthread_create(&t1, NULL, threadFunc, (void *) &id1);
    pthread_create(&t2, NULL, threadFunc, (void *) &id2);
 
		pthread_join(t1, NULL);
		pthread_join(t2, NULL);
    return 0;
}

我們用 pmap 查看下該程序的內存分布情況:

741545:   ./memory_test
         Address Perm   Offset Device    Inode   Size  Rss Pss  Mapping
    56127705a000 r-xp 00000000  08:02 62259273      4    4   4  memory_test
    56127725a000 r--p 00000000  08:02 62259273      4    4   4  memory_test
    56127725b000 rw-p 00001000  08:02 62259273      4    4   4  memory_test
    5612784b9000 rw-p 00000000  00:00        0    132    8   8  [heap]
    **7f0df0000000 rw-p 00000000  00:00        0    404  404 404  
    7f0df0065000 ---p 00000000  00:00        0  65132    0   0  
    7f0df8000000 rw-p 00000000  00:00        0    404  404 404  
    7f0df8065000 ---p 00000000  00:00        0  65132    0   0**  
    7f0dff467000 ---p 00000000  00:00        0      4    0   0  
    7f0dff468000 rw-p 00000000  00:00        0   8192    8   8  
    7f0dffc68000 ---p 00000000  00:00        0      4    0   0  
    7f0dffc69000 rw-p 00000000  00:00        0   8192    8   8  
    7f0e00469000 r-xp 00000000  08:02 50856517   1620 1052   9   libc-2.24.so
    7f0e005fe000 ---p 00195000  08:02 50856517   2048    0   0   libc-2.24.so
    7f0e007fe000 r--p 00195000  08:02 50856517     16   16  16   libc-2.24.so
    7f0e00802000 rw-p 00199000  08:02 50856517      8    8   8   libc-2.24.so
    7f0e00804000 rw-p 00000000  00:00        0     16   12  12   
    7f0e00808000 r-xp 00000000  08:02 50856539     96   96   1   libpthread-2.24.so
    7f0e00820000 ---p 00018000  08:02 50856539   2044    0   0   libpthread-2.24.so
    7f0e00a1f000 r--p 00017000  08:02 50856539      4    4   4   libpthread-2.24.so
    7f0e00a20000 rw-p 00018000  08:02 50856539      4    4   4   libpthread-2.24.so
    7f0e00a21000 rw-p 00000000  00:00        0     16    4   4   
    7f0e00a25000 r-xp 00000000  08:02 50856513    140  140   1   ld-2.24.so
    7f0e00c31000 rw-p 00000000  00:00        0     16   16  16   
    7f0e00c48000 r--p 00023000  08:02 50856513      4    4   4   ld-2.24.so
    7f0e00c49000 rw-p 00024000  08:02 50856513      4    4   4   ld-2.24.so
    7f0e00c4a000 rw-p 00000000  00:00        0      4    4   4   
    7ffe340be000 rw-p 00000000  00:00        0    132   12  12   [stack]
    7ffe3415c000 r--p 00000000  00:00        0      8    0   0   [vvar]
    7ffe3415e000 r-xp 00000000  00:00        0      8    4   0   [vdso]
ffffffffff600000 r-xp 00000000  00:00        0      4    0   0   [vsyscall]
                                               ====== ==== === 
                                               153800 2224 943

關注上面加粗的部分,紅色區域加起來是 65536K,其中有 404K 是 rw-p (可讀可寫)權限,65132K 是 —-p (不可讀寫)權限;黃色區域類似。兩個線程分配的時 ptmalloc 分別給了 動態分區,并且每次申請 64M 內存,再從這 64M 中切分出一部分給應用程序。

這里還有一個有意思的現象:我們用strace -f -e "brk, mmap, munmap" -p {pid}去跟蹤程序查看下 malloc 中的系統調用:

mmap(NULL, 8392704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f624a169000
strace: Process 774601 attached
[pid 774018] mmap(NULL, 8392704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f6249968000
[pid 774601] mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7f6241968000
[pid 774601] munmap(0x7f6241968000, 40468480strace: Process 774602 attached
) = 0
[pid 774601] munmap(0x7f6248000000, 26640384) = 0
[pid 774602] mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7f623c000000
[pid 774602] munmap(0x7f6240000000, 67108864) = 0

這里主線程 [774018] 要求分配了 8M+4k 空間;線程1 [774601] 先 mmap 了 128M 空間,再分歸還了 0x7f6241968000 為起始地址的 40468480 字節 和 0x7f6248000000為起始地址的 26640384 字節,那剩余的部分是 0x7F6244000000 ~ 0x7F6248000000。先申請再歸還是為了讓分配的這部分內存的起止地址是字節對齊的。

Curve 的內存管理

Curve 中選擇了兩種分配器:ptmallocjemalloc。其中 MDS 使用默認的 ptmalloc,Chunkserver 和 Client 端使用 jemalloc。

本文開頭提到的兩個問題在這里進行說明。首先是MDS內存緩慢增長,現象是每天增長 3G。這個問題分析的過程如下:

  1. 首先是使用pmap查看內存分布。我們用 pmap 查看內存緩慢增長的 curve-mds 內存分配情況,發現在 Memory Mapping Region 存在著大量分配的 64M 內存,且觀察一段時間后都不釋放還在一直分配。從這里懷疑存在內存泄露。

  2. 然后查看應用上請求的壓力情況。查看MDS 上相關的業務 metric,發現 MDS 上的壓力都很小,一些控制面 rpc 的 iops 在幾百左右,不應該是業務壓力較大導致的。

  3. 接下來查看 curve-mds 部分 64M 內存上的數據。使用gdb -p {pid} attach跟蹤線程,dump meemory mem.bin {addr1} {addr2}獲取指定地址段的內存,然后查看這部分內存內容,基本確定幾個懷疑點。

  4. 根據這幾個點去排查代碼,看是否有內存泄露。

Chunkserver 端不是開始就使用 jemalloc 的,最初也是用的默認的 ptmalloc。換成 jemalloc 是本文開始提到的 Chunkserver 在測試過程中出現內存無法釋放的問題,這個問題的現象是:chunkserver的內存在 2 個 小時內增長很快,一共增長了 50G 左右,但后面并未釋放。這個問題分析的過程如下:

  1. 首先分析內存中數據來源。這一點跟 MDS 不同,MDS 上都是控制面的請求以及一些元數據的緩存。而Chunkserver 上的內存增長一般來自兩個地方:一是用戶發送的請求,二是 copyset 的 leader 和 follower 之間同步數據。這兩個都會涉及到 brpc 模塊。

    brpc 的內存管理有兩個模塊 IOBuf 和 ResourcePool。IOBuf 中的空間一般用于存放用戶數據,ResourcePool 管理 socket、bthread_id 等對象,管理的內存對象單位是 64K 。

  2. 查看對應模塊的一些趨勢指標。觀察這兩個模塊的metric,發現 IOBuf 和 ResourcePool 這段時間內占用的內存都有相同的增長趨勢。

    IOBuf 后面將占用的內存歸還給 ptmalloc, ResourcePool 中管理的內存不會歸還給 ptmalloc 而是自己管理。

    從這個現象我們懷疑 IOBuf 歸還給 ptmalloc 的內存 ptmalloc 無法釋放。

  3. 分析驗證。結合第二節的內存分配策略,如果堆頂的空間一直被占用,那堆頂向下的空間也是無法被釋放的。仍然可以使用 pmap 查看當前堆上內存的大小以及內存的權限(是否有很多 —-p 權限的內存)來確定猜想。因此后面 Chunkserver 使用了 jemalloc。這里可以看到在多線程情況下,如果一部分內存被應用長期持有,使用 ptmalloc 也許就會遇到內存無法釋放的問題。

以上就是怎么分析Curve中的內存管理,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

库尔勒市| 兰西县| 无锡市| 会昌县| 余庆县| 白银市| 锡林浩特市| 易门县| 鲁山县| 海门市| 海兴县| 南澳县| 金坛市| 甘泉县| 颍上县| 南木林县| 南丹县| 靖安县| 西宁市| 临洮县| 黄冈市| 开平市| 伊春市| 永兴县| 双峰县| 韶山市| 犍为县| 乐山市| 资源县| 天峻县| 都江堰市| 蓬安县| 襄樊市| 内黄县| 沂南县| 托克托县| 报价| 香格里拉县| 昌吉市| 长泰县| 吉木萨尔县|