liunx 内存管理

liunx 内存管理
一、虚拟地址

​ 任何一个程序,正常运行都需要内存资源,用来存储变量,常量,函数代码等,这些不同的内容,所存储的区域是不同的,且不同的区域有不同的特性,因此需要了解程序的内存布局,以及内存区域的特性。

​ 每个c语言进程都拥有一片结构相同的虚拟内存,所谓的虚拟内存,就是从实际物理内存映射出来的地址规范范围,最重要的特征是所有的虚拟内存布局都是相同的,极大的方便内核管理不同的进程,如下图,三个完全不相干的进程p1,p2,p3,它们很显然会占据不同的物理内存,但经过系统的变化和映射,它们虚拟内存的布局是完全一样的。

PM(Physical Memory):物理内存
VM(Virtual Memory):虚拟内存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HaWDI1Fz-1650367948548)(C:\Users\yyh\AppData\Roaming\Typora\typora-user-images\image-20220419112818464.png)]

随机将一个进程的虚拟内存放大来看,它会包含以下内容:

栈空间、堆空间、数据段、代码段

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBHOUDtA-1650367930170)(C:\Users\yyh\AppData\Roaming\Typora\typora-user-images\image-20220419112926861.png)]

虚拟内存的典型布局

虚拟内存,内核区域对于应用程序而言是禁闭的,它用于存储操作系统的关键性代码,另外由于Linux系统的历史原因,在虚拟内存的最低端0x0000 0000 ~ 0x0804 8000之间有一段禁闭区域,该区域不可访问。

二、进程内存布局

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ydsoMNK5-1650367930171)(C:\Users\yyh\AppData\Roaming\Typora\typora-user-images\image-20220419150509442.png)]

1.栈内存:

​ 按照”后进先出“的原则来操作;栈全称”运行时栈(run-time stack)",栈会随着进程运行时不断发生变化,一旦有新的函数被调用,就会立即在栈顶分配一帧内存,专门用于存放该函数内定义的局部变量(包括所有的形参),当一个函数执行完毕返回之后,它所占用的那帧内存将被立即释放。

​ 栈主要是用来存储进程执行过程所产生的局部变量(为了实现函数的嵌套调用和返回,栈还必须包含函数切换时当下的代码地址和相关寄存器的值,这个过程被称为”保护现场“,等被调函数执行结束后,再”恢复现场“)。栈的大小一般为8MB,超过了这个最大值会产生所谓的”栈溢出“导致程序崩溃。所以:在进程中不宜嵌套调用太深,也不要定义太多太大的局部变量

2.堆内存:

​ 堆内存是一块自由内存,原因是在这个区域定义和释放变量完全由你来决定,即所谓的自由区。堆跟栈的最大区别在于是不设置限制,最大值取决于系统的物理内存。

​ 堆的全称是”运行时堆(run-time heap)“,跟栈一样,会随着进程的运行而不断增大或缩小。

3.数据段:

​ 数据段实际上分为三部分,地址从高到低分别是.bss段、.data段和.rodata段

.bss段:

​ 存放未初试化的静态数据,它们都将会被初试化为零

.data段:

​ 存放已初始化的静态数据,这些初始值从程序文件拷贝而来

.rodata段:

​ 放只读数据,即常量,比如进程中所有的字符串、字符常量、整型浮点型常量等。

注意:

静态数据指的是 所有的全局变量,以及static型局部变量

4.代码段:

​ 代码段实际上也至少分为两部分:.text段和.init段

.text段:

​ 存放用户代码,包括main函数在内的所有用户自定义函数

.ini段:

​ 存放系统给每一个可执行程序自动添加的”初始化“代码,这部分代码功能包括环境变量的准备、命令行参数的组织和传递等,并且这部分数据被放置在栈底。init段是存放的系统初始化,这部分代码之所以要放在.init段是这个段当中的代码默认只会执行一遍(初始化只能执行一遍),完成任务之后所占据的内存会被立即释放,以便节省系统资源。
在这里插入图片描述

注意:

​ (1)栈中的环境变量和命令行参数,在程序一开始就固定在栈底(即紧挨着内核的地方),且在整个进程运行期间中不再发生变化,若进程运行时对环境变量的个数或者值做了修改,则为了能够容纳修改后的内容,新的环境变量将会拷贝放置在堆中

​ (2)堆和栈都是动态变化的,分别向上和向下增长,大小随着进程不断变大变小。

​ (3)数据段的大小在程序一开始就固定了,没有初始化的静态数据(所有全局变量,以及static型局部变量),.bss段就会默认给这些数据初始化为0

​ (4)尽量避免使用静态数据:①静态数据的生命周期和整个进程相当(在进程退出之前它们都不会释放),会一直占用内存。 ②静态数据是一直典型的共享资源,尤其在多线程程序中使用全局变量,会产生”竞态“。若要使用需同步互斥手段进行包护

三、堆(heap)

​ 堆内存被称为内存中的自由区,在此区域内存的生命周期我们是可控的;对比其他区域的内存,如栈内存,栈的特点是临时分配临时释放,一个变量如果是局部变量,它就会被定义在栈内存中,一旦这个局部变量所在的函数退出,就会被立即释放;再如静态数据,它们都是被存储在数据段,这些变量会一直占用内存直到进程退出为止。

1.堆内存的生命周期

从malloc()/calloc()/realloc()开始,到free()结束。

2.堆内存的申请和释放(堆内存操作API)
malloc()、free() 、calloc()、realloc()
2.1 malloc()
#include <stdlib.h>	//头文件

void *malloc(size_t size);
//功能:在堆中申请一块大小为size的连续内存
//参数:size:内存大小(字节)
//返回值:成功,返回新申请的内存基地址;失败,返回NULL
//备注:该函数申请的内存是未初试化的
2.2 free()
void free(void *ptr);
//功能:将指针ptr所指向的堆内存释放
//返回值:无
//备注:参数ptr必须是malloc()/calloc()/realloc()的返回值
2.3 calloc()
void *calloc(size_t nmemb, size_t size);
//功能:在堆中申请一块具有nmemb个元素的匿名数组,每个元素大小为size
//参数:nmemb:有多少个;size:每个元素的大小
//返回值:成功,返回新申请的内存基地址;失败,返回NULL
//备注:该函数申请的内存将被初试化为0
2.4 realloc()
void *realloc(void *ptr, size_t size);
//功能:将ptr所指向的堆内存大小扩展为size
//参数:nmemb:有多少个;size:每个元素的大小
//返回值:成功,返回扩展后的内存基地址;失败,返回NULL
//备注:①返回的基地址可能与原地址ptr相同,也有可能不同(发生了迁移)②当size为0是,该函数相当于free(ptr);③如果ptr原来是NULL,就相当于malloc(size)
注意:

​ 使用realloc()时,如果新size大于原来申请的size,也就是扩大内存操作,则会把ptr指向的内存的数据拷贝到新地址;如果新size小于原来申请的size,也就是缩小内存操作,则原数据会被拷贝并截取到新size的位置。如果使用realloc()时申请内存失败,返回NULL,原来的ptr指针依然可以用。

四、作用域

​ C语言中,标识符都有一定的可见范围,这些可见范围保证了表示符只能在一个有限的区域内使用,这个可见范围,被称为作用域

​ 在软件开发中,尽量缩小标识符的作用域是一项基本原则,一个标识符的作用域超过它实际所需要的范围时,就会对整个软件的命名空间造成污染,导致一些不必要的名字冲突和误解。

1.函数声明作用域:

​ 在函数声明表达式中定义的变量,其可见范围仅限于该函数声明式

void func(int a,char *s);

注意:

​ 变量a和s只能在函数声明式中可见

​ 函数声明,变量a和s可以省略(一般不省略,因为可以对参数注解)

2.局部作用域
int a=10;
{
    int a=20;
    printf("a = %d",a);
}

注意:

​ 代码块是指用{}括起来的区域

​ 代码块可以嵌套包含,外层的标识符会被内嵌的同名标识符临时覆盖变的不可见

​ 代码块作用域的变量,由于其可见范围是局部的,因此称为局部变量

3.全局作用域

​ 在代码块外部定义的变量,其可见范围可以跨越多个文件

test.c
    int lable;	//lable除了本文件可见,还可以被作为外部引用
extern in lable;	//外部引用这个变量,这个变量在test.c文件里
int main()
{
    lable=20;
    printf("lable = %d",lable);
    return 0;
}
小结:

​ 代码块作用域:(局部变量,静态局部变量)

​ 从变量定义或者声明开始,到当前这个代码结束(作用域之外,不能再次使用)

文件作用域:(全局变量)

​ 从全局变量的定义或声明位置开始,整个文件都可以用

​ 函数原型作用域:(当前文件的普通函数)

​ 从当前函数定义或声明位置开始时,整个文件可以用

​ goto标签: 包含它的当前函数任意位置都可以跳转,不同函数之间是不行的,只作用域包含它的函数

量)

五、变量存储期(生命周期)

​ C语言中,变量都是由一定的生存周期的,所谓的生存周期指的是分配到释放的时间间隔,为变量分配内存相当于变量的诞生,释放放其内存,相当于变量死亡,从诞生到死亡就是一个变量的生命周期

根据定义方式的不同,变量的生命周期有三种形式:

自动存储期

自定义存储期

静态存储期

在这里插入图片描述

自动存储期:

​ 在栈内存中分配的变量,统统拥有自动存储期,因此也都称为自动变量,这里自动的含义,指的是这些变量的内存管理不需要开发者操心,都是自动的,在变量定义处自动分配,出了变量的作用域就自动释放

以下三个概念是等价的

自动变量:从存储期的角度,描述变量的时间特性

临时变量:同上

局部变量:从作用域的角度,描述变量的空间特性

可以直接统称为栈变量

静态存储期:

​ 在数据段中分配的变量,统统拥有静态存储期,因此也都被称为静态变量,这里静态的含义,指的是这些变量不会因为程序运行而发生临时分配和释放。它们的生命周期是恒定的,跟整个程序一致的

静态变量包含:

​ 全局变量,不管加staic关键字还是不加,都是静态变量

​ 静态局部变量

注意:

​ 若定义时未初始化,系统会将所有的静态数据自动初始化为0

​ 静态数据初始化,只会执行一遍

​ 静态数据从程序开始运行时便已经存在,直到程序退出时才释放

​ static修饰局部变量,使之从栈内存临时数据,变成静态数据

​ static修饰全局变量,使之由各个文件可见的静态数据,变成本文件可见的静态数据

自定义存储期

​ 在堆中分配的变量,统统拥有自定义存储期,也就说说这些变量的分配和释放,都是由开发者决定的,由于堆内存拥有高度自治权,因此堆是程序开发中用得最多的一片区域

相关的API

申请堆内存:malloc(),calloc(),realloc()

清零:bzero()
int *p = malloc(20);
bzero(p, 20);

释放内存:free()

注意:

​ malloc申请的堆内存,默认情况下随机值,一般需要用bzero()来清零

​ calloc申请的堆内存,默认情况下是已经清零了的,不需要再清零

​ free只能释放堆内存的空间,不能释放别的区段的内存

释放内存的含义:

​ 释放内存意味着将内存的使用权归还给系统

​ 释放内存并不会改变指针的指向

​ 释放内存并不会对内存做任何修改,更不会将内存清零

小结:
自动存储期:栈空间里面的数据

​ 普通局部变量,函数的形参

​ 在定义位置开始,出作用域结束

静态存储期:数据段里面的数据

​ 静态局部变量,全局变量

​ 在定义位置开始,出程序退出结束

自定义存储期:堆空间里面的数据

​ 由malloc(),calloc(),realloc()申请成功开始,到free()释放结束

六、链接类型
①外部链接:

​ 普通的全局变量,函数,对外公开

②无链接:

​ 普通局部变量(包含静态局部变量),函数形参

③内部链接:

​ staic修饰的全局变量,函数,对外不公开,对内公开

普通的全局变量,函数,要想在外部被引用,需要用到extern关键字,做外部声明,表示声明的变量或函数在其他文件有定义。

多个文件编译:
gcc file1.c file2.c ... -o 可执行文件(目标文件)
七、static关键字

​ static关键字的作用

修饰全局变量:

​ 普通全局变量------》静态全局变量

​ 数据段 ------------》数据段

​ 静态存储期--------》静态存储期

​ 外部链接 ----------》内部链接

修饰局部变量:

​ 普通局部变量------》静态局部变量

​ 栈空间 ------------》数据段

​ 自动存储期--------》静态存储期

​ 无链接 ------------》无连接

修饰函数:

​ 普通函数----------》静态函数

​ 外部链接----------》内部链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yengi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值