《嵌入式软件的时间分析》读书活动:3 第三章读书笔记-操作系统
<p id="u1ca3378f">第三章讲解了嵌入式操作系统相关的知识,但是只限于调度器。</p><p id="ud7ae0cd2">章节如下:</p>
<pre>
<code>第3章 操作系统 33
3.1 无操作系统: 无限循环加中断 33
- 3.1.1 周期中断的实现示例 33
- 3.1.2 轮询——无中断地实现 34
- 3.1.3 可扩展性 36
3.2 OSEK/VDX 36
- 3.2.1 任务 36
- 3.2.2 中断 39
- 3.2.3 ErrorHook 39
- 3.2.4 基本调度策略 39
3.3 多任务: 协作与抢占 41
- 3.3.1 两种追踪的图示说明(示例 1) 41
- 3.3.2 堆栈消耗(示例 2) 43
- 3.3.3 确保数据一致性 45
- 3.3.4 协作式多任务处理的限制 45
- 3.3.5 为减少OS_Schedule()调用而可实施的优化 47
- 3.3.6 总结 47
3.4 POSIX 47
- 3.4.1 进程 49
- 3.4.2 线程 49
- 3.4.3 POSIX 线程状态图 51
- 3.4.4 调度策略 52
3.5 总结 52</code></pre>
<h1 data-lake-index-type="2" id="GDZOP">1. 无操作系统</h1>
<p id="u95a2cfc4">只要系统的调度非常简单,并且对操作系统没有明确的要求,则可以不使用操作系统,操作系统会代码额外的资源消耗(内存、运行时间、堆栈,存储器等)。</p>
<p id="u12863b95">不使用操作系统有如下两种方式:</p>
<ol>
<li data-lake-index-type="0" id="ucbac19d7">周期中断实现:一个定时器中断ISR用来做周期任务,周期任务在中断handler中执行,main()中的while(1)死循环用来做一些其他不需要周期性执行的任务;</li>
<li data-lake-index-type="0" id="u03932b18">轮询-无中断实现:需要定时器中断,但是不需要定时器中断的handler,在main()中的while(1)中查询中断溢出标志,如果中断溢出,表示定时时间到了,然后开始执行周期任务。</li>
</ol>
<p id="ub19e7b76">上述第二种方式的周期任务并不一定能及时被执行,因为while(1)中还有其他不需要周期执行的代码可能会影响周期性代码执行的实时性,实际执行实际时间和计划执行时间之间的差值称为抖动。</p>
<h1 data-lake-index-type="2" id="TxyJJ">2. OSEK/VDX</h1>
<p id="u60d5bd04">OSEK/VDX是一种用于汽车电子领域的开放式系统和相应接口标准。</p>
<p id="u2c2e3f48">下图为OSEK/VDX规范中定义的操作系统状态:</p>
<div style="text-align: center;"></div>
<div style="text-align: center;">
<div style="text-align: center;"></div>
<p> </p>
</div>
<p id="u31df5e40"> </p>
<h1 data-lake-index-type="2" id="qRq0j">3. 多任务:协作与抢占</h1>
<p id="u76b75ae1">在实时操作系统中,OSEK/VDX通过支持<strong>可抢占</strong>和<strong>不可抢占</strong>的属性来实现的目标通常称为<strong>抢占式</strong>和<strong>协作式</strong>多任务处理。</p>
<p id="ub2fd499a">下面是我对协作式和抢占式多任务的总结:</p>
<ul>
<li data-lake-index-type="0" id="u8b95ba65">协作式:任务之间不会抢占,高优先级的任务不会打断正在执行的低优先级的任务,而是等待低优先级的任务执行完成或者是主动放弃CPU之后才会执行;</li>
<li data-lake-index-type="0" id="uf86bad58">抢占式:任务之间存在抢占,高优先级的任务会打断正在执行的低优先级的任务,当高优先级的任务执行完成之后再返回之前的低优先级的任务继续执行。</li>
</ul>
<h2 data-lake-index-type="2" id="PvKXK">3.1. 堆栈消耗</h2>
<p id="u038ecb5d">书中通过具体示例讲解了协作式多任务和抢占式多任务对堆栈的消耗,由于协作式多任务不存在抢占,所以堆栈消耗比抢占式少很多;而且对于抢占式多任务,由于不知道任务什么时候被抢占,所以对堆栈的计算也很难。</p>
<p id="uefa68846">我的理解是,抢占式多任务对堆栈的消耗有点类似于函数嵌套,当抢占的任务较多的时候,一级一级嵌套导致堆栈消耗巨大,所以就需要对任务的执行时间和周期进行严格的设计,避免这种情况。</p>
<h2 data-lake-index-type="2" id="aBf1C">3.2. 数据一致性</h2>
<p id="u598cff38">抢占式多任务:需要额外的运行时间和RAM空间来保证任务的数据一致性,因为需要创建数据副本,在任务开始和节数的时候进行同步。</p>
<p id="u78a29862">协同式多任务:不存在任务抢占,所以不需要考虑数据一致性,可以节省大量运行时间和RAM空间。</p>
<h2 data-lake-index-type="2" id="dH84z">3.3. 优缺点</h2>
<p id="u993ff0b8">协作式多任务的优点:</p>
<ul>
<li data-lake-index-type="0" id="u6a2ec59c">由于进行任务变更,避免了所有典型的实时问题。</li>
<li data-lake-index-type="0" id="u893f9fa8">在正确配置的情况下,可以省去大部分确保数据一致性的工作,减少执行时间和内存需求。</li>
<li data-lake-index-type="0" id="uc4bb8591">堆栈使用较少。</li>
</ul>
<p id="u2cb17fe0">虽然协作式多任务优点很多,但是也存在一个致命的缺点,就是任务的实时性得不到保障,可能存在高优先级的任务不能得到及时的执行,需要通过合理的系统设计来解决这样的问题,对系统设计要求比较高。</p>
<p id="u8909315c">抢占式多任务处理的优点:</p>
<ul>
<li data-lake-index-type="0" id="ubc5d043d">启动优先级较高的任务时,确定的延迟时间较短,带来的抖动远小于协作式多任务。</li>
<li data-lake-index-type="0" id="u20651677">由于无须限制函数的执行时间,因此不需要因为过长的执行时间来分割函数(这是是协作式方法所必需的)。</li>
</ul>
<h1 data-lake-index-type="2" id="pQzPT">4. POSIX</h1>
<p id="u9e6f89aa">POSIX 标准是一整套IEEE标准,其核心是描述<strong>应用程序与操作系统之间的接口</strong>。</p>
<div style="text-align: center;"></div>
<p id="uf3870035">POSIX有PSE51、PSE52、PSE53以及PSE54功这几个版本,本书中讲解了各个版本的特性,可以在书中找到相关描述(page 48),做个简单说明:</p>
<ul>
<li data-lake-index-type="0" id="uceb37864">PSE51:最小-实时系统框架;</li>
<li data-lake-index-type="0" id="u617e8374">PSE52:实时控制器-系统框架;</li>
<li data-lake-index-type="0" id="uc4c658cb">PSE53:专用-实时系统框架;</li>
<li data-lake-index-type="0" id="ucdf830bd">PSE54:多用途-实时系统框架。</li>
</ul>
<h2 data-lake-index-type="2" id="U4acp">4.1. 进程和线程</h2>
<p id="uedd04434">进程:</p>
<p id="u8bed4c6f">进程(Process)是利用自己的数据以及执行所需操作系统的数据执行的程序,包括状态信息、访问权限信息等。</p>
<p id="ue8362ef3">书中这句话读下来还是云里雾里的,网上找到一般比较好的讲解LINUX进程的文章,可以看看:<a data-href="https://www.bookstack.cn/read/understand_linux_process/process_basic-what_is_process.md" href="https://www.bookstack.cn/read/understand_linux_process/process_basic-what_is_process.md" target="_blank">https://www.bookstack.cn/read/understand_linux_process/process_basic-what_is_process.md</a></p>
<p id="u1e7bf51c">PSE53和PSE54允许同时执行多个进程(程序)。每个进程都有一个虚拟内存区域,其他进程无法访问该虚拟内存区域。此外,进程还可以创建被称为“子进程”的新进程。</p>
<p id="uabbbb0c6">线程:</p>
<p id="uf0413b78">机器指令的处理在程序中就像线程(Thread)一样,更准确地说,就像单线程一样,指令将按顺序依次执行。每个进程都从单线程或主线程开始。如果要并行执行活动,即分割程序流,必须创建更多线程,这叫作多线程处理。这些线程都可以访问进程的虚拟内存。</p>
<p id="udd3f2c48">线程中的数据一致性问题:</p>
<p id="ubb8de41c">数据一致性问题在线程中也存在,可以通过<strong>互斥锁</strong>(Mutex)来保护共享的数据。</p>
<h2 data-lake-index-type="2" id="jhegF">4.2. 线程状态图</h2>
<div style="text-align: center;"></div>
<ol>
<li data-lake-index-type="0" id="uf1677ce6">当程序启动时,会创建一个进程,所关联的主线程被设置为“新建”状态。</li>
<li data-lake-index-type="0" id="uc538ca30">操作系统执行基本初始化之后,线程将变更为“就绪”状态,线程将等待其代码执行。</li>
<li data-lake-index-type="0" id="u2e5b718d">开始执行时,会为线程分配“运行”状态。</li>
<li data-lake-index-type="0" id="uf440742b">可能有一些原因会导致其状态变更为“等待”(休眠或阻塞)。</li>
<li data-lake-index-type="0" id="uef8dbe8f">优先级最高的正等待处理的线程将会恢复“运行”状态。</li>
<li data-lake-index-type="0" id="uff5da09f">线程结束后会变成“完成”状态。</li>
</ol>
<p id="ua5dafbaa">发现书中可能出现的笔误:</p>
<div style="text-align: center;"></div>
<h2 data-lake-index-type="2" id="ywasO">4.3. 多线程调度策略</h2>
<p id="u30c90ca1">当多个线程处于“就绪”状态时,调度策略定义了一组规则,用于确定将执行哪些可用于处理的线程。以下仅列出了最重要的一些调度策略:</p>
<ul>
<li data-lake-index-type="0" id="uff30a9c1">按优先级选择:与抢占式OSEK/VDX类似,首先执行优先级最高的线程。</li>
<li data-lake-index-type="0" id="uc44fa5e8">时间片流程(即轮询):每个线程都将在定义的时间段内进行处理,然后再轮到下一个线程。</li>
<li data-lake-index-type="0" id="u88c74b19">先进先出(FIFO):在这种情况下,“先进先出”是指按切换到“就绪”状态的顺序处理线程。</li>
</ul>
<h1 data-lake-index-type="2" id="y78dh">5. 总结</h1>
<p id="ucb6b6b16">本章首先介绍了不使用操作系统的优势,以及使用操作系统的优势,还介绍了OSEK/VDX和POSIX这两种规则的操作系统,都是汽车行业会使用的。对协作式和抢占式多任务做了利弊分析,最后讲解了POSIX中的进程和线程。</p>
<p id="u893d287f">本章节介绍的相对简单,因为一个学习一个系统不是一个短短的章节就能说明清楚的,作者只是找出了系统中最重要的部分做了分析说明,相对系统进一步掌握,还需要专门的查看相关文档。</p>
页:
[1]