2

我正在使用 sys_brk 系统调用在堆中动态分配内存。我注意到在获取当前中断位置时,我通常会得到类似于以下的值:

mov rax, 0x0C
mov rdi, 0x00
syscall

结果是

rax   0x401000

该值通常以 512 字节对齐。所以想问一下break值有一些对齐要求吗?或者我们可以按照我们想要的方式错位它?

4

1 回答 1

1

内核确实以字节粒度跟踪中断。但是,如果您完全关心性能,请不要将其直接用于小分配。


评论中有一些关于内核将中断舍入到页面边界的讨论,但事实并非如此。 使用 this的实现sys_brk(添加了我的评论,因此脱离上下文有意义)

newbrk = PAGE_ALIGN(brk);     // the syscall arg
oldbrk = PAGE_ALIGN(mm->brk); // the current break
if (oldbrk == newbrk)
    goto set_brk;      // no need to map / unmap any pages, just update mm->brk

这将检查中断是否移动到不同的页面,但最终mm->brk = brk;将当前中断设置为传递给系统调用的确切参数(如果它有效)。如果当前中断总是页面对齐,内核就不需要PAGE_ALIGN()它了。


当然,内存保护至少具有页面粒度(如果内核选择使用匿名大页面进行此映射,则可能还有大页面)。因此,您可以将内存访问到包含中断的页面末尾而不会出错。这就是为什么内核代码只是检查中断是否移动到不同的页面以跳过映射/取消映射逻辑,但仍会更新实际的 brk。

AFAIK,没有什么会使用中断上方的映射内存作为暂存空间,因此它不像堆栈指针下方的内存可以被异步破坏。

brk只是一个内置于内核的简单内存管理系统。系统调用很昂贵,因此如果您关心性能,您应该跟踪用户空间中的内容,并且只在需要新页面时才进行系统调用。 直接sys_brk用于微小的分配对性能来说是很糟糕的,尤其是在启用了 Meltdown + Spectre 缓解的内核中(使系统调用更加昂贵,比如数万个时钟周期 + TLB 和分支预测失效,而不是数百个时钟周期)。

于 2018-03-22T02:41:58.740 回答