程序在内存中运行的奥秘

从MFCC到GMM:手把手教你用Python实现说话人识别(附完整代码) 本文详细介绍了如何使用Python实现说话人识别系统,从MFCC特征提取到GMM模型训练,涵盖完整的技术流程和代码示例。通过梅尔频率倒谱系数(MFCC)和高斯混合模型(GMM)的结合,构建高效的说话人识别系统,适用于身份认证和智能交互场景。文章还探讨了从传统方法到深度学习的进阶话题,如i-vector和x-vector技术。 阅读详情
简介
当丰富多彩的应用程序在计算机上运行,为你每天的工作和生活带来便利时,你是否知道它们是如何在计算机中工作呢?本文用形象的图表与生动的解释,揭示了程序在计算机中运行的奥秘。

内存管理是操作系统的核心功能,无论对于开发者还是系统管理员内存管理的重要性都是不言而喻的。我会在接下来的几篇文章通过计算机的实际运行过程谈谈内存管理,当然在必要的时候我也会从底层原理去阐释这个问题。我们提到的概念是不局限于平台特性的通用概念,不过为了阐述这些概念我们选取的实例大多来源于Linux和基于x86架构的32位Windows操作系统。这篇文章,我们首先来看看程序是如何使用内存的。

多任务操作系统中,每一个进程都有它自己的内存“沙盒”。所谓“沙盒”,是指虚拟地址空间,在32位模式下,虚拟地址空间最多能表示4GB容量。通过页表机制,虚拟地址空间能够映射到物理内存。页表由操作系统内核来管理,并可被处理器访问。每个进程有着属于自己的页表,不过进程也不能随心所欲。因为虚拟地址一旦投入使用,所有在计算机中运行的软件都会占用虚拟地址空间,包括操作系统内核自身。也就是说,操作系统内核将保留一部分虚拟地址空间。

这并不意味着系统内核能够肆无忌惮的使用物理内存,系统内核只能使用其管辖的虚拟地址空间所对应的物理内存。系统内核所使用的内存空间通过特权码(privileged code,2级或者更低)来标记,以防止用户模式的程序访问到内核空间而发生页面错误。在Linux中,内核始终占用着一定空间,并且每个内核进程映射的物理内存地址是固定的。因此,内核代码与数据在内存中的地址总是能够被准确定位,从而为时刻处理中断以及系统调用做好了准备。与此相反,只要用户进程状态发生变化,其映射的地址空间也随即改变。

图中蓝色区域表示虚拟地址中映射到物理内存的部分,白色区域则是未映射。在这个例子中,Firefox惊人的内存需求让它使用的虚拟地址远远超过了其自身的地址空间。内存地址空间是由诸如堆、栈等段式内存管理方式进行管理的。需要指出的是,这里段的概念只不过是表示了一段内存地址,它和Intel段表机制(Intel-style segments)没有任何关系。总的来说,我们在这里讨论的是Linux系统进程标准的段式内存管理方法。

如果运行过程轻松愉快、准确无误,那么上图显示的段式虚拟地址管理启用过程对于计算机内几乎所有进程都完全一致。而这种机制为远程攻击带来了安全隐患。远程攻击往往需要参考绝对内存地址:诸如栈地址、库函数地址等等。而远程攻击者们知道了这些地址空间是固定的,他们闭着眼睛都能找到他们需要的位置。倘若真的如此,那么人们毫无疑问就会被黑客攻击了。正因为这样,随机地址空间已经成为流行的内存地址管理方式。Linux随机为栈(stack)、内存映射段(memorymapping segment)以及堆(heap )的起始地址添加偏移量。不幸的是,32位地址空间非常吃紧,限制了随机分配地址的范围和效率(hamperingits effectiveness)。

进程地址空间的首段地址便是栈,它储存了局部变量以及大多数编程语言的函数参数。当调用方法或者函数时,会有一个新的元素进栈。一旦函数返回了值,那么该元素就会被销毁。这种简单的设计,很有可能是考虑到数据操作都符合后进先出(LIFO )规则,这意味着访问栈的内容并不需要复杂的数据结构,一个简单的栈顶指针就能搞定一切。进栈和出栈的操作方便快捷,不需要过多判断。另外,栈的反复使用能够使栈驻留在CPU缓存(cpu caches)中,从而加快数据存取。每个进程中的每个线程都有属于自己的栈。

如果映射的栈地址空间被压入了超过栈容量的数据,那么栈便无法继续工作了。这种情况会导致一个由expand_stack()函数处理的页面错误,这个函数会调用acct_stack_growth() 函数去检查是否应该为这个栈增加容量。如果这个栈的容量低于RLIMIT_STACK (通常为 8MB)限定的值,那么栈的容量会正常增加,程序也会继续正常运行,并且程序不会知道刚刚发生了什么。当然,这是根据实际需要来调整栈大小的一般机制,如果栈的容量达到了最大值上限,那么栈就会溢出,程序也会收到一个段出错的信息。虽然在程序需要的时候映射的栈空间会增加,但是栈使用的空间减少时,栈却不会释放多余的空间。这就好像联邦政府预算,只可能越来越多。

程序存取上图所示的未映射区域,是唯一正常实现动态增加栈空间的情况,程序访问其他未映射内存访问将会出现页面错误最终导致段错误。有些映射区域是只读的,程序试图写入这些区域同样会导致这种错误。

说到堆,我们就不得不提它的内存使用机制。堆支持运行时内存分配,和栈不同,大多数语言都允许程序使用堆管理内存。满足内存需求是语言运行时和C语言核心之间的联结点,而堆的内存管理接口是通过malloc()及其友元函数来实现的,在C#这样支持垃圾回收机制的语言中,其接口是新定义的关键字。

当堆的空间能够满足程序的内存请求时,那么请求的处理过程就可以直接由语言运行时来负责,而不必有系统内核参与。但是如果堆的空间不能满足程序的内存申请,那么brk()函数会执行系统调用(implementation)来增加堆的内存空间以满足程序的请求。堆管理的实现过程十分复杂,,面对程序内存分配变化莫测的情况,堆管理需要成熟的算法去提升请求的响应速度与内存利用率。系统响应堆的内存请求花费的时间往往变化很大。实时操作系统解决这个问题的方法是采用专用内存分配器(special-purposeal locators)。堆在内存中的分布情况和其他内存管理机制一样充满了碎片,如下图所示:


最后,我们来聊聊刚才图中位置最下方的几个内存段:BSS段、数据段和程序段。在C语言中,BSS段和数据段存储的都是静态(全局)变量。这几个段的不同之处在于BSS段存储的静态变量没有初始化——程序员在源代码中没有为这些静态变量赋值。由于BSS段并没有映射任何文件,所以BSS段在内存中是以匿名形式存在的。举个例子,假设你定义了变量static int cntActiveUsers,那么cntActiveUsers 的数据就保存在BSS段中。

与BSS段不同的是,数据段储存了在源代码中经过了初始化的静态变量。因此,数据段的内存区域并不是匿名的。数据段映射了程序二进制映像中源代码给出静态变量初值的部分。所以,如果你定义了static int cntWorkerBees = 10,那么cntWorkerBees变量会赋以初值10并在数据段中保存下来。尽管数据段映射了文件,但这种内存映射是私有的,也就是说,数据段的内存更新不会在其映射的文件中生效。这样造成的结果就是,虽然全局变量的改变应用到了文件在内存中的二进制映像,但是文件本身却不能作出相应的变化!

下面图表中的示例由于使用了指针所以看起来不那么明了。在这个示例中,指针在数据段中占用了4个字节,但是指针所指向的字符串则不在数据段中。对于字符串,内存为它们准备了专门的文本段,文本段以只读的形式存储程序中诸如字符串类型等不会被直接执行的代码。文本段同样会将二进制文件映射到内存,但文件映射区域的写入操作只能以程序收到段错误而告终。这种机制能有效防止指针的错误指向而导致的误操作,不过也不得不承认这种做法显然没有直接在C语言代码中进行保护来的效率高。下面的图表显示了刚刚我们讨论到的段以及变量示例:

如果你想了解Linux中的进程是如何使用内存的,可以读读源代码文件/proc/pid_of_process/maps。值得一提的是,一个内存段往往由多个区域组成。例如,每个正确映射到内存的文件都有属于自己的段,动态库文件则拥有另外的段,这些段类似于BSS段与数据段。下一篇文章我们将进一步探讨“区域”的含义。另外,也会谈谈我个人对“数据段就是数据、BSS以及堆的总和”这种观点的看法。

使用nm 和objdump命令能够显示二进制映像的标识,映像的地址、段等等信息都可以查阅。最后要指出的是,上文讨论的Linux虚拟地址管理机制是“灵活”的,该机制在Linux中作为首选已经沿用了几年。使用这种机制要求程序为RLIMIT_STACK变量赋值,如果没有,那么Linux则退回到“传统”方式管理内存,如下图所示:


该图呈现了虚拟地址空间的管理方式。下一篇文章我们将讨论系统内核是如何跟踪这些内存区域的。进而我们会看看内存映射原理、与之相关的文件读写机制以及内存使用情况图表所揭示的含义。

 

同一文章的另一处翻译: 剖析程序的内存布局
uniapp [安卓苹果App端] - 详细实现PDF预览功能在线预览PDF文档,Uniapp App端实现pdf文件预览在线预览pdf二进制文件流、本地pdf文档、PDF文件地址url路径(解决白屏 uni-app,安卓Android,苹果ios,安卓app端,苹果App端,uniapp苹果安卓app,uniapp在线预览pdf功能,在安卓苹果app平台实现PDF的预览查看,uniappApp端实现pdf文件预览,UNIAPP在线预览PDF文档,Uniappapp在线预览pdf文件流,从后端接口里获取pdf文件如何转成链接预览,预览pdf的插件,uni-app的pdf预览功能,uniapp使用pdf.js插件预览pdf文件,预览pdf白屏没有报错,打开后没反应,打开是空白,加载PDF时发生错误,如 阅读详情

相关推荐

完整版:ultralytics下运行yoloworld并训练

本文介绍了Ultralytics YOLO环境的快速配置与使用流程:1) 通过conda创建Python 3.9环境并安装依赖包;2) 配置中科大镜像源加速安装PyTorch和Ultralytics;3) 准备YOLO格式数据集并修改data.yaml配置文件;4) 提供模型预测、训练和验证的常用指令,包括predict/train/val等命令参数设置。文章还包含CLIP模型下载、权重文件获取以及本地安装等注意事项,完整覆盖了从环境搭建到模型部署的全过程。

weixin_50209871的博客 1345

程序在内存中的运行原理

一、背景介绍 当进程被加载到内存时,会被分成很多段 bss段:   bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。   bss是英文Block Started by Symbol的简称。   bss段属于静态内存分配。 data段:   数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。   数据段属于静态内存分配。 text段:   代码段(code segment/text segment)通常.

sinat_31608641的博客 3462

AxMath行间公式转化为office公式求和符号以及编号显示问题

AxMath行间公式转化为office公式求和符号以及编号显示问题问题描述解决方法 问题描述 首先打出axmath公式,如下: 转换后的OfficeMath公式: 字体先不说,word公式字体默认cambria math且无法编辑。比较难受的一点是求和符号与后面的 ai+bi2a_i+b_i^2ai​+bi2​比例不匹配啊!正常的比例应该是: (虽然也不太正常,将就下) 解决方法 点击变换后的office公式右侧,选择更改为"显示" 可以看到,多了一些软回车符号(⬇),如果你想删除它,不要直接选中

weixin_43543177的博客 4655

程序在计算机中是如何运行起来的

汇编语言与程序的编译和解释

小Y的博客 1980

超级超级细的CLIP官方模型解读及代码逐行讲解

本文详细解析了OpenAI开源的CLIP模型代码架构。文章首先介绍模型概念,包括训练时通过对称损失计算图像-文本相似度,预测时通过余弦相似度匹配最佳文本。随后重点解析各模块代码实现:LayerNorm改进、QuickGELU激活函数、Transformer编码器结构、ViT图像编码器的特征提取过程、文本编码器的掩码机制和特征投影。最后阐述CLIP整体架构如何整合视觉和文本编码器,并详细说明了特征归一化和相似度计算的关键实现。全文通过代码级解读帮助读者深入理解CLIP模型的实现细节。

m0_64595440的博客 2426

使用Simulink对固定翼无人机的自动起飞与降落进行仿真

本文介绍了基于Simulink的固定翼无人机自动起飞与降落控制系统设计方法。系统包含飞行器动力学模型、控制系统、环境模拟等模块,通过简化动力学方程描述无人机运动特性。详细说明了从创建模型、设置动力学方程、设计PID控制器(包括自动起飞和降落控制策略)到添加环境模拟的完整仿真流程。重点阐述了升降舵和发动机推力的控制律设计,以及性能评估要点。本方法可有效验证控制系统设计,优化飞行稳定性和安全性参数,为无人机自主控制提供仿真支持。

amy_mhd的博客 223

程序在计算机中是如何运行的?搞懂内存和CPU(*)

1.程序在计算机中是如何运行的? 运算器 控制器 存储器 输入设备 输出设备 在计算机中,保存信息主要靠存储器,而存储器又分为内部存储器和外部存储器,内部存储器就是内存,而这个外部存储器主要就是磁盘,磁盘又分为硬盘和软盘,而我们电脑中的大部分是硬盘,这里也分为固态硬盘和传统机械硬盘。 2.CPU和内存 3.内存是什么? 内存条,我们电脑中的内存条,它是属于内部存储器,主要就是用来存放数据的,也可以理解为我们这里说的内存,这是实际存在的,我们可以看得见的内存条,但是关于数据存储,怎么存储,我们抽象点儿来

weixin_42408447的博客 6275

Ollama系列---【ollama使用gpu运行大模型】

一、安装CudaToolkit 使用控制台命令查看当前显卡驱动中的cuda版本 nvidia-smi 二、下载CudaToolkit 注意:CudaToolkit版本要低于上面的显卡Cuda版本。 Cuda各版本下载地址:https://developer.nvidia.com/cuda-toolkit-archive 下载好之后,一路下一步,一直默认即可。 如果安装到了自定义的位置,记得加环...

-少年-的博客 7983

C语言:内存中程序是如何运行的

程序是保存在硬盘中的,要载入内存才能运行,CPU 也被设计为只能从内存中读取数据和指令。对于 CPU 来说,内存仅仅是一个存放指令和数据的地方,并不能在内存中完成计算功能,例如要计算 a = b +c,必须将 a、b、c 都读取到 CPU 内部才能进行加法运算。为了了解具体的运算过程,我们不妨先来看一下CPU 的结构。CPU 是一个复杂的计算机部件,它内部又包含很多小零件,如下图所示:运算单元是 CPU 的大脑,负责加减乘除、比较、位移等运算工作,每种运算都有对应的电路支持,速度很快。

Dola的博客 1716

TC397 EB MCAL开发从0开始系列 之 [15.1] Fee配置实战 - 双扇区demo

TC397 EB MCAL Fee模块实战之双扇区配置使用

yy九歌的博客 1127

手把手教你学Simulink——基于Simulink的电压型 vs 电流型整流器性能对比

摘要:本文基于Simulink平台对比分析了电压型整流器(VSR)和电流型整流器(CSR)的性能差异。VSR采用电容滤波,具有高功率因数(PF>0.99)、低输入电流THD(<3%)等优势,但动态响应较慢;CSR采用电感滤波,动态响应快(<0.05s),但THD较高(5%~8%)。研究建立了统一测试平台,详细对比了两者在稳态性能、动态响应、效率等方面的差异,并给出了控制算法实现和参数优化建议。通过量化分析,为电力电子工程师提供了拓扑选型依据,可根据恒压/恒流需求选择最优方案,有效缩短开发周

MHD0815的博客 173

【操作系统】内存管理(二)—— 程序运行的基本过程和要求

内存可存放数据。程序执行前需要先放到内存中才能被CPU处理—— 缓和CPU与硬盘之间的速度矛盾思考:在多道程序环境下,系统中会有多个程序并发执行,也就是说会有多个程序的数据需要同时放到内存中。那么,如何区分各个程序的数据是放在什么地方的呢?答案:给内存的存储单元编地址内存地址从 0 开始,每个地址对应一个存储单元如果计算机“按字节编址”,则每个存储单元大小为 1字节,即 1B,即 8个二进制位;如果字长为16位的计算机“按字编址”,则每个存储单元大小为 1个字;每个字的大小为 16 个二进制位。

haoweixl的博客 1852

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

【转】程序在内存中运行的奥秘

内存管理是操作系统的核心功能,无论对于开发者还是系统管理员内存管理的重要性都是不言而喻的。我会在接下来的几篇文章通过计算机的实际运行过程谈谈内存管理,当然在必要的时候我也会从底层原理去阐释这个问题。我们提到的概念是不局限于平台特性的通用概念,不过为了阐述这些概念我们选取的实例大多来源于Linux和基于x86架构的32位Windows操作系统。这篇文章,我们首先来看看程序是...

weixin_30376509的博客 147

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

转载(程序在内存中运行的奥秘)

程序在内存中运行的奥秘 来源: 译言发布时间: 2010-06-20 14:10   内存管理是操作系统的核心功能,无论对于开发者还是系统管理员内存管理的重要性都是不言而喻的。我会在接下来的几篇文章通过计算机的实际运行过程谈谈内存管理,当然在必要的时候我也会从底层原理去阐释这个问题。我们提到的概念是不局限于平台特性的通用概念,不过为了阐述这些概念我们选取的实例大多...

507

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【内存管理】程序在内存中运行的奥秘

 内存管理是操作系统的核心功能,无论对于开发者还是系统管理员内存管理的重要性都是不言而喻的。我会在接下来的几篇文章通过计算机的实际运行过程谈谈内存管理,当然在必要的时候我也会从底层原理去阐释这个问题。我们提到的概念是不局限于平台特性的通用概念,不过为了阐述这些概念我们选取的实例大多来源于Linux和基于x86架构的32位Windows操作系统。这篇文章,我们首先来看看程序是如何使用内存的。   ...

爱喝奶茶的博客 586
上一篇: 关于text段、data段和bss段
下一篇: objdump查看目标文件构成
unix21
博客等级 码龄14年 115粉丝 248原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值