给定下面的程序,segfault()
将(顾名思义)通过访问堆栈下方的 256k 来对程序进行段错误。 nofault()
然而,逐渐压入堆栈下方,一直到 1m 以下,但从不出现段错误。
此外,在 nofault()
之后运行 segfault()
也不会导致错误。
如果我将 sleep()
放入 nofault()
并使用时间来 cat/proc/$pid/maps
我明白了分配的堆栈空间在第一次和第二次调用之间增长,这解释了为什么 segfault()
不会在之后崩溃 - 有足够的内存。
但反汇编显示 %rsp
没有任何变化。这是有道理的,因为那会搞砸调用堆栈。
我假设最大堆栈大小会在编译时写入二进制文件(回想起来这对编译器来说很难做到)或者它只是定期检查 %rsp
和之后添加一个缓冲区。
内核如何知道何时增加堆栈内存?
#include <stdio.h>
#include <unistd.h>
void segfault(){
char * x;
int a;
for( x = (char *)&x-1024*256; x<(char *)(&x+1); x++){
a = *x & 0xFF;
printf("%p = 0x%02x\n",x,a);
}
}
void nofault(){
char * x;
int a;
sleep(20);
for( x = (char *)(&x); x>(char *)&x-1024*1024; x--){
a = *x & 0xFF;
printf("%p = 0x%02x\n",x,a);
}
sleep(20);
}
int main(){
nofault();
segfault();
}
最佳答案
当您访问未映射的页面时,处理器会引发页面错误。内核的页面错误处理程序检查地址是否合理地接近进程的 %rsp
,如果是,它分配一些内存并恢复进程。如果低于 %rsp
太远,内核会将故障作为信号传递给进程。
我试图找到关于哪些地址足够接近 %rsp
以触发堆栈增长的精确定义,并从 linux/arch/x86/mm.c< 中得出了这个
:
/*
* Accessing the stack below %sp is always a bug.
* The large cushion allows instructions like enter
* and pusha to work. ("enter $65535, $31" pushes
* 32 pointers and then decrements %sp by 65535.)
*/
if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
bad_area(regs, error_code, address);
return;
}
但在试验您的程序时我发现 65536+32*sizeof(unsigned long)
不是段错误和无段错误之间的实际分界点。它似乎是该值的两倍。因此,我将坚持使用含糊不清的“合理接近”作为我的官方回答。
关于linux - linux 如何知道何时将更多页面分配给调用堆栈?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22426041/