鉴于以下程序,segfault()
将(顾名思义)通过访问堆栈下方的256k来对程序进行分段。 nofault()
但是,逐渐向堆栈下方一直推到1m以下,但绝不会出现段错误。
此外,在segfault()
之后运行nofault()
也不会导致错误。
如果我将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();
}
答案 0 :(得分:3)
当您访问未映射的页面时,处理器会引发页面错误。内核的页面错误处理程序检查地址是否合理地接近进程的%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)
并不是段错误和没有段错之间的实际截止点。它似乎是这个值的两倍左右。所以我只是坚持模糊的#34;合理地接近&#34;作为我的正式答案。