内存管理技术:
程序:存放在磁盘/硬盘上的可执行文件
进程:运行在内存中的程序,叫做进程;同一个程序可以对应多个进程
进程映像:进程在内存中的布局
进程中的内存区域划分
int num;
int main(){
int num;
return 0;
}
代码区:(Text)
主要用于存放具体的功能代码,函数指针指向该区域;如可执行指令
只读常量区:(Text)
主要用于存放常量字符串,字面值常量,
具有常属性并且经被初始化的全局变量和静态局部变量
全局区/数据区:(Data)
主要用于存放不具有常属性但已经被初始化的全局变量和静态局部变量;
BBS段:(BBS)
主要用于存放不具有常属性也没有初始化的全局变量和静态局部变量;
该区域会在main函数之前自动清零
堆区:(Heap)
主要指使用malloc/calloc/realloc/free操作的区域
该区域有程序员手动申请和 释放;
栈区:(Stack)
主要存放非静态的局部变量
该区域由操作系统自动管理;
进程在内存空间中的布局按照从小到大的顺序:
代码区,只读常量区,全局区/数据区,BBS区,堆区,栈区;
一般来说,堆区中的内存地址从小到大依次分配
栈区内部地址从大到小分配
堆和栈中间有一段空间,叫做预留空间;一般用来存放共享库/共享内存
栈区的最上面预留的一段空间,用来保存环境表信息,命令行参数
存放常量字符串不同形式的比较:
比如:
char *pc = "hello";
char ps[6] = "hello";
printf("pc = %p &pc = %p\n",pc,&pc);
// ps 表示第一个元素的首地址
// &ps 表示整个数组的首地址,都是统一个地址
printf("ps = %p &ps = %p\n",ps,&ps);
printf("****************************\n");
// 试图修改指针的指向
pc = "world";// ok 指针指向的是地址,想指谁都可以
ps = "world";// error 指向的是元素首地址, 数组名是常量,不可修改
// 试图修改指针指向的内容
strcpy(pc,"1234"); // error pc 指向的是只读常量区,不可修改 段错误
strcpy(ps,"1234");// ok
对于一个记录常量字符串的字符指针来说:指针指向的内容不可以改变,指针的指向可以改变
对于一个记录常量字符串的字符数组来说:指针指向的内容可以改变,指针不可以改变
对于一个记录动态内存的字符串指针来说:指针指向的内容和指针的指向都可以改变
******虚拟内存管理技术******
在linux系统中,一般都采用虚拟内存管理技术来进行内存空间管理
即:每个进程都可以有0~4G -1的地址空间(虚拟的,并不是真实存在的)
由操作系统负责建立虚拟地址到真实物理内存/文件的映射
因此不同进程中的虚拟地址看起来是一样,但是所对应的真实物理内存/文件是不一样的;
其中0~3G -1之间的地址叫做用户空间
3G~4G -1之间的地址空间叫做内核空间
一般用户程序都在用户空间,不能直接访问内核空间,
但是操作系统的内核提供了相关的api函数可以访问内核空间
内存地址的基本单位:字节
内存映射的基本单位:内存叶,一个内存叶的大小所4kb(4096个字节)
段错误的由来:
1.试图操作没有操作权限的内存空间;比如:修改只读常量区的数据
2.试图操作没有经过映射的虚拟地址;比如:给任意一个虚拟地址进行赋值
使用malloc函数申请动态内存:
1.注意事项
使用malloc函数时,除了申请参数指定的内存空间时,可能会申请额外的12个字节
一般用于存放该动态内存的大小,是否可用等管理信息
切忌不要对malloc申请的内存不要进行越界赋值操作,否则会破坏内存管理信息时,
可能会引发段错误
2.申请动态内存的一般原则
一般来说,使用malloc函数申请比较小块的动态内存时,
系统一般会一次性映射33个内存叶,用于提高效率
#include <unistd.h>
#include <sys/types.h>
getpid():用于获取当前进程的编号
cat /proc/进程号/maps:查看当前进程的内存叶大小
使用free释放动态内存的一般原则:
一般来说,使用free函数释放动态内存时,释放多少则从映射总量中减去多少,
当所有的动态内存都释放完毕时,操作系统依然会保留33个内存叶,用于提高效率
******内存管理的相关函数
#include <unistd.h>
int getpagesize(void);
函数功能:用于获取当前系统中一个内存叶的大小并返回;
int brk(void *addr);
函数功能:用于调整动态内存的大小,具体的调整方式如下:
addr > 原来的末尾地址时,表示申请动态内存
addr < 原来的末尾地址时,表示释放动态内存
addr = 原来的末尾地址时,表示动态内存不变
void *sbrk(intptr_t increment);
函数功能:用于调整动态内存的大小,调整方式如下:
increment > 0:表示增加/申请动态内存
increment = 0:表示获取当前动态内存的末尾地址
increment < 0:表示减少/释放动态内存
无论增加,减少还是获取操作,当该函数调用成功时都会返回[**调整之前**]的动态内存末尾地址;
失败返回(void *)-1;
一般来说,使用sbrk函数申请比较小块的动态内存时,
系统一般会映射一个内存叶大小的内存空间,
当申请的动态内存超过一个内存叶时,系统会再次映射一个内存叶,
当释放完毕所有动态内存时,系统不会保留任何的内存映射
malloc() 与 free() 函数相比,sbrk()函数更节省内存空间,但是效率没有malloc()高
一般只用sbrk()来分配内存空间,是否内存空间的话采用向匹配的brk()函数