随手记——栈空间不足导致的系统异常问题

在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的无效信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。

1 问题引入

栈空间不足的问题出现的概率其实不是很高。因为默认的栈空间都是MB级别的,如果调用深度不是很深或者局部变量不是很大是很难发生栈空间不足的,除了以下3种情况:

  • 使用了未知的第三方代码库。
  • 业务剧烈变更。
  • 新手。

出现栈空间不足的表象一般都是任务异常和段错误。按道理来讲,出现段错误很明显啊,在测试阶段直接定位解决了不就行了,也不算是什么疑难杂症。

如果真实的工程都和学校里的示例程序一样简单,当然这个推论是成立的。但是业务场景复杂之后,只有流程跑到这里才会暴露问题,而且这个问题还是一个致命问题。

为什么栈空间不足了还和流程相关呢?请看下文。。。

2 问题分析定位

当出现任务异常和段错误后,第一反应是指针解引用错误。往往都是一顿查指针的使用。然后,有时候排查完指针的使用也没有什么头绪时,就要考虑是否是栈空间不足导致的了(如果是第一次遇到,没有这个意识,确实会一头雾水)。

2.1 -fstack-protector-all只适用于栈泄露

有人推荐使用 -fstack-protector-all 选项定位是否栈空间不足了。但我亲测发现它 罩不住

考虑到有的TX对这个编译选项还不是很熟悉,借此机会简单介绍一下。

2.1.1 监控栈泄露

下面是一个C语言写的小例子。

# 编译时添加栈保护机制选项
gcc -fstack-protector-all -g stack.c

其中12和13行都有内存越界访问,但是,b数组的越界访问不属于栈泄露。因为在没有栈溢出保护机制下编译时,局部变量的定义顺序即入栈顺序。在添加了栈溢出保护机制后,局部变量的入栈顺序和类型相关,比如char类型先入栈,int类型后入栈;对于同一种类型(例如本例中的a和b数组),入栈顺序和定义顺序刚好相反。

所以,b数组虽然存在越界访问,但是不属于栈溢出。因此,-fstack-protector-all的能力很有限。

  /*C源文件*/
  1 #include <stdio.h>
  2
  3 #define MAX (1000)
  4
  5 void test(void)
  6 {
  7         char a[MAX] = {0};
  8         char b[8] = {0};
  9
 10         printf("debug add_a = %p; add_b = %p \n", a, b);
 11
 12         a[MAX-1+8] = 3; /*可以被栈溢出机制检测到*/
 13         b[16] = 4; /*不可以被栈溢出机制检测到*/
 14 }
 15
 16 int main(void)
 17 {
 18         test();
 19
 20         return 0;
 21 }

栈泄露的监控结果如下:

debug add_a = 0x7ffcec109b00; add_b = 0x7ffcec109af0
*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib64/libc.so.6(__fortify_fail+0x37)[0x7f2b039429e7]
/lib64/libc.so.6(+0x1179a2)[0x7f2b039429a2]
./a.out[0x40060b]
./a.out[0x400629]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f2b0384d3d5]
./a.out[0x4004c9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fd:02 6988561411                         /home/zhaoyuandong/mycode/0713_stack/1/a.out
00600000-00601000 r--p 00000000 fd:02 6988561411                         /home/zhaoyuandong/mycode/0713_stack/1/a.out
00601000-00602000 rw-p 00001000 fd:02 6988561411                         /home/zhaoyuandong/mycode/0713_stack/1/a.out
00ad7000-00af8000 rw-p 00000000 00:00 0                                  [heap]
7f2b03615000-7f2b0362a000 r-xp 00000000 fd:00 41943555                   /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f2b0362a000-7f2b03829000 ---p 00015000 fd:00 41943555                   /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f2b03829000-7f2b0382a000 r--p 00014000 fd:00 41943555                   /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f2b0382a000-7f2b0382b000 rw-p 00015000 fd:00 41943555                   /usr/lib64/libgcc_s-4.8.5-20150702.so.1
7f2b0382b000-7f2b039ed000 r-xp 00000000 fd:00 41960524                   /usr/lib64/libc-2.17.so
7f2b039ed000-7f2b03bed000 ---p 001c2000 fd:00 41960524                   /usr/lib64/libc-2.17.so
7f2b03bed000-7f2b03bf1000 r--p 001c2000 fd:00 41960524                   /usr/lib64/libc-2.17.so
7f2b03bf1000-7f2b03bf3000 rw-p 001c6000 fd:00 41960524                   /usr/lib64/libc-2.17.so
7f2b03bf3000-7f2b03bf8000 rw-p 00000000 00:00 0
7f2b03bf8000-7f2b03c1a000 r-xp 00000000 fd:00 41960517                   /usr/lib64/ld-2.17.so
7f2b03df7000-7f2b03dfa000 rw-p 00000000 00:00 0
7f2b03e16000-7f2b03e19000 rw-p 00000000 00:00 0
7f2b03e19000-7f2b03e1a000 r--p 00021000 fd:00 41960517                   /usr/lib64/ld-2.17.so
7f2b03e1a000-7f2b03e1b000 rw-p 00022000 fd:00 41960517                   /usr/lib64/ld-2.17.so
7f2b03e1b000-7f2b03e1c000 rw-p 00000000 00:00 0
7ffcec0ea000-7ffcec10b000 rw-p 00000000 00:00 0                          [stack]
7ffcec1f2000-7ffcec1f4000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted (core dumped)

2.1.2 无法监控栈空间不足

我们再构造一种栈空间不足的场景,编译时同样加上 -fstack-protector-all 选项,看看能否生效。

Tips:我使用的x86平台创建一个进程默认栈空间大小是8MB。至于如何查看和修改请看问题解决一节。

  1 #include <stdio.h>
  2
  3 #define MAX (8192 * 1024) // 定义长度为默认的栈空间大小
  4 void test(void)
  5 {
  6         char a[MAX];
  7
  8         a[MAX] = 3; // ok
  9
 10         printf("a[-1] = %d.\n", a[MAX-8]); // not ok
 11 }
 12
 13 int main(void)
 14 {
 15         test();
 16
 17         return 0;
 18 }

运行结果如下所示。

Segmentation fault (core dumped)

我们发现,-fstack-protector-all是无法有效监控栈空间不足的问题的。

那么我们如何跟踪定位这个问题呢?答案是使用GDB。


2.2 GDB跟踪调试

使用GDB可以精准的定位到是哪一行代码出现了问题(编译时加上-g选项),对GDB不太熟悉的TX可以看这篇:随手记——GDB调试入门看这一篇就够了

(gdb) r
Starting program: /home/zhaoyuandong/mycode/0713_stack/1/a.out

Program received signal SIGSEGV, Segmentation fault.
0x000000000040053f in test () at main2.c:10
10              printf("a[-1] = %d.\n", a[MAX-8]); // not ok

比较细心的TX可能会文了,即使用GDB跟到了这里,那又怎么确定是指针使用错误还是栈空间不足导致的呢?

好问题。有两个比较简单的方式可以验证:

  • 一个是缩减变量长度测试。
  • 一个是将局部变量定义成全局变量;或者直接在局部变量前添加static将其定义到静态区后测试。

如果上述两个方式生效了,就基本说明是栈空间不足导致的。

当然,还可以实际查看一下当前线程的栈空间大小和地址(maps信息)以便进一步石锤。


3 问题解决

3.1 更改变量大小或位置

解决栈空间不足的问题优选删除或减小过大的局部变量。

如果该函数属于调用比较频繁的可以考虑使用局部static变量。

如果调用不是非常频繁,对性能也没有太高要求的话,可以考虑使用malloc动态申请内存。

3.2 扩展栈空间

如果优化效果不明显,或者由于业务扩张导致的栈空间严重不足的话就只能选择扩充栈空间了。

3.2.1 通过shell指令
# 查询当前默认的栈空间大小
[xxx@bogon 1]$ ulimit -s
8192 # 我的Linux系统上默认的是8192 MB

# 设置默认的栈空间大小
[xxx@bogon 1]$ ulimit -s 8193
3.2.2 通过posix接口

配置堆栈空间大小一般在创建线程之前进行。使用下述接口获取和配置:

int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);

如果你也遇到栈空间不足的问题,在代码里找到上述接口,把栈空间大小改合适就可以了。


4 复盘

在问题引入时,我们提到了这个问题的引入大体分为3种情况:使用第三方代码;业务变动;新手编程。

最后一种情况抛开不讲(涉及到公司的培训制度)。另外两种情况出现时是否有应对策略?

其实是有的,但是大部分国内的公司并没有做。最简单直接的方式就是在上述两种情况产生时,一定要使用栈空间监测工具进行测试,提前发现 病灶 ,提前治疗。

对于栈空间监测工具的制作也并不复杂,我们下回接着说。


恭喜你又坚持看完了一篇博客,又进步了一点点!如果感觉还不错就点个赞再走吧,你的点赞和关注将是我持续输出的哒哒哒动力~~

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
当RT-Thread空间不足时,通常会出现系统运行不稳定或者死机的现象。这是因为空间不足导致无法正确保存和管理函数的局部变量、参数和返回地址,进而影响系统运行和任务切换。解决这个问题可以通过以下方法: 1. 调整任务空间:可以通过修改任务配置文件或者动态调整任务的空间大小来解决空间不足问题。需要根据实际情况分析任务的运行情况和空间的使用情况来合理地调整。 2. 减少递归调用:递归调用会消耗大量的空间,可以尽量避免使用递归或者优化递归算法,减少空间的占用。 3. 减少局部变量的使用:合理管理局部变量的生命周期和作用域,尽量减少局部变量的使用,或者使用静态变量来代替局部变量,减少空间的占用。 4. 使用动态内存分配:如果空间不足,可以考虑使用动态内存分配来代替空间,将一部分变量和数据存储在堆上,减少空间的压力。 5. 调整系统配置:可以通过配置内核参数、关闭不必要的功能或者模块来减小系统的内存占用,从而释放一部分空间。 总的来说,解决RT-Thread空间不足问题需要综合考虑系统的整体架构和功能实现,并根据实际情况进行合理的优化和调整。只有在充分了解系统运行情况和资源利用情况的基础上,才能有效地解决空间不足问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穿越临界点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值