内存分配
前序课程
操作系统接口:https://dreamerjonson.com/2020/01/04/6-s081-1/
系统编程(Systems programming)
- 与应用程序编程相比,系统编程的主要区别在于,应用程序编程旨在产生直接向用户提供服务的软件。
- 系统编程主要为其他应用程序提供服务,直接操作操作系统。它的目标是实现对可用资源的有效利用。
例如:
- unix utilities
- K/V servers
- ssh
- bigint library
挑战:
- 低级编程环境
- 数字是64位(不是无限的整数)
- 分配/释放内存
- 并发
- 允许并行处理请求
- 崩溃
- 性能
- 为应用程序提供硬件支持
动态内存分配是一项基本的系统服务
- 底层编程: 指针操作,类型转换
- 使用底层操作系统请求内存块(memory chunks)
- 性能非常重要
- 支持广泛类型的应用程序负载
应用程序结构
-
text
-
data (静态存储)
-
stack (栈存储)
-
heap (动态内存分配)
使用 sbrk() o或 mmap() 操作系统接口扩展堆。
[text | data | heap -> … <- stack]
0 top of address space -
data段的内存分配是静态的,始终存在。
-
栈的分配在函数中,随着函数消亡而释放。
-
堆的分配与释放,调用接口:
— malloc(int sz)
— free§
堆分配的目标
- 快速分配和释放
- 内存开销小
- 想要使用所有内存
- 避免碎片化
下面介绍几种malloc实现的方式
方式1:K&R malloc
又叫做first-fit规则, 即查找第一个可用的匹配块。与之相对应的是查找第一个最符合(best-fit)的可用块。
K&R malloc的实现来自书籍 the C programming language by Kernighan and Ritchie (K&R) Section 8.7
维持一个链表
维持的free list是一个环。 第一个元素是base。
#define NALLOC 1024 /* minimum #units to request */
struct header {
struct header *ptr;
size_t size;
};
typedef struct header Header;
static Header base;
static Header *freep = NULL;
内存分配
指定分配的内存大小为sizeof(Header)的倍数,且一定大于nbytes。nunits就是这个倍数。
- 循环free list。 找到第一个符合即大于等于nbytes的块。
— 如果刚好合适,则链表删除此块并返回此块。
— 如果大于,则截断此元素
— 如果没有找到合适的块,则调用moreheap新分配一个。
/* malloc: general-purpose storage allocator */
void *
kr_malloc(size_t nbytes)
{
Header *p, *prevp;
unsigned nunits;
nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1;
// base作为第一个元素。
if ((prevp = freep) == NULL) { /* no free list yet */
base.ptr = freep = prevp = &base;
base.size = 0;
}
for (p = prevp->ptr; ; prevp = p, p = p->ptr) {
if (p->size >= nunits) { /* big enough */
if (p->size == nunits) /* exactly */
prevp->ptr = p->ptr;
else { /* allocate tail end */
p->size -= nunits;
p += p->size;
p->size = nunits;
}
freep