GNU/Linux下进程与虚拟内存

简介: 对进程与虚拟内存的简单理解


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。


intro

  • 本篇博客是对进程的结构与属性,虚拟内存的布局和内容的简单讲解

进程与程序之间的关系

process 是 program的实体化

  • 程序包含了是一系列信息的文件,这些信息描述了如何在运行时创建一个进程

    • 二进制格式表示:每个程序文件都包含用于描述可执行文件格式的 元信息(meta information),内核(kernel)利用此信息来解释文件中的其他信息
    • 机器语言指令
    • 程序入口地址
    • 数据
    • 符号表及重定位表
    • 共享库和动态链接信息
    • 其他
  • 准确来说:process是内核定义的抽象的实体,并为该实体分配用以执行程序的各项系统资源

进程由用户内存空间 + 一系列内核数据结构组成

  • 用户内存空间与内核数据结构分别存放进程的相关信息
  • 用户内存空间:

    • 程序代码
    • 代码所使用变量
  • 内核数据结构:维护进程状态信息

    • 与进程相关的标识号
    • 虚拟内存表
    • 打开的文件描述符表
    • 信号传递及处理的有关信息
    • 进程资源使用及限制
    • 当前工作目录
    • 大量其他信息...

进程内存布局

  • 每一个C/C++程序员都会遇到可恶的Segmentation fault,它的发生是因为对内存进行了不当操作,想要很好的理解这个错误,就要搞清楚进程内存布局是怎么回事,我们到底是在操作了什么东西使SIGSEGV信号产生并杀死了进程
  • 心动的感觉

  • 进程所分配的内存由很多部分组成,通常称之为""

    • 文本段:(text)

      • 包含了程序运行的程序机器语言指令
      • 只读属性
      • 可共享,一份程序代码的拷贝可以映射到所有进程的虚拟地址空间中
    • 初始化数据段:(data)

      • 显示初始化的全局变量和静态变量
    • 未初始化数据段:(bss)

      • 未进行显示初始化的全局变量和静态变量
      • BSS段(block started by symbol)
      • 程序启动前,系统将本段所有内存初始化为0(未初始化变量为0的原因)
      • 可执行文件只需记录其位置及所需大小,直到运行时再有程序加载器为其分配空间
    • 栈:(stack)

      • 动态增长与收缩的段,由栈帧(stack frames)组成
      • 栈帧中存储了函数的局部变量,实参返回值
    • 堆:(heap)

      • 可在运行时进行动态内存分配的区域
      • 顶端叫做program break
  • 内存布局

  • 由图可知,进程在运行中对内存的访问是有限制的,段错误发生在访问了不可访问的内存,这个内存要么是不存在的(例如越过了progrram break),要么是受系统保护的

虚拟内存管理

  • 进程内存布局存在于虚拟内存中
  • 虚拟内存管理技术利用了访问局部性:追求高效使用CPU和RAM

    • 空间局部性(Spatial locality):是指程序倾向于访问在最近访问过的内存地址附近的内存(由于指令是顺序执行的,且有时会按顺序处理数据结构)
    • 时间局部性(Temporal locality):是指程序倾向于在不久的将来再次访问最近刚访问过的内存地址(由于循环)
    • 由于访问局部性的存在,使程序在只有部分地址空间存在于RAM的情况下依然可以执行
  • 虚拟内存规划:

    • 将每个程序分割为小型的,固定大小的页(page)单元
    • 将RAM(物理硬件)分为一系列与虚拟页尺寸相当的页帧
    • 任一时刻,每个程序仅有部分页需要驻留在物理内存页帧中:这些页构成了所谓驻留集(resident set)
    • 程序未使用的页拷贝保存在交换区(swap area)内—这是磁盘空间中的保留区域(计算机RAM的补充)
  • 内核为每个进程维护一个页表

    • 描述了每*在进程虚拟地址空间(virtual address space)中的位置(可为进程所用的所有虚拟内存页面的集合)
    • 页表中每个条目要么指出一个虚拟页面在RAM中的位置,要么表明其当前驻留在磁盘上(不是在RAM中就是在磁盘上还未被加载)
    • 并非所有的地址范围都需要页表条目,大段的未使用的虚拟地址空间就没必要为其维护页表条目
    • 若进程试图访问的地址无页表条目与之对应,那么进程将收到SIGSEGV信号

  • 内核能够为进程分配和释放/页表条目,所有进程的虚拟地址范围在其生命周期可以发生变化

    • 栈向下增长超出之前曾达到的位置
    • 在堆中分配或释放内存时,通过调用 brk()、 sbrk()或 malloc 函数族来提升 program break 的位置
    • 当调用 shmat()连接 System V 共享内存区时, 或者当调用 shmdt()脱离共享内存区时
    • 当调用 mmap()创建内存映射时,或者当调用 munmap()解除内存映射时
  • 虚拟内存管理使进程的虚拟地址空间与 RAM 物理地址空间隔离开来,这带来许多优点:

    • 进程与进程,进程与内核相互隔离:一个进程不能读取或修改另一进程或内核的内存
    • 可以实现共享内存:内核可以使不同进程的页表条目指向相同的RAM页

      • 执行同一程序的多个进程可共享一份(只读的)程序代码副本
      • 进程可以使用 shmget()和 mmap()系统调用显式地请求与其他进程共享内存区:实现进程间通信
      • 实现内存保护机制:某一进程对某一页帧为只读,而另一进程则可以读写同一页帧
      • 程序员无需操心RAM的物理布局
      • 加速程序的加载与运行
      • RAM中容纳的程序变多,CPU可执行程序变多,效率提高

栈和栈帧

  • 函数的调用和返回使栈的增长和收缩呈线性
  • 专用寄存器—栈指针(stack pointer),用于跟踪当前栈顶。每次调用函数时,会在栈上新分配一帧,每当函数返回时,再从栈上将此帧移去
  • 内核栈是每个进程保留在内核内存中的内存区域,在执行系统调用的过程中供(内核)内部函数调用使用
  • 用户栈帧保存:

    • 函数实参和局部变量
    • 函数调用的链接信息:每当一函数调用另一函数时,会在被调用函数的栈帧中保存使用过的寄存器的副本,以便函数返回时能为函数调用者将寄存器恢复原状
  • 因为函数能够嵌套调用,所以栈中可能有多个栈帧

参考

  • 《TLPI》
目录
相关文章
|
7天前
|
消息中间件 存储 网络协议
从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
本文详细介绍了进程间通信(IPC)的六种主要方式:管道、信号、消息队列、共享内存、信号量和套接字。每种方式都有其特点和适用场景,如管道适用于父子进程间的通信,消息队列能传递结构化数据,共享内存提供高速数据交换,信号量用于同步控制,套接字支持跨网络通信。通过对比和分析,帮助读者理解并选择合适的IPC机制,以提高系统性能和可靠性。
64 14
|
11天前
|
缓存 Linux
linux 手动释放内存
在 Linux 系统中,内存管理通常自动处理,但业务繁忙时缓存占用过多可能导致内存不足,影响性能。此时可在业务闲时手动释放内存。
64 17
|
1月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
83 1
|
13天前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
76 20
|
1月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
107 13
|
1月前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
1月前
|
C语言 开发者 内存技术
探索操作系统核心:从进程管理到内存分配
本文将深入探讨操作系统的两大核心功能——进程管理和内存分配。通过直观的代码示例,我们将了解如何在操作系统中实现这些基本功能,以及它们如何影响系统性能和稳定性。文章旨在为读者提供一个清晰的操作系统内部工作机制视角,同时强调理解和掌握这些概念对于任何软件开发人员的重要性。
|
1月前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
1月前
|
Linux 调度 C语言
深入理解操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅,从进程管理的基本概念出发,逐步探索到内存管理的高级技巧。我们将通过实际代码示例,揭示操作系统如何高效地调度和优化资源,确保系统稳定运行。无论你是初学者还是有一定基础的开发者,这篇文章都将为你打开一扇了解操作系统深层工作原理的大门。
|
1月前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####

热门文章

最新文章