每个进程有自己独有的虚拟地址空间,此虚拟地址空间包括五部分:Text section(要由处理器执行的二进制指令)、BSS(包括初始化为0的静态数据等)、Heap(包括动态分配的数据)、Stack(包括自定义变量、函数参数等)
Heap由下(头部)向上(尾部)增长,程序中断指针或brk指针指向heap的尾部
如果想要在heap中分配更多的内存,需要向系统申请brk。
Sbrk系统调用可以操控program break:
Sbrk(0):提供程序中断的当前地址。
Sbrk(x):向系统申请x字节的内存
Sbrk(-x):释放x字节的内存给系统,出错返回(void*)-1
代码解读
1.当heap中没有满足条件的内存块时,可以通过sbrk(x)向系统申请内存放入heap;但是如果我们要释放heap末尾的内存块时(因为操作系统提供的堆内存是连续的。因此,我们只能释放堆末尾的内存。我们不能在操作系统的中间释放一块内存。),就会很麻烦,因为不知道要释放的内存块究竟多大。所以,我们可以为每个内存块添加一个信息头header结构体,包括:有效内存块大小、是否可用、一个header链表(指针指向下一个内存块的header部分的首地址)。并且header结构体放在一个union内,16字节对齐。
2.为了防止两个或多个线程同时访问内存,我们设置一个全局锁,在内存上的每一个操作之前,你必须获取锁,操作完成后,必须释放锁。
mymalloc.cpp
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include "mymalloc.h"
// 将包含内存块头部信息的结构体放在union中,从而控制头部header所占空间的长度
union header {
struct {
size_t size;
unsigned is_free;
union header *next;
} s;
char Align[16]; // 控制长度
};
typedef union header header_t;
header_t *head = NULL, *tail = NULL;
// 为了防止两个或多个线程同时访问内存,设置一个全局锁
pthread_mutex_t global_malloc_lock;
/*
循环遍历链表,判断是否存在满足条件的内存块
返回值:
存在 返回内存块的有效内存部分的首地址
不存在 返回NULL
*/
header_t *get_free_block(size_t size)
{
header_t *curr = head;
while(curr) {
if (curr->s.is_free && curr->s.size >= size)
return curr;
curr = curr->s.next;
}
return NULL;
}
/*
释放内存块
*/
void myfree(void *block)
{
header_t *header, *tmp;
// void指针可以指向任意类型的数据
void *programbreak;
if (!block)
return;
pthread_mutex_lock(&global_malloc_lock);
// 将header指向 block(有效内存的首地址)所在内存块的头部
header = (header_t*)block - 1;
// sbrk(0)获取当前程序中断地址
programbreak = sbrk(0);
/* 判断要释放的内存块是否为heap的末尾内存块 */
// 是,则将内存块释放给系统
if ((char*)block + header->s.size == programbreak) {
if (head == tail) {
head = tail = NULL;
} else {
tmp = head;
while (tmp) {
if(tmp->s.next == tail) {
tmp->s.next = NULL;
tail = tmp;
}
tmp = tmp->s.next;
}
}
sbrk(0 - header->s.size - sizeof(header_t));
pthread_mutex_unlock(&global_malloc_lock);
return;
}
// 否,则直接将该内存块的头部信息的s.is_free置为1,表示此内存快可被使用
// 但并不释放给系统,还在进程的heap内
header->s.is_free = 1;
pthread_mutex_unlock(&global_malloc_lock);
}
/*
动态申请内存
*/
void *mymalloc(size_t size)
{
size_t total_size;
void *block;
header_t *header;
if (!size)
return NULL;
pthread_mutex_lock(&global_malloc_lock);
header = get_free_block(size);
if (header) {
header->s.is_free = 0;
pthread_mutex_unlock(&global_malloc_lock);
return (void*)(header + 1);
}
total_size = sizeof(header_t) + size;
// sbrk(x):向系统申请x字节的内存,出错返回(void*)-1
block = sbrk(total_size);
if (block == (void*) -1) {
pthread_mutex_unlock(&global_malloc_lock);
return NULL;
}
header = block;
header->s.size = size;
header->s.is_free = 0;
header->s.next = NULL;
// 如果head为空,说明此事heap为空,tail自然也为空;此时直接将head指向header首地址
if (!head)
head = header;
// 如果tail不为空,则说明tail是heap末尾内存块的头部,此时应该将tail的s.next指向header首地址
if (tail)
tail->s.next = header;
// 如果tail为空,则直接将tail指向header首地址
tail = header;
pthread_mutex_unlock(&global_malloc_lock);
return (void*)(header + 1);
}
mymalloc .h
#ifndef MYMALLOC_H_INCLUDED
#define MYMALLOC_H_INCLUDED
void myfree(void *block);
void *mymalloc(size_t size);
#endif // MYMALLOC_H_INCLUDED
main
#include <stdio.h>
#include "mymalloc.h"
void main(int argc, char *argv[]){
int *a;
a = (int *) mymalloc(6 * sizeof(int ));
a[3] = 9;
printf("%d\n", a[3]);
myfree(a);
return ;
}
编译完成后在Linux shell命令行下打包执行