在研究操作系统(主要以Linux作为参考)时,我在研究的材料中找不到很好的解释.
加载到存储器中的程序通常被描述为被分成文本,数据,堆栈等的段,甚至在诸如Linux的操作系统的上下文中,其中虚拟存储器完全基于分页.它只是程序,而不是被称为分段的内存本身吗?如果是这样,我发现术语令人困惑.
我看到malloc可以在Linux中使用调用'sbrk'来实现,这会增加数据段的大小.同样,这个"数据段"是否只是按惯例用于数据的内存区域而不是"真实"段?(额外的问题:'sbrk'似乎无法减小'段'的大小.这是否意味着进程永远不会释放内存到操作系统而不是退出?)
此外,我有兴趣知道为什么现代操作系统似乎不使用(分页)分段.它不会阻止某些类型的攻击使代码驻留在它自己的受保护段中,从而提高安全性吗?另一方面,这会使JIT编译变得不可能/困难吗?
除了上述问题的"是"/"否"答案之外,我对任何有关该主题的深刻阐述感兴趣.
先感谢您.
我有以下问题:
我通过分配一大块的存储器(多个GIB)mmap用MAP_ANONYMOUS.该块包含一个大的哈希映射,需要时不时地归零.并非整个映射可以在每一轮中使用(并非每个页面都有错误),因此memset不是一个好主意 - 需要太长时间.
快速做到这一点的最佳策略是什么?
将
madvise(ptr, length, MADV_DONTNEED);
Run Code Online (Sandbox Code Playgroud)
保证我任何后续访问都提供新的空页面?
从Linux man madvise页面:
此调用不会影响应用程序的语义(MADV_DONTNEED除外),但可能会影响其性能.内核可以自由地忽略这些建议.
...
MADV_DONTNEED
对此范围内页面的后续访问将成功,但将导致从底层映射文件(请参阅mmap(2))或零填充按需页面重新加载内存内容,以便在没有基础文件的情况下进行映射.
...
当前的Linux实现(2.4.0)将此系统调用视为命令而不是建议...
或者我是否必须munmap重新重新映射该区域?
它必须在Linux上工作,理想情况下在OS X上具有相同的行为.
在我的机器(XP,64)上,ASP.net工作进程(w3wp.exe)始终启动,保留5.5GB的虚拟内存.无论它托管的Web应用程序是什么,都会发生这种情况(它可以是任何内容,甚至是aspx中的空网页).
这个大块的虚拟内存在进程启动时保留,所以这不是某种渐进的内存"泄漏".
一些人使用windbg窥探显示内存问题是Private,Reserved和RegionUsageIsVAD,这表明它可能是某人调用VirtualAlloc的工作.它还表明,所讨论的内存分配/保留为4个大块,每块1GB,还有几个较小的块(每个1/4GB).
所以我想我需要弄清楚谁在调用VirtualAlloc并保留所有这些内存.我怎么做?
在内存分配之前将调试器附加到进程是很棘手的,因为w3wp.exe是由svchost.exe(即IIS/ASP.Net过滤器)启动的进程,如果我尝试自己启动它以便调试它它只是关闭而没有所有这些大量的内存预留.此外,如果我重新使用它们,命令行参数将无效(这是有道理的,因为它是由调用进程创建的管道).
我可以在事后将windbg附加到进程(这是我找到有问题的内存区域的方式),但我不确定在那时可以确定谁分配了什么.
我们正在使用基于gcc 2.6.0的遗留编译器来交叉编译我们仍在使用的旧嵌入式处理器(是的,它自1994年以来一直在使用!).为这款芯片做gcc端口的工程师早就开始了.虽然我们可以从网络上的某个地方恢复gcc 2.6.0源代码,但这个芯片的更改集已经在公司历史的大厅中消失了.直到最近,由于编译器仍然运行并生成可行的可执行文件,我们一直糊涂,但是从Linux内核2.6.25(以及2.6.26)开始,它失败并显示消息gcc: virtual memory exhausted......即使在没有参数的情况下运行或仅运行-v.我已经使用2.6.24内核重新启动了我的开发系统(从2.6.26开始)并且编译器再次工作(使用2.6.25重启不会).
我们有一个系统,我们保留在2.6.24只是为了为这个芯片做构建,但是感觉有点暴露,以防linux世界转移到我们不能再重建一个将运行的系统的程度编译器(即我们的2.6.24系统死机,我们无法在新系统上安装和运行2.6.24,因为某些软件部分不再可用).
有没有人有任何想法,我们可以做一些更现代的安装,以使这个遗留的编译器运行?
编辑:
回答一些评论......
遗憾的是,这是我们芯片特有的源代码更改丢失了.这种损失发生在两个主要的公司重组和几个系统管理员(其中一些确实留下了一团糟).我们现在使用配置控制,但这对于这个问题来说太晚关闭了谷仓门.
使用VM是一个好主意,也可能是我们最终要做的.谢谢你的想法.
最后,我尝试了strace作为ephemient建议,发现最后一次系统调用是brk(),它在新系统(2.6.26内核)上返回错误并在旧系统(2.6.24内核)上返回成功.这表明我真的用完了虚拟内存,除了tcsh"limit"在旧系统和新系统上返回相同的值,而/ proc/meminfo显示新系统有更多的内存和更多的交换空间.也许这是一个碎片问题或程序加载的地方?
我做了一些进一步的研究,并在内核2.6.25中添加了"brk randomization",但是CONFIG_COMPAT_BRK默认情况下会启用(禁用brk随机化).
编辑:
好的,更多信息:它看起来真的像brk随机化是罪魁祸首,传统的gcc正在调用brk()来改变数据段的结束并且现在失败,导致传统的gcc报告"虚拟内存耗尽".有一些记录的方法可以禁用brk随机化:
sudo echo 0 > /proc/sys/kernel/randomize_va_space
sudo sysctl -w kernel.randomize_va_space=0
用setarch i386 -R tcsh(或"-R -L")开始一个新的shell
我已经尝试了它们,它们确实有效,因为brk()返回值与没有它们(并且在内核2.6.25和2.6.26上都试过)不同(并且总是相同),但是brk()仍然失败,所以传统的gcc仍然失败:-(.
此外,我已经设置vm.legacy_va_layout=1并vm.overcommit_memory=2没有改变,我已经与重新启动vm.legacy_va_layout=1,并kernel.randomize_va_space=0保存在/etc/sysctl.conf中的设置.仍然没有变化.
编辑:
使用kernel.randomize_va_space=0内核2.6.26(2.6.25和)在以下brk的结果()通过所报告的呼叫strace legacy-gcc:
brk(0x80556d4) = 0x8056000
这表明brk()失败了,但看起来它失败了,因为数据段已经超出了请求的范围.使用objdump,我可以看到数据段应该以0x805518c结束,而失败的brk()表示数据段当前以0x8056000结束:
Sections: Idx Name Size VMA LMA File off Algn 0 .interp 00000013 080480d4 080480d4 000000d4 2**0 …
我正在编写一个内核模块,由进程p1调用以覆盖属于目标进程p2的数据页.
首先,在内核模块内部并且在响应p1发出的proc文件系统的写事件时.我使用目标进程的进程id(p2)来搜索后者的任务结构(p2_task).
要搜索我使用的特定页面get_user_pages(),显然在(p2_task->mm)上调用它.然后我调用了kmap()上一个函数返回的页面.一旦我得到指针,我就使用典型的内存函数(memset())来写入该内存.最后叫了kunmap().
但是,一旦进程再次开始运行,我就可以看到我所做的对目标进程p2没有影响.
我不确定我做错了什么.有人可以帮忙吗?
我怀疑在回应来自p2的请求时,某种程度上你无法写入内存属于进程p2.从这里开始,我们处于不同的背景下.
这是真的,如果不是我能检查的其他内容.如果是问题,反正我可以解决这个问题吗?
首先,采用单处理器系统,其中多个进程以伪并行方式运行.当进程触发页面错误时,这会强制CPU停止执行所有程序,直到从磁盘加载页面为止?
如果是这样,这在多核或多处理器系统上是否会发生变化,或者在处理页面错误时其他进程是否可以继续读取和写入内存?
谢谢!
我需要使用两个PTE位来存储我的内核模块在拦截页面保护错误时将使用的自定义"状态"值.
我正在开发Galaxy Nexus,它有一个ARM Cortex A9(我相信ARM v7).Linux内核版本3.0.31.Linux PTE定义如下(来自arch/arm/include/asm/pgtable.h:
/*
* "Linux" PTE definitions.
*
* We keep two sets of PTEs - the hardware and the linux version.
* This allows greater flexibility in the way we map the Linux bits
* onto the hardware tables, and allows us to have YOUNG and DIRTY
* bits.
*
* The PTE table pointer refers to the hardware entries; the "Linux"
* entries are stored 1024 bytes below.
*/
#define L_PTE_PRESENT …Run Code Online (Sandbox Code Playgroud) 有什么区别之间mach_vm_region以及mach_vm_region_recurse在哪些情况下,你会用哪个呢?
函数声明的签名是:
kern_return_t mach_vm_region
(
vm_map_t target_task,
mach_vm_address_t *address,
mach_vm_size_t *size,
vm_region_flavor_t flavor,
vm_region_info_t info,
mach_msg_type_number_t *infoCnt,
mach_port_t *object_name
);
kern_return_t mach_vm_region_recurse
(
vm_map_t target_task,
mach_vm_address_t *address,
mach_vm_size_t *size,
natural_t *nesting_depth,
vm_region_recurse_info_t info,
mach_msg_type_number_t *infoCnt
);
Run Code Online (Sandbox Code Playgroud)
如果我想要,我应该使用哪个功能...
Windows 8.1/Server 2012RC2 刚刚引入了用于虚拟内存管理的新 API:OfferVirtualMemory()、ReclaimVirtualMemory()、DiscardVirtualMemory(),它们的用法非常简单,只需查看它们的名称即可。
我不明白的是这些 API 如何与VirtualAlloc()标志MEM_RESET和一起工作MEM_RESET_UNDO,以及有哪些细微的区别。
对于OfferVirtualMemory(),MSDN 表示它与VirtualAlloc()+非常相似MEM_RESET,只不过它从工作集中删除页面,并限制对页面的进一步访问。
因此,基本上它限制了对页面的访问,如果我想再次访问这些页面,我必须调用ReclaimVirtualMemory(). 这很好,但不应该MEM_RESET也从工作集中删除页面吗?不应该MEM_RESET充当 POSIXMADV_DONTNEED标志,madvise(2)它基本上从进程的页表中删除页面,如果我将来再次访问这些页面,访问将生成软故障,并且这些页面将再次重新分配,初始化为零。
当然,如果这是真的,页面将从进程的工作集中删除,因为它们基本上被释放,即使进程保留分配的虚拟地址,并看到它们“已提交”。
现在,让我们看看DiscardVirtualMemory():这里 MSDN 没有提到任何关于MEM_RESETflag 的内容,但是如果我阅读了这个 API 的描述,它似乎与+确实是一样的。VirtualAlloc()MEM_RESET
那么,有谁知道这些 API 之间是否存在一些差异,以及这些细微差异的正确用例是什么?如果他们引入了像 一样的全新 API DiscardVirtualMemory(),那么与旧方法应该会有一些区别。
如果我想从 POSIX 移植一个使用madvise(2)和 的MADV_DONTNEED应用程序MADV_WILLNEED,模仿这种 POSIX 行为的最佳方法是什么?到目前为止,我使用VirtualAlloc()+ MEM_RESETforMADV_DONTNEED …
我试图更深入地了解linux的虚拟内存管理器.如果找到一本名为"了解Linux虚拟内存管理器[1]"的书,由Mel Gorman编写,看起来相当精细.不幸的是它基于2.4内核系列所以它的那种古老.
有谁读过这本书?它仍然相关吗?备择方案?
virtual-memory ×10
linux-kernel ×3
c ×2
memory ×2
arm ×1
asp.net ×1
gcc ×1
iis ×1
legacy ×1
linux ×1
macos ×1
mmap ×1
posix ×1
winapi ×1
windows-8.1 ×1