<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>RTOS on Hacper&#39;s Blog</title>
    <link>https://hacperme.com/tags/rtos/</link>
    <description>Recent content in RTOS on Hacper&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh</language>
    <lastBuildDate>Fri, 08 Nov 2024 00:49:37 +0800</lastBuildDate>
    <atom:link href="https://hacperme.com/tags/rtos/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>【EC200A 学习笔记】-- ASR1803S&#43;AIC8800DW&#43;SZ18201 简单测速结果</title>
      <link>https://hacperme.com/posts/series/asr1803/20241108_ec200a_wifi_eth_speed_test/</link>
      <pubDate>Fri, 08 Nov 2024 00:49:37 +0800</pubDate>
      <guid>https://hacperme.com/posts/series/asr1803/20241108_ec200a_wifi_eth_speed_test/</guid>
      <description>ASR1803S+AIC8800DW+SZ18201 简单测速的验证结果。</description>
      <content:encoded><![CDATA[<p>硬件环境：ASR1803S+AIC8800DW+SZ18201，手机连接 ASR1803S 热点，PC 端通过网线连接 ASR1803S，简单测速验证结果。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># PC 端作为 server， 手机端作为 client</span>
</span></span><span class="line"><span class="cl">❯ .<span class="se">\i</span>perf3.exe -s
</span></span><span class="line"><span class="cl">-----------------------------------------------------------
</span></span><span class="line"><span class="cl">Server listening on <span class="m">5201</span>
</span></span><span class="line"><span class="cl">-----------------------------------------------------------
</span></span><span class="line"><span class="cl">Accepted connection from 192.168.0.200, port <span class="m">39734</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span> <span class="nb">local</span> 192.168.0.100 port <span class="m">5201</span> connected to 192.168.0.200 port <span class="m">39744</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> ID<span class="o">]</span> Interval           Transfer     Bandwidth
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   0.00-1.00   sec  4.07 MBytes  34.1 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   1.00-2.00   sec  4.22 MBytes  35.3 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   2.00-3.00   sec  4.18 MBytes  35.1 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   3.00-4.00   sec  4.35 MBytes  36.5 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   4.00-5.00   sec  4.29 MBytes  35.9 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   5.00-6.00   sec  4.31 MBytes  36.3 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   6.00-7.00   sec  4.26 MBytes  35.7 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   7.00-8.00   sec  4.09 MBytes  34.3 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   8.00-9.00   sec  4.32 MBytes  36.2 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   9.00-10.00  sec  4.42 MBytes  37.0 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>  10.00-10.06  sec   <span class="m">218</span> KBytes  30.2 Mbits/sec
</span></span><span class="line"><span class="cl">- - - - - - - - - - - - - - - - - - - - - - - - -
</span></span><span class="line"><span class="cl"><span class="o">[</span> ID<span class="o">]</span> Interval           Transfer     Bandwidth
</span></span><span class="line"><span class="cl"><span class="o">[</span>  5<span class="o">]</span>   0.00-10.06  sec  42.7 MBytes  35.6 Mbits/sec                  receiver
</span></span><span class="line"><span class="cl">-----------------------------------------------------------
</span></span></code></pre></td></tr></table>
</div>
</div><div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># PC 端作为 client， 手机端作为 server</span>
</span></span><span class="line"><span class="cl">❯ .<span class="se">\i</span>perf3.exe -c 192.168.0.200
</span></span><span class="line"><span class="cl">Connecting to host 192.168.0.200, port <span class="m">5201</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span> <span class="nb">local</span> 192.168.0.100 port <span class="m">63669</span> connected to 192.168.0.200 port <span class="m">5201</span>
</span></span><span class="line"><span class="cl"><span class="o">[</span> ID<span class="o">]</span> Interval           Transfer     Bandwidth
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   0.00-1.01   sec  2.12 MBytes  17.7 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   1.01-2.01   sec  3.88 MBytes  32.6 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   2.01-3.01   sec  4.50 MBytes  37.7 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   3.01-4.01   sec  4.75 MBytes  39.7 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   4.01-5.00   sec  5.00 MBytes  42.4 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   5.00-6.01   sec  4.75 MBytes  39.5 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   6.01-7.01   sec  4.75 MBytes  39.9 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   7.01-8.01   sec  6.12 MBytes  51.4 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   8.01-9.00   sec  6.00 MBytes  50.8 Mbits/sec
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   9.00-10.00  sec  5.75 MBytes  48.2 Mbits/sec
</span></span><span class="line"><span class="cl">- - - - - - - - - - - - - - - - - - - - - - - - -
</span></span><span class="line"><span class="cl"><span class="o">[</span> ID<span class="o">]</span> Interval           Transfer     Bandwidth
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   0.00-10.00  sec  47.6 MBytes  39.9 Mbits/sec                  sender
</span></span><span class="line"><span class="cl"><span class="o">[</span>  4<span class="o">]</span>   0.00-10.00  sec  47.6 MBytes  39.9 Mbits/sec                  receiver
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">iperf Done.
</span></span></code></pre></td></tr></table>
</div>
</div><p>然后实网测速结果：
<img loading="lazy" src="https://github.com/hacperme/picx-images-hosting/raw/master/20241108/cdfaaa201d0d42722f2c9c3aa3ad254.6m3wz3nffy.webp" alt=""  />
</p>
]]></content:encoded>
    </item>
    <item>
      <title>FreeRTOS 中的栈溢出检测机制</title>
      <link>https://hacperme.com/posts/series/dump/2021-04-11-freertos_stack_overflow_checking/</link>
      <pubDate>Sun, 11 Apr 2021 23:26:09 +0800</pubDate>
      <guid>https://hacperme.com/posts/series/dump/2021-04-11-freertos_stack_overflow_checking/</guid>
      <description>FreeRTOS中的任务栈结构介绍 在FreeRTOS中，创建任务A、B、C三个任务，以栈的生长方向从高到低为例，其任务栈结构如下图所示： 1 2</description>
      <content:encoded><![CDATA[<h2 id="freertos中的任务栈结构介绍">FreeRTOS中的任务栈结构介绍</h2>
<p>在FreeRTOS中，创建任务A、B、C三个任务，以栈的生长方向从高到低为例，其任务栈结构如下图所示：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl">                           <span class="o">+-----------&gt;+--------+</span>        <span class="n">Low</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|</span>        <span class="o">|</span>         <span class="o">^</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|</span><span class="n">No</span> <span class="n">used</span> <span class="o">|</span>   <span class="n">SP</span>    <span class="o">|</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|--------|&lt;------</span>  <span class="o">|</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|</span>  <span class="n">regs</span>  <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">+--------+</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl">                           <span class="o">|</span>            <span class="o">|</span> <span class="n">Func</span> <span class="mi">4</span> <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="n">Low</span>   <span class="o">+---------------+</span>    <span class="o">|</span>            <span class="o">+--------+</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">^</span>    <span class="o">|</span>               <span class="o">|</span>    <span class="o">|</span>            <span class="o">|</span> <span class="n">Func</span> <span class="mi">3</span> <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span> <span class="n">Task</span> <span class="n">C</span> <span class="n">stack</span>  <span class="o">|</span>    <span class="o">|</span>            <span class="o">+--------+</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-+</span>    <span class="o">|</span>            <span class="o">|</span> <span class="n">Func</span> <span class="mi">2</span> <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>     <span class="n">TCB</span>       <span class="o">|</span>    <span class="o">|</span>            <span class="o">+--------+</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+---------------+----+</span>            <span class="o">|</span> <span class="n">Func</span> <span class="mi">1</span> <span class="o">|</span>         <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>               <span class="o">|</span>           <span class="o">+----&gt;+--------+</span>        <span class="n">High</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span> <span class="n">Task</span> <span class="n">B</span> <span class="n">stack</span>  <span class="o">|</span>           <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-+-----------+</span>     <span class="n">Task</span> <span class="n">B</span> <span class="n">stack</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>     <span class="n">TCB</span>       <span class="o">|</span>           
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+---------------+</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>               <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span> <span class="n">Task</span> <span class="n">A</span> <span class="n">stack</span>  <span class="o">|</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">+-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-</span> <span class="o">-+</span>
</span></span><span class="line"><span class="cl"> <span class="o">|</span>    <span class="o">|</span>     <span class="n">TCB</span>       <span class="o">|</span>
</span></span><span class="line"><span class="cl"><span class="n">High</span>  <span class="o">+---------------+</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在内存上可以先简单看作是每个任务按任务控制块（TCB）+ stack 依次存储，任务在执行过程中的上下文数据会保存在栈中，比如函数的局部变量，传递参数，返回地址等都会存储在栈里面。在task B的栈空间里，函数调用关系是 Func 1 调用 → Func 2  调用 → Func 3 调用 → Func 4。在这样的栈存储结构，任务在运行的过程中可能会出现下面两个问题：</p>
<ol>
<li>栈溢出导致的问题，如果任务中函数调用的层级过深，或者函数内部有定义占用空间较大的局部变量，则有可能在压栈的时候使用的空间超出了任务的栈空间大小，例如task B 中的函数调用层级过多，会导致task B 的数据写到task C的堆栈空间里，从而破坏task C的栈空间。</li>
<li>局部变量溢出导致的问题，以task B为例，如果在Func 4函数里面操作局部变量，不小心把局部变量“写穿了”，则有可能会把返回地址给覆盖了，Func 4返回时会跳转的错误的地址而出现异常，Func 4执行完也就无法返回到Func 3了，如果覆盖的空间较大，则有可能将数据写到task A的栈空间，从而导致task A的栈空间也被破坏。</li>
</ol>
<h2 id="freertos的栈溢出检测机制">FreeRTOS的栈溢出检测机制</h2>
<p>根据FreeRTOS的官方文档介绍，FreeRTOS提供两种栈溢出的检测方法，可以通过配置configCHECK_FOR_STACK_OVERFLOW 这个宏来选择使用哪种方法，当kernel检测到栈溢出的情况之后，会调用钩子函数vApplicationStackOverflowHook，该函数的原型如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vApplicationStackOverflowHook</span><span class="p">(</span> <span class="n">TaskHandle_t</span> <span class="n">xTask</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                    <span class="kt">signed</span> <span class="kt">char</span> <span class="o">*</span><span class="n">pcTaskName</span> <span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="栈溢出检测方式1">栈溢出检测方式1</h3>
<p>第一种检测方式的原理是检查当前任务的栈顶指针是否在该任务的栈空间范围之内，如果栈顶指针不在任务的栈空间，则会触发钩子函数vApplicationStackOverflowHook，通过配置宏configCHECK_FOR_STACK_OVERFLOW为1使用这种检测方式，具体可以看下include/stack_macros.h路径的源码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) &amp;&amp; ( portSTACK_GROWTH &lt; 0 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Only the current stack state is to be checked. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">        </span><span class="cm">/* Is the currently saved stack pointer within the stack limit? */</span><span class="cp">                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">        if( pxCurrentTCB-&gt;pxTopOfStack &lt;= pxCurrentTCB-&gt;pxStack )                                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        {                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB-&gt;pcTaskName ); \
</span></span></span><span class="line"><span class="cl"><span class="cp">        }                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* configCHECK_FOR_STACK_OVERFLOW == 1 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) &amp;&amp; ( portSTACK_GROWTH &gt; 0 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Only the current stack state is to be checked. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">        </span><span class="cm">/* Is the currently saved stack pointer within the stack limit? */</span><span class="cp">                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">        if( pxCurrentTCB-&gt;pxTopOfStack &gt;= pxCurrentTCB-&gt;pxEndOfStack )                                \
</span></span></span><span class="line"><span class="cl"><span class="cp">        {                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB-&gt;pcTaskName ); \
</span></span></span><span class="line"><span class="cl"><span class="cp">        }                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* configCHECK_FOR_STACK_OVERFLOW == 1 */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在分析代码之前要留意一下栈的生长方向，其中宏 portSTACK_GROWTH 表示栈的生长方向，当配置portSTACK_GROWTH &lt; 0 时，说明栈的生长方向是从高地址向低地址生长，反之，当配置portSTACK_GROWTH &gt; 0 是，表示栈是从低地址向高地址增长。pxTopOfStack 记录了当前任务的栈顶指针，pxStack是当前任务栈的起始地址，而pxEndOfStack是当前任务栈的结束地址。从代码可以看出此方式是直接通过判断当前堆栈指针是否超出了任务栈空间的合法范围来检测溢出的，如果当前任务栈被其他任务破坏了，这种方式就检测不出来。</p>
<h3 id="栈溢出检测方式2">栈溢出检测方式2</h3>
<p>方式2的检测原理是在任务栈初始化的时候，填充固定数据0xA5，kernel 会检查任务栈的最后16个字节的数据是否被篡改，如果发现这16个字节不是0xA5，则会触发vApplicationStackOverflowHook的调用，通过配置configCHECK_FOR_STACK_OVERFLOW &gt;1 来使用此方式。以下是该检测方式的源码，分析时要注意栈的增长方向：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#if ( ( configCHECK_FOR_STACK_OVERFLOW &gt; 1 ) &amp;&amp; ( portSTACK_GROWTH &lt; 0 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">        const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB-&gt;pxStack;                       \
</span></span></span><span class="line"><span class="cl"><span class="cp">        const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5;                                        \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">        if( ( pulStack[ 0 ] != ulCheckValue ) ||                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">            ( pulStack[ 1 ] != ulCheckValue ) ||                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">            ( pulStack[ 2 ] != ulCheckValue ) ||                                                      \
</span></span></span><span class="line"><span class="cl"><span class="cp">            ( pulStack[ 3 ] != ulCheckValue ) )                                                       \
</span></span></span><span class="line"><span class="cl"><span class="cp">        {                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB-&gt;pcTaskName ); \
</span></span></span><span class="line"><span class="cl"><span class="cp">        }                                                                                             \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* #if( configCHECK_FOR_STACK_OVERFLOW &gt; 1 ) */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cp">#if ( ( configCHECK_FOR_STACK_OVERFLOW &gt; 1 ) &amp;&amp; ( portSTACK_GROWTH &gt; 0 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#define taskCHECK_FOR_STACK_OVERFLOW()                                                                                                \
</span></span></span><span class="line"><span class="cl"><span class="cp">    {                                                                                                                                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        int8_t * pcEndOfStack = ( int8_t * ) pxCurrentTCB-&gt;pxEndOfStack;                                                                  \
</span></span></span><span class="line"><span class="cl"><span class="cp">        static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE,   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                        tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE,   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                        tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE,   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                        tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE,   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                        tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                                                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                                                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">        pcEndOfStack -= sizeof( ucExpectedStackBytes );                                                                                   \
</span></span></span><span class="line"><span class="cl"><span class="cp">                                                                                                                                          \
</span></span></span><span class="line"><span class="cl"><span class="cp">        </span><span class="cm">/* Has the extremity of the task stack ever been written over? */</span><span class="cp">                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">        if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 )                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        {                                                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB-&gt;pcTaskName );                                     \
</span></span></span><span class="line"><span class="cl"><span class="cp">        }                                                                                                                                 \
</span></span></span><span class="line"><span class="cl"><span class="cp">    }
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif </span><span class="cm">/* #if( configCHECK_FOR_STACK_OVERFLOW &gt; 1 ) */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>在任务栈初始化的时候填充已知的固定数据0xA5，除了做栈溢出检测之外，还可以作为评估线程运行时占用栈空间大小的方法。其原理是在线程运行一段时间之后，从栈的低地址统计有多少字节的0xA5得到栈空间未使用的大小（假设栈从高地址向低地址生长），用栈的总大小减去未使用的空间便可得到该任务运行时栈可能的最大使用大小。</p>
<h2 id="kernel-在什么时候检查栈溢出呢">kernel 在什么时候检查栈溢出呢？</h2>
<p>在 tasks.c 文件可以查到taskCHECK_FOR_STACK_OVERFLOW在 void vTaskSwitchContext( void )函数中被调用，也就是在任务上下文切换的时候做检查，从这点可以看出软件检查栈溢出的方式具有一定的滞后性，任务栈被破坏了并不能马上检测到问题。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span><span class="lnt">74
</span><span class="lnt">75
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">vTaskSwitchContext</span><span class="p">(</span> <span class="kt">void</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span><span class="p">(</span> <span class="n">uxSchedulerSuspended</span> <span class="o">!=</span> <span class="p">(</span> <span class="n">UBaseType_t</span> <span class="p">)</span> <span class="n">pdFALSE</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* The scheduler is currently suspended - do not allow a context
</span></span></span><span class="line"><span class="cl"><span class="cm">         * switch. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">xYieldPending</span> <span class="o">=</span> <span class="n">pdTRUE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">xYieldPending</span> <span class="o">=</span> <span class="n">pdFALSE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nf">traceTASK_SWITCHED_OUT</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cp">#if ( configGENERATE_RUN_TIME_STATS == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cp">#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>                    <span class="nf">portALT_GET_RUN_TIME_COUNTER_VALUE</span><span class="p">(</span> <span class="n">ulTotalRunTime</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="cp">#else
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>                    <span class="n">ulTotalRunTime</span> <span class="o">=</span> <span class="nf">portGET_RUN_TIME_COUNTER_VALUE</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">                <span class="cm">/* Add the amount of time the task has been running to the
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * accumulated time so far.  The time the task started running was
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * stored in ulTaskSwitchedInTime.  Note that there is no overflow
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * protection here so count values are only valid until the timer
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * overflows.  The guard against negative values is to protect
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * against suspect run time stat counter implementations - which
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * are provided by the application, not the kernel. */</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span><span class="p">(</span> <span class="n">ulTotalRunTime</span> <span class="o">&gt;</span> <span class="n">ulTaskSwitchedInTime</span> <span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="n">pxCurrentTCB</span><span class="o">-&gt;</span><span class="n">ulRunTimeCounter</span> <span class="o">+=</span> <span class="p">(</span> <span class="n">ulTotalRunTime</span> <span class="o">-</span> <span class="n">ulTaskSwitchedInTime</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">                <span class="k">else</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="nf">mtCOVERAGE_TEST_MARKER</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="n">ulTaskSwitchedInTime</span> <span class="o">=</span> <span class="n">ulTotalRunTime</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#endif </span><span class="cm">/* configGENERATE_RUN_TIME_STATS */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Check for stack overflow, if configured. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">taskCHECK_FOR_STACK_OVERFLOW</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* Before the currently running task is switched out, save its errno. */</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#if ( configUSE_POSIX_ERRNO == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">pxCurrentTCB</span><span class="o">-&gt;</span><span class="n">iTaskErrno</span> <span class="o">=</span> <span class="n">FreeRTOS_errno</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">        <span class="cm">/* Select a new task to run using either the generic C or port
</span></span></span><span class="line"><span class="cl"><span class="cm">         * optimised asm code. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">taskSELECT_HIGHEST_PRIORITY_TASK</span><span class="p">();</span> <span class="cm">/*lint !e9079 void * is used as this macro is used with timers and co-routines too.  Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */</span>
</span></span><span class="line"><span class="cl">        <span class="nf">traceTASK_SWITCHED_IN</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cm">/* After the new task is switched in, update the global errno. */</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#if ( configUSE_POSIX_ERRNO == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">FreeRTOS_errno</span> <span class="o">=</span> <span class="n">pxCurrentTCB</span><span class="o">-&gt;</span><span class="n">iTaskErrno</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">        <span class="cp">#if ( configUSE_NEWLIB_REENTRANT == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="cm">/* Switch Newlib&#39;s _impure_ptr variable to point to the _reent
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * structure specific to this task.
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
</span></span></span><span class="line"><span class="cl"><span class="cm">                 * for additional information. */</span>
</span></span><span class="line"><span class="cl">                <span class="n">_impure_ptr</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">(</span> <span class="n">pxCurrentTCB</span><span class="o">-&gt;</span><span class="n">xNewLib_reent</span> <span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="cp">#endif </span><span class="cm">/* configUSE_NEWLIB_REENTRANT */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="cm">/*-----------------------------------------------------------*/</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="栈溢出实例分析">栈溢出实例分析</h2>
<h3 id="任务控制块tcb中的与栈相关的成员介绍">任务控制块（TCB）中的与栈相关的成员介绍</h3>
<p>FreeRTOS的TCB数据结构在 tasks.c 文件中定义，源码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span><span class="lnt">73
</span><span class="lnt">74
</span><span class="lnt">75
</span><span class="lnt">76
</span><span class="lnt">77
</span><span class="lnt">78
</span><span class="lnt">79
</span><span class="lnt">80
</span><span class="lnt">81
</span><span class="lnt">82
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cm">/*
</span></span></span><span class="line"><span class="cl"><span class="cm"> * Task control block.  A task control block (TCB) is allocated for each task,
</span></span></span><span class="line"><span class="cl"><span class="cm"> * and stores task state information, including a pointer to the task&#39;s context
</span></span></span><span class="line"><span class="cl"><span class="cm"> * (the task&#39;s run time environment, including register values)
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">tskTaskControlBlock</span>       <span class="cm">/* The old naming convention is used to prevent breaking kernel aware debuggers. */</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">volatile</span> <span class="n">StackType_t</span> <span class="o">*</span> <span class="n">pxTopOfStack</span><span class="p">;</span> <span class="cm">/*&lt; Points to the location of the last item placed on the tasks stack.  THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( portUSING_MPU_WRAPPERS == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">xMPU_SETTINGS</span> <span class="n">xMPUSettings</span><span class="p">;</span> <span class="cm">/*&lt; The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="n">ListItem_t</span> <span class="n">xStateListItem</span><span class="p">;</span>                  <span class="cm">/*&lt; The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */</span>
</span></span><span class="line"><span class="cl">    <span class="n">ListItem_t</span> <span class="n">xEventListItem</span><span class="p">;</span>                  <span class="cm">/*&lt; Used to reference a task from an event list. */</span>
</span></span><span class="line"><span class="cl">    <span class="n">UBaseType_t</span> <span class="n">uxPriority</span><span class="p">;</span>                     <span class="cm">/*&lt; The priority of the task.  0 is the lowest priority. */</span>
</span></span><span class="line"><span class="cl">    <span class="n">StackType_t</span> <span class="o">*</span> <span class="n">pxStack</span><span class="p">;</span>                      <span class="cm">/*&lt; Points to the start of the stack. */</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="n">pcTaskName</span><span class="p">[</span> <span class="n">configMAX_TASK_NAME_LEN</span> <span class="p">];</span> <span class="cm">/*&lt; Descriptive name given to the task when created.  Facilitates debugging only. */</span> <span class="cm">/*lint !e971 Unqualified char types are allowed for strings and single characters only. */</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( ( portSTACK_GROWTH &gt; 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">StackType_t</span> <span class="o">*</span> <span class="n">pxEndOfStack</span><span class="p">;</span> <span class="cm">/*&lt; Points to the highest valid address for the stack. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( portCRITICAL_NESTING_IN_TCB == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">UBaseType_t</span> <span class="n">uxCriticalNesting</span><span class="p">;</span> <span class="cm">/*&lt; Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_TRACE_FACILITY == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">UBaseType_t</span> <span class="n">uxTCBNumber</span><span class="p">;</span>  <span class="cm">/*&lt; Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">UBaseType_t</span> <span class="n">uxTaskNumber</span><span class="p">;</span> <span class="cm">/*&lt; Stores a number specifically for use by third party trace code. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_MUTEXES == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">UBaseType_t</span> <span class="n">uxBasePriority</span><span class="p">;</span> <span class="cm">/*&lt; The priority last assigned to the task - used by the priority inheritance mechanism. */</span>
</span></span><span class="line"><span class="cl">        <span class="n">UBaseType_t</span> <span class="n">uxMutexesHeld</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_APPLICATION_TASK_TAG == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="n">TaskHookFunction_t</span> <span class="n">pxTaskTag</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS &gt; 0 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">void</span> <span class="o">*</span> <span class="n">pvThreadLocalStoragePointers</span><span class="p">[</span> <span class="n">configNUM_THREAD_LOCAL_STORAGE_POINTERS</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configGENERATE_RUN_TIME_STATS == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">uint32_t</span> <span class="n">ulRunTimeCounter</span><span class="p">;</span> <span class="cm">/*&lt; Stores the amount of time the task has spent in the Running state. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_NEWLIB_REENTRANT == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="cm">/* Allocate a Newlib reent structure that is specific to this task.
</span></span></span><span class="line"><span class="cl"><span class="cm">         * Note Newlib support has been included by popular demand, but is not
</span></span></span><span class="line"><span class="cl"><span class="cm">         * used by the FreeRTOS maintainers themselves.  FreeRTOS is not
</span></span></span><span class="line"><span class="cl"><span class="cm">         * responsible for resulting newlib operation.  User must be familiar with
</span></span></span><span class="line"><span class="cl"><span class="cm">         * newlib and must provide system-wide implementations of the necessary
</span></span></span><span class="line"><span class="cl"><span class="cm">         * stubs. Be warned that (at the time of writing) the current newlib design
</span></span></span><span class="line"><span class="cl"><span class="cm">         * implements a system-wide malloc() that must be provided with locks.
</span></span></span><span class="line"><span class="cl"><span class="cm">         *
</span></span></span><span class="line"><span class="cl"><span class="cm">         * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
</span></span></span><span class="line"><span class="cl"><span class="cm">         * for additional information. */</span>
</span></span><span class="line"><span class="cl">        <span class="k">struct</span>  <span class="n">_reent</span> <span class="n">xNewLib_reent</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_TASK_NOTIFICATIONS == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">ulNotifiedValue</span><span class="p">[</span> <span class="n">configTASK_NOTIFICATION_ARRAY_ENTRIES</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">        <span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="n">ucNotifyState</span><span class="p">[</span> <span class="n">configTASK_NOTIFICATION_ARRAY_ENTRIES</span> <span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* See the comments in FreeRTOS.h with the definition of
</span></span></span><span class="line"><span class="cl"><span class="cm">     * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) </span><span class="cm">/*lint !e731 !e9029 Macro has been consolidated for readability reasons. */</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">uint8_t</span> <span class="n">ucStaticallyAllocated</span><span class="p">;</span>                     <span class="cm">/*&lt; Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( INCLUDE_xTaskAbortDelay == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">uint8_t</span> <span class="n">ucDelayAborted</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">    <span class="cp">#if ( configUSE_POSIX_ERRNO == 1 )
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>        <span class="kt">int</span> <span class="n">iTaskErrno</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="p">}</span> <span class="n">tskTCB</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>这里仅介绍几个与任务栈相关的几个成员。</p>
<ul>
<li>pxTopOfStack</li>
</ul>
<p>pxTopOfStack 存储任务栈的栈顶地址。</p>
<ul>
<li>pxStack</li>
</ul>
<p>pxStack 存储任务栈空间的起始地址，也就栈的最低地址。</p>
<ul>
<li>pxEndOfStack</li>
</ul>
<p>pxEndOfStack 存储任务栈空间的结束地址，也就是栈的最高地址。</p>
<h3 id="ec200u-栈溢出导致memory-dump-分析">EC200U 栈溢出导致memory dump 分析</h3>
<p>EC200U模组（Unisoc 8910Dm）的open sdk使用的RTOS是FreeRTOS，模组死机时导出的memory dump 文件可以通过trace 32工具分析，以下是一个栈溢出的dump实例。</p>
<p>通过trace 32加载dump文件，首先看到死机前函数栈帧里有调用vApplicationStackOverflowHook函数，说明kernel在任务上下文切换时检测到了栈溢出问题。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled.3ljv1vf7grq0.png" alt=""  />
</p>
<p>查看线程列表，正在执行的task是rm_app_start这个线程，也就是在这个任务死机了。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-1.vqzir71hzpc.png" alt=""  />
</p>
<p>接着查看rm_app_start的任务控制块数据，其中有三个数据需要关注：</p>
<ol>
<li>TCB的起始地址→0x80bf78b8</li>
</ol>
<p>还记得前面介绍的FreeRTOS栈存储结构吗？是按照TCB+stack方式存储的。EC200U的栈增长方向是按高地址向地址增长的。</p>
<p>TCB+stack紧挨着，所以知道了TCB的起始地址也就知道了栈的结束地址（最高地址，栈底）。</p>
<ol start="2">
<li>栈顶地址 pxTopOfStack →0x80bf7504</li>
</ol>
<p>从栈底到栈顶这段空间存储了函数调用的地址，对分析函数调用关系挺有帮助。</p>
<ol start="3">
<li>栈的起始地址 pxStack → 0x80bf74a8</li>
</ol>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-2.3jp3qqf5xdc0.png" alt=""  />
</p>
<p>可以使用trace 32查看这些地址的内存数据，比如，先来看TCB的起始地址→0x80bf78b8的数据，下图右箭头所指的地方就是TCB的起始地址，我们可以看到开始的4个字节数据是0x80bf7504，也就是存储着pxTopOfStack的地址。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-3.3bfxtptmgl80.png" alt=""  />
</p>
<p>接着再看栈的最低地址0x80bf74a8的数据，通过前面栈溢出的检测机制介绍可以知道，栈在初始化的时候会填充默认值0xA5，然后kernel在任务上下文切换的时候会去比较栈起始地址开始的16个字节是不是保持着默认值0xA5，如果初始值被篡改，则会触发vApplicationStackOverflowHook。从下图的数据来看，栈最低地址0x80bf74a8的数据已经被破坏了。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-4.2hud1c5k2rs0.png" alt=""  />
</p>
<p>我们再找一个正常的task做对比，也就是下图的IDLE task。同样的方法，先查看TCB数据，找到栈的起始地址0x80992500, 栈的结束地址0x809934f8，栈顶地址0x8099342c。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-5.12q9ltcipo8g.png" alt=""  />
</p>
<p>查看栈的起始地址0x80992500的内存数据，开始的16个字节都是0xA5，说明栈空间够，没有溢出，且还有一段未曾使用的空间。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-6.mjuoih6h8hc.png" alt=""  />
</p>
<p>再看栈顶地址0x8099342c的数据，在0x8099342c之上有一段不是0xA5的数据，说明曾经有发生过函数调用，且成功返回了。栈保存的函数调用信息在出栈之后并不会主动清除，只会在下次入栈的时候数据覆盖，由此我们也可以通过这个信息查看这个任务曾经调用过什么函数。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-7.v4jbtnlhacw.png" alt=""  />
</p>
<p>从栈顶到栈底这段空间保存在函数调用的信息，我们可以使用trace 32来解析函数调用关系。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-8.5d2amkqv3u80.png" alt=""  />
</p>
<p>在栈顶与栈底这段内存数据里，我先找到其中的函数地址，再通过设置断点的方式根据地址找到函数名。比如下图依次找到两个程序地址：0x6014B6CF 和 0x6014B651，通过设置断点查到对应的函数名prvIdleTask和xTaskResumeAll，再根据栈的增长方向可以判断是 prvIdleTask  调用→ xTaskResumeAll。</p>
<p><img loading="lazy" src="https://github.com/hacperme/picx_hosting/raw/master/images/qns/Untitled-9.67ablvydo580.png" alt=""  />
</p>
<p>把栈空间所有程序地址找出来，按照同样的方法就能找到这个任务的函数调用痕迹了：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B795</span>	<span class="n">vTaskSwitchContext</span><span class="err">\</span><span class="mi">51</span><span class="o">+</span><span class="mh">0x1</span>
</span></span><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B795</span>	<span class="n">vTaskSwitchContext</span><span class="err">\</span><span class="mi">51</span><span class="o">+</span><span class="mh">0x1</span>
</span></span><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B795</span> 	<span class="n">vTaskSwitchContext</span><span class="err">\</span><span class="mi">51</span><span class="o">+</span><span class="mh">0x1</span>
</span></span><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B651</span>	<span class="n">xTaskResumeAll</span><span class="err">\</span><span class="mi">88</span><span class="o">+</span><span class="mh">0x1</span>
</span></span><span class="line"><span class="cl"><span class="mi">6014</span><span class="n">B6CF</span> 	<span class="n">prvIdleTask</span><span class="err">\</span><span class="mi">104</span><span class="o">+</span><span class="mh">0x5</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>有一个规律，EC200U的程序地址都是60XXX开头的，比如6014B6CF、6014B651、6014B795等，可以按照这个规律排查是否是程序地址。当然，如果你能确定内存布局，在栈的数据里判断是否是程序地址就不会出错了。</p>
<h2 id="总结">总结</h2>
<ol>
<li>从任务栈的结构的角度分析，便于理解栈溢出产生的原因和栈溢出导致的两类问题。</li>
<li>FreeRTOS提供两种软件栈溢出检测方式，两种方法各有优缺点，且不一定能够在任何时候都检测到所有类型的栈溢出。kernel 是在任务的上下文切换的时候进行栈溢出检查，通过软件检测栈溢出的方法，在发现问题上具有一定的“滞后性”。</li>
<li>在memory dump中分析栈问题有几个关键的信息点需要掌握：栈的生长方向、栈的起止地址、栈顶地址，栈的保护标记（栈空间初始化设置的固定值）以及通过栈空间的数据分析函数调用关系。</li>
</ol>
<h2 id="参考资料">参考资料</h2>
<ol>
<li><a href="https://www.notion.so/FreeRTOS-stacks-and-stack-overflow-checking-df93402e952b4aa79b381d28693b2dc5">FreeRTOS - stacks and stack overflow checking</a></li>
<li><a href="https://github.com/FreeRTOS/FreeRTOS-Kernel">https://github.com/FreeRTOS/FreeRTOS-Kernel</a></li>
<li><a href="https://www.notion.so/Zephyr-cf671a64822a4bd7be4e200ba9240676">Zephyr使用的堆栈保护技术</a></li>
</ol>
]]></content:encoded>
    </item>
  </channel>
</rss>
