欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

内存管理函数

程序员文章站 2022-06-04 11:06:08
...

一、基本用法

#include <unistd.h>
int brk(void *addr);
void *sbrk(intptr_t increment);  //返回空间地址

brk是系统调用,通过传递的addr来重新设置program break,成功返回0,否则返回-1。
sbrk不是系统调用,是C库函数,用来增加heap,增加的大小通过参数increment来决定。increment可以是正负、负数、零。
    brk和sbrk会改变program break的位置,program break被定义为程序数据段的结束位置。这里的程序数据段包含data segment、bss segment和heap,所以program break就是指heap的结束地址。

#include <sys/mman.h>
// 将一个文件或者其它对象映射进内存
void *mmap(void *start, size_t length, int port, int flags, int fd, off_t offset);
// 成功返回被映射区的指针,失败返回MAP_FAILED(其值为(void *)-1)。
int munmap(void *start, size_t length);
// 成功返回0,失败返回-1。
#include <malloc.h>
void *alloca(size_t);

#include <stdlib.h>
void* malloc(unsigned size);
void* calloc(size_t numElements, size_t sizeOfElement);
void* realloc(void* ptr, unsigned newsize);

(1)alloca是向栈申请内存,因此无需手动释放。
(2)malloc向堆中申请内存,不能初始化所分配的内存,因此需要调用memset来初始化这部分的内存空间。
(3)calloc与malloc类似,会将所分配的内存空间中的每一位都初始化为零(空指针)。
(4)realloc给一个已经分配了地址的指针重新分配空间,参数ptr为原有的空间地址,newsize是重新申请的地址长度。
    realloc可能会导致数据移动。realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩张或是缩小,原有内存的中内容将保持不变。对于缩小,则被缩小的那一部分的内容会丢失。对于扩大,realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果满足则直接返回,如果不满足,就使用堆上第一个有足够大小的*块,将现存的数据拷贝至新的位置,老的数据块放回到堆中。

二、进程的内存使用

1、当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作:
(1)检查要访问的虚拟地址是否合法
(2)查找/分配一个物理页
(3)填充物理页内容(读取磁盘、或者直接置0、或者啥也不干)
(4)建立映射关系(虚地址到物理地址)
重新执行发生缺页中断的那条命令
如果第3步需要读取磁盘,那么这次缺页中断就是majflt,否则就是minflt。

// 查看进程发生缺页中断的次数
ps -o majflt,minflt -C program_name

其中majflt代表major fault,中文名叫大错误。minflt代表minor fault,中文名叫小错误。
这两个数值表示一个进程自启动以来所发生的缺页中断的次数,是一个累加值。

2、从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap。
(1)brk是将数据段(.data)的最高地址指针_edata往高地址推
(2)mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存。

这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已经分配的虚拟地址空间的时候,发生缺页中断,
操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。

3、标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk/mmap、munmap这些系统调用实现的。

malloc小于128k的内存,使用brk分配内存,将_edata往高地址推(只分配虚拟地址,不对应物理内存)。
malloc大于128k的内存(可由M_MMAP_THRESHOLD选项调节)使用mmap系统调用,从堆和栈的中间分配一块虚拟内存。
第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后与虚拟地址空间建立映射关系。
如果malloc分配了A这块内存后,从来不访问它,那么A对应的物理页是不会被分配的。

下面通过一个例子来说明malloc的用法:
[1]进程调用A=malloc(30k),malloc函数调用brk系统调用,将_edata指针往高地址推30k,就完成虚拟内存分配。

[2]进程调用B=malloc(40k),则将_edata指针再往高地址推40k。

[3]进程调用C=malloc(100k),则将_edata指针再往高地址推100k。

[4]进程调用free(B),此时B的虚拟内存和物理内存都没有释放,因为只有一个_edata指针,如果往回推,那么C这块内存怎么办。当然,B这块内存是可以重用的,如果这个时候再来一个40k的请求,那么malloc很可能就把B这块内存返回出去。

[5]进程调用free(C),此时B和C连接起来,变成一块140k的空闲内存。
默认情况下,当最高地址的空闲内存超过128k(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在步骤[5]执行free操作时,发现最高地址空闲内存超过128k,于是内存紧缩。

[6]进程调用D=malloc(200k),malloc函数调用mmap系统调用,在文件映射区分配200k虚拟内存。

[7]进程调用free(D),D对应的虚拟内存和物理内存一起释放。

相关标签: 内存管理