haproxy-代碼閱讀-記憶體管理

来源:http://www.cnblogs.com/shenlinken/archive/2017/05/29/6917509.html
-Advertisement-
Play Games

haproxy記憶體池概述 記憶體池按照類型分類,每個類型的記憶體池都有一個名字,用鏈表記錄空閑的記憶體塊,每個記憶體塊大小相等,並按照16位元組對齊。 haporxy用pool_head 結構記錄記憶體池 在程式執行過程中,產生的記憶體池,很有可能按照大小,排列成如下方式: 記憶體池的創建 haproxy創建記憶體池 ...


haproxy記憶體池概述

記憶體池按照類型分類,每個類型的記憶體池都有一個名字,用鏈表記錄空閑的記憶體塊,每個記憶體塊大小相等,並按照16位元組對齊。
haporxy用pool_head 結構記錄記憶體池

struct pool_head {
    void **free_list;   /* 空閑鏈表 */
    struct list list;   /* 雙向鏈表,鏈接每種類型的記憶體池 */
    unsigned int used;  /* 使用了多少記憶體塊 */
    unsigned int allocated; /* 分配了多少記憶體塊 */
    unsigned int limit; /* 記憶體塊上限 */
    unsigned int minavail;  /* 最少保留幾個,回收時不會全部回收 */
    unsigned int size;  /* 記憶體塊大小 */
    unsigned int flags; /* 能否共用,類型不同,但大小相同的,能否共用一個pool_head */
    unsigned int users; /* 記憶體池有幾個使用者 */
    char name[12];      /* 記憶體池名稱 */
};

在程式執行過程中,產生的記憶體池,很有可能按照大小,排列成如下方式:

記憶體池的創建

haproxy創建記憶體池時,會先檢查記憶體池中,有沒有與所需大小相同的記憶體池,有且記憶體池可共用,將pool_head.users++。若沒有,則新創建一個記憶體池。

struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
{
    struct pool_head *pool;
    struct pool_head *entry;
    struct list *start;
    unsigned int align;
    
    //按照16位元組對齊
    align = 16;
    size  = (size + align - 1) & -align;
    //pools是全局變數,記憶體池的頭節點
    start = &pools;
    pool = NULL;

    
    list_for_each_entry(entry, &pools, list) {
        if (entry->size == size) {
            if (flags & entry->flags & MEM_F_SHARED) {//大小相等且可共用
                pool = entry;
                break;
            }
        }
        else if (entry->size > size) { //記憶體池按照大小排序,新pool_head,插在適當位置
            start = &entry->list;
            break;
        }
    }
    //創建一個新的記憶體池
    if (!pool) {
        pool = CALLOC(1, sizeof(*pool));
        if (!pool)
            return NULL;
        if (name)
            strlcpy2(pool->name, name, sizeof(pool->name));
        pool->size = size;
        pool->flags = flags;
        LIST_ADDQ(start, &pool->list);
    }
    pool->users++;
    return pool;
}

記憶體申請

create_pool僅僅是申請了記憶體池的類型,還沒有具體分配記憶體,分配記憶體的工作由pool_refill_alloc來完成

void *pool_refill_alloc(struct pool_head *pool)
{
    void *ret;

    //如果可申請的記憶體塊有上限,且已達上限,不再申請
    if (pool->limit && (pool->allocated >= pool->limit))
        return NULL;
    ret = MALLOC(pool->size);
    //如果申請失敗,pool_gc2()垃圾回收,然後再申請一次,再失敗就放棄
    if (!ret) {
        pool_gc2(); 
        ret = MALLOC(pool->size);
        if (!ret)
            return NULL;
    }
    pool->allocated++;
    pool->used++;
    return ret;
}

其中,pool_gc2()垃圾回收函數,會遍歷所有記憶體池,並釋放空閑記憶體塊(留下minavail的數量)。
使用者申請記憶體,不是直接調用pool_refill_alloc,而是通過調用pool_alloc2,如果free_list中沒有空閑記憶體了,則調用pool_refill_alloc申請下記憶體。如果還有空閑記憶體,則使用第一塊記憶體,free_list指向下一塊。

#define pool_alloc2(pool)                                     \
({                                                            \
        void *__p;                                            \
        if ((__p = pool->free_list) == NULL)                  \
                __p = pool_refill_alloc(pool);                \
        else {                                                \
                pool->free_list = *(void **)pool->free_list;  \
                pool->used++;                                 \
        }                                                     \
        __p;                                                  \
})

記憶體釋放

如果記憶體塊的使用者,在申請記憶體塊後,不主動釋放記憶體塊,那麼銷毀記憶體池後,記憶體塊將無法回到記憶體。所以,必須註意,要主動釋放記憶體塊。
記憶體塊的釋放很簡單,將記憶體塊直接放到空閑鏈表的第一個節點就行。

#define pool_free2(pool, ptr)                           \
({                                                      \
        *(void **)ptr = (void *)pool->free_list;        \
        pool->free_list = (void *)ptr;                  \
        pool->used--;                                   \
        pool_gc2_ifneed(pool);                          \
})

銷毀記憶體池

記憶體池的銷毀很簡單,釋放所有空閑記憶體塊,然後釋放記憶體池。如果還有使用中的記憶體(pool->used != 0),停止釋放

void *pool_destroy2(struct pool_head *pool)
{
    if (pool) {
        pool_flush2(pool);
        if (pool->used)
            return pool;
        pool->users--;
        if (!pool->users) {
            LIST_DEL(&pool->list);
            FREE(pool);
        }
    }
    return NULL;
}

其中, pool_flush2函數會直接釋放掉所有空閑記憶體

void pool_flush2(struct pool_head *pool)
{
    void *temp, *next;
    if (!pool)
        return;

    next = pool->free_list;
    while (next) {
        temp = next;
        next = *(void **)temp;
        pool->allocated--;
        FREE(temp);
    }
    pool->free_list = next;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • haproxy的記憶體管理中,通過pool_head free_list,存儲空閑記憶體塊,free_list是個二級指針,卻把空閑記憶體塊都串了起來,沒有用next,pre之類的指針。怎麼實現的?著實思考了半個小時才明白。 pool_head結構: 可知,free_list是個二級指針,二級指針是指向指 ...
  • vim: 移動游標至段首:^或者home鍵 移動游標至段尾:$或者end鍵 刪除游標位置到本行開頭:d0或者d^ 刪除游標位置到本行末尾:D或者d$ 撤銷操作:u 取消撤銷操作:ctrl+r ...
  • 1.使用xinput list查看與觸摸板相關的id,以下是本機的輸出,沒搞清楚為什麼是Mouse!!! jello@jello:~$ xinput list⎡ Virtual core pointer id=2 [master pointer (3)]⎜ ↳ Virtual core XTEST ...
  • SPI (Serial Peripheral interface),顧名思義就是串列外圍設備介面。SPI是一種高速的,全雙工,同步的通信匯流排,並且在晶元的管腳上只占用四根線,節約了晶元的管腳,同時為PCB的佈局上節省空間,提供方便,主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字 ...
  • 在FPGA里想寫順序執行的語句沒有C語言來的簡便花哨,比如for迴圈,或者再厲害點的來個嵌套迴圈。FPGA里要實現順序執行某個操作功能需要藉助狀態機實現。下麵比較了下兩種狀態機的寫法。 test_fsm和test_fsm2是兩個工程。 test_fsm是仿順序寫法,即數節拍的寫法,行數多,看著繞人, ...
  • 1、df -lh查看是否已經掛載2、fdisk -l 查看需要掛載的數據盤信息3、mount /dev/vdb /mntecho '/dev/vdb /mnt ext3 defaults 0 0' >> /etc/fstabdf -lhinit6//重啟服務df -lh查看是否掛載成功 伺服器為專有 ...
  • ################################################################################ Name : Mahavairocana # Author : Mahavairocana # QQ : 10353512 # WeCha ...
  • 今天腦補了普通Windows 操作系統與Windows Server區別,感覺清楚了很多。 Microsoft WindowsServer,是美國微軟公司研製的一套操作體系,它面世於1985年,起先僅僅是Microsoft-DOS模仿環境,後續的體系版別因為微軟不斷的更新晉級,不光易用,也漸漸的變成 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...