今天进入主要函数的学习。首先看用户申请内存函数mem_malloc:
- static void *mem_malloc(size_t n)
- {
- obj * volatile *my_free_list;
- obj *result;
- /* 如果申请的内存超过我们设置的最大内存,直接使用crt */
- if (n > (size_t)__MAX_BYTES) {
- void *p = (void *)malloc(n);
- return p;
- }
- /* 寻找16个free list链表中适当的一个 */
- my_free_list = free_list + __freelist_index(n);
- result = *my_free_list;
- if (result == 0) {
- /* 当没有可用的free list时,准备重新填充free list */
- void *ret = refill(__round_up(n));
- return ret;
- }
- /* 调整链表 */
- *my_free_list = result->free_list_link;
- return result;
- }
这里需要说明的是在开始的时候,为了提高效率,内存分配及填充free list会推迟到用户申请内存的时候,其他没什么说的。接着看释放内存函数mem_free:
- static void mem_free(void *p, size_t n)
- {
- obj *q = (obj*)p;
- obj * volatile *my_free_list;
- /* 大于__MAX_BYTES直接释放 */
- if (n > (size_t) __MAX_BYTES) {
- free(p);
- return;
- }
- /* 找到适当的链表,回收区块并调整链表 */
- my_free_list = free_list + __freelist_index(n);
- q->free_list_link = *my_free_list;
- *my_free_list = q;
- }
释放过程也没什么可说的,接着看填充链表函数refill:
- static void *refill(size_t n)
- {
- int nobjs = 20;
- char *chunk = chunk_alloc(n, &nobjs);
- obj * volatile *my_free_list;
- obj *result;
- obj *current_obj, *next_obj;
- int i;
- if (nobjs == 1)
- return chunk;
- my_free_list = free_list + __freelist_index(n);
- result = (obj *)chunk;/* 这个区块返回给用户 */
- /* free list指向新配置的空间(取自内存池)*/
- *my_free_list = next_obj = (obj *)(chunk + n);
- /* 以下将各个节点串接起来 */
- for (i = 1; ; i++) {/* 从1开始,因为第0块返回给用户 */
- current_obj = next_obj;
- next_obj = (obj *)((char *)next_obj + n);
- if (nobjs - 1 == i) {/* 最后一个节点*/
- current_obj->free_list_link = NULL;
- break;
- } else {
- current_obj->free_list_link = next_obj;
- }
- }
- return result;
- }
当用户调用申请mem_malloc发现找到的对应链表指针指向的NULL时,将会调用refill,refill会调用chunk_alloc从内存池‘要’一定数量的用户所申请区块大小的内存。如果此时内存池返回1个区块,那么直接返回给用户,完成申请过程,否者将除去返给用户的区块外,剩下内存串接起来以备下次使用。接下来看内存池函数chunk_alloc,她的工作是从内存池中取空间给free list用:
- static char *chunk_alloc(size_t size, int *nobjs)
- {
- char *result;
- size_t total_bytes = size * (*nobjs); /* 要申请的内存总量 */
- size_t byte_left = end_free - start_free; /* 内存池剩余空间 */
- if (byte_left >= total_bytes) { /* 内存池中的内存完全满足需要 */
- result = start_free;
- start_free += total_bytes;
- return result;
- } else if (byte_left >= size) { /* 内存池不能完全满足需求,但能提供一块以上的区块 */
- *nobjs = byte_left / size;
- total_bytes = size * (*nobjs);
- result = start_free;
- start_free += total_bytes;
- return result;
- } else {
- /* 内存池剩余空间连一个区块的大小都无法提供 */
- size_t bytes_to_get = + __round_up(heap_size >> 4);
- /* 将内存池的零头配给适当的free list */
- if (byte_left > 0) {
- obj * volatile * my_free_list = free_list + __freelist_index(byte_left);
- ((obj*)start_free)->free_list_link = *my_free_list;
- *my_free_list = (obj *)start_free;
- }
- /* 配置heap空间,用来补充内存池 */
- start_free = (char *)malloc(bytes_to_get);
- if (0 == start_free) { /* heap 空间不足,malloc()失败 */
- size_t i;
- obj * volatile *my_free_list;
- obj *p;
- /* 搜寻适当的(比当前size大)free list */
- for (i = size; i <= __MAX_BYTES; i += __ALIGN) {
- my_free_list = free_list + __freelist_index(i);
- p = *my_free_list;
- if (0 != p) { /* free list 内尚有未用区块 */
- /* 调整free list以释放出未用区块 */
- *my_free_list = p->free_list_link;
- start_free = (char *)p;
- end_free = start_free + i;
- return chunk_alloc(size, nobjs); /* 递归调用,为了修正nobjs */
- /* ps:任何残余零头最终会编入适当free list中备用 */
- }
- }
- end_free = 0;
- start_free = (char*)malloc(bytes_to_get);
- }
- /* 记录当前内存容量 */
- heap_size += bytes_to_get;
- end_free = start_free + bytes_to_get;
- return chunk_alloc(size,nobjs);
- }
- }
chunk_alloc的作用是从内存池中取内存给free list,所以会竭尽全力满足用户需要。如果有足够的空间,会返回指定数量的区块,否者返回实际能提供数量的区块,或者无法满足用户需要,那么会将内存池剩下的零头编入适当的链表中,然后向系统索要空间来补充内存池。如果系统也无内存可用,则寻找free list中是否有比size大的区块,将其用来补充空间。
如果到处都已无内存可用了,会再次配置空间,可能抛出异常。否则记录内存池大小。接下来我们看内存池的使用。
补上mem_realloc函数:
- static void *mem_realloc(void *ptr, size_t new_sz, size_t old_sz)
- {
- void *result;
- size_t copy_sz;
- /* 如果old_sz 和 new_sz均大于我们设置的最大内存,那么直接用 crt */
- if (old_sz > (size_t)__MAX_BYTES && new_sz > (size_t)__MAX_BYTES)
- return realloc(ptr, new_sz);
- if (__round_up(old_sz) == __round_up(new_sz))
- return ptr;
- result = mem_malloc(new_sz);
- copy_sz = new_sz > old_sz ? old_sz : new_sz;
- memcpy(result, ptr, copy_sz);
- mem_free(ptr, old_sz);
- return result;
- }
mem_realloc的作用是替换是替换realloc函数,这里要结合上下文来理解,如果old_sz 和 new_sz均大于我们设置的最大内存,那么直接用 crt。那么假设old_sz < __MAX_BYTES = 128呢?这里分两种情况考虑:
1、new_sz > __MAX_BYTES:显然内存池不能满足需要,可为什么程序还要执行这句:result = mem_malloc(new_sz)呢,这里别忘了mem_malloc对超过__MAX_BYTES的内存会使用crt
2、new_sz <= __MAX_BYTES:
a、__round_up(old_sz) == __round_up(new_sz):直接返回ptr。这里可能存在的一点疑惑的是,假设old_sz = 1, new_sz = 5,那么直接返回的话不是没有满足用户需要吗?其实不然,原因是我们使用mem_malloc(old_sz)时分配的内存是8啊!
b、__round_up(old_sz) != __round_up(new_sz):这种情况直接执行后续操作——分配内存,将原有内容复制到新内存,释放旧内存。
如果 old_sz > __MAX_BYTES 且 new_sz < __MAX_BYTES呢?很简单,如2.b一样,直接执行后续操作。
能说的就这么一点了。