Chinaunix首页 | 论坛 | 博客
  • 博客访问: 249754
  • 博文数量: 51
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 575
  • 用 户 组: 普通用户
  • 注册时间: 2006-09-22 13:49
文章分类
文章存档

2009年(1)

2008年(13)

2007年(7)

2006年(30)

我的朋友

分类:

2006-12-16 16:32:58

1、经典的C动态内存管理相关函数

标准C提供了malloc, calloc, realloc, free等基于内存堆的管理函数,负责分配可用内存以及释放用过的内存。这些函数本身只负责告诉调用程序,当前有你想要size大小的可用内存块,它的首地址是xxxxx,并不会检查指针的使用是否越过了这个size或者说是offset,这个需要程序员自己去检查。

#include <stdlib.h>
void *malloc(size_t size);


malloc的作用分配一块大小为size个字节的可用内存块,并返回首地址。不能分配的时候返回NULL。

#include <stdlib.h>
void calloc(size_t nmemb, size_t size);


calloc的作用是分配并初始化内存块,返回一个指向nmemb块数组的指针,每块大小为size个字节。它和malloc的主要不同之处是会初始化(清零)分配到的内存。

#include <stdlib.h>
void *realloc(void *ptr, size_t size);


realloc以ptr所指地址为首址,分配size个字节的内存,并返回ptr所指地址。realloc不会初始化分配到的内存块,如果ptr为NULL则相当于malloc,如果size为NULL则相当于free(ptr)。不能分配返回NULL。

#include <stdlib.h>
void free(void *ptr);


free清除ptr所指向的地址,它只作清除的工作,并告诉系统,这块地址已经被释放和清除,可以重新被分配。

一个带bug的程序:

/*
 * mallocfree.c - try to access illegally an address has wrong allocated
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
        char *ptr = malloc(sizeof(char) * 5);
        strcpy(ptr, "123456789");
        printf("ptr : %s\n", ptr);
        printf("freeing ptr\n");
        free(ptr);
        sleep(1);
        printf("ptr : %s\n", ptr);
        printf("trying to write the address has freed\n");
        strcpy(ptr, "54321");
        sleep(1);
        printf("ptr : %s\n", ptr);
        return 0;
}


sleep程序在里面是为了让肉眼更直观的分析程序的运行。执行make mallocfree,生成可执行文件,并运行之:

# ./mallocfree

ptr : 123456789

freeing ptr

ptr :

trying to write the address has freed

ptr : 54321


在这里,ptr使用的地址已经越过了malloc分配给它的界限,而且指针在释放以后继续使用,这导致缓冲区溢出的隐患。

另一个程序:

/*
 * reallocit.c - realloc a pointer and free it
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
        char *str1 = malloc(sizeof(char) * 4), *str2;
        strcpy(str1, "abc");
        printf("str1: %s\n", str1);
        str2 = realloc(str1, sizeof(char) * 7);
        printf ("str2: %s\n", str2);
        free(str1);
        return 0;
}


这个程序先给str1分配一段内存,然后给重新分配这段内存,并分配给str2,最后释放str1所指的内存。注意此时释放的是realloc后的sizeof(char) * 7个字节的空间,而不是原来malloc给str1的sizeof(char) * 4个字节的空间。注意在此代码中,如果再次free(str2),编译运行后程序将报错,因为此地址已被释放;如果在重新分配内存之前继续使用str2指针,可能会造成缓冲区溢出。

上面的函数都是在堆中分配内存,而alloca是在进程栈中获得内存,它的功能也是分配一块未经初始化的内存。

#include <stdlib.h>
void *alloca(size_t size);


2、Linux的内存映像管理函数

内存映像的意思是把磁盘文件映像到内存中,以加速I/O操作和便于共享数据,而在共享数据时,为了避免多个进程同时读写数据引起不可预料的后果,通常使用锁或者信号灯等机制实现对共享对象的序列化访问。Linux提供了在sys/mman.h中定义的一系列内存映像管理的函数。

#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);


mmap把打开的磁盘文件fd从offset开始,映像到内存start处,大小为length。成功返回该映像的指针,失败返回-1并设置相应的errno。
映像可选的保护模式prot包括:PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC等。
映像的可选属性flags包括:MAP_FIXED, MAP_PRIVATE, MAP_SHARED, MAP_ANON, MAP_DENYWRITE, MAP_GROWSDOWN, MAP_LOCKED等。其中MAP_PRIVATE或者MAP_SHARED两者必须且只能选择一个,其它都是可选值,用逻辑或添加。MAP_FIXED强制使用start指定的地址,否则执行失败,如果没有使用这个选项,则mmap在start不可用时会尝试把mmap放到其它地方。MAP_LOCKED只用在root权限的进程才能使用,以防止锁定所有可用内存的恶意攻击。

#include <sys/mman.h>
int munmap(void *start, size_t length);


munmap解除从start开始大小为length个字节的内存映像并释放内存,如果在munmap之后试图继续访问start,将会产生段错误。在进程终止运行时,所有的内存映像会被自动解除。

#include <sys/mman.h>
int msync(const void *start, size_t length, int flags);


msync把从start开始的大小为length个字节的内存映像同步到磁盘,flags包括:MS_ASYNC, MS_SYNC, MS_INVALIDATE。成功返回0,失败返回-1并设置errno。

#include <sys/mman.h>
int mprotect(const void *start, size_t len, int prot);


mprotect修改start开始的大小为len个字节的内存映像的保护模式为prot。成功返回0,失败返回-1并设置errno。

#include <sys/mman.h>
int mlock(const void *start, size_t len);
int munlock(void *start, size_t len);
int mlockall(int flags);
int munlockall(void);


以上函数对指定的内存映像加锁和解锁,其中mlockall的flags包括MCL_CURRENT和MCL_FUTURE。只有root权限才能使用它们。

#include <sys/mman.h>
void *mremap(void *old_addr, size_t old_len, size_t new_len, unsigned long flags);


mremap用指定的flags把地址在old_addr的内存映像大小从old_len调整为new_len,flags如果为MREMAP_MAYMOVE则调整此内存映像的地址。成功返回新地址,失败返回NULL。

阅读(2826) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~