作者: LemonNan

原文: https://juejin.im/post/5ee3c34a518825430c3ad31d

前言

本篇是对Linux内存分配的一个学习笔记.

程序内存结构

下面是在 Linux/x86-32 中典型的一个进程内存结构

  • 文本段包含了进程运行的程序机器语言指令. 文本段具有只读属性, 以防止进程通过勘误指针意外修改自身指令. 因为多个进程可以同时运行同一程序, 所以又将 文本段设为可共享 , 这样一份程序代码的拷贝可以映射到所有这些进程到虚拟地址空间中.

  • 初始化数据段包含显示初始化的全局变量和静态变量. 程序加载到内存时, 从可执行文件中读取这些变量的值.

  • 未初始化数据段包含了未进行显示初始化的全局变量和静态变量 . (书上写了一堆, 实际上就是懒加载)

  • 栈(stack)是一个动态增长和收缩的段, 由栈帧(stack frames)组成 . 系统会为每个当前调用的函数分配一个栈帧.栈帧中存储局部变量(所谓自动变量), 实参和返回值.

  • 堆(heap)时刻在运行时(为变量)动态进行内存分配的一块区域, 堆顶端称做 program break .

3g(32位)以上的虚拟内存地址, 程序无法访问.

程序的起始地址为 0x08048000 (32位)、0x00400000 (64位)

malloc 和 free

malloc

void *malloc(size_t size);
  • 栈向下增长超出之前曾达到的位置

  • 挡在堆中分配或者释放内存时, 通过调用 brk()、sbrk() 或 malloc 函数族来提升 program break 的位置.

  • 调用 shmat() 连接 System V 共享内存区或者当调用 shmdt() 脱离共享内存区时.

  • 调用 mmap() 创建内存映射或者 munmap() 解除内存映射

特点

  • malloc() 返回的内存快所采用的字节对齐的方式, 在大多数的硬件架构上, 意味着 malloc 是基于 8字节 或者 16字节 边界来分配内存的.

  • malloc 之后的内存, 在不使用的需要需要手动 free, malloc 和 free 一一对应, 否则可能会导致 未知错误(多次free) 或者 内存泄漏(没有调用free).

  • 允许分配小块内存

  • 允许随意释放内存快, 它们被维护于一张空闲内存列表中, 在后续内存分配调用时循环使用

free

free() 函数释放 ptr 参数所指向的内存快

void free(void *ptr);

特点

  • free 并不降低 program break 的位置, 而是将这块内存添加到空闲内存列表, 供后续的 malloc() 函数循环使用.这么做有几个原因:

    • 被释放的内存快通常位于堆的中间, 而非堆堆顶部, 因而降低 program break 不能达到效果

    • 最大限度减少程序必须执行 sbrk() 调用次数(减少系统调用的开销)

  • free 传入空指针不会做任何处理(从设计上来说这不是错误代码)

  • 调用 free 后堆参数 ptr 的使用, 比如再次调用 free, 会产生错误并且可能导致不可预知的结果.

为什么是8/16字节对齐

  • CPU 读取8字节对齐, 比如 double/long, 不对齐的话需要读写2次

  • CPU高速缓存行大小通常是 32 或者 64 字节. 如果对象是8字节对齐的数据, 则只需要占用一个缓存行, 如果不是8字节对齐的话, 则可能一部分数据在一个缓存行, 另一部分数据在其它的缓存行, 所以读写这个数据需要用到2个缓存行的数据而不是一个, 所有(目前1、2、3)级别的缓存都会受到此影响.

  • 对于在磁盘中的数据, 都是以512字节为最低的单位(一个扇区的数据大小), 如果是8字节对齐的话, 则数据会被存放在一个扇区里, 可以只通过一次读取将数据都读取出来, 如果数据不是8字节对齐, 则 数据可能会被存放到不同的扇区中, 并且还有可能不是相邻的扇区, 这就会 导致随机I/O , 降低数据处理的效率, 消耗更多的硬件资源. 对于上层来说, 数据是相连的(逻辑), 但是对于底层的物理硬件来说, 数据很有可能位于不相邻的扇区(数据处理最小单元).

so, 总结下来就是, 非对齐的数据访问 会因为增加硬件访问次数 比对齐的数据访问效率低.

说起缓存行, Java中有一些框架(比如Disruptor)考虑到了不同的CPU架构, 使用了CPU支持的缓存行填充, 以防止 伪共享(这里暂不做过多描述) 的发生从而降低效率.

通过 sysctl -a 查看

# 我的电脑中的数据hw.cachelinesize: 64hw.l1icachesize: 32768hw.l1dcachesize: 32768hw.l2cachesize: 262144hw.l3cachesize: 3145728

虚拟内存管理

内核为每一个进程都维护一张页表(page table) , 页表中的每个条目要么指出一个虚拟页面在 RAM 中的所在位置, 要么表明其当前驻留在磁盘上, 若进程访问的地址并无页表条目与之对应, 进程将会收到一个 SIGSEGV 信号.

Q: 虚拟页面的数据为什么会在磁盘上?

A: 每个程序中只有一部分 page 会驻留在 物理内存(RAM) 中, 未使用的 page 会被拷贝保存到交换区(swap area)内, 这是磁盘空间中的保留区域, 作为 RAM 的补充, 只有在需要的时候才会载入 物理内存.

进程在读取的时候, 如果访问的页面没有驻留在物理内存中, 将会发生页面错误(page fault), 内核即刻挂起的执行, 同时从磁盘中将该页面载入内存.

在 x86-32 中, page size 为 4096 字节(4KB), 一些其它的Linux使用的页面比 4096 字节更大.

Alpha 使用的 page size = 8192 字节(8KB), IA-64 的page size是可以改变的, 默认为 16384 字节.程序通过调用 sysconf(_SC_PAGESIZE) 获取系统虚拟内存的 page size.


虚拟内存的实现需要硬件中分页内存管理单元(PMMU)的支持, PMMU 把要访问的每个虚拟内存地址转换成相应的物理内存地址, 当特定虚拟内存地址所对应的页没有驻留于 RAM 中时, 将以页面错误(page fault)通知内核.

有效虚拟内存范围

由于 内核能为进程分配和释放页(和页表条目) , 所以进程的有效虚拟地址范围在其生命周期中可以发生变化. 如下场景会导致范围变化:

  • 栈向下增长超出之前曾达到的位置

  • 挡在堆中分配或者释放内存时, 通过调用 brk()、sbrk() 或 malloc 函数族来提升 program break 的位置.

  • 调用 shmat() 连接 System V 共享内存区或者当调用 shmdt() 脱离共享内存区时.

  • 调用 mmap() 创建内存映射或者 munmap() 解除内存映射

局部性原理

在计算机中大多数程序都有一个共同特点, 访问局部性 .

访问局部性包含两方面:

  • 空间局部性: 程序倾向于访问在最近访问过的内存地址附近的内存(由于指令是顺序执行的, 并且有时会按顺序处理数据结构)

  • 时间局部性: 这意味着数据被访问到, 在之后较短的时间内会被再次访问到(可能是由于循环)

优点

虚拟内存使得进程的虚拟地址空间和RAM的物理地址空间隔离开, 有以下一些好处

  • 进程与进程、进程与内核相互隔离, 所以一个进程不能读取其它进程或内核的内存, 因为每个进程的页表条目指向截然不同的物理内存地址.

  • 适当情况下, 多个进程鞥狗共享内存. 因为不同的进程页表条目可以指向相同的物理内存(RAM)地址.通常发生在如下的场景:

    • 执行同一程序的多个进程, 共享一份程序代码副本. 当多个进程执行相同的程序文件(或加载相同的共享库), 会隐式实现这一类型的共享.

    • 进程通过 shmget() 和 mmap() 系统调用显示请求与其它进程共享内存, 这样的目的是为了进程间的通信.

  • 实现保护机制: 相同的内存, 不同的进程可以设置不同的访问权限, 某些进程只读、某些拥有所有权限等.

  • 因为需要驻留在内存中的仅是程序的一部分, 所以程序的加载和运行都变快了, 而且一个程序所占用的大小(虚拟内存) 能够超出 RAM 容量.(因为有的事通过虚拟内存管理存放到了磁盘上)

一个进程所使用的RAM减少了, RAM中同时可容纳的进程数量增多. 这样的话加大了在任一时刻CPU可执行至少一个进程的概率, 这样往往也会提高CPU的利用率.

读取当前linux进程内存_(笔记)Linux上的内存分配相关推荐

  1. Linux进程通信的四种方式——共享内存、信号量、无名管道、消息队列|实验、代码、分析、总结

    Linux进程通信的四种方式--共享内存.信号量.无名管道.消息队列|实验.代码.分析.总结 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须 ...

  2. Linux进程线程学习笔记:运行新程序

    Linux进程线程学习笔记:运行新程序 周银辉 在上一篇中我们说到,当启动一个新进程以后,新进程会复制父进程的大部份上下文并接着运行父进程中的代码,如果我们使新进程不运行原父进程的代码,转而运行另外一 ...

  3. epub 深入linux内核架构_深入分析Linux内核源代码6-Linux 内存管理(2)

    每天十五分钟,熟读一个技术点,水滴石穿,一切只为渴望更优秀的你! ----零声学院 6.3 内存的分配和回收 在内存初始化完成以后,内存中就常驻有内核映像(内核代码和数据).以后,随着用 户程序的执行 ...

  4. Linux进程管理原理笔记

    一.程序从编译(编译汇编.链接.装载到内存)到运行为进程 1. 在Linux上写程序和编译程序,也需要一系列的开发套件,运行下面的命令,就可以在centOS 7操作系统上安装开发套件: yum -y ...

  5. linux一个进程通知另外一个进程,Linux进程通信学习笔记

    一.为什么需要进程通信 1)数据传输 一个进程需要把它的数据发送给另一个进程. 2)资源共享 多个进程之间共享同样的资源. 3)通知事件 一个进程向另外一个进程发送消息,通知它发生了某事件. 4)进程 ...

  6. linux目录表及功能n鸟哥,鸟哥linux私房菜_笔记_Linux的文件权限与目录配置

    5.1 使用者与群组 1. 文件拥有者: 2. 群组概念: 3. 其他人的概念: Linux 使用者身份与群组记录的文件 默认的情况下,所有的系统上的帐号与一般身份使用者,还有那个root的相关信息, ...

  7. linux 进程装入 物理内存 页表,linux内存管理解析----linux物理,线性内存布局及页表的初始化...

    主要议题: 1分页,分段模式及实模式 2Linux分页 3linux内存线性地址空间布局及物理内存空间布局 4linux页表初始化及代码解析 1.1.1内存寻址和保护模式 在X86平台上,内存控制单元 ...

  8. Linux进程的概念笔记

    1.进程的概念 表示应用程序的一次执行过程,它是应用程序的运行实例,是一个动态的过程.当一个进程开始执行的时候,就启动了这个动态过程.进程主要包括执行 的程序和数据两部分. 2.进程的三种状态:运行. ...

  9. Linux系列教程——1 Linux磁盘管理、2 Linux进程管理、3 Linux系统服务、 4 Linux计划任务

    文章目录 1 Linux磁盘管理 1.磁盘的基本概念 1.什么是磁盘 2.磁盘的基本结构 3.磁盘的预备知识 1.磁盘的接口类型 2.磁盘的基本术语 3.磁盘在系统上的命名方式 4.磁盘基本分区Fdi ...

最新文章

  1. DPU(Data Processing Unit)数据处理器
  2. php 5.3 construct_PHP 5.3新增魔术方法__invoke概述
  3. pythonrandrange_Python3 randrange() 函数
  4. Git的使用(快速入门)
  5. Python手写神经网络实现3层感知机
  6. u8 api开发报类型不匹配错误_小程序云开发入门学习,小程序支付功能常见错误汇总及解决方案...
  7. Tomcat 的安装与配置
  8. c#使用word、excel、pdf ——转
  9. bootstrap插件bootbox
  10. 主动微波遥感和被动微波遥感
  11. Element-UI下拉框el-select实现拼音搜索
  12. cad重新加载php命令,cad刷新命令是什么?
  13. python导入自定义模块_python引入不同文件夹下的自定义模块方法
  14. GCD中dispatch_apply函数的使用方法
  15. C++:实现量化exchangerate汇率测试实例
  16. Mac OS X 10.8.3搭建Android工程源码的编译环境(解决找不到GCC、GIT、PYTHON的问题)...
  17. 基于ATX自动化测试解决方案
  18. STM32——安装keil环境
  19. 刚刚结束一家公司的战略规划项目的感想
  20. 【Android】Installed Build Tools revision _.0.0 is corrupted

热门文章

  1. 如何在用例之间传递值_接口测试:A12_HttpRunner_cookie整理_01_提取指定cookie值
  2. 8屏 旌宇多屏管理软件_如何选择拼接屏,不能说的秘密,都在这!
  3. 1-2-3 CodeForces - 863C(规律+思维)
  4. [蓝桥杯][2014年第五届真题]排列序数(思维)
  5. mysql5.0源码安装_linux小白 mysql5.0源码安装配置
  6. DOS命令行数据乱码解决
  7. django 轮播图上传_django之动态轮播图技术的实现
  8. 计算机体系结构--第一章1----体系结构的分类
  9. 机器学习-cs229-线性回归-泰勒展开法
  10. Java show两个整数加减_怎么样用java编写界面实现两个数的加法运算