设计二——内存管理
一、实验目的
1. 了解linux 系统的内存机制、虚拟内存管理、分页和分段机制;
2. 掌握malloc,realloc,free,calloc 函数的使用
3. 了解怎样使用物理内存和swap 分区。
4. 了解malloc,realloc,free 的实现方法,掌握linux 的内存分配算法。
二、预备知识
1.malloc函数
原型:extern void *malloc(unsigned int num_bytes);
头文件:在TC2.0中可以用malloc.h或 alloc.h (注意:alloc.h 与 malloc.h 的内容是完全一致的),而在Visual C++6.0中可以用malloc.h或者stdlib.h。
功能:分配长度为num_bytes字节的内存块
返回值:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。
说明:关于该函数的原型,在旧的版本中malloc返回的是char型指针,新的ANSIC标准规定,该函数返回为void型指针,因此必要时要进行类型转换。
名称解释:malloc的全称是memory allocation,中文叫动态内存分配。
2.realloc函数
原型:extern void *realloc(void *mem_address, unsigned int newsize);
语法:指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。
头文件:#include <stdlib.h> 有些编译器需要#include <alloc.h>,在TC2.0中可以使用alloc.h头文件
功能:先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域,同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
返回值:如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
注意:这里原始内存中的数据还是保持不变的。当内存不再使用时,应使用free()函数将内存块释放。
3.free函数
原型:extern void *realloc(void *mem_address, unsigned int newsize);
语法:指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。
头文件:#include <stdlib.h> 有些编译器需要#include <alloc.h>,在TC2.0中可以使用alloc.h头文件
功能:先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域,同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
返回值:如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
注意:这里原始内存中的数据还是保持不变的。当内存不再使用时,应使用free()函数将内存块释放。
4.calloc函数
功 能:在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
跟malloc的区别:calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。
用 法: void *calloc(unsigned n,unsigned size);
头文件:stdlib.h或malloc.h
三、实验心得
1. 设置和修改环境变量LD_PRELOAD可用一句话实现:export LD_PRELOAD="./malloc.so"
2. 重载malloc后,ll、ls、cat等命令都无法使用了,会报段错误(一般是使用了未分配空间的内存);clear、pwd等命令则能正常使用。
3. 自定义的malloc能成功使用,不过效率比系统自带的malloc要低很多。
4. 要想在已赋值的字符串指针后加上新的字符串,则可用:
sprintf(some_memory+strlen(some_memory)," Hello Fedora13!");
5. 所有系统自带的命令都在bin文件夹下。
6. 使用Ctrl+C能关闭正在运行的进程。
7. 自定义malloc中的free、malloc和realloc函数:
/* free 函数的实现*/
void free(void *firstbyte) {
struct mem_control_block *free;
free = firstbyte - sizeof(struct mem_control_block);
free->is_available = 1;
return;
}
/*malloc 函数的实现*/
void *malloc(size_t numbytes)
{
void *current_location;
struct mem_control_block *current_location_mcb;
/* 返回内存地址初始化为0,直到找到合适的内存地址*/
void *memory_location;
/* 开始初始化*/
if(! has_initialized)
{
malloc_init();
}
numbytes = numbytes + sizeof(struct mem_control_block);
memory_location = 0;
/* 开始查找可以使用的内存空间*/
current_location = managed_memory_start;
while(current_location != last_valid_address)
{
/* current_location 和current_location_mcb
*指向同一个内存地址,但是, current_location_mcb 是一个结构体变量。
*current_location 可以记录当前的内存地址*/
current_location_mcb = (struct mem_control_block *)current_location;
if(current_location_mcb->is_available)
{
if(current_location_mcb->size >= numbytes)
{
/* 找到了符合大小要求的内存空间*/
current_location_mcb->is_available = 0;
memory_location = current_location;
break;
}
}
current_location = current_location +
current_location_mcb->size;
}
if(! memory_location)
{
sbrk(numbytes);
memory_location = last_valid_address;
last_valid_address = last_valid_address + numbytes;
current_location_mcb = memory_location;
current_location_mcb->is_available = 0;
current_location_mcb->size = numbytes;
}
memory_location = memory_location + sizeof(struct
mem_control_block);
return memory_location;
}
/* realloc 函数的实现*/
void *realloc(void *first,size_t numb)
{
void *NewFirst;
if(!first)
{
return malloc(numb);
}
else if(numb == 0)
{
free(first);
}
else
{
NewFirst = malloc(numb);
if(NewFirst)
{
memcpy(NewFirst,first,numb);
free(first);
return NewFirst;
}
}
return;
}
8. 内存分配的分界点是4K:小于4K,则按实际大小分配;若大于4K则在实际使用空间前,系统只会象征性的分配4K空间。这就是为什么memory2每次分配1M,但内存实际却无大变化。其最终实际只分配了256*2*4K=2M的空间,而且由于运行时间较短,所以看上去内存并没有变化。而memory3则不同,它每次只要1K,但要了一千次,按照上述原理,1K*1K=1M。所以每经过一次for循环,它会成功要到1M的内存空间!
9. Malloc函数给每个变量分配空间时,都会额外的建一个结构体(就类似于文件头),会有固定大小,有两个属性:是否可用、文件大小。一般的变量指针都是指向数据段的头地址,要想指到整个变量所占的空间的头地址,则需将指针减去前面结构体的大小:free = firstbyte - sizeof(struct mem_control_block);
四、实验测试代码
//memory1.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define A_MEGABYTE (1024*1024)
int main()
{
char *some_memory;
int megabyte=A_MEGABYTE;
int exit_code=EXIT_FAILURE;
some_memory=(char *)malloc(megabyte);
if(some_memory!=NULL)
{
sprintf(some_memory,"Hello Fedora13/n");
printf("%s",some_memory);
exit_code=EXIT_SUCCESS;
}
exit(exit_code);
}
//memory2.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define ONE_M (1024*1024)
#define PHY_MEM_MEGS 256
int main()
{
char *some_memory;
int size_to_allocate=ONE_M;
int megs_obtained=0;
while(megs_obtained < (PHY_MEM_MEGS*2))
{
some_memory=(char *)malloc(size_to_allocate);
if(some_memory!=NULL)
{
megs_obtained++;
sprintf(some_memory,"Hello Fedora13");
printf("%s - now allocate %d Megabytes/n",some_memory,megs_obtained);
}
else
{
exit(EXIT_FAILURE);
}
sleep(0.5);
}
exit(EXIT_SUCCESS);
}
//memory3.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define ONE_K (1024)
#define MAX_MEMORY (500)
int main()
{
char *some_memory;
int size_to_allocate=ONE_K;
int megs_obtained=0;
int ks_obtained=0;
while(megs_obtained < MAX_MEMORY)
{
for( ks_obtained=0; ks_obtained<ONE_K; ks_obtained++)
{
some_memory=(char *)malloc(size_to_allocate);
if(some_memory==NULL) exit(EXIT_FAILURE);
sprintf(some_memory,"Hello Fedora13");
}
sleep(0.5);
megs_obtained++;
printf("%s - now allocate %d Megabytes/n",some_memory,megs_obtained);
}
exit(EXIT_SUCCESS);
}
//memory4.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define ONE_K (1024)
#define MAX_MEMORY (30)
int main()
{
char *some_memory;
int size_to_allocate=ONE_K;
int megs_obtained=0;
int ks_obtained=0;
for( ks_obtained=0; ks_obtained<MAX_MEMORY; ks_obtained++)
{
some_memory=(char *)malloc(size_to_allocate);
if(some_memory==NULL) exit(EXIT_FAILURE);
sprintf(some_memory,"Hello World!");
sleep(0.5);
megs_obtained++;
printf("%s - now allocate %d KB/n",some_memory,megs_obtained);
some_memory=(char *)realloc(some_memory,size_to_allocate*2);
if(some_memory==NULL) exit(EXIT_FAILURE);
sprintf(some_memory+strlen(some_memory)," Hello Fedora13!");
//some_memory += "Hello Fedora13!";
//printf("%d/n",strlen(some_memory));
sleep(0.5);
megs_obtained += 2;
printf("%s - now allocate %d KB/n",some_memory,megs_obtained);
}
exit(EXIT_SUCCESS);
}
另外转一篇学习过程中搜到的好文章:
Linux系统内存错误产生的原因及调试方法(段错误|core dumped)[转]
http://blog.csdn.net/lkq0211/archive/2009/01/12/3759595.aspx