1) 如果我们不能访问超出给定进程空间的内存,那么如何
是我释放/释放内存后能够访问内存吗?
(第一个例子)
因为 C 或 C++ 运行时会保留一个“堆”内存,并且当您调用 free 或 delete 时,内存实际上并没有呈现为对进程不可用,它只是简单地放回了进程的“空闲内存”区域堆,所以它会被重用。这样做是因为进程分配一些内存、释放它、再次分配一些、释放它等是很常见的。例如
void readfile(const std::string& fname)
{
std::ifstream f(fname.c_str());
std::string* content = new std::string;
while(cin.getline(content))
{
...
}
delete content;
}
这个函数(愚蠢的是,因为我们不应该分配std::string)将首先为std::string分配空间,然后在getline调用被调用时为std:string内的内容[可能分几个部分]分配空间读取文件。例如`std::ifstream 中可能还有其他内存分配。
堆旨在最大限度地减少它要求操作系统将内存从全局物理内存映射/取消映射到特定进程的次数,因为它在性能方面非常“昂贵”,几乎可以映射和取消映射虚拟内存所有处理器(特别是,从其他内核卸载现已失效的内存页面将涉及向另一个处理器发送消息,另一个处理器停止它当前正在执行的操作,更新它的虚拟映射,然后回复“我已经完成了” ,然后继续原处)。当然,如果操作系统在进程停止使用它时没有取消映射进程的内存,那么同一个进程确实可以“使用”该内存地址来查找其他进程的内容——这将是一件坏事,所以操作系统将强制所有处理器内核放弃它的内存映射,然后该位内存可以再次用于另一个进程[至少]。
编辑:澄清一下,堆有时会将内存释放回操作系统。例如,如果您进行 LARGE 分配,然后释放相同的分配,它可能会立即取消映射,因此您将无法在释放后访问内存。在内存被释放后对内存的任何访问都是未定义的行为,运行时可以(并且经常会)在那时对内存做任何它喜欢的事情。最常见的情况是:
- 保持原样,但将其放入“释放”堆中。
- 保留它作为释放的内存,但用一些“神奇”模式填充它以检测它何时被写入,因此可以检测到“释放后使用”(检测非常好!)
- 内存未映射,不再可供当前进程使用。
- 几乎立即将内存分配给其他用途,并再次使用。
同一个操作系统可以在不同时间以几乎任何顺序使用这些场景中的任何一个。
另外,如果是这种情况,那么防病毒软件如何扫描内存
由其他进程分配?
完全不同的问题。他们要么使用调试接口来读取另一个进程的内存,要么使用他们自己的内核驱动程序功能,该功能使用允许任何进程读取任何其他进程的内存的内核函数——毕竟,内核可以做任何事情。 [实际上,反病毒软件通常对从文件加载到内存中的内容更感兴趣,因此应用文件访问过滤器来检查对文件的读取/写入数据,而不是扫描内存中的内容]
2) 如果我动态分配但不释放内存,那么它会
导致内存泄漏。但是如果我的整个过程都被杀死了怎么办?或者
完成执行,所以关闭。那么操作系统不会确保清理
分配给这个进程的所有资源?内存泄漏也是如此
只发生在长期运行的进程中?
当进程死亡时,进程的内存被释放。总是,每次。或者,您可以通过启动分配大量内存的进程、故意杀死它、再次运行它、杀死它、运行、杀死等来导致系统失败。那将是一件坏事,对吧?
你当然可以有一个非常快的内存泄漏。试试:
std:string str;
str.append(100000, 'x');
std::vector<std::string> v;
int i = 0;
while(1)
{
std::cout << "i=" << i << std::endl;
v.push_back(str);
在系统启动 swappng 之前不会花费那么多秒,稍后它会被杀死(如果你不觉得无聊,先杀死它)。如果你这样做,预计 linux 系统会变得相当迟钝......
如果这是真的,那么当我明确地尝试访问
另一个过程操作系统如何确保它不会释放这个
内存?
一个正常的进程将无法访问属于另一个进程的内存——只有通过调试接口或专门编写的内核驱动程序等有限的接口才能做到这一点。或者通过使用操作系统支持的共享内存,当然,在操作系统允许的情况下,相同的内存被映射到两个不同的进程中。
这些访问另一个进程内存的方法将涉及某种形式的“引用计数”[事实上,同样适用于例如,如果进程当前正在尝试保存一个 1000MB 文件的系统调用,并且该进程是为一个原因或另一个被杀死 - 说另一个线程导致不可恢复的故障] - 操作系统跟踪给定内存有多少“用户”,这样它就不会从某个进程的脚下拉扯地毯[或本身]。