每个进程获得4 GB的虚拟地址空间,而在4 GB中,用户空间获得2 GB。在这2 GB中,我想读取操作系统分配给进程的整个内存堆栈。
假设我在堆栈上声明了一个大小为2000,000(char chrArray[2000000];
)的本地数组。我想问几个问题。
1)使用gcc
和cygwin
,我能够在没有初始化的情况下读取其内容,但其内容大部分为空(我使用%c
将数组打印到文件中)或某些内容此数组末尾的垃圾数据。我在垃圾数据中存在一个字符串并在内存转储中查找它(使用DumpIt
工具),但此字符串在内存转储中不存在。
我的问题是垃圾数据来自哪里?它位于硬盘中吗?
2)我想在每个子进程中使用fork()
来声明char数组,但我想,它会复制父进程的整个地址空间并使用Copy-on-Write
技术,因此它似乎没用。我对这种解释是对的吗?
3)我想要的是声明一个大尺寸的数组,每次映射到一些新的物理内存地址。这是可以实现的,如果可以,怎么样?
通过重复这个过程,我想扫描整个内存。
我尝试bash
脚本多次运行此程序,但它总是从相同的虚拟基地址开始。
我尝试运行一个for
循环来声明(比方说)10个char数组但是通过打印它们的基地址,我看到所有数组都有相同的地址。
我正在使用Windows 7
和C
计划,我认为,我缺乏必要的操作系统知识。
感谢。
P.S。如果有人好奇为什么我这样做,我正在这方面做研究。
答案 0 :(得分:2)
为避免安全问题,在分配物理页面时,大多数内核都会确保物理页面不包含任何数据(例如,用零填充)。否则,一个进程可以用敏感信息(例如密码)填充页面,然后释放它,另一个进程可以分配该页面并访问敏感数据。
因此,您在阵列中看到的垃圾是您自己的垃圾。
当一个进程首次启动时,很多事情通常会在main()
获得控制权之前发生。这些东西可能包括动态链接和设置系统库,执行构造函数(用于C ++)等。这意味着在代码运行之前,事情会被放到堆栈中(然后从堆栈中删除)。这是你看到的垃圾。
答案 1 :(得分:2)
假设我声明了一个大小为2000,000(
的本地数组char chrArray[2000000];
)
这很危险。如果你的堆积不足,你的程序就会崩溃。
此数组末尾的一些垃圾数据。
堆栈向下增长,因此数组的末尾(较高部分)对应于堆栈的旧部分。程序启动时数据归零,程序运行时会将数据写入堆栈。所以“垃圾数据”只是剩下的局部变量和你调用的函数的其他堆栈帧。 (大多数操作系统使用专门的写时复制技术将进程内存归零 - 我说“专用”,因为当你知道整个块是零时,很容易复制一块内存。)
它位于硬盘中吗?
来自硬盘的数据仅出现在您的内存空间中。当您的进程重用其他进程的内存时,内存首先归零。
我想过使用fork()来声明每个子进程中的char数组,但我想,它会复制父进程的整个地址空间并使用Copy-on-Write技术,所以它似乎没用。我对这种解释是对的吗?
这是标记的Windows,除了使用Cygwin时,它没有fork()
。我不知道Cygwin的实现。我不确定你要完成什么,或者你期望什么,但这正是fork()
应该表现的方式。 fork()
调用会复制整个过程,包括内存。
我想要的是声明一个大尺寸的数组,每次映射到一些新的物理内存地址。这是可以实现的吗?如果可以,那该怎么办?
用户空间程序不适用于物理内存地址,仅适用于虚拟内存地址。不,这是不可能的。
更糟糕的是,假设您在虚拟地址1000处分配了一个数组char arr[10000]
,并假设虚拟地址1000对应于物理地址6000.
&arr[0]
的地址是虚拟地址1000和物理地址6000 ......除非你的进程被换出,此时它的没有物理地址。所以物理地址并不总是存在。
当您的流程重新进入时,&arr[0]
仍然是虚拟地址1000,但它可能是物理地址6000或8000或999000 ......谁知道?
&arr[1000]
的地址始终是虚拟地址2000,但它可能是物理地址10000或85000或完全不同的东西......谁知道?
进一步阅读:您可能希望了解操作系统的“虚拟内存”子系统。