简体   繁体   English

Linux如何知道何时将更多页面分配给调用堆栈?

[英]How does linux know when to allocate more pages to a call stack?

Given the program below, segfault() will (As the name suggests) segfault the program by accessing 256k below the stack. 给定下面的程序, segfault()将(通过顾名思义)通过访问堆栈下方的256k对程序进行segfault。 nofault() however, gradually pushes below the stack all the way to 1m below, but never segfaults. 但是, nofault()逐渐将堆栈下方的内容一直推至下方的1m,但绝不会出现段错误。

Additionally, running segfault() after nofault() doesn't result in an error either. 此外,在nofault() segfault()之后运行segfault()也不会导致错误。

If I put sleep() s in nofault() and use the time to cat /proc/$pid/maps I see the allocated stack space grows between the first and second call, this explains why segfault() doesn't crash afterwards - there's plenty of memory. 如果我将sleep()放入nofault()并花时间处理cat /proc/$pid/maps我看到分配的堆栈空间在第一次调用和第二次调用之间增长,这说明了segfault()之后不会崩溃的原因-有足够的内存。

But the disassembly shows there's no change to %rsp . 但是反汇编显示%rsp没有变化。 This makes sense since that would screw up the call stack. 这是有道理的,因为那会破坏调用堆栈。

I presumed that the maximum stack size would be baked into the binary at compile time (In retrospect that would be very hard for a compiler to do) or that it would just periodically check %rsp and add a buffer after that. 我以为最大堆栈大小将在编译时放入二进制文件中(回想起来,这对于编译器来说很难做到),或者只是定期检查%rsp并在此之后添加缓冲区。

How does the kernel know when to increase the stack memory? 内核如何知道何时增加堆栈内存?

#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();
}

The processor raises a page fault when you access an unmapped page. 当您访问未映射的页面时,处理器会引发页面错误。 The kernel's page fault handler checks whether the address is reasonably close to the process's %rsp and if so, it allocates some memory and resumes the process. 内核的页面错误处理程序检查该地址是否合理地接近进程的%rsp ,如果是,则分配一些内存并恢复进程。 If you are too far below %rsp , the kernel passes the fault along to the process as a signal. 如果您离%rsp太远,内核会将故障作为信号传递给进程。

I tried to find the precise definition of what addresses are close enough to %rsp to trigger stack growth, and came up with this from linux/arch/x86/mm.c : 我试图找到确切的定义来确定哪些地址离%rsp足够近以触发堆栈增长,并从linux/arch/x86/mm.c提出了这个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;
}

But experimenting with your program I found that 65536+32*sizeof(unsigned long) isn't the actual cutoff point between segfault and no segfault. 但是,通过对您的程序进行试验,我发现65536+32*sizeof(unsigned long)不是段错误和无段错误之间的实际临界点。 It seems to be about twice that value. 它似乎是该值的两倍。 So I'll just stick with the vague "reasonably close" as my official answer. 因此,我将坚持以模糊的“合理接近”作为我的正式答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM