DPDK_API_rte_malloc源码分析

概念:

librte_malloc库提供了一套用于管理内存空间的API接口,它管理的内存是hugepages上创建出来的memzone,而不是系统的堆空间。通过这套接口,可以提高系统访问内存的命中率,防止了在使用Linux用户空间环境的4K页内存管理时容易出现TLB miss。


接口函数:

  1. void? rte_malloc( void ?ptr, size_t size, unsigned align ):用来替代malloc,从内存的huge_page中分配所需内存空间,分配的空间未被初始化。当size为0或者align不是2的整数倍的时候,返回NULL。如果align为0,则对齐方式为任意长。若空间不足或者参数错误(size=0或者align不是2的整数倍)则函数返回NULL,若执行成功则返回分配的内存空间的起始地址。
  2. void ?rte_zmalloc (const char?type, size_t size, unsigned align):和rte_malloc基本相同,只是额外将申请的内存空间初始化为0。若空间不足或者参数错误(size=0或者align不是2的整数倍)则函数返回NULL,若执行成功则返回分配的内存空间的起始地址。
  3. void ?rte_calloc (const char?type, size_t num, size_t size, unsigned align):用来替代calloc,和rte_malloc基本相同,申请的总空间大小为num * size,申请了num个连续空间,每个空间的大小为size。内存空间初始化为0。若空间不足或者参数错误(size=0或者align不是2的整数倍)则函数返回NULL,若执行成功则返回分配的内存空间的起始地址。
  4. void ?rte_realloc (void?ptr, size_t size, unsigned align):用来替代realloc,重新分配ptr指向的内存空间的大小。如果size为0,则释放此ptr指向空间。若空间不足或者参数错误(align不是2的整数倍)则函数返回NULL,若执行成功则返回分配的内存空间的起始地址。
  5. int rte_malloc_validate (void?ptr, size_t?size):如果debug宏被打开,那么此函数会检查ptr指向的内存空间的header和trailer标记是否正常,如果正常则将这个空间的长度写入到size中。当ptr无效或者pte指向的内存空间有错误时,函数返回-1,否则返回0。
  6. void rte_free( void ?ptr ):释放ptr指向的内存空间,和free函数基本一致。如果ptr为NULL,则不做任何改变。
  7. void rte_malloc_dump_stats (const char?type):将指定的type的信息转存到控制台。如果type为NULL,则转存所有的内存type。
  8. int rte_malloc_set_limit (const char?type, size_t max):设置type所能分配的最大内存。若执行成功则返回0,否则返回-1。

源码分析:

rte_malloc()

代码如下所示:

void *
rte_malloc(const char *type, size_t size, unsigned align)
{
    unsigned malloc_socket = malloc_get_numa_socket();
    /* return NULL if size is 0 or alignment is not power-of-2 */
    if (size == 0 || !rte_is_power_of_2(align))
        return NULL;
    return malloc_heap_alloc(&malloc_heap[malloc_socket], type,
            size, align == 0 ? 1 : align);
}

主要包含两个函数调用:

- malloc_get_numa_socket():获取程序分配的socket的id。程序在哪个socket上运行,就从哪个socket上分配内存。

- malloc_heap_alloc():将传入的需要malloc的空间大小和align按照CACHE_LINE_SIZE做了对齐。

size = CACHE_LINE_ROUNDUP(size);
align = CACHE_LINE_ROUNDUP(align);

在rte_config.mem_config->malloc_heaps[]数组里,此socket对应的堆中,进行匹配,查找是否有合适内存可以分配。find_suitable_element()用来在堆中找到一块合适大小的内存,分配的内存是从堆的底部开始查找的。

struct malloc_elem *prev, *elem = find_suitable_element(heap,
size, align, &prev);

如果没有找到合适的空间,则需要调用malloc_heap_add_memzone()在rte_config.mem_config->memzone[]中给堆分配一块内存:

if (elem == NULL){
    malloc_heap_add_memzone(heap, size, align);
    elem = find_suitable_element(heap, size, align, &prev);
}

调用malloc_elem_alloc()在堆中,将需要分配的内存划分出去。

if (elem != NULL)
    elem = malloc_elem_alloc(elem, size, align, prev);

注意,在malloc_elem_alloc()中,采用的也是动态划分内存块的方式,即,如果当前适用于分配的内存块大于MALLOC_ELEM_OVERHEAD + MIN_DATA_SIZE,就会将这块内存拆分开,前一部分继续还放在原有的可用于分配的内存堆中,后一部分作为malloc的内存块。详见malloc_elem_alloc()函数。

rte_zmalloc()

代码如下所示:

void *
rte_zmalloc(const char *type, size_t size, unsigned align)
{
    void *ptr = rte_malloc(type, size, align);

    if (ptr != NULL)
        memset(ptr, 0, size);
    return ptr;
}

直接调用的rte_malloc(),仅仅是做了memset处理,将分配的内存空间初始化为0。

rte_calloc()

代码如下所示:

void *
rte_calloc(const char *type, size_t num, size_t size, unsigned align)
{
    return rte_zmalloc(type, num * size, align);
}

直接调用的rte_zmalloc,仅仅是输入参数不同而已。

rte_realloc()

代码如下所示:

void *
rte_realloc(void *ptr, size_t size, unsigned align)
{
    if (ptr == NULL)
        return rte_malloc(NULL, size, align);

    struct malloc_elem *elem = malloc_elem_from_data(ptr);
    if (elem == NULL)
        rte_panic("Fatal error: memory corruption detected\n");

    size = CACHE_LINE_ROUNDUP(size), align = CACHE_LINE_ROUNDUP(align);
    /* check alignment matches first, and if ok, see if we can resize block */
    if (RTE_ALIGN(ptr,align) == ptr &&
            malloc_elem_resize(elem, size) == 0)
        return ptr;

    /* either alignment is off, or we have no room to expand,
     * so move data. */
    void *new_ptr = rte_malloc(NULL, size, align);
    if (new_ptr == NULL)
        return NULL;
    const unsigned old_size = elem->size - MALLOC_ELEM_OVERHEAD;
    rte_memcpy(new_ptr, ptr, old_size < size ? old_size : size);
    rte_free(ptr);

    return new_ptr;
}

如果输入参数ptr为NULL,那么功能和rte_malloc完全一致。

首先会尝试从当前传入的内存块之后,找到一个连续的可分配空间。调用malloc_elem_resize()来实现,在当前传入的内存块之后,找到一个连续的可分配空间,如果当前内存块的大小和这个连续内存块的可用空间之和满足分配的要求,那么就进行分配。

如果未能成功分配,则会调用rte_malloc()进行分配,之后把传入的内存块进行free,并将原内存中的数据搬移到新分配的内存块中。

rte_malloc_validate()

代码如下所示:

int
rte_malloc_validate(void *ptr, size_t *size)
{
    struct malloc_elem *elem = malloc_elem_from_data(ptr);
    if (!malloc_elem_cookies_ok(elem))
        return -1;
    if (size != NULL)
        *size = elem->size - elem->pad - MALLOC_ELEM_OVERHEAD;
    return 0;
}

此函数会检查ptr指向的内存空间的header和trailer标记是否正常,如果正常则将这个空间的长度写入到size中。

rte_free()

代码如下所示:

void rte_free(void *addr)
{
    if (addr == NULL) return;
    if (malloc_elem_free(malloc_elem_from_data(addr)) < 0)
        rte_panic("Fatal error: Invalid memory\n");
}

如果传入的指针为空,则不作任何处理。

如果传入的指针不为空,则找到这块内存块的真正起始地址(通过malloc_elem_from_data()),然后调用malloc_elem_free()处理。

释放的过程其实主要就是做了内存块free链表的操作,将新释放的内存块加入到free链表中。加入的策略如下:

- 如果此被释放的内存块的next指针指向的内存块为free,则将它和后一个内存块合并,并将后一个内存块从free链表中去除;

- 如果此被释放的内存块的pre指针指向的内存块为free,则将它和前一个内存块亦进行合并;

- 将合并后(或不满足合并条件则不合并)的内存块,插入到free链表的头部。

rte_malloc_dump_stats()

代码如下所示:

void
rte_malloc_dump_stats(__rte_unused const char *type)
{
    return;
}

并未做任何处理,所以和API手册中的说明似乎不符?至少在INTELDPDK.L.1.2.3_3版本未做实现,源码中还标识着TODO。

rte_malloc_set_limit()

代码如下所示:

int
rte_malloc_set_limit(__rte_unused const char *type,
        __rte_unused size_t max)
{
    return 0;
}

并未做任何处理,所以和API手册中的说明似乎不符?至少在INTELDPDK.L.1.2.3_3版本未做实现,源码中还标识着TODO。

时间: 2024-08-01 11:18:59

DPDK_API_rte_malloc源码分析的相关文章

TeamTalk源码分析之login_server

login_server是TeamTalk的登录服务器,负责分配一个负载较小的MsgServer给客户端使用,按照新版TeamTalk完整部署教程来配置的话,login_server的服务端口就是8080,客户端登录服务器地址配置如下(这里是win版本客户端): 1.login_server启动流程 login_server的启动是从login_server.cpp中的main函数开始的,login_server.cpp所在工程路径为server\src\login_server.下表是logi

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A

HashMap与TreeMap源码分析

1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Java这么久,也写过一些小项目,也使用过TreeMap无数次,但到现在才明白它的实现原理).因此本着"不要重复造轮子"的思想,就用这篇博客来记录分析TreeMap源码的过程,也顺便瞅一瞅HashMap. 2. 继承结构 (1) 继承结构 下面是HashMap与TreeMap的继承结构: pu

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线

Spark的Master和Worker集群启动的源码分析

基于spark1.3.1的源码进行分析 spark master启动源码分析 1.在start-master.sh调用master的main方法,main方法调用 def main(argStrings: Array[String]) { SignalLogger.register(log) val conf = new SparkConf val args = new MasterArguments(argStrings, conf) val (actorSystem, _, _, _) =

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三)

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及PeerSync策略.本文以及后续的文章将重点介绍Replication策略.Replication策略不但可以在SolrCloud中起到leader到replica的数据同步,也可以在用多个单独的Solr来实现主从同步.本文先介绍在SolrCloud的leader到replica的数据同步,下一篇

zg手册 之 python2.7.7源码分析(4)-- pyc字节码文件

什么是字节码 python解释器在执行python脚本文件时,对文件中的python源代码进行编译,编译的结果就是byte code(字节码) python虚拟机执行编译好的字节码,完成程序的运行 python会为导入的模块创建字节码文件 字节码文件的创建过程 当a.py依赖b.py时,如在a.py中import b python先检查是否有b.pyc文件(字节码文件),如果有,并且修改时间比b.py晚,就直接调用b.pyc 否则编译b.py生成b.pyc,然后加载新生成的字节码文件 字节码对象

LevelDB源码分析--Iterator

我们先来参考来至使用Iterator简化代码2-TwoLevelIterator的例子,略微修改希望能帮助更加容易立即,如果有不理解请各位看客阅读原文. 下面我们再来看一个例子,我们为一个书店写程序,书店里有许多书Book,每个书架(BookShelf)上有多本书. 类结构如下所示 class Book { private: string book_name_; }; class Shelf { private: vector<Book> books_; }; 如何遍历书架上所有的书呢?一种实

【Heritrix源码分析】Heritrix基本内容介绍

1.版本说明 (1)最新版本:3.3.0 (2)最新release版本:3.2.0 (3)重要历史版本:1.14.4 3.1.0及之前的版本:http://sourceforge.net/projects/archive-crawler/files/ 3.2.0及之后的版本:http://archive.org/ 由于国情需要,后者无法访问,因此本blog研究的是1.14.4版本. 2.官方材料 source:http://sourceforge.net/projects/archive-cra