glibc malloc、free流程 2.27+

2022-07-31

malloc(主线程)

1.tcache bin

先从tcache bin中寻找可用的chunk。

2.fast bin

tcache bin中如果没有,会尝试从fast bin中获取可用chunk(在比较新版本的glibc中,在tachebin中没有chunk而fast bin中有chunk的情况,会将fastbin中的chunk拆出,放入tcache bin中)。

3.small bin

接下来尝试在small bin中寻找可用chunk.

4.unsorted bin

在smallbin中没有chunk时,会进行consolidate流程,合并fastbin中的chunk并放入unsorted bin中。从unsortedbin中找合适的chunk

5. large bin

以上4步都没有分配到合适chunk时,在large bin中寻找chunk

6. top chunk

large bin没有时,会在top chunk中切割一小块内存。

7. sysmalloc

连top chunk都没有足够内存时,如果申请的size过大,会尝试使用mmap分配;
否则会使用brk函数尝试扩展top chunk后再进行分配;

malloc(非主线程)

对于非主线程来说,每个线程会有一个独立的arena管理一个区域(均有mmap分配而来)。<br 采用如下函数进行测试:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

void * thread1(void * arg){
    int i = (int)*((int *)arg);
    char * a = malloc(0x20);
    printf("%d: chunk address:%p\n",i,a);
}

int main(){
    int i;
    long long ret[10];
    pthread_t tid[10];
    pthread_attr_t pthread_attr[10];
    int number[10];
    for(i=0;i<5;i++){
        number[i]=i;
        pthread_attr_init(&pthread_attr[i]);
        pthread_attr_setdetachstate(&pthread_attr[i],  PTHREAD_CREATE_JOINABLE);
        pthread_create(&tid[i],&pthread_attr[i],thread1,&number[i]);
        pthread_join(tid[i],NULL);
        pthread_attr_destroy(&pthread_attr[i]);
    }
    return 0;
}

运行结果如下图所示: 可以看到 每个chunk分配的地址都位于文件映射区域,因此非主线程arena管理的memory由mmap分配而来。
但是由一个问题,刚刚上面提到了,每个非主线程对应一个arena管理内存,为什么创建了5个线程,其实都位于一个arena中呢?
使用如下代码进行测试:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

void * thread1(void * arg){
    int i = (int)*((int *)arg);
    char * a = malloc(0x20);
    printf("%d: chunk address:%p\n",i,a);
    getchar(); //difference1
}

int main(){
    int i;
    long long ret[10];
    pthread_t tid[10];
    pthread_attr_t pthread_attr[10];
    int number[10];
    for(i=0;i<5;i++){
        number[i]=i;
        pthread_attr_init(&pthread_attr[i]);
        pthread_attr_setdetachstate(&pthread_attr[i],  PTHREAD_CREATE_DETACHED);//difference2
        pthread_create(&tid[i],&pthread_attr[i],thread1,&number[i]);
        pthread_join(tid[i],NULL);
        pthread_attr_destroy(&pthread_attr[i]);
    }
    return 0;
}

这段代码和上述代码只有2个不同,一个是每个线程都添加了getchar()函数让线程不会销毁,其实是使用 PTHREAD_CREATE_DETACHED参数,使得主线程在调用pthread_join函数时不会因为线程未结束而陷入等待。其结果如下图所示: 可以看到,在存在多个线程时,每个线程申请的chunk都对应一个arena管理的区域,每个chunk的地址差别也很大。

free流程

1.放入tcachebin

如果符合tcachebin且tcachebin没有满,放入tcachebin

2.放入fastbin

3.放入unsortedbin

如果释放的chunk既不能放入tcachebin,也不能放入fastbin,会进行合并操作(前向合并和后向合并),随后放入unsorted bin中
当然,如果被释放chunk的下一个chunk是top chunk,会直接合并到top chunk中。