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

标签 linux segmentation-fault callstack

给定下面的程序,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/

相关文章:

python - 如何在python和 Node 进程之间共享mmap

c - 从原始文件读取时出现段错误

使用 fscanf 从文件中的数据生成数组时出现 C 段错误

java - java程序的动态分析

python - 从调用者的角度发出警告(又名 Python 等同于 Perl 的鲤鱼)?

linux - Postfix 邮件发送问题?

java - 堆空间 - 内存管理

linux - 如何在 GNU Octave 中抑制警告

sql-server - 使用 MS ODBC Driver 1.0 for Linux 在 Linux 上使用 pyodbc 重新连接到 SQL Server 数据库时出现段错误

c# - 获取特定的 StackFrame 而不是 StackTrace.GetFrame 是否更便宜?