基本方式
1.使用锁的方式来支持多线程
我们之前介绍过,库函数的实现方式是通过一个主分配区和多个非主分配区来组织的,每一个分配区中又有一个bins.我们先说对于大的分配区是如何保证线程安全的。
首先,每一个分配区均有一个结构体,这个结构体中有一个线程锁和一些其他的数据结构,其中别的东西我们今天并不关心,我们只看看这个锁今天做了哪些工作。全局还有一个锁list_mutex.主要是用来同步分配区链表的。
初始化
1.初始化分配区锁
mutex_init(&main_area.mutex);
main_arena.next = &main_area;
对于主分区,首先初始化主分区的锁,然后让主分配区自己形成一个循环链表。对于其他的分配区也是如此。各个线程通过把自己所用的分配区指针保存到自己的线程私有实例上,标识自己所用分配区。
2.全局初始化
mutex_init(&list_lock);
//初始化全局的锁
tsd_key_create(&arena_key,NULL);
//创建线程的私有变量
tsd_setspecific(arena_key,(void *)&main_arena);
//将线程的私有变量先设置成主分区
thread_atfork(ptmalloc_lock_all,ptmalloc_unlock_all,ptmalloc_unlock_all2);
这里前边三句都很简单,第四步主要就是使用了几种函数处理了如果有线程正在被创建,又需要分配空间的问题。
thread_atfork()函数被调用说明这个进程正在创建别的线程,首先回调ptmalloc_lock_all 函数,获取全部分配区的锁,禁止所有的分配区分配内存,当子线程创建完毕,父进程就调用ptmalloc_lock_unlock 释放所有分配区的锁,子线程调用ptmalloc_unlock_all2()重新初始化内一个分配区的锁。
3.分配
再分配空间的时候,首先线程查看自己的线程私有数据中是否含有分配区的指针,如果没有就去分配区链表中寻找没有被锁住的分配区,如果有就尝试给分配区加锁,成功后开始执行分配过程。如果遍历所有分配区都没有就只能等待某个分配区的锁被释放后。
博主今天淋雨感冒,暂时就写这里吧,有时间详细介绍下thread_atfork(_)这里,应该有的。