C语言编写一个简单的内存分配器:大家都以为简单,实则暗藏玄机

本文主要介绍如何用C语言编写一个简单的内存分配器。我们将实现malloc()calloc()realloc()free()函数。

这是一篇面向初学者的文章,因此我不会详述每一个细节。这个内存分配器不会很快且高效,我们也不会调整已分配的内存使其与页面边界对齐,但我们会构建一个能正常工作的内存分配器,仅此而已。(如果你想查看完整的代码私聊我)在开始构建内存分配器之前,你需要熟悉程序的内存布局。一个进程在其自己的虚拟地址空间中运行,该虚拟地址空间与其他进程的虚拟地址空间是不同的。这个虚拟地址空间通常由五个部分组成:

  1. 文本段:包含要由处理器执行的二进制指令的部分。
  2. 数据段:包含已初始化且值不为零的静态数据。
  3. BSS段(由符号开始的块):包含已初始化为零的静态数据。程序中未初始化的静态数据会被初始化为0并存储在这里。
  4. :包含动态分配的数据。
  5. :包含自动变量、函数参数、基指针的副本等。

从图中可以看到,栈和堆的增长方向是相反的。有时,数据段、BSS段和堆段会被统称为“数据段”,其末尾由一个名为程序断点(program break)或brk的指针来界定。也就是说,brk指向堆的末尾。

现在,如果我们想在堆中分配更多内存,就需要请求系统增加brk的值。同样,要释放内存,我们需要请求系统减小brk的值。

假设我们运行的是Linux(或类Unix系统),我们可以使用sbrk()系统调用来操作程序断点。

  • 调用sbrk(0)会返回程序断点的当前地址。
  • 调用sbrk(x)x为正值)会使brk增加x个字节,从而分配内存。
  • 调用sbrk(-x)x为正值)会使brk减少x个字节,从而释放内存。

如果调用失败,sbrk()会返回(void*) -1

说实话,sbrk()并不是我们最好的选择。如今有更好的替代方案,比如mmap()sbrk()并不是线程安全的,并且它只能按照后进先出(LIFO)的顺序增长或收缩。

扫描二维码关注微信公众号,回复密码,即可获取密码

阅读剩余
THE END