DPDK內存管理(1)(轉)

1 前言

DPDK將利用hugepage預留的物理內存統一的組織管理起來,而後以庫的方式對外提供使用的接口。下圖展現了DPDK中內存有關的模塊的相互關係。html

rte_eal            是統一的組織管理者(固然rte_eal不僅是作內存的工做)linux

rte_malloc       對外提供分配釋放內存的API,分配的內存都是rte_eal中管理的內存數組

rte_ring          提供無鎖隊列,他之間使用了rte_eal管理的內存app

rte_mempool  利用rte_eal中的內存和rte_ring提供內存池的功能socket

ret_mbuf       函數

 

2 rte_eal 對內存的組織與管理

這塊是在rte_eal中實現的。有關的函數有:ui

rte_eal_init -> eal_hugepage_info_initthis

rte_eal_init -> rte_eal_memory_initspa

rte_eal_init -> rte_eal_memzone_init3d

rte_eal_init -> eal_check_mem_on_local_socket

 

預留的內存按頁大小分類( i386 architecture supports 4K and 4M (2M in PAE mode) page sizes, ia64 architecture supports multiple page sizes 4K, 8K, 64K, 256K, 1M, 4M, 16M, 256M and ppc64 supports 4K and 16M. ),可能會有多類,DPDK會把這些信息所有讀取,使用struct hugepage_info進行保存,每一類對應一個struct hugepage_info類型的對象,全部的這些對象保存在struct internal_config的數組中。DPDK有個internal_config的全局變量,記錄這些信息。

struct hugepage_info {
    size_t hugepage_sz;   /**< size of a huge page */
    const char *hugedir;    /**< dir where hugetlbfs is mounted */
    uint32_t num_pages[RTE_MAX_NUMA_NODES];
                /**< number of hugepages of that size on each socket */
    int lock_descriptor;    /**< file descriptor for hugepage dir */
};
struct internal_config {
   ...
    unsigned num_hugepage_sizes;      /**< how many sizes on this system */
    struct hugepage_info hugepage_info[MAX_HUGEPAGE_SIZES];
};

總結 eal_hugepage_info_init,就是獲取了系統中預留的內存信息,有幾類(相同頁大小的是一類),將每一類的信息(頁大小、頁數、掛載點)存儲在internal_config.hugepage_info數組中。hugepage_info數組也進行了排序,大頁的信息放前面,小頁的信息放後面。

每一類全部內存頁,也分處在哪一個 socket上(不明白的查看NUMA相關知識補齊)的,hugepage_info中統計內存頁數會按屬於處在哪一個socket上進行統計,但在這一步(eal_hugepage_info_init)中,還區分不了每一個頁處在哪一個socket上,所以這裏尚未按socket統計頁數,將內存頁數直接記錄到num_pages[0]裏面了。

2.2 rte_eal_memory_init

int
rte_eal_memory_init(void)
{
    RTE_LOG(INFO, EAL, "Setting up memory...\n");
    const int retval = rte_eal_process_type() == RTE_PROC_PRIMARY ?
            rte_eal_hugepage_init() :
            rte_eal_hugepage_attach();
    if (retval < 0)
        return -1;

    if (internal_config.no_shconf == 0 && rte_eal_memdevice_init() < 0)
        return -1;

    return 0;
}

DPDK多進程狀態下,分爲RTE_PROC_PRIMARY進程及RTE_PROC_SECONDARY進程,RTE_PROC_PRIMARY負責初始化內存,RTE_PROC_SECONDARY獲取 RTE_PROC_PRIMARY 內存映射的信息,建立與RTE_PROC_PRIMARY同樣的內存映射。這是DPDK多進程共享內存的方式。此處先不展開描述。隨着流程的展開,天然會明白。

2.2.1 rte_eal_mem_init -> rte_eal_hugepage_init 

DPDK有個全局變量

static struct rte_config rte_config

該變量的成員 struct rte_mem_config *mem_config;  保存了DPDK管理的內存的全部信息。DPDK將內存統一的管理,就是將全部內存保存到mem_config:memseg[]中。

struct rte_mem_config {
    volatile uint32_t magic;   /**< Magic number - Sanity check. */

    /* memory topology */
    uint32_t nchannel;    /**< Number of channels (0 if unknown). */
    uint32_t nrank;       /**< Number of ranks (0 if unknown). */

    /**
     * current lock nest order
     *  - qlock->mlock (ring/hash/lpm)
     *  - mplock->qlock->mlock (mempool)
     * Notice:
     *  *ALWAYS* obtain qlock first if having to obtain both qlock and mlock
     */
    rte_rwlock_t mlock;   /**< only used by memzone LIB for thread-safe. */
    rte_rwlock_t qlock;   /**< used for tailq operation for thread safe. */
    rte_rwlock_t mplock;  /**< only used by mempool LIB for thread-safe. */

    uint32_t memzone_idx; /**< Index of memzone */

    /* memory segments and zones */
    struct rte_memseg memseg[RTE_MAX_MEMSEG];    /**< Physmem descriptors. */
    struct rte_memzone memzone[RTE_MAX_MEMZONE]; /**< Memzone descriptors. */

    /* Runtime Physmem descriptors. */
    struct rte_memseg free_memseg[RTE_MAX_MEMSEG];

    struct rte_tailq_head tailq_head[RTE_MAX_TAILQ]; /**< Tailqs for objects */

    /* Heaps of Malloc per socket */
    struct malloc_heap malloc_heaps[RTE_MAX_NUMA_NODES];
} __attribute__((__packed__));

函數先根據 struct hugepage_info hugepage_info[MAX_HUGEPAGE_SIZES]; (eal_hugepage_info_init中獲取的信息)獲得了內存頁的總數。而後爲struct hugepage_file *tmp_hp申請了內存,有多少個內存頁,就分配了多少個hugepage_file。

struct hugepage_file {
    void *orig_va;      /**< virtual addr of first mmap() */
    void *final_va;     /**< virtual addr of 2nd mmap() */
    uint64_t physaddr;  /**< physical addr */
    size_t size;        /**< the page size */
    int socket_id;      /**< NUMA socket ID */
    int file_id;        /**< the '%d' in HUGEFILE_FMT */
    int memseg_id;      /**< the memory segment to which page belongs */
#ifdef RTE_EAL_SINGLE_FILE_SEGMENTS
    int repeated;        /**< number of times the page size is repeated */
#endif
    char filepath[MAX_HUGEPAGE_PATH]; /**< path to backing file on filesystem */
};

接着針對每一類內存(相同頁大小的內存算一類)進行處理,

for(每一類) {

  for(這一類的每一頁) {         

    每一個頁對應一個hugepage_file,file_id,size,filepath初始化。其中file_id是頁的序號,這個序號是給filepath用的,是爲了給每一個頁對應的文件起一個獨立的文件名(與其餘頁不會重複)。

    創建文件<filepath>,這個文件是由於使用hugepage的內存必須按這樣的方式(先創建文件,再mmap,獲得內存虛擬地址使用)使用。

    執行mmap得到頁的虛擬地址,賦給hugepage_file:orig_va。

  } (map_all_hugepages())

全部hugepage預留的物理內存都映射完成,能夠根據hugepage_file:orig_va進行訪問了。

  for(這一類的每一頁) {

    獲取物理地址,賦給hugepage_file:physaddr。

    獲取物理地址的方式,是linux內核提供的功能,不瞭解的能夠點擊這裏補齊。

  }(find_physaddrs())

  

  for(這一類的每一頁) {

     獲取每一個頁所在的socket,賦給hugepage_file:socket_id。 

     獲取的方法也是linux內核提供的功能,具體不詳述。

  }(find_numasocket())

 

  對這一類的頁的hugepage_file[]按頁物理地址進行排序。

 

  for(這一類的每一頁) {

    從新執行mmap,並將獲取的虛擬地址賦給hugepage_file:final_va。    

  }(map_all_hugepages())

  本次從新mmap,與第一次的不一樣是,會主動先申請一塊足夠映射本類全部內存頁的虛擬地址空間,而後將本類的全部頁連續的映射的該虛擬地址空間中。

  若是找不到這麼大的虛擬地址空間,仍是會一頁一頁的映射,讓內核去決定虛擬地址空間。

  第二次映射用代碼的註釋解釋 The second mapping tries to  map continguous physical blocks in contiguous virtual blocks.

 

  將第一次的映射unmap掉

繼續

更新 internal_config.hugepage_info[].num_pages[],咱們知道bugepage_info在eal_hugepage_info_init中初始化時,不知道每一個頁是屬於哪一個socket的,所以直接使用num_pages[0]記錄了全部的頁數,但在上一步中,已經知道了每一個頁屬於哪一個socket,所以這裏更新了num_pages,準確的記錄了哪一個socket上有多少個頁。

接下來,創建了共享內存,將hugepage_file拷貝到共享內存中,這樣RTE_PROC_SECONDARY啓動後能夠在共享內存中讀取這些信息,而後建立與RTE_PROC_PRIMARY一致的映射。

最後,使用mem_config->memseg[]將全部內存管理起來。

一個struct rte_memsg對象,保存一塊相同socket的、相同頁大小的、物理地址連續的、虛擬地址連續的內存。所以整個內存由多個memseg組成。

總結rte_eal_hugepage_init,它獲取hugepge的方式預留的全部物理內存,統一的映射到虛擬地址空間中,保存到memseg中。至此,理論上能夠經過memseg獲取虛擬地址使用這些內存了。但DPDK提供了統一的方式來使用,不是誰用就能夠來拿的。

相關文章
相關標籤/搜索