标准C的内存管理
C语言用malloc, calloc, realloc, free函数提供了对动态内存分配的支持。
#include <stdlib.h>
void *calloc(size_t nmemb, size_t size);
void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);
注意,不必对malloc返回的指针作强制类型转换,因为在赋值的时候会自动的转换成正确的类型(一般情况下,你可以将一个void类型的指针赋值给任何其他类型的变量 ,而不会丢失任何信息,反之依然。),使用malloc函数得到的内存是没有初始化的,所以在使用之前要将其进行初始化的操作,为了防止出现内存泄露,使用malloc得到的内存,使用完之后,要调用free函数将其归还给系统 ,使用calloc得到的内存是经过初始化的,其每一位都被置0,realloc函数用来改变之前申请的内存的大小,内存分配函数从一个称为堆(heap)的存储池中获得内存。所以他是一个有限的资源,使用完后必须要归还,当然还要注意悬空指针的问题,所谓悬空指针是指其指向的内存已经返还给系统,并且没有被初始化。
alloca函数
#include <alloca.h>
void *alloca(size_t size);
alloca也是分配一块未初始化的内存,但与上面不同的是,他不是从堆中获得内存,而是从进程的栈中获得。而且当调用alloca返回时,已分配的内存会被自动的释放。
内存映像文件
Linux允许任何一个进程将一个磁盘文件映像到内存中,在磁盘文件和他在内存中的映像文件间建立逐字节的对应关系。
使用内存映像文件能够加速文件I/O操作,普通的I/O操作需要内核缓冲数据,而对内存中的映像文件I/O操作却不需要缓冲,使用内存映像文件还能够用来共享数据,内存映像文件能够向任何进程独立的提供数据访问,并把内存区中的内容保存在一个磁盘文件中,如果这样使用映像文件的话,还需要对内存中的数据采取一种串行访问的方法。
一,加载与卸载
#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *start, size_t length);
mmap函数是将文件映像到内存中,将fd指定的文件从文件开始出偏移offset个位置开始映像到内存中start开始的内存中length指定了映像的大小,flags是指出映像文件所在内存的属性,port指出被映像的内存的保护模式,他的取值可以是下面中的值相或的结果。
保护模式 PROT_NONE 不允许访问
PROT_READ 被映像的内存可读
PROT_WRITE 被映像的内存可写
PROT_EXEC 被映像的内存可执行
属性
标志 POSIX是否兼容 描述
MAP_FIXED YES 如果start无效或者正在使用,则失败
MAP_PRIVATE YES 对映像内存取得写入操作是进程私有的
MAP_SHARED YES 对映像内存的写入也被复制到文件
MAP_ANON(MAP_ANONYMUOS) NO 创建匿名映像,忽略fd (不会包含物理文件)
MAP_DENYWRITE NO 不允许正常的写文件 (文件不会被写入到磁盘)
MAP_GROWSDOWN NO 映像内存区是向下增长的
MAP_LOCKED NO 把页面所定在内存中
如果函数执行成功则返回指向内存的指针,失败则返回-1,并设置errno值。
映像内存的属性必须标志为MAP_PRIVATE或者MAP_SHARED,其他的值都是可选的。私有映像文件使得进程对映像文件的修改不会反应到下面的磁盘文件中,其他进程也不会得到这些修改。
munmap函数用来解除映像,并释放内存。当一个进程终止时,所有的内存映像都将会被释放。函数成功返回0,失败返回-1,并设置errno。
二,写入磁盘
#include <sys/mman.h>
int msync(void *start, size_t length, int flags);
函数msync可以对被更改过的内存中的映像文件,写入到被映像的文件中去,被强行写入的内存区从start开始,写入length个字节,其中,flags的值可以是下面中的一个或多个的或的结果。
MS_ASYNC 调度一次写入操作然后返回
MS_SYNC 在msync返回前写入数据
MS_INVALIDATE 让映像到同一文件的映像无效,一边用心数据更新
三,修改保护模式
#include <sys/mman.h>
int mprotect(const void *addr, size_t len, int prot);
函数mportect用来修改从start开始,长度为len的映像文件的内存的保护模式的值,其值可以是上面说过的值。
四,锁定内存
简单的说,锁定内存就是为了让指定的内存不会被交换到磁盘上面去。锁定内存就是在内存上设置了一个标志防止被交换出去。
#include <sys/mman.h>
int mlock(const void *addr, size_t len);
int munlock(const void *addr, size_t len);
int mlockall(int flags);
int munlockall(void);
其中,要说的只是flags的值,他可以是MCL_CURRENT(在调用返回前请求锁住所有内存页面)或者MCL_FUTURE(锁住所有增加到进程的地址空间的内存页面。)或者两者都是。注意,只有拥有root用户权限的进程能够进行该类操作.
五,更改映像大小
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/mman.h>
void * mremap(void *old_address, size_t old_size , size_t new_size, int flags);
其中,flags用来指出是否在内存中移动内存区,MREMAP_MAYMOVE允许移动,函数执行成功返回,调整后的内存指针,失败返回NULL。
/*
error--memery.c:40: 警告:隐式声明函数 ‘mremap’ -------------I have include <sys/mman.h>, why till show this
error--memery.c:40: 错误:‘MREMAP_MAYMOVE’ 未声明 (在此函数内第一次使用)
error--memery.c:40: 错误:(即使在一个函数内多次出现,每个未声明的标识符在其
error--memery.c:40: 错误:所在的函数内只报告一次。)
error--memery.c:40: 警告:赋值时将整数赋给指针,未作类型转换
*/
//#define _GUN_SOUREC
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char * argv[])
{
int fd;
struct stat fmsg;
off_t len;
char * mstart;
if (0 > (fd = open(argv[1], O_RDONLY)))
{
perror("open file failed\n");
exit(EXIT_FAILURE);
}
if (0 > fstat(fd, &fmsg))
{
perror("get file message failed\n");
exit(EXIT_FAILURE);
}
len = fmsg.st_size;
if ((void *)-1 ==
(mstart = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0)))
{
perror("map file failed\n");
exit(EXIT_FAILURE);
}
printf("%s\n", mstart);
mstart = mremap(mstart, len, len + 10, MREMAP_MAYMOVE);
memcpy(mstart + len, "\n123", 5);
printf("%s\n",mstart);
close(fd);
munmap(mstart, len);
return 0;
}