阅读FreeRadius源码时,发现内存管理使用的是talloc,而不是malloc。现在来学习一下怎么使用talloc。
What is talloc
Talloc is a hierarchical, reference counted memory pool system with destructors. It is built atop the C standard library and it defines a set of utility functions that altogether simplifies allocation and deallocation of data, especially for complex structures that contain many dynamically allocated elements such as strings and arrays.
Talloc是一个层次化、具备引用计数和析构功能的内存池系统。它在C标准库的基础上定义了一组工具,简化了分配/释放内存的操作(特别是对于像string和数组这样包含许多动态分配成员的复杂数据结构)
The main goals of this library are: removing the needs for creating a cleanup function for every complex structure, providing a logical organization of allocated memory blocks and reducing the likelihood of creating memory leaks in long-running applications. All of this is achieved by allocating memory in a hierarchical structure of talloc contexts such that deallocating one context recursively frees all of its descendants as well.
Talloc的主要目标是:
- 消除为每个复杂数据结构编写清理函数的麻烦
- 对分配的内存块进行逻辑组织
- 降低长时间运行的程序出现内存泄露的可能性
上面的三个目标是通过这样的方法实现的:将分配的内存以层次化的数据结构 talloc context进行管理,需要deallocating时递归的对talloc context进行free即可。
主要特点:
- 开源
- 层次化的内存模型
- Natural projection of data structures into the memory space
- 简化了对较大数据结构的内存管理
- 内存被释放之前,自动执行“析构函数”
- 模拟了一个动态类型系统
- 实现了一个透明的内存池
1. Talloc context
It is a logical unit which represents a memory space managed by talloc.
talloc context的地位是最重要的,它代表了一块由talloc管理的内存空间。
从程序员的角度来看,talloc context与C标准库内存管理工具返回的指针完全相同。也就是说,由talloc返回的context可以直接作为标准库函数的参数来使用。
char *str1 = strdup("I am NOT a talloc context");
char *str2 = talloc_strdup(NULL, "I AM a talloc context");
printf("%d\n", strcmp(str1, str2) == 0);
free(str1);
talloc_free(str2); /* we can not use free() on str2 */
为什么会如此神奇?原因就是:
This is possible because the context is internally handled as a special fixed-length structure called talloc chunk. Each chunk stores context metadata followed by the memory space requested by the programmer. When a talloc function returns a context (pointer), it will in fact return a pointer to the user space portion of the talloc chunk. If we to manipulate this context using talloc functions, the talloc library transforms the user-space pointer back to the starting address of the chunk. This is also the reason why we were unable to use free(str2) in the previous example - because str2 does not point at the beginning of the allocated block of memory. This is illustrated on the next image:
context在内部作为固定长度的结构被处理,称为talloc chunk。每个chunk存储context的metadata(元数据),元数据后面是用户请求的内存空间。当某个talloc 函数返回一个context(指针)时,它将会返回该chunk的用户空间的指针。当使用talloc函数操作这个context时,talloc库会将用户空间的指针转换回chunk的起始地址。这也就是为什么上面的context不能使用free释放的原因,因为str2并没有指向动态内存的起始地址。
如上图所示,整个空间用allocated block表示,它由talloc chunk和requested memory两部分构成,给用户返回的context指向requestd memory的起始地址。talloc chunk中应该存放者这个chunk的metadata(引用计数、context size)
1.1 Context meta data
Every talloc context carries several pieces of internal information along with the allocated memory:
context的metadata有:
- name 用来报告context的层次结构并模拟一个动态类型系统
- requested memory 的大小
- 析构函数,在free之前被调用
- 引用计数
- child和parent的context 用来创建内存的层次结构
1.2 Hierarchy of talloc context
每个talloc context都包含parent和child的信息。talloc使用这些信息创建一个层次型结构。说句人话,就是以树形结构管理相关的context,树的根节点称为top level context。
使用树形结构的好处:
- 当释放一个context时,它的child会被正确的释放。
- 一个context的parent可以改变,可以将一个子树移动到其他树中。
- 管理数据结构更加自然。
举个栗子:
struct user {
uid_t uid;
char *username;
size_t num_groups;
char **groups;
};
适应talloc,上述数据结构会被组织成下面的树状结构:
/* create new top level context */
struct user *user = talloc(NULL, struct user);
user->uid = 1000;
user->num_groups = N;
/* make user the parent of following contexts */
user->username = talloc_strdup(user, "Test user");
user->groups = talloc_array(user, char*, user->num_groups);
for (i = 0; i < user->num_groups; i++) {
/* make user->groups the parent of following context */
user->groups[i] = talloc_asprintf(user->groups,
"Test group %d", i);
}
这个结构体中有许多动态分配的成分,当需要释放时,只需要这么做:
talloc_free(user);
talloc库会为你释放所有的内存。
画了一个内存的层次结构图,黑色箭头指向parent,蓝色虚线指向child,绿色实线指向数据机构中对应的内存。每个节点表示一个context,每个context由metadata(节点的深蓝色部分)和requested memory(节点的浅蓝色)组成。