x86汇编中向栈空间写入大型数组触发段错误的问题咨询
问题分析与解决方案
嘿,这个问题我刚好有经验,来帮你理清楚~
为什么会触发段错误,且上限有波动?
首先,Linux系统中进程的默认栈大小通常是8MB(你可以用ulimit -s命令查看,默认输出是8192,单位是KB,刚好对应8MB)。但栈的初始空间并不是全部可用的:程序启动时,栈上已经存放了argc、argv指针、环境变量列表、初始栈帧的返回地址等数据,所以实际能用来写入的空间会比8MB略少。
而你遇到的“上限波动”,是因为每次运行程序时,环境变量的数量、长度可能有微小差异(比如某些临时环境变量的变化),导致栈上预存数据的大小不一样,可用的剩余栈空间也就跟着有波动,所以能写入的条目数会在837万左右浮动。
另外,你的代码逻辑其实有问题:
mov %rsp, %rbp mov $8378658, %rdx writeDataLoop: sub %rdx, %rbp ; 每次将rbp跳转到 rsp - rdx 的位置 movb $0b1, (%rbp) add %rdx, %rbp ; 又跳回原来的rsp位置 sub $1, %rdx cmp $0, %rdx jg writeDataLoop
这段代码并没有逐个向栈的下方(低地址)写入数据,而是每次跳到rsp - 当前rdx的位置写一个字节,然后跳回。当rsp - rdx的地址超出了栈的可用范围(低于栈的最低边界,进入内核的栈保护页),就会触发段错误。
怎么向栈写入更多数据?
1. 修正代码逻辑,正确写入栈空间
首先要改成正常的栈写入方式:每次将栈指针向下移动1字节,然后写入数据,这样才是合理地使用栈空间:
mov %rsp, %rbp mov $8378658, %rdx writeDataLoop: sub $1, %rbp ; 栈指针向下移动1字节,指向新的写入位置 movb $0b1, (%rbp) ; 写入数据 sub $1, %rdx cmp $0, %rdx jg writeDataLoop
2. 调整栈大小限制
如果需要写入超过默认8MB的数据,你有几种方式修改栈大小:
- 临时修改(当前终端生效):在运行程序前,用
ulimit -s <新大小>设置栈大小,比如要设置16MB,就执行:ulimit -s 16384 ./your_program - 程序内动态修改:可以通过
setrlimit系统调用在程序启动时调整栈大小。比如用C代码配合汇编的话,在main函数里添加:
或者直接在汇编中调用#include <sys/resource.h> int main() { struct rlimit rlim; getrlimit(RLIMIT_STACK, &rlim); rlim.rlim_cur = 16 * 1024 * 1024; // 设置为16MB setrlimit(RLIMIT_STACK, &rlim); // 调用你的汇编函数 return 0; }setrlimit系统调用(x86_64 Linux下系统调用号是164)。
注意事项
- 不要无限制扩大栈大小:栈是进程的虚拟内存空间的一部分,过大的栈会占用过多虚拟内存,可能影响系统稳定性。
- 栈溢出保护:现代编译器和系统默认开启栈溢出保护机制,确保你的写入操作不会触发误判的保护。
内容的提问来源于stack exchange,提问作者Alice F
相关产品推荐
相关产品推荐

