鉴于以下程序,segfault()
(顾名思义)将通过访问堆栈下方的 256k 来使程序出现段错误。nofault()
然而,逐渐将堆栈推到下方 1m,但永远不会出现段错误。
此外,运行segfault()
after nofault()
也不会导致错误。
如果我把sleep()
s in 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)
不是段错误和无段错误之间的实际分界点。看起来大约是这个值的两倍。因此,我将坚持使用含糊的“相当接近”作为我的官方答案。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)