Linux内存相关知识点
1.虚拟内存、物理内存、半导体内存和换页文件
虚拟内存:地址空间,虚拟的存储区域,应用程序所访问的都是虚拟内存。
物理内存:存储空间,实际的存储区域,只有系统内核可以访问物理内存。
虚拟内存和物理内存之间存在对应关系,当应用程序访问虚拟内存时,系统内核会依据这种对应关系找到与之相应的物理内存。上述对应关系存储在内核中的内存映射表中。
物理内存包括半导体内存和换页文件两部分。
当半导体内存不够用时,可以把一些长期闲置的代码和数据从半导体内存中缓存到换页文件中,这叫页面换出,一旦需要使用被换出的代码和数据,再把它们从换页文件恢复到半导体内存中,这叫页面换入。因此,系统中的虚拟内存比半导体内存大得多。
2. 进程映射(Process Maps)
每个进程都拥有独立的4G字节的虚拟内存,分别被映射到不同的物理内存区域。
内存映射和换入换出都是以页为单位,1页=4096字节。
4G虚拟内存中高地址的1G被映射到内核的代码和数据区,这1个G在各个进程间共享。用户的应用程序只能直接访问低地址的3个G虚拟内存,因此该区域被称为用户空间,而高地址的1个G虚拟内存则被称为内核空间。用户空间中的代码只能直接访问用户空间的数据,如果要想访问内核空间中的代码和数据必须借助专门的系统调用完成。
用户空间的3G虚拟内存可以进一步被划分为如下区域:
------------------
系统内核(1G)
高地址------------------
环境变量和
命令行参数
------------------
栈区:非静态局部变量
- - - - - - - -
v
3G
^
- - - - - - - -
堆区:动态内存分配(malloc函数族)
---------------------
BSS区:无初值的全局和静态局部变量
---------------------
数据区:非const型有初值的全局和静态局部变量
---------------------
只读常量:字面值常量,const型有初值的全局和静态局部变量
代码区(正文段):可执行指令
低地址---------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
const int const_global = 10; // 常全局变量
int init_global = 10; // 初始化全局变量
int uninit_global; // 未初始化全局变量
int main(int argc, char* argv[]) {// 常静态变量const static int const_static = 10;// 初始化静态变量static int init_static = 10;// 未初始化静态变量static int uninit_static;// 常局部变量const int const_local = 10;int prev_local; // 前局部变量int next_local; // 后局部变量// 前堆变量int* prev_heap = malloc(sizeof(int));// 后堆变量int* next_heap = malloc(sizeof(int));// 字面值常量char const* literal = "literal";extern char** environ; // 环境变量printf("---- 命令行参数与环境变量 ----\n");printf(" 环境变量:%p\n", environ);printf(" 命令行参数:%p\n", argv);printf("-------------- 栈区 ------------\n");printf(" 后局部变量:%p\n", &next_local);printf(" 前局部变量:%p\n", &prev_local);printf(" 常局部变量:%p\n", &const_local);printf("-------------- 堆区 ------------\n");printf(" 后堆变量:%p\n", next_heap);printf(" 前堆变量:%p\n", prev_heap);printf("------------- BSS区 ------------\n");printf(" 未初始化全局变量:%p\n", &uninit_global);printf(" 未初始化静态变量:%p\n", &uninit_static);printf("------------ 数据区 ------------\n");printf(" 初始化静态变量:%p\n", &init_static);printf(" 初始化全局变量:%p\n", &init_global);printf("------------ 代码区 ------------\n");printf(" 常静态变量:%p\n", &const_static);printf(" 字面值常量:%p\n", literal);printf(" 常全局变量:%p\n", &const_global);printf(" 函数:%p\n", main);printf("------------------------------\n");return 0;
}
通过size命令查看一个可执行程序的代码区、数据区和BSS区的大小。
每个进程的用户空间都拥有独立的从虚拟内存到物理内存的映射,谓之进程间的内存壁垒。
3.内存的分配与释放
malloc/calloc/realloc/free
|
v
brk/sbrk
|
v
mmap/munmap
|
v
kmalloc/kfree
以增量方式分配或释放虚拟内存
分配:映射+占有
| \____________
在地址空间(虚拟内存)和 \
存储空间(物理内存)之间 指定内存空
建立映射关系 间的归属性
释放:放弃占有+解除映射
| |
解除对内存空 消除地址空间(虚拟内存)和存储
间的归属约束 空间(物理内存)之间的映射关系
#include <unistd.h>
void* sbrk(intptr_t increment);
堆顶->- - - - - - - -
堆区
-----------------
sbrk(10)
堆顶->- - - - - - - -
10字节
- - - - - - - -<-返回值
堆区
-----------------
sbrk(-10)
- - - - - - - -<-返回值
10字节
堆顶->- - - - - - - -
堆区
-----------------
成功返回调用该函数之前的堆顶指针,失败返回void*类型-1。
increment
>0 - 堆顶指针上移,增大堆空间,分配虚拟内存
<0 - 堆顶指针下移,缩小堆空间,释放虚拟内存
=0 - 不分配也不释放虚拟内存,仅仅返回当前堆顶指针
系统内核维护一个指针,指向堆内存的顶端,即有效堆内存中最后一个字节的下一个位置。sbrk函数根据增量参数increment调整该指针的位置,同时返回该指针原来的位置,期间若发生内存耗尽或空闲,则自动追加或取消相应内存页的映射。
123____~~~~~~~~........
^ ^ ^ ^
堆顶
#include <stdio.h>
#include <unistd.h>
int main(void) {setbuf(stdout, NULL);int* p1 = (int*)sbrk(sizeof(int));if (p1 == (int*)-1) {perror("sbrk");return -1;}*p1 = 0;printf("%d\n", *p1);double* p2 = (double*)sbrk(sizeof(double));if (p2 == (double*)-1) {perror("sbrk");return -1;}*p2 = 1.2;printf("%g\n", *p2);char* p3 = (char*)sbrk(256 * sizeof(char));if (p3 == (char*)-1) {perror("sbrk");return -1;}sprintf(p3, "Hello, World!");printf("%s\n", p3);/*if (sbrk(-(256 * sizeof(char) + sizeof(double) + sizeof(int))) == (void*)-1) {perror("sbrk");return -1;}*/if (brk(p1) == -1) {perror("brk");return -1;}return 0;
}
以绝对地址的方式分配或释放虚拟内存
int brk(void* end_data_segment);
成功返回0,失败返回-1。
end_data_segment
>当前堆顶,分配虚拟内存
<当前堆顶,释放虚拟内存
=当前堆顶,空操作
堆顶->- - - - - - - -<-void* p = sbrk(0);
堆区
-----------------
brk(p+10)
- - - - - - - -<-p+10
10字节
堆顶->- - - - - - - -
堆区
-----------------
brk(p)
堆顶->- - - - - - - -<-p
堆区
-----------------
系统内核维护一个指针,指向当前堆顶,brk函数根据指针参数end_data_segment设置堆顶的新位置,期间若发生内存耗尽或空闲,则自动追加或取消相应内存页的映射。
#include <stdio.h>
#include <unistd.h>
/** xxxIIIIDDDDDDDDCC...C* ^ ^ ^ ^* p1 p2 p3*/
int main(void) {setbuf(stdout, NULL);int* p1 = (int*)sbrk(0);if (p1 == (int*)-1) {perror("sbrk");return -1;}double* p2 = (double*)(p1 + 1);if (brk(p2) == -1) {perror("brk");return -1;}*p1 = 0;printf("%d\n", *p1);char* p3 = (char*)(p2 + 1);if (brk(p3) == -1) {perror("brk");return -1;}*p2 = 1.2;printf("%g\n", *p2);void* p4 = p3 + 256;if (brk(p4) == -1) {perror("brk");return -1;}sprintf(p3, "Hello, World!");printf("%s\n", p3);if (brk(p1) == -1) {perror("brk");return -1;}return 0;
}
建立虚拟内存到物理内存或文件的映射
#include <sys/mman.h>
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
成功返回映射区虚拟内存的起始地址,失败返回MAP_FAILED(void*类型的-1)。
start - 映射区虚拟内存的起始地址,NULL表示自动选择
length - 映射区的字节数,自动按页(4096字节)取整
prot - 访问权限,可取以下值:
PROT_READ - 可读
PROT_WRITE - 可写
PROT_EXEC - 可执行
PROT_NONE - 不可访问
flags - 映射标志,可取以下值:
MAP_ANONYMOUS - 匿名映射,将虚拟内存映射到物理内存,函数的最后两个参数fd和offset被忽略
MAP_PRIVATE - 私有映射,将虚拟内存映射到文件的内存缓冲区中而非磁盘文件
MAP_SHARED - 共享映射,将虚拟内存映射到磁盘文件中
MAP_DENYWRITE - 拒写映射,文件中被映射区域不能存在其它写入操作
MAP_FIXED - 固定映射,若在start上无法创建映射,则失败(无此标志系统会自动调整)
MAP_LOCKED - 锁定映射,禁止被换出到换页文件
fd - 文件描述符
offset - 文件偏移量,自动按页对齐
解除虚拟内存到物理内存或文件的映射
int munmap(void* start, size_t length);
成功返回0,失败返回-1。
start - 映射区的起始地址
length - 映射区的字节数
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
int main(void) {char* psz = mmap(NULL, 8192, PROT_READ | PROT_WRITE,MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);if (psz == MAP_FAILED) {perror("mmap");return -1;}sprintf(psz, "第一页");sprintf(psz + 4096, "第二页");printf("%s\n", psz);printf("%s\n", psz + 4096);if (munmap(psz, 4096) == -1) {perror("munmap");return -1;}//printf("%s\n", psz);printf("%s\n", psz + 4096);if (munmap(psz + 4096, 4096) == -1) {perror("munmap");return -1;}return 0;
}
Linux内存相关知识点相关推荐
- Linux 内存相关问题汇总
这篇文章是对 Linux 内存相关问题的集合,工作中会有很大的帮助.关注公号的朋友应该知道之前我写过从内核态到用户态 Linux 内存管理相关的基础文章,在阅读前最好浏览下,链接如下: CPU是如何访 ...
- Linux面试相关知识点看着一文就够了
今天和大家分享一下linux操作系统下主要用到的几个知识点,分别是:linux目录结构.linux常用命令.文件权限操作.服务操作.yum安装命令.docker服务.vim编译器.pymysql测试连 ...
- linux内存相关命令汇总
内存整体信息 查看内存剩余free: e0005055@ibudev20:~$ freetotal used free shared buff/cache available Mem: 3279172 ...
- linux/shell相关知识点
阿里Linux Shell脚本面试25个经典问答 Linux运维工程师12道面试题整理 感谢作者分享!
- Linux 磁盘相关知识点总结
Linux Swap分区 注:磁盘都是提前准备好的,还望个人准备好后在进行操作 Linux 的 Swap交换分区,就相当于Windows系统上的虚拟内存,默认状态下,Swap分区是不会启用的,只有当物 ...
- Linux设备相关信息查询
Linux内存相关信息 内存容量查询 $ cat /proc/meminfo | grep MemTotal MemTotal: 内存相关信息 $ cat /proc/meminfo 查看磁盘的厂家及 ...
- 万字整理,图解Linux内存管理所有知识点
Linux的内存管理可谓是学好Linux的必经之路,也是Linux的关键知识点,有人说打通了内存管理的知识,也就打通了Linux的任督二脉,这一点不夸张.有人问网上有很多Linux内存管理的内容,为什 ...
- Linux内存管理:知识点总结(ARM64)
https://mp.weixin.qq.com/s/7zFrBuJUK9JMQP4TmymGjA 目录 Linux内存管理之CPU访问内存的过程 虚拟地址转换为物理地址的本质 Linux内存初始化 ...
- 万字整理,肝翻Linux内存管理所有知识点
Linux的内存管理可谓是学好Linux的必经之路,也是Linux的关键知识点,有人说打通了内存管理的知识,也就打通了Linux的任督二脉,这一点不夸张.有人问网上有很多Linux内存管理的内容,为什 ...
最新文章
- [原创]windows server 2012 AD架构 试验 系列 – 3 创建备份DC2
- 90. Subsets II
- SAP 自动付款的配置
- 小学英语运用计算机教学自评,信息技术在小学英语课堂中如何更有效地利用教学评价...
- 关于重载函数的一些学习
- 【Python】函数调用外部变量
- 台式计算机时间不准,每天开机电脑时间都不正确怎么办?试试这个办法!
- Arm发布移动端v9体系新架构,CPU、GPU、IP全囊括了!
- 【精】【火】关于CSDN博客与博主的第二个博客之间的区别与联系
- 【Python+OpenCV】Windows+Python3.6.0(Anaconda3)+OpenCV3.2.0安装配置
- 力扣-268 丢失的数字
- go实现json格式文件的输出---小示例
- Python爬虫实战+数据分析+数据可视化(前程无忧招聘信息)
- 实训十二:交换机隔离VLAN配置
- 3.虚幻4-游戏开始界面的制作
- Java给pdf添加页码(这是我之前的一篇文章)出现内存溢出Java heap space
- 2020十大最佳大数据分析工具,果断收藏
- 刷爆力扣之等价多米诺骨牌对的数量
- 修改http默认的80端口为其它端口
- 进入微信后提示用浏览器打开