【ARM Linux 系统稳定性分析入门及渐进 4 -- 栈分类】


请阅读嵌入式及芯片开发学必备专栏


请阅读【ARM Linux 系统稳定性分析专栏导读】

上篇文章:ARM Linux 系统稳定性分析入门及渐进 3 – 栈溢出
下篇文章:ARM Linux 系统稳定性分析入门及渐进 5 – kernel hung task 工作原理

1.1 RT-Thread 的系统栈

如果是 GCC编译器,系统栈底的值一般会在系统链接脚本 link.lds 中设置, 如下配置系统栈的大小为 1K
如在 rtthread 代码 swm320-lq100/drivers/linker_scripts/link.lds 中有如下定义:

_system_stack_size = 0x200;

在系统启动阶段一般都是先通过汇编代码进行部分处理,比如 data 段中的数据copy, 及 bss 段的清零,然后一般会跳转到C代码,在使用C代码之前需要设置好栈的地址。

我们知道 C 代码实现的函数通过反汇编可以看到:在函数之间的调用处会保存一些关键信息,比如 PC/LR/SP/FP,及相关参数到栈中,所以在跳转到 C 代码之前一定要配置好栈的地址。

系统栈地址的配置一般都是在汇编阶段给 SP指针赋值,在 RT-Thread 中 Cortex-M7 平台的 BSP目录下一般会有个名叫 startup.s 的文件,在该文件中的会对 SP 进行赋值:

Reset_Handler:
	ldr sp, =_estack

_estack 即为栈底(栈底是高地址)的位置,该值会在连接脚本中赋值, 如下:

.stack :
{
	. = ALIGN(4);
	__sstack = .;
	. = . + system_stack_size;
	. = ALIGN(4)
	_estack = .;	
}> RAM

在 RT-Thread 中需要知道系统栈(中断栈)与进程栈不是一回事

1.2 RT-Thread 进程栈

1.1 节内容中对于栈的描述是在 scheduler 启动之前的情况,在 scheduler 启动后(中断的打开也是在调度器启动的第一个线程前打开的),也即 在多线程系统中,每个线程都是独立的、互不干扰的,所以要为每个线程都分配独立的栈空间。这个栈空间可以是:

  • 一个预先定义好的全局数组,定义线程栈的示例代码如下:
ALIGN(RT_ALIGN_SIZE)
 
rt_uint8_t thread1_stack[512];
rt_uint8_t thread2_stack[1024];
  • 也可以是动态分配的一段内存空间:

thread->stack_addr = (void *)RT_KERNEL_MALLOC(stack_size);

static rt_err_t _thread_init(struct rt_thread *thread, ...)
{
	... 
    /* stack init */
    thread->stack_addr = stack_start;
    thread->stack_size = stack_size;
 
    /* init thread stack */
    rt_memset(thread->stack_addr, '#', thread->stack_size);
	...

从上面可以看到 task 初始化的时候会将栈空间填入'#', 这个是为了后面检查栈是否溢出的一种方式。

1.2.1 线程栈溢出检查

OS 在调度时 (rt_scheudle) 主动检查栈是否溢出:

  • 线程栈底部预留内存中的 pattern 是不是有被修改;
  • SP 是否超出线程栈范围
    栈增长方向不同,略有区别,原理一样,ARM 架构通常使用向下增长方式。
#ifdef RT_USING_OVERFLOW_CHECK
static void _rt_scheduler_stack_check(struct rt_thread *thread)
{
    RT_ASSERT(thread != RT_NULL);

#ifdef ARCH_CPU_STACK_GROWS_UPWARD
    if (*((rt_uint8_t *)((rt_ubase_t)thread->stack_addr + thread->stack_size - 1)) != '#' ||
#else
    if (*((rt_uint8_t *)thread->stack_addr) != '#' ||
#endif /* ARCH_CPU_STACK_GROWS_UPWARD */
        (rt_ubase_t)thread->sp <= (rt_ubase_t)thread->stack_addr ||
        (rt_ubase_t)thread->sp >
        (rt_ubase_t)thread->stack_addr + (rt_ubase_t)thread->stack_size)
    {
        rt_ubase_t level;

        rt_kprintf("thread:%s stack overflow\n", thread->name);

        level = rt_hw_interrupt_disable();
        while (level);
    }
    ... ...
}
#endif /* RT_USING_OVERFLOW_CHECK */
stack frameaddr
栈底: thread->stack_addrlow
stack size
栈顶:thread->stack_addr + thread->stack_sizehigh

上篇文章:ARM Linux 系统稳定性分析入门及渐进 3 – 栈溢出
下篇文章:ARM Linux 系统稳定性分析入门及渐进 5 – kernel hung task 工作原理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

主公CodingCos

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

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

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

打赏作者

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

抵扣说明:

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

余额充值