内存管理函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010318270/article/details/89350432

一、基本用法

#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对应的虚拟内存和物理内存一起释放。

猜你喜欢

转载自blog.csdn.net/u010318270/article/details/89350432