malloc()和calloc()
进程对动态内存的请求被认为是不紧迫的。例如,当进程的可执行文件被装入时,进程并不一定立即对所有的代码进行访问。类似地,当进程调用malloc() 请求动态内存时,并不意味着进程很快就会访问所有获得的内存。因此一般来说,内核总是尽量推迟给用户态进程动态分配内存。
The kernel succeeds in deferring the allocation of dynamic memory to processes by using a new kind of resource. When a User Mode process asks for dynamic memory, it doesn't get additional page frames; instead, it gets the right to use a new range of linear addresses, which become part of its address space. This interval is called a "memory region."
内核使用一种资源成功实现了对进程动态内存的推迟分配。当用户态进程请求动态内存时,并没有获得请求的页框,而仅仅获得对一个新的线性地址区的使用权,而这一线性地址区间就成为进程地址空间的一部分。这个区间叫做线性区(memory region).
the Page Fault exception handler in deferring the allocation of page frames to processes.缺页异常处理程序最终为进程获取当前所需的page frames. 摘自《深入理解linux内核(第二版)》P269 -- 进程地址空间一章
brk()是最常用的系统调用,用户进程通过它向内核申请空间(memory region或许还有page frames)。通过malloc()一类的C语言库函数间接地用到brk()。如果把malloc()想像成零售,brk()就是批发。库函数 malloc()为用户进程维护一个小仓库,当进程需要使用更多的内存空间时就向小仓库要,小仓库中存量不足时就通过brk()向内核批发。alloc ()是如何间接用到brk()的?《情景分析(上)》P160
malloc()
-------------------------------------------
malloc()函数用来分配内存:将总共需要的字节数作为参数传递给该函数,返回值是指向最新分配的内存的指针,而如果内存没有分配好,则返回值是NULL。
malloc()的使用技术:
some_type *pointer;
pointer = malloc(count * sizeof(*pointer));
注:
(1) 这个方法保证malloc()会分配正确数量的内存,而不用考虑pointer的生命。如果pointer的类型后来变了,sizeof算子自动确保要分配的字节数仍然正确。
(2) malloc()返回的内存是“没有“初始化的。这块内存可能包含任何随机的垃圾,你可以马上用有效数据或者至少是用零来初始化这块内存。要用0初始化,可以用
void *memset(void *s, int c, size_t n);
(3) malloc()最终通过缺页异常获取的物理内存中的原有数据,大多数情况下是0(但不能保证一定是0)
calloc()
-------------------------
calloc()函数是malloc的简单包装。它的主要优点是把动态分配的内存清零。
void *calloc(size_t nmemb, size_t size);
用经验的程序员更喜欢使用calloc(),因为这样的话新分配内存的内容就不会有什么问题,调用calloc()肯定会清0,并且可以避免调用memset().
malloc()实践
-----------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *a;
a = (char *)malloc(10 * sizeof(*a)); //(1)
memset(a, 'a', 10); //(2)
printf("a = %s ", a);
free(a); //(3)
a = NULL; //(5)
char *b;
b = (char *)malloc(10 * sizeof(*b)); //(4)
memset(b, 'b', 10);
printf("a = %s ", a);
printf("b = %s ", b);
free(b);
}
注:
-----------------------------------------
(1)
用户地址空间(虚拟内存)
| |
|--------|
| |
| | 10bytes
| |
a ------->| |0x804a008
|--------|
| |
a = malloc();执行后
(2)
真正的物理内存在此通过缺页异常获得
|------------|------------|--------------|
| 0000100000 | 0001001010 | 000000001000 |
|------------|------------|--------------|
| | |
| - |---------| | |---------| | |---------|
| - | | | | | | | |
| - | | | | | | |---------|
| - | | | | | | | a |
| - | | | | | | | ... |10bytes
| - | | | | | | | ... |
+->| |--+ +-->| |--+ | | a |
| | - | | | - | +--->|- -------|
| | - | | | - | | |
|---------| - +---->|---------| - +----->|---------|
页目录表 页表 存放有效数据的物理页
物理内存
memset(a, 'a', 10);执行后
(3)
执行free(a)以后a依然指向0x804a008处的一段用户空间,但是该空间已经不再属于a指针管理了,相应的物理内存也被回收并清0了。 如果释放指针a之后立刻再次使用a访问其所指空间,缺页异常将再次为该进程空间分配新的物理内存(可能还是释放前的那块物理空间,但是已经清0了)。
如果有新的动态内存请求b = malloc(),指针a所释放的进程空间可能会被重新分配。用指针a然依然可以访问(读或者写)该进程空间所对应的物理空间(该物理空间是访问新指针时 由缺页异常为新指针分配的,可能就是为指针p所分配的物理内存)(不会报错),但是这样的操作是非法的,因为该空间可能已经被新的主人(新指针)所管理。
(4)
用户地址空间
| |
|--------|
| |
| | 10bytes
| |
b ------->| |0x804a008
(a) |--------|
| |
b = malloc();执行后
此时a和b同时指向相同的进程空间(该进程空间所对应的物理空间也相同),对a或b的操作都将会操作相同的空间(物理空间),这是很危险的,因为这块空间的正真主人是b,而a也有能力修改b所指的空间(哇塞!以前的房主混进房子里来了,新房主的财产可就危险了)
(5)
所以在释放指针所指进程空间后,应立刻将该指针置为空指针。该指针就没有机会再访问以前的进程空间了!
malloc()和calloc()都是用于分配内存的函数。
函数malloc()和calloc()都可以用来动态分配内存空间,但两者稍有区别。
malloc()函数有一个参数,即要分配的内存空间的大小:
void*malloc(size_tsize);
calloc()函数有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小。
void*calloc(size_tnumElements,size_tsizeOfElement);
如果调用成功,函数malloc()和函数calloc()都将返回所分配的内存空间的首地址。
函数malloc()和函数calloc() 的主要区别是前者不能初始化所分配的内存空间,而后者能。如果由malloc()函数分配的内存空间原来没有被使用过,则其中的每一位可能都是0;反之, 如果这部分内存曾经被分配过,则其中可能遗留有各种各样的数据。也就是说,使用malloc()函数的程序开始时(内存空间还没有被重新分配)能正常进行,但经过一段时间(内存空间还已经被重新分配)可能会出现问题。
函数calloc() 会将所分配的内存空间中的每一位都初始化为零,也就是说,如果你是为字符类型或整数类型的元素分配内存,那麽这些元素将保证会被初始化为0;如果你是为指 针类型的元素分配内存,那麽这些元素通常会被初始化为空指针;如果你为实型数据分配内存,则这些元素会被初始化为浮点型的零。
memset
memset 函数详细说明
#include <stdio.h>
#include <string.h>
int main()
{
char s[]="Golden Global View";
memset(s,'G',strlen(s));//貌似这里有点问题//
printf("%s",s);
return 0;
}
说明:返回指向buffer的指针。
来源:oschina
链接:https://my.oschina.net/u/104077/blog/196646