计算机原理-操作系统-剖析程序的内存布局

操作系统引导全过程-详细 硬盘结构:硬盘内部有MBR(Master Boot Record主引导记录)、以及多个分区,每个主分区也有自己的PBR(分区引导记录)。硬盘扇区默认大小512B,空间分配一般按簇分配(多个扇区为一组簇)。BIOS工作在实模式下,最大20位寻址,也就是1M寻址范围(0x00000 ~ 0xFFFFF)(最初的8086处理器能够访问的内存最大只有1MB,保证兼容性)MBR大小512B,位于0柱面、0磁头、1扇区,包括:主引导程序(446B)、硬盘分区表DPT(64B)、MBR结束标志AA55(2B) 阅读详情

本文分享来自:底层软件架构

原文来自国外的小哥:http://duartes.org/gustavo/blog/

微信公众号:技术原理君

内存管理模块是操作系统的心脏;它对应用程序和系统管理非常重要。在这篇文章中,我将着眼于实际的内存问题,但也不避讳其中的技术内幕。由于不少概念是通用的,所以文中大部分例子取自32位x86平台的Linux和Windows系统。本系列第一篇文章讲述应用程序的内存布局。

在多任务操作系统中的每一个进程都运行在一个属于它自己的内存池子中。这个池子就是虚拟地址空间(virtual address space),在32位模式下它总是一个4GB的内存地址块。这些虚拟地址通过页表(page table)映射到物理内存,页表由操作系统维护并被处理器引用。每一个进程拥有一套属于它自己的页表,但是还有一个隐情。只要虚拟地址被使能,那么它就会作用于这台机器上运行的所有软件,包括内核本身。因此一部分虚拟地址必须保留给内核使用:

剖析程序的内存布局

 

这并不意味着内核使用了那么多的物理内存,仅表示它可支配这么大的地址空间,可根据内核需要,将其映射到物理内存。内核空间在页表中拥有较高的特权级(ring 2或以下),因此只要用户态的程序试图访问这些页,就会导致一个页错误(page fault)。在Linux中,内核空间是持续存在的,并且在所有进程中都映射到同样的物理内存。内核代码和数据总是可寻址的,随时准备处理中断和系统调用。与此相反,用户模式地址空间的映射随进程切换的发生而不断变化:

剖析程序的内存布局

 

蓝色区域表示映射到物理内存的虚拟地址,而白色区域表示未映射的部分。在上面的例子中,Firefox使用了相当多的虚拟地址空间,因为它是传说中的吃内存大户。地址空间中的各个条带对应于不同的内存段(memory segment),如:堆、栈之类的。记住,这些段只是简单的内存地址范围,与Intel处理器的段没有关系。不管怎样,下面是一个Linux进程的标准的内存段布局:

 

剖析程序的内存布局

 

 

当计算机开心、安全、可爱、正常的运转时,几乎每一个进程的各个段的起始虚拟地址都与上图完全一致,这也给远程发掘程序安全漏洞打开了方便之门。一个发掘过程往往需要引用绝对内存地址:栈地址,库函数地址等。远程攻击者必须依赖地址空间布局的一致性,摸索着选择这些地址。如果让他们猜个正着,有人就会被整了。因此,地址空间的随机排布方式逐渐流行起来。Linux通过对栈、内存映射段、堆的起始地址加上随机的偏移量来打乱布局。不幸的是,32位地址空间相当紧凑,给随机化所留下的空当不大,削弱了这种技巧的效果。

进程地址空间中最顶部的段是栈,大多数编程语言将之用于存储局部变量和函数参数。调用一个方法或函数会将一个新的栈桢(stack frame)压入栈中。栈桢在函数返回时被清理。也许是因为数据严格的遵从LIFO的顺序,这个简单的设计意味着不必使用复杂的数据结构来追踪栈的内容,只需要一个简单的指针指向栈的顶端即可。因此压栈(pushing)和退栈(popping)过程非常迅速、准确。另外,持续的重用栈空间有助于使活跃的栈内存保持在CPU缓存中,从而加速访问。进程中的每一个线程都有属于自己的栈。

通过不断向栈中压入的数据,超出其容量就有会耗尽栈所对应的内存区域。这将触发一个页故障(page fault),并被Linux的expand_stack()处理,它会调用acct_stack_growth()来检查是否还有合适的地方用于栈的增长。如果栈的大小低于RLIMIT_STACK(通常是8MB),那么一般情况下栈会被加长,程序继续愉快的运行,感觉不到发生了什么事情。这是一种将栈扩展至所需大小的常规机制。然而,如果达到了最大的栈空间大小,就会栈溢出(stack overflow),程序收到一个段错误(Segmentation Fault)。当映射了的栈区域扩展到所需的大小后,它就不会再收缩回去,即使栈不那么满了。这就好比联邦预算,它总是在增长的。

动态栈增长是唯一一种访问未映射内存区域(图中白色区域)而被允许的情形。其它任何对未映射内存区域的访问都会触发页故障,从而导致段错误。一些被映射的区域是只读的,因此企图写这些区域也会导致段错误。

在栈的下方,是我们的内存映射段。此处,内核将文件的内容直接映射到内存。任何应用程序都可以通过Linux的mmap()系统调用(实现)或Windows的CreateFileMapping() / MapViewOfFile()请求这种映射。内存映射是一种方便高效的文件I/O方式,所以它被用于加载动态库。创建一个不对应于任何文件的匿名内存映射也是可能的,此方法用于存放程序的数据。在Linux中,如果你通过malloc()请求一大块内存,C运行库将会创建这样一个匿名映射而不是使用堆内存。'大块'意味着比MMAP_THRESHOLD还大,缺省是128KB,可以通过mallopt()调整。

说到堆,它是接下来的一块地址空间。与栈一样,堆用于运行时内存分配;但不同点是,堆用于存储那些生存期与函数调用无关的数据。大部分语言都提供了堆管理功能。因此,满足内存请求就成了语言运行时库及内核共同的任务。在C语言中,堆分配的接口是malloc()系列函数,而在具有垃圾收集功能的语言(如C#)中,此接口是new关键字。

如果堆中有足够的空间来满足内存请求,它就可以被语言运行时库处理而不需要内核参与。否则,堆会被扩大,通过brk()系统调用(实现)来分配请求所需的内存块。堆管理是很复杂的,需要精细的算法,应付我们程序中杂乱的分配模式,优化速度和内存使用效率。处理一个堆请求所需的时间会大幅度的变动。实时系统通过特殊目的分配器来解决这个问题。堆也可能会变得零零碎碎,如下图所示:

 

剖析程序的内存布局

 

最后,我们来看看最底部的内存段:BSS,数据段,代码段。在C语言中,BSS和数据段保存的都是静态(全局)变量的内容。区别在于BSS保存的是未被初始化的静态变量内容,它们的值不是直接在程序的源代码中设定的。BSS内存区域是匿名的:它不映射到任何文件。如果你写static int cntActiveUsers,则cntActiveUsers的内容就会保存在BSS中。

另一方面,数据段保存在源代码中已经初始化了的静态变量内容。这个内存区域不是匿名的。它映射了一部分的程序二进制镜像,也就是源代码中指定了初始值的静态变量。所以,如果你写static int cntWorkerBees = 10,则cntWorkerBees的内容就保存在数据段中了,而且初始值为10。尽管数据段映射了一个文件,但它是一个私有内存映射,这意味着更改此处的内存不会影响到被映射的文件。也必须如此,否则给全局变量赋值将会改动你硬盘上的二进制镜像,这是不可想象的。

下图中数据段的例子更加复杂,因为它用了一个指针。在此情况下,指针gonzo(4字节内存地址)本身的值保 存在数据段中。而它所指向的实际字符串则不在这里。这个字符串保存在代码段中,代码段是只读的,保存了你全部的代码外加零零碎碎的东西,比如字符串字面 值。代码段将你的二进制文件也映射到了内存中,但对此区域的写操作都会使你的程序收到段错误。这有助于防范指针错误,虽然不像在C语言编程时就注意防范来得那么有效。下图展示了这些段以及我们例子中的变量:

 

剖析程序的内存布局

 

 

你可以通过阅读文件/proc/pid_of_process/maps来检验一个Linux进程中的内存区域。记住一个段可能包含许多区域。比如,每个内存映射文件在mmap段中都有属于自己的区域,动态库拥有类似BSS和数据段的额外区域。下一篇文章讲说明这些"区域"(area)的真正含义。有时人们提到"数据段",指的就是全部的数据段 + BSS + 堆。

你可以通过nm和objdump命令来察看二进制镜像,打印其中的符号,它们的地址,段等信息。最后需要指出的是,前文描述的虚拟地址布局在Linux中是一种"灵活布局"(flexible layout),而且以此作为默认方式已经有些年头了。它假设我们有值RLIMIT_STACK。当情况不是这样时,Linux退回使用"经典布局"(classic layout),如下图所示:

 

剖析程序的内存布局

 

 

对虚拟地址空间的布局就讲这些吧。下一篇文章将讨论内核是如何跟踪这些内存区域的。我们会分析内存映射,看看文件的读写操作是如何与之关联的,以及内存使用概况的含义。

基于C语言的操作系统内存管理实验项目实战 为了统一管理内存状态,定义如下全局结构体:// 块大小(含头部)// 是否空闲// 链表指针初始化函数负责设置初始空闲块:此设计使得所有后续分配操作均从free_list中查找合适块,释放后重新插入并尝试合并相邻空闲块。 阅读详情

相关推荐

西门子RS485自由口通信Modbus RTU 通信协议

部分西门子的RS485模块不能使用西门子自带的Modbus RTU通信指令,需要通过自己拼接、发送、接收和解析报文完成Modbus RTU通信。通过多次尝试完成该功能: #R_TRIG_Instance(CLK := #触发, Q => #触发上升沿); IF #触发上升沿 THEN // 触发后先将读取完成复位 #读写完成 := 0; END_IF; CASE #功能码 OF 1: // 线圈状态读取 ; 2:

qq_20792765的博客 1万+

Anatomy of a Program in Memory.剖析程序的内存布局

原文标题:Anatomy of a Program in Memory 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。] 内存管理模块是操作系统的心脏;它对应用程序和系统管理非常重要。今后的几篇文章中,我将着眼于实际的内存问题,但也不避讳其中的技...

weixin_30480651的博客 260

科研党必备!用Requests+BeautifulSoup升级版Nature爬虫(含PDF下载功能)

本文介绍了一套基于Python Requests和BeautifulSoup的Nature期刊智能爬虫系统,专为科研人员设计,可自动抓取论文标题、摘要及开放获取的PDF文档。系统采用模块化设计,包含请求管理、PDF检测和邮件通知等功能,确保高效合规地获取学术资源,提升科研效率。

weixin_29041767的博客 256

Qt 布局原理及原则(一)什么是软件布局?

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar

u012579905的博客 655

汇川机器人与PLC通信-ModbusTCP超详细案例

通过汇川机器人与PLC的ModbusTCP通信项目案例,我们可以了解通讯配置的过程,地址映射的方式,对接信号的转换,运行时序的逻辑等注意事项,帮助我们快速应用到项目中。

weixin_44166380的博客 1万+

模拟IC设计笔记:拆解一个经典的时钟占空比校正(DCC)电路模块,从理论到SPICE仿真

本文深入解析了时钟占空比校正(DCC)电路在模拟IC设计中的关键作用,特别是在高速ADC应用中。通过晶体管级拆解,详细介绍了基于电荷泵和积分器的模拟反馈型DCC电路设计,包括占空比检测、误差放大与校正执行单元的实现。文章还提供了SPICE仿真验证方法,确保电路在100MHz时钟下的稳定性和性能。

weixin_33725722的博客 425

Anatomy of a Program in Memory(程序运行时内存结构)

转载自:http://duartes.org/gustavo/blog/post/anatomy-of-a-program-in-memory Memory management is the heart of operating systems; it is crucial for both programming and system administration. In t

张宸铭的专栏 1606

RK3568平台(camera篇) MPP编码

同芯片的差异,为使用者提供统一的视频媒体处理接口(Media Process Interface,缩写 MPI)。瑞芯微提供的媒体处理软件平台(Media Process Platform,简称 MPP)是适用于瑞芯微芯片系列的。mpi_enc_test 的命令参数中,图像宽度(w)图像高度(h),码流类型(t)为强制要求参数,其他参。视频拷贝,缩放,色彩空间转换,场视频解交织(Deinterlace)数如输入文件(i),输出文件(o),编码帧数(n)等为可选参数。设置一些MPP的模式。

weixin_49303682的博客 7379

计算机操作系统-操作系统概述

操作系统(Operating System,OS)是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配,以提供给用户和其他软件方便的接口和环境,它是计算机系统中最基本的系统软件。我们打开我们的任务管理器,我们操作系统上可管理我们的软件资源,也可以同时查看我们的硬件资源,所以操作系统是作为用户与计算机硬件系统之间的接口。或者说,用户在OS帮助下能够方便、快捷、可靠地操纵计算机硬件和运行自己的程序。

Xixicjj的博客 1907

流体仿真软件:COMSOL Multiphysics二次开发_(11).COMSOL与外部软件的数据交换

通过上述方法,您可以实现 COMSOL Multiphysics 与外部软件的数据交换,从而更好地集成和分析仿真结果。无论是从文本文件导入数据,导出数据到 MATLAB,还是通过 API 和 COMSOL Server 实现实时通信,都能满足不同场景下的需求。希望这些内容对您在流体仿真领域的开发工作有所帮助。

kkchenjj的博客 1553

FANUC机器人_KAREL编程入门学习(1)

FANUC机器人_KAREL编程入门学习(1)

Robot_PLC_HMI_Automation 6420

数组越界:从内存布局到程序崩溃的深度剖析

本文深入剖析了数组越界问题,从内存布局到程序崩溃的底层原理,揭示了这一常见错误的三种致命表现:程序崩溃、数据污染和死循环。通过GDB调试和AddressSanitizer工具的使用,提供了实战调试方法,并提出了六大防御性编程法则,帮助开发者有效避免数组越界问题,提升代码安全性和稳定性。

weixin_33696106的博客 611

基于射频技术的校园无人超市管理系统_360

本设计实现了一种基于射频技术的校园无人超市管理系统,系统集成了环境监测与商品管理两大核心模块。在环境监测部分,采用DHT11温湿度传感器与MQ2烟雾传感器对超市内部环境进行实时监测,结合温控与报警机制,实现自动通风与烟雾报警功能,并通过1.44寸SPI LCD显示屏进行本地信息显示。商品管理部分基于RC522-RFID读卡器完成商品识别,结合Qt上位机软件实现顾客自助选购与结算,同时支持管理员进行商品入库与会员卡管理等操作。系统采用STM32F103RCT6为主控核心,结合串口通信方式与Qt开发的上位机进行

375

剖析程序的内存布局

转载:http://blog.youkuaiyun.com/drshenlei/article/details/4339110 原文标题:Anatomy of a Program in Memory 原文地址:http://duartes.org/gustavo/blog/   [注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下。一来自己复习,二来与大家分享。]       内存管理

kernel_digger的专栏 1833

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Statistics and Machine Learning Toolbox

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

c linux new使内存耗尽_剖析程序的内存布局

本文分享来自:底层软件架构原文来自国外的小哥:http://duartes.org/gustavo/blog/微信公众号:技术原理君内存管理模块是操作系统的心脏;它对应用程序和系统管理非常重要。在这篇文章中,我将着眼于实际的内存问题,但也不避讳其中的技术内幕。由于不少概念是通用的,所以文中大部分例子取自32位x86平台的Linux和Windows系统。本系列第一篇文章讲述应用程序的内存布局。在多任...

weixin_33256096的博客 102

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

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

指针底层原理深度剖析:从寄存器到内存地址

本文深入剖析指针的底层原理,揭示其作为内存地址抽象的本质。指针不仅是存储虚拟地址的变量,其类型还决定了地址偏移量和解引用宽度。通过分析CPU、内存与总线的交互机制,以及汇编层面的实现细节,展示了指针运算、多级指针、指针与数组等价性等核心概念。同时探讨了操作系统的内存保护机制对非法指针访问的防御,以及指针操作对缓存性能的影响。理解这些底层原理有助于开发者编写更高效、安全的代码,并深入把握计算机系统的工作机制。

m0_55384532的博客 1198

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

操作系统内存管理 | 原理 / 机制 / 策略

……

u013669912的博客 1707

一文聊透对象在JVM中的内存布局,以及内存对齐和压缩指针的原理及应用

文章从计算机组成原理的角度详细阐述了对象在Jvm内存中的布局,以及字段排列规则。后面介绍了内存对齐,以及为什么要内存对齐,还有Jvm中压缩指针的原理及应用。中间介绍了false sharing产生的原因以及造成的性能影响和修复方案

bin的技术小屋 690

现代操作系统-原理与实现(上)【银杏书-读书笔记】

看看多久才会读完---买于20年双十一 目录 第1章-操作系统概述 第2章-硬件结构 第3章-操作系统结构 第4章-内存管理 第1章-操作系统概述 从硬件角度 对硬件进行管理,处理各种错误 对硬件进行抽象,形成不依赖硬件的资源 从应用角度 提供不同的接口,满足不同类型的访问控制,应用间交互等服务 进行资源分配与管理 操作系统提供不同层次的接口 系统调用接口,例如printf【printf 从应用程序-》libc-》下陷处理-》系统调用处理】[从应用程序-》libc-》是..

一只爱做笔记的码农 4332

C++ 中堆内存与栈内存的区别:从原理到实践

管理方式:栈由编译器自动管理,采用 LIFO 方式分配和释放;堆由程序员手动管理(或通过智能指针/容器间接管理),支持任意顺序的分配和释放。性能差异:栈分配极快(仅移动栈指针,O(1)),堆分配较慢(需查找空闲块,可能触发系统调用)。栈访问通常比堆访问具有更好的缓存局部性。大小限制:栈空间有限(通常 1-8MB),适合小型对象和局部变量;堆空间大(受系统内存限制),适合大对象和动态数组。生命周期:栈对象与作用域严格绑定,离开作用域自动析构(RAII 的基础);

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 1200

别再只知其然!操作系统核心运行链路剖析

这个章节主要让大家简单了解一个操作系统中有哪些模块,具体的分析学习后面文章会详细讲解。

电气工程专业本科生、辅修计算机。分享嵌入式、后端等方面的系统学习路径。 1218

STM32-栈内存布局:从高地址到低地址的奥秘

栈是一种后进先出(LIFO)的数据结构,在内存中从高地址向低地址生长,用于存储局部变量和函数调用上下文。该布局设计使栈与堆(向高地址生长)相向生长,有效利用内存空间并避免冲突。文章详细剖析了栈的内存布局原理,包括其生长方向、函数调用时的变化过程,以及存在高地址的原因。同时介绍了栈溢出的原理与预防方法,对比了栈与堆的特性差异,并提供了GDB、ASan等调试工具的使用技巧。理解栈的布局对程序内存管理、性能优化和安全防护具有重要意义。

2401_85638163的博客 1334
上一篇: 计算机原理-网络-一篇文章搞懂,内网和外网到底怎么通信?
下一篇: 计算机原理-操作系统-内核如何管理内存
shankusu2017
博客等级 码龄15年 155粉丝 155原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值