_S_refill函数
//负责把分配好的chunk块进行连接,添加到自由链表当中
static void* _S_refill(size_t __n);
我们假设申请8字节的空间,但是发现0下标元素是空值,也就是未开辟内存池。
我们进入 _S_refill函数:
nobjs:代表节点的个数:20
_S_chunk_alloc的函数作用是:按照相应的自由链表的相应元素位的字节数开辟相应的chunk块。
相当于我们在说Allocate的时候画的已经开辟好的chunk块
我们当时画的时候,每一个chunk的头部被定义成union类型,union里面的_M_free_list_link就是next的作用(存下一个节点的地址)。这些事情都在_S_refill函数里做。内存开辟是在_S_chunk_alloc函数里执行的,我们后面叙述。
我们先从整体上分析一下这个_S_refill函数做了什么事情:
通过_S_chunk_alloc函数,我们假设分配了这些内存,每一块内存都是8字节,称为chunk块,具体分配多少个,我们在_S_chunk_alloc叙述。
我们假设现在已经开辟好了。
把这段开辟好的8字节的chunk块内存池起始地址返回来,赋值给_chunk指针:
然后定义了__my_free_list这个二级指针,用来遍历第一维的指针数组。
然后定义了一些函数的局部变量
然后有个if语句:nobjs的值为1的时候,为什么这个nobjs的值还会变?
因为_S_chunk_alloc函数是以引用接收的。
nobjs的值为1的时候,相当于8字节的chunk块内存池现在就剩下一个节点了,所以直接返回就可以了。
重要的是我们看下面这段代码。负责把相应大小的chunk块内存池分配好了。
获取元素的下标(假设n是8字节,就是起始地址+0)
指向数组的第一个位置
然后让result指向chunk指向的地方:
图解如下:
然后是chunk的地址(指向内存池的首地址)加上n(我们的n是8字节,chunk跑8字节)
把第1个chunk块分配出去了,下面的chunk块空闲等待下次分配出去。
然后把chunk类型转成Obj类型,就可以访问chunk块的头部了。
然后把它赋值给__next_obj了
然后再赋值给_my_free_list的解引用(就是第0下标位置的元素Obj * 指针)。
图解如下:
然后就是进入for循环,i=1,相当于是死循环
__current_obj=_next_obj,
图解如下:
然后把__next_obj先转为char*类型,是为了指针每次加1次,就是跑1个字节。
然后让__next_obj指针总共偏移8个字节(n)
图解如下:
然后把__next_obj转成Obj类型,带有chunk的头部信息,赋值给__next_obj
然后是:
nobjs就是我们开辟的chunk块的个数。
这个if语句做了什么事情?
这里是每处理1个节点,_i就++,当__objs-1==__i的时候,相当于__current_obj指向最后一个chunk块节点了。
这些chunk块分配出去,以后还是要回收的,回收的话怎么把它们管理起来呢?每个节点的头部都有union类型,如下图所示。
_S_refill函数就是做2件事情:
1.负责分配相应指定大小的chunk块内存池
2.上图的循环,把chunk的每一个节点连接起来,相当于静态链表,每一个节点的头部存下一个节点的地址。做完之后把result返回回去,result就是分配给用户的chunk块节点的起始地址。