- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我是Linux和虚拟内存的初学者,仍然在努力理解虚拟内存和可执行对象文件之间的关系。
假设我们有一个可执行的对象文件a.out存储在硬盘上,假设a.out最初有一个带全局变量的.data节,值为2018。
当加载程序运行时,它分配一个连续的虚拟页块,将它们标记为无效(即未缓存),并将它们的页表条目指向a.out中的适当位置。加载程序实际上从未将任何数据从磁盘复制到内存中。在第一次引用每个页面时,虚拟内存系统会根据需要自动调出数据。
我的问题是:假设程序在运行时将全局变量的值从2018更改为2019,并且包含全局变量的虚拟页似乎最终会向外翻页到磁盘,这意味着.data section的全局变量现在是2019,所以我们更改了不应该更改的可执行对象文件被改变了?否则每次完成并再次运行程序时,我们会得到不同的值?
最佳答案
一般来说(不是专门针对Linux的)。
启动可执行文件时,操作系统(内核)创建一个虚拟地址空间和一个(最初为空)进程,并检查可执行文件的头。可执行文件的头描述了“部分”(例如.text
、.rodata
、.data
、.bss
、.bss
等),其中每个部分都有不同的属性-如果该部分的内容是否应该放在虚拟地址空间中(例如是符号表或运行时不使用的东西),如果内容是否是文件的一部分(例如.bss
),如果该区域应该是可执行的,则为只读或读/写。
通常,可执行文件的(使用部分)由虚拟文件系统缓存;已经在vfs缓存中的文件片段可以映射(作为“写入时复制”)到新进程的虚拟地址空间。对于不在vfs缓存中的部分,可以将这些文件片段映射为“需要获取”到新进程的虚拟地址空间。
然后启动进程(给定CPU时间)。
如果进程从尚未加载的页读取数据;操作系统(内核)暂停进程,将页从磁盘上的文件提取到vfs缓存中,然后将页映射为“写时复制”到进程中;然后允许进程继续(允许进程重试从未加载的页读取,这将在页面加载后生效)。
如果进程写入一个仍然是“写时复制”的页;操作系统(内核)暂停进程,分配一个新页并将原始页的数据复制到其中,然后用进程自己的副本替换原始页;然后允许进程继续(允许进程重试写操作,该操作将在进程拥有自己的副本后生效复制)。
如果进程从一个尚未加载的页面写入数据;操作系统(内核)将前面的两件事结合起来(将原始页面从磁盘提取到vfs缓存中,创建一个副本,将进程的“副本”映射到进程的虚拟地址空间)。
如果操作系统开始耗尽可用RAM,则:
vfs缓存中的文件数据页,但未与任何进程共享为“写时复制”,可以在vfs中释放,而无需执行任何其他操作。下次使用文件时,这些页面将从磁盘上的文件提取到vfs缓存中。
vfs缓存中的文件数据页,以及与任何进程共享为“写时复制”的文件数据页,可以在vfs中释放,并且在任何/所有标记为“尚未提取”的进程中释放副本。下次使用文件时(包括当进程访问“尚未获取”页时),这些页将从磁盘上的文件获取到vfs缓存,然后在进程中映射为“写时复制”。
已修改的数据页(可能是因为它们最初是“写时复制”但被复制的,或者是因为它们根本不是可执行文件的一部分(例如SIGSEGV
节、可执行文件的堆空间等)可以被保存以交换空间,然后被释放。当进程再次访问页面时,它们将从交换空间中获取。
注意:如果可执行文件存储在不可靠的媒体上(例如可能被刮伤的CD),一个“比平均水平更聪明”的操作系统可能会在开始时将整个可执行文件加载到VFS缓存和/或交换空间;因为当进程正在使用文件时,除了使进程崩溃(例如2018
)并使其看起来像可执行文件在未运行时出现错误之外,没有合理的方法来处理“从内存映射文件读取错误”,因为这提高了可靠性(因为您依赖于更可靠的交换,而不依赖于不太可靠的刮擦CD)。此外,如果操作系统防止文件损坏或恶意软件(例如,在可执行文件中内置CRC或数字签名),则操作系统可以(应该)将所有内容加载到内存(VFS缓存)中,以便在允许执行可执行文件之前检查CRC或数字签名,并且(对于安全系统,如果在可执行文件运行时修改了磁盘上的文件,则释放RAM时,可以将未修改的页存储在“更受信任”的交换空间中(与修改页时相同),以避免从原始“不太受信任”的文件中提取数据(部分原因是不希望每次页被修改时都执行整个数字签名检查从文件中加载)。
我的问题是:假设程序在运行时将全局变量的值从2018更改为2019,并且包含全局变量的虚拟页似乎最终会向外翻页到磁盘,这意味着.data section的全局变量现在是2019,所以我们更改了不应该更改的可执行对象文件被改变了?
包含2019
的页面将以“not fetched”开头,然后(当其被访问时)加载到vfs缓存中并映射到进程中作为“copy on write”。在这两个点上,如果需要,操作系统可以释放内存并从磁盘上的可执行文件中获取数据(尚未更改)。
当进程修改全局变量(将其更改为包含
关于linux - 可执行目标文件和虚拟内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53492168/
我创建了一个基于命令行可移植脚本的工业化不可知构建系统,可用于快速构建多个依赖项目,而不必依赖特定的 IDE 或构建工厂。它是不可知的,因为它不是基于单个构建引擎。我使用 cmake 创建了第一个版本
我最初使用 Java 目标开发了一个语法(用于 TestRig 支持),然后将其移植到 Python(从 git hub 语法存储库扩展了 Python3 语法,因此需要将操作移植到 Python
我有一个以 iPhone 和 watchOS 为目标的 Xcode 项目。 iPhone 目标使用加速度计,模拟器不支持。我可以只启动 iPhone 应用程序而不启动 watch 目标吗?我从: Ca
您好,我想创建一个批处理文件,用于在 .eml 文件(目标 A)中查找某些关键字,然后删除它们所在的行。之后,我需要批处理文件将"new"文件放入(目标 B)中的单独 .eml 文件中。文件也可以是
当尝试通过 IntelliJ 运行示例 CorDapp (GitHub CorDapp) 时,我收到以下错误: Cannot inline bytecode built with JVM target
我在尝试向我的 kotlin spring 项目添加一些依赖项时遇到问题。我使用 spring boot 初始化程序来运行一个基本项目。 我的问题:如果我取消对 jackson 或 Koin 依赖项的
这是有问题的网站: http://www.onepixelroom.com/londonrefurb 当我点击关于部分后面的多个圆圈时,我希望它更改上面文本中的引号。 到目前为止,我得到它来显示 文本
单击后,我将删除两个元素 $(this) 和 $("#foo")。 目前我的代码如下所示: $(this).remove(); $("#foo").remove(); 如何在不重复自己的情况下优化它?
我有一个小脚本,可将 Markdown 文件编译为 html,并将其与一些样式表和 javascript 一起插入到模板的主体中。我有一个 GNU makefile 来完成这个: output.htm
已关闭。此问题需要 debugging details 。目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and the
一些背景知识: 在android中我们开发了同样的应用,基本上我们先开发了Android应用,现在我们创建了它的IOS版本,所以这个应用有多个客户端。在 android 中,我们实际上是使用 Andr
我想知道是否可以使用 knockout 来更改html中的目标() 我的所有其他信息都在 JavaScript 中,所以这对我来说是一个大问题。这是我的 JavaScript: var library
这个问题在这里已经有了答案: Selecting and manipulating CSS pseudo-elements such as ::before and ::after using j
我在我的有向图中添加了一堆节点和顶点,使用设置 typedef boost::adjacency_list graph; 创建 Node有一个节点名称字符串,Edge它的分数有一个整数。我试图遍历所有
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 8 年前。 Improve
如何存储我在 NSUserDefaults 中创建的 Goal 类型的对象数组? ( swift ) 代码如下: func saveGoalList ( newGoalList : [Goal] ){
Array.prototype.indexOf 和 Date.now 已在 ES5 中引入。如果我编译存储在文件 test.ts 中的以下代码,为什么 Typescript 不能转译? Date.no
我正在阅读有关属性的内容,并了解到可以使用您的代码将它们应用于不同的目标实体 -(请参阅 Attribute Targets)。 因此,查看我项目中的 AssemblyInfo.cs 文件,我可以看到
给定一个 Makefile: all: build/a build/b build/c # need to change this to all: build/* build/a:
我有一个带有多框架目标的项目- netstandard2.0;net471 . 我想为 netframework 构建解决方案和 netstandard分别。 目前我使用这个 MSBuild 命令:
我是一名优秀的程序员,十分优秀!