程序和进程的内存分布解析

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

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

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

查看本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/OS/unix/

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

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

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

时间: 2024-08-04 02:47:27

程序和进程的内存分布解析的相关文章

程序、进程、内存映射

 程序如何产生的?源代码经过下面四个步骤: 1.预编译 2.编译 3.汇编 4.连接 程序就产生了. 这些工作都是编译器做的,可见编译器的重要性. 程序执行的过程是什么?程序和进程有什么区别? 程序是存储在硬盘上的静态的二进制可执行代码,进程是在内存中运行,并不断发生变化的活的二进制执行代码. 程序如何变成进程的? 当你执行程序时,内核首先将你的程序读入内存,并分配内存空间. 随后内核为你的程序分配进程标识符,以及运行需要的各种资源,这时进程已经产生了? 最后内核将你的进程放入调度队列,在合适的

linux下如何编写代码用于监控特定程序的CPU,内存,磁盘和网络使用情况?

问题描述 linux下如何编写代码用于监控特定程序的CPU,内存,磁盘和网络使用情况? 初学,linux(ubuntu)环境下,如何编写代码用于监控特定程序的CPU,内存,磁盘和网络使用情况?初学者,希望从这些基础程序入手,谢谢大家~ 解决方案 读取proc下面的文件. /proc/stat 里面是CPU使用情况,/proc/meminfo里面是内存使用情况,进程信息在/proc/$pid/stat,statm里面. /proc/net/dev里面是网络使用情况,磁盘IO可以使用iostat命令

如何观察进程的内存占用情况

概述       想必在linux上写过程序的同学都有分析进程占用多少内存的经历,或者被问到这样的问题--你的程序在运行时占用了多少内存(物理内存)?通常我们可以通过top命令查看进程占用了多少内存.这里我们可以看到VIRT.RES和SHR三个重要的指标,他们分别代表什么意思呢?这是本文需要跟大家一起探讨的问题.当然如果更加深入一点,你可能会问进程所占用的那些物理内存都用在了哪些地方?这时候top命令可能不能给到你你所想要的答案了,不过我们可以分析proc文件系统提供的smaps文件,这个文件详

《BGP设计与实现》一2.2 BGP进程和内存使用

2.2 BGP进程和内存使用 BGP设计与实现Cisco IOS软件有3种主要的BGP进程: 输入输出(I/O):路由器(Router):扫描仪(Scanner).图2-1显示了3种BGP进程以及在IOS中所有主要的BGP组件之间的相互作用. BGP I/O进程处理读.写和执行BGP消息的任务.它为TCP和BGP之间提供了一个接口.一方面,它从TCP套接字(socket)中读取消息,并把它们放到BGP输入队列(Input Queue,InQ)中,以便被BGP Router进程操作.另一方面,积聚

《OSPF和IS-IS详解》一2.2 BGP进程和内存使用

2.2 BGP进程和内存使用 BGP设计与实现Cisco IOS软件有3种主要的BGP进程: 输入输出(I/O): 路由器(Router): 扫描仪(Scanner). 图2-1显示了3种BGP进程以及在IOS中所有主要的BGP组件之间的相互作用. BGP I/O进程处理读.写和执行BGP消息的任务.它为TCP和BGP之间提供了一个接口.一方面,它从TCP套接字(socket)中读取消息,并把它们放到BGP输入队列(Input Queue,InQ)中,以便被BGP Router进程操作.另一方面

LINUX类主机JAVA应用程序占用CPU、内存过高分析手段

转载声明:本文为DBA+社群原创文章,转载必须连同本订阅号二维码全文转载,并注明作者名字及来源:DBA+社群(dbaplus).   做为一个IT运维人员,通常在运维过程中会遇到各种各样的问题,系统问题.应用问题.程序问题,而在这当中必然会涉及到性能问题,当用户量过大,或者服务器性能不足以支持大用户量,但同时又得不到扩容的情况下,进行性能分析,并对系统.应用.程序进行优化则显得尤为重要,同时也是节省资源的一种必不可少的手段,目前大多数的运维产品都是基于JAVA语言开发的,下面我给大家介绍一下在l

Linux基础命令介绍九:进程与内存

计算机存在的目的就是为了运行各种各样的程序,迄今我们介绍的绝大多数命令,都是为了完成某种计算而用编程语言编写的程序,它们以文件的形式保存在操作系统之中(比如/bin下的各种命令);但静态的程序并不能"自发的"产生结果,只有在操作系统中为其指定输入数据并运行起来,才能得到输出结果.而操作系统中程序运行的最主要表现形式便是进程. 静态程序可以长久的存在,动态的进程具有有限的生命周期.每次程序运行的开始(如键入一条命令后按下回车键),操作系统都要为程序的运行准备各种资源,这些资源绝大多数都处

Linux进程的内存使用情况

在linux下,使用top,ps等命令查看进程的内存使用情况时,经常看到VIRT,RES,SHR等,他们都代表什么意思呢?不同的大小对进程有什么影响呢?这篇文章将来聊一聊这个问题.阅读本篇前建议先阅读Linux内存管理,了解一些Linux下内存的基本概念,如什么是anonymous和file backed映射等. 查看进程所使用的内存 在进程的眼里,所有的内存都是虚拟内存,但是这些虚拟内存所对应的物理内存是多少呢?正如我们在Linux内存管理中所介绍的那样,并不是每块虚拟内存都有对应的物理内存,

Linux有问必答:如何查看Linux上程序或进程用到的库

Linux有问必答:如何查看Linux上程序或进程用到的库 问题:我想知道当我调用一个特定的可执行文件在运行时载入了哪些共享库.是否有方法可以明确Linux上可执行程序或运行进程的共享库依赖关系? 查看可执行程序的共享库依赖关系 要找出某个特定可执行依赖的库,可以使用ldd命令.这个命令调用动态链接器去找到程序的库文件依赖关系. $ ldd /path/to/program 注意!并不推荐为任何不可信的第三方可执行程序运行ldd,因为某些版本的ldd可能会直接调用可执行程序来明确其库文件依赖关系