5.5.2 ptmalloc_init()函数
ptmalloc_init()
函数比较长,将分段对这个函数做介绍。
static void
ptmalloc_init (void)
{
#if __STD_C
const char* s;
#else
char* s;
#endif
int secure = 0;
if(__malloc_initialized >= 0) return;
__malloc_initialized = 0;
首先检查全局变量
__malloc_initialized
是否大于等于
0
,如果该值大于
0
,表示
ptmalloc
已经初始化,如果改值为
0
,表示
ptmalloc
正在初始化,全局变量
__malloc_initialized
用来保证全局只初始化
ptmalloc
一次。
#ifdef _LIBC
# if defined SHARED && !USE___THREAD
/* ptmalloc_init_minimal may already have been called via
__libc_malloc_pthread_startup, above. */
if (mp_.pagesize == 0)
# endif
#endif
ptmalloc_init_minimal();
#ifndef NO_THREADS
# if defined _LIBC
/* We know __pthread_initialize_minimal has already been called,
and that is enough. */
# define NO_STARTER
# endif
# ifndef NO_STARTER
/* With some threads implementations, creating thread-specific data
or initializing a mutex may call malloc() itself. Provide a
simple starter version (realloc() won't work). */
save_malloc_hook = __malloc_hook;
save_memalign_hook = __memalign_hook;
save_free_hook = __free_hook;
__malloc_hook = malloc_starter;
__memalign_hook = memalign_starter;
__free_hook = free_starter;
# ifdef _LIBC
/* Initialize the pthreads interface. */
if (__pthread_initialize != NULL)
__pthread_initialize();
# endif /* !defined _LIBC */
# endif /* !defined NO_STARTER */
#endif /* !defined NO_THREADS */
为多线程版本的
ptmalloc
的
pthread
初始化做准备,保存当前的
hooks
函数,并把
ptmalloc
为初始化时所有使用的分配
/
释放函数赋给
hooks
函数,因为在线程初始化一些私有实例时,
ptmalloc
还没有初始化,所以需要做特殊处理。从这些
hooks
函数可以看出,在
ptmalloc
未初始化时,不能使用
remalloc
函数。在相关的
hooks
函数赋值以后,执行
pthread_initilaize()
初始化
pthread
。
mutex_init(&main_arena.mutex);
main_arena.next = &main_arena;
初始化主分配区的
mutex
,并将主分配区的
next
指针指向自身组成环形链表。
#if defined _LIBC && defined SHARED
/* In case this libc copy is in a non-default namespace, never use brk.
Likewise if dlopened from statically linked program. */
Dl_info di;
struct link_map *l;
if (_dl_open_hook != NULL
|| (_dl_addr (ptmalloc_init, &di, &l, NULL) != 0
&& l->l_ns != LM_ID_BASE))
__morecore = __failing_morecore;
#endif
Ptmalloc需要保证只有主分配区才能使用
sbrk()
分配连续虚拟内存空间,如果有多个分配区使用
sbrk()
就不能获得连续的虚拟地址空间,大多数情况下
Glibc
库都是以动态链接库的形式加载的,处于默认命名空间,多个进程共用
Glibc
库,
Glibc
库代码段在内存中只有一份拷贝,数据段在每个用户进程都有一份拷贝。但如果
Glibc
库不在默认名字空间,或是用户程序是静态编译的并调用了
dlopen
函数加载
Glibc
库中的
ptamalloc_init()
,这种情况下的
ptmalloc
不允许使用
sbrk()
分配内存,只需修改
__morecore
函数指针指向
__failing_morecore
就可以禁止使用
sbrk()
了,
__morecore
默认指向
sbrk()
。
mutex_init(&list_lock);
tsd_key_create(&arena_key, NULL);
tsd_setspecific(arena_key, (Void_t *)&main_arena);
thread_atfork(ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2);
初始化全局锁
list_lock
,
list_lock
主要用于同步分配区的单向循环链表。然后创建线程私有实例
arena_key
,该私有实例保存的是分配区(
arena
)的
malloc_state
实例指针。
arena_key
指向的可能是主分配区的指针,也可能是非主分配区的指针,这里将调用
ptmalloc_init()
的线程的
arena_key
绑定到主分配区上。意味着本线程首选从主分配区分配内存。
然后调用
thread_atfork()
设置当前进程在
fork
子线程(
linux
下线程是轻量级进程,使用类似
fork
进程的机制创建)时处理
mutex
的回调函数,在本进程
fork
子线程时,调用
ptmalloc_lock_all()
获得所有分配区的锁,禁止所有分配区分配内存,当子线程创建完毕,父进程调用
ptmalloc_unlock_all()
重新
unlock
每个分配区的锁
mutex
,子线程调用
ptmalloc_unlock_all2()
重新初始化每个分配区的锁
mutex
。这几个回调函数将在
5.5.3
节介绍。
#ifndef NO_THREADS
# ifndef NO_STARTER
__malloc_hook = save_malloc_hook;
__memalign_hook = save_memalign_hook;
__free_hook = save_free_hook;
# else
# undef NO_STARTER
# endif
#endif
当pthread
初始化完成后,将相应的
hooks
函数还原为原值。
#ifdef _LIBC
secure = __libc_enable_secure;
s = NULL;
if (__builtin_expect (_environ != NULL, 1))
{
char **runp = _environ;
char *envline;
while (__builtin_expect ((envline = next_env_entry (&runp)) != NULL,
0))
{
size_t len = strcspn (envline, "=");
if (envline[len] != '=')
/* This is a "MALLOC_" variable at the end of the string
without a '=' character. Ignore it since otherwise we
will access invalid memory below. */
continue;
switch (len)
{
case 6:
if (memcmp (envline, "CHECK_", 6) == 0)
s = &envline[7];
break;
case 8:
if (! secure)
{
if (memcmp (envline, "TOP_PAD_", 8) == 0)
mALLOPt(M_TOP_PAD, atoi(&envline[9]));
else if (memcmp (envline, "PERTURB_", 8) == 0)
mALLOPt(M_PERTURB, atoi(&envline[9]));
}
break;
case 9:
if (! secure)
{
if (memcmp (envline, "MMAP_MAX_", 9) == 0)
mALLOPt(M_MMAP_MAX, atoi(&envline[10]));
#ifdef PER_THREAD
else if (memcmp (envline, "ARENA_MAX", 9) == 0)
mALLOPt(M_ARENA_MAX, atoi(&envline[10]));
#endif
}
break;
#ifdef PER_THREAD
case 10:
if (! secure)
{
if (memcmp (envline, "ARENA_TEST", 10) == 0)
mALLOPt(M_ARENA_TEST, atoi(&envline[11]));
}
break;
#endif
case 15:
if (! secure)
{
if (memcmp (envline, "TRIM_THRESHOLD_", 15) == 0)
mALLOPt(M_TRIM_THRESHOLD, atoi(&envline[16]));
else if (memcmp (envline, "MMAP_THRESHOLD_", 15) == 0)
mALLOPt(M_MMAP_THRESHOLD, atoi(&envline[16]));
}
break;
default:
break;
}
}
}
#else
if (! secure)
{
if((s = getenv("MALLOC_TRIM_THRESHOLD_")))
mALLOPt(M_TRIM_THRESHOLD, atoi(s));
if((s = getenv("MALLOC_TOP_PAD_")))
mALLOPt(M_TOP_PAD, atoi(s));
if((s = getenv("MALLOC_PERTURB_")))
mALLOPt(M_PERTURB, atoi(s));
if((s = getenv("MALLOC_MMAP_THRESHOLD_")))
mALLOPt(M_MMAP_THRESHOLD, atoi(s));
if((s = getenv("MALLOC_MMAP_MAX_")))
mALLOPt(M_MMAP_MAX, atoi(s));
}
s = getenv("MALLOC_CHECK_");
#endif
if(s && s[0]) {
mALLOPt(M_CHECK_ACTION, (int)(s[0] - '0'));
if (check_action != 0)
__malloc_check_init();
}
从环境变量中读取相应的配置参数值,这些参数包括
MALLOC_TRIM_THRESHOLD_
,
MALLOC_TOP_PAD_
,
MALLOC_PERTURB_
,
MALLOC_MMAP_THRESHOLD_
,
MALLOC_CHECK_
,
MALLOC_MMAP_MAX_
,
MALLOC_ ARENA_MAX,MALLOC_ ARENA_TEST,
如果这些选项中的某些项存在,调用
mallopt()
函数设置相应的选项。如果这段程序是在
Glibc
库初始化中执行的,会做更多的安全检查工作。
void (*hook) (void) = force_reg (__malloc_initialize_hook);
if (hook != NULL)
(*hook)();
__malloc_initialized = 1;
}
在ptmalloc_init()
函数结束处,查看是否存在
__malloc_initialize_hook
函数,如果存在,执行该
hook
函数。最后将全局变量
__malloc_initialized
设置为
1
,表示
ptmalloc_init()
已经初始化完成。
分享到:
评论