<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Embedded on 施星宇</title><link>https://www.hugoshih.com/zh-tw/categories/embedded/</link><description>Recent content in Embedded on 施星宇</description><generator>Hugo -- gohugo.io</generator><language>zh-Hant-TW</language><lastBuildDate>Sat, 15 Aug 2026 19:48:32 +0800</lastBuildDate><atom:link href="https://www.hugoshih.com/zh-tw/categories/embedded/index.xml" rel="self" type="application/rss+xml"/><item><title>關於 Mutex、Semaphore 與 Spinlock</title><link>https://www.hugoshih.com/zh-tw/p/%E9%97%9C%E6%96%BC-mutexsemaphore-%E8%88%87-spinlock/</link><pubDate>Sat, 15 Aug 2026 19:48:32 +0800</pubDate><guid>https://www.hugoshih.com/zh-tw/p/%E9%97%9C%E6%96%BC-mutexsemaphore-%E8%88%87-spinlock/</guid><description>&lt;img src="https://i.imgur.com/hnF8mC8.jpeg" alt="Featured image of post 關於 Mutex、Semaphore 與 Spinlock" /&gt;&lt;h2 id="起源"&gt;起源&#10;&lt;/h2&gt;&lt;p&gt;今年年初在準備一些嵌入式系統相關的面試時，又遇到了 &lt;code&gt;Mutex&lt;/code&gt;、&lt;code&gt;Semaphore&lt;/code&gt; 跟 &lt;code&gt;Spinlock&lt;/code&gt; 這三個東西，其實之前在面一些大廠時就有被問過，像是某薯條公司或是某水果公司，但當時沒什麼準備，所以答得也不是很好，所以趁著準備面試時再來複習一下，有修過作業系統的人對這三個名詞應該不陌生，只要碰到 RTOS、Linux Kernel 或是多執行緒程式之後多少都會遇到&lt;/p&gt;&#10;&lt;p&gt;其實如果沒有特別去記，大多人的印象都是：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;「Mutex 不就是鎖嗎？」&lt;/li&gt;&#10;&lt;li&gt;「Semaphore 好像也是鎖？」&lt;/li&gt;&#10;&lt;li&gt;「那 Spinlock 又是什麼鬼，為什麼已經有 Mutex 還要再多一個 Spinlock？」&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;而且三個東西印象中都像是在做同一件事情：&lt;strong&gt;不要讓大家同時碰同一個東西&lt;/strong&gt;，所以這篇一樣想用一個超級白話的方式把這三個東西整理起來，以免哪天又忘記 Mutex、Semaphore 跟 Spinlock 到底差在哪裡，就可以快速回來複習一下&lt;/p&gt;&#10;&lt;h2 id="所以為什麼會需要這些東西"&gt;所以為什麼會需要這些東西？&#10;&lt;/h2&gt;&lt;p&gt;我們先不要管 Mutex、Semaphore 跟 Spinlock 這些東西各自在幹嘛，先設想一個情境好了，如果今天寫的是一個超級簡單的 Bare Metal Firmware：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;span class="lnt"&gt;7&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;main()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 讀 Sensor&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 控制 Motor&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 更新 LED&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;CPU 就這樣一件事情一件事情照順序做下去對吧，其實很多時候根本不會需要到什麼 Mutex 或 Semaphore，這就是我們在 MCU 上最常見的寫法，也不太會有什麼問題，因為同一個時間基本上就只有現在這段 Code 在執行，可以自由使用系統裡的資源&lt;/p&gt;&#10;&lt;p&gt;但如果我們今天需要系統有多個執行緒分別去跑不同的邏輯，開始使用 RTOS，例如 Zephyr、FreeRTOS、ThreadX，那情況就不一樣了&lt;/p&gt;&#10;&lt;p&gt;我們假設系統裡面有三個 Thread：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Sensor Thread&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Motor Thread&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;UI Thread&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;通常 MCU 只有一個 CPU Core，而 RTOS Scheduler 會在不同的 Thread 之間快速切換，例如說系統裡可能同時有用來收集 Sensor 資料的 Sensor Thread、用來控制馬達狀態的 Motor Thread 跟用來顯示裝置狀態的 UI Thread，Scheduler 會讓它們輪流使用 CPU，雖然這些 Thread 並不是真的同時執行，但只要其中一個 Thread 做到一半時切換到另一個 Thread，另一個 Thread 就有機會碰到還在被前一個 Thread 使用的資源，在 Single Core 上，前一個 Thread 雖然已經暫停執行，但資源可能仍處於使用中的狀態，而在 Multicore 上則真的可能發生同一個資源被兩個跑在不同 Core 上的 Thread 同時存取，所以問題就來了：&lt;strong&gt;如果兩個 Thread 同時想要操作同一個東西怎麼辦？&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;例如 Motor Thread 跟 UI Thread 可能同時修改 Motor 的控制狀態，或是 Sensor Thread 跟其他 Thread 同時存取某一塊 Shared Memory，這時候如果沒有適當的保護就會出問題&lt;/p&gt;&#10;&lt;p&gt;我們先從一個最簡單的狀況開始，假設有一個很單純的變數：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;看起來只是一行，但 CPU 實際上通常需要先讀取 &lt;code&gt;counter&lt;/code&gt;、加一，再把結果寫回 Memory&lt;/p&gt;&#10;&lt;p&gt;如果 Thread A 跟 Thread B 都在差不多的時間讀到 &lt;code&gt;counter = 10&lt;/code&gt;，它們最後就可能各自寫回 &lt;code&gt;11&lt;/code&gt;，明明執行了兩次 &lt;code&gt;counter++&lt;/code&gt;，結果卻只增加一次，這個大家在作業系統課程裡應該都有學過，這就是很典型的 Race Condition，而 Mutex、Semaphore、Spinlock 這些東西，本質上就是為了解決這類 &lt;strong&gt;Concurrency / Synchronization&lt;/strong&gt; 問題而存在的&lt;/p&gt;&#10;&lt;p&gt;嚴格來說，並不是說「Bare Metal 就絕對不會有這種 Race Condition」，實際上只要有 Interrupt、Multicore 或其他非同步的 Execution Context，其實 Bare Metal 一樣可能碰到 Synchronization 問題，只是說一般在嵌入式韌體開發中，這些搶佔情形比較常出現在開始使用 RTOS、Linux 或其他有 Thread / Scheduler 的系統之後，即使只有單核心 CPU，只要系統存在 Preemption，一個 Thread 在 Critical Section 中被另一個 Thread 搶走 CPU，一樣可能產生 Race Condition，Linux Kernel 的文件也特別指出，即使只有一顆 CPU，Preemption 仍然可以造成類似的 Race Condition（&lt;a class="link" href="https://docs.kernel.org/next/kernel-hacking/locking.html" target="_blank" rel="noopener"&#10; &gt;Linux Kernel Documentation — Locking&lt;/a&gt;）&lt;/p&gt;&#10;&lt;p&gt;所以回到 Mutex、Semaphore 跟 Spinlock，這些可以理解為是作業系統為了管理這種搶佔狀況而提供的同步機制，也就是說如果我們今天只是用 Bare Metal，那通常就沒有 RTOS 直接提供的現成 Lock 可以用啦，如果需要就要自己在系統裡去刻出來了&lt;/p&gt;&#10;&lt;p&gt;OK，所以知道問題從哪裡來之後，接下來就看看這三種東西到底分別在幹嘛，以及想解決什麼問題吧&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="mutex這個東西現在是我的你們先不要碰"&gt;Mutex：這個東西現在是我的，你們先不要碰&#10;&lt;/h2&gt;&lt;p&gt;先從最好理解的 Mutex 開始，Mutex 全名是 &lt;strong&gt;Mutual Exclusion&lt;/strong&gt;，從名字就差不多知道它想做什麼：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;同一個時間，只允許一個 Thread 進來&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;假設 Motor Thread 跟 UI Thread 都可能修改同一顆 Motor 的狀態，我們不希望其中一邊正準備把轉速設成 1000 RPM，另一邊突然把它改成 200 RPM，兩個 Thread 一起修改 Driver State 或 Hardware Register，最後控制結果很容易亂掉而且出現不可預測的隨機狀態，很可怕，這時候 Mutex 就上場了&lt;/p&gt;&#10;&lt;p&gt;Thread 在操作 Motor 之前要先取得 Mutex，拿到之後，其他 Thread 就不能進入同一段程式，這段只有 Lock 持有者能執行的區域，就是作業系統課程裡常聽到的 &lt;strong&gt;Critical Section&lt;/strong&gt;&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;mutex_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;motor_mutex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;motor_set_speed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;motor_set_direction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FORWARD&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;mutex_unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;motor_mutex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;等 Thread A 操作完 Motor 並 Unlock 之後，Thread B 才能取得 Mutex 繼續執行，這樣兩個 Thread 就不會同時碰同一個硬體資源了，Zephyr 對 Mutex 的建議用途也是讓多個 Thread 互斥地存取同一個資源，像是保護一個 Physical Device（&lt;a class="link" href="https://docs.zephyrproject.org/latest/kernel/services/synchronization/mutexes.html" target="_blank" rel="noopener"&#10; &gt;Zephyr Project Documentation — Mutexes&lt;/a&gt;）&lt;/p&gt;&#10;&lt;p&gt;當然，Mutex 保護的不一定是硬體，也可以是 Shared Memory、Linked List、File、I2C Bus、SPI Bus、UART 或 Driver State，總之呢，只要某個東西&lt;strong&gt;同一時間只能讓一個 Thread 操作&lt;/strong&gt;，第一個可以想到的通常就是 Mutex&lt;/p&gt;&#10;&lt;p&gt;這時候一定有人會好奇說，那如果 Mutex 已經被別人拿走，系統又是怎麼調度 Thread B 呢？&lt;/p&gt;&#10;&lt;p&gt;其實做法很直接，RTOS 會把 Thread B 從 Running State 移到 Blocked State，接著讓 Scheduler 從 Ready Queue 裡挑選其他可以執行的 Thread，例如 Sensor Thread，這段期間 Thread B 不會一直檢查 Mutex，也不會繼續消耗 CPU&lt;/p&gt;&#10;&lt;p&gt;等 Thread A 呼叫 &lt;code&gt;mutex_unlock()&lt;/code&gt;，RTOS 再把 Thread B 移回 Ready State，如果 Thread B 的 Priority 比目前執行中的 Thread 高，就可能直接搶占 CPU，否則就留在 Ready Queue 等待下一次調度，所以這也是 Mutex 跟後面 Spinlock 很重要的一個差別：&lt;strong&gt;Mutex 等不到的話，可以睡&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;一個實際的例子是 Linux Kernel 裡的 Mutex 就屬於 Sleeping Lock，拿不到 Mutex 時，Task 可以 Suspend，讓 CPU 去執行其他工作（&lt;a class="link" href="https://docs.kernel.org/next/kernel-hacking/locking.html" target="_blank" rel="noopener"&#10; &gt;Linux Kernel Documentation — Locking&lt;/a&gt;）&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="semaphore我不是在鎖東西我是在數還有幾個"&gt;Semaphore：我不是在鎖東西，我是在數「還有幾個」&#10;&lt;/h2&gt;&lt;p&gt;接下來來說說 Semaphore，中文通常翻作「信號量」，這個名字第一次看到其實不太容易懂，其實可以把它想成一個裝著 Token 的盒子，假設盒子裡有三個 Token，Semaphore Count 就是 &lt;code&gt;3&lt;/code&gt;，每當 Thread &lt;code&gt;Take()&lt;/code&gt; 一個 Token，Count 就減一；有人 &lt;code&gt;Give()&lt;/code&gt; 一個，而且 Count 還沒有到上限時，Count 就加一，所以 Semaphore 最核心的東西其實就是：&lt;strong&gt;一個 Counter&lt;/strong&gt;（&lt;a class="link" href="https://docs.zephyrproject.org/latest/kernel/services/synchronization/semaphores.html" target="_blank" rel="noopener"&#10; &gt;Zephyr Project Documentation — Semaphores&lt;/a&gt;）&lt;/p&gt;&#10;&lt;p&gt;如果 Count 一開始是 &lt;code&gt;3&lt;/code&gt;，Thread A、B、C 各自拿走一個之後，Count 就會變成 &lt;code&gt;0&lt;/code&gt;，這時 Thread D 再呼叫 &lt;code&gt;Take()&lt;/code&gt;，就只能等到有人 &lt;code&gt;Give()&lt;/code&gt; 一個回來，這種可以記錄多個 Token 的 Semaphore，就叫做 &lt;strong&gt;Counting Semaphore&lt;/strong&gt;，如果 Count 只有 &lt;code&gt;0&lt;/code&gt; 或 &lt;code&gt;1&lt;/code&gt;，則稱作 &lt;strong&gt;Binary Semaphore&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;看到這邊可能會冒出一個問題：「等等，Binary Semaphore 最大就是 1，那不就跟 Mutex 一樣嗎？」&lt;/p&gt;&#10;&lt;p&gt;沒錯，表面看起來真的很像，但兩者想表達的概念不太一樣：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Mutex 在意的是 &lt;strong&gt;Ownership&lt;/strong&gt;：這個 Resource 現在是誰的？&lt;/li&gt;&#10;&lt;li&gt;Semaphore 在意的是 &lt;strong&gt;Count / Signal&lt;/strong&gt;：現在有幾個 Resource，或者發生了幾次 Event？&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Mutex 通常有明確的 Owner，也就是哪一個 Thread Lock，就應該由那一個 Thread Unlock，而 Semaphore 則不一定有這種 Ownership 關係，以 FreeRTOS 為例，Mutex 額外具有 Priority Inheritance，而 Binary Semaphore 沒有，因此官方也建議 Resource Mutual Exclusion 與 Synchronization 分別使用不同的 Primitive（&lt;a class="link" href="https://www.freertos.org/Documentation/02-Kernel/02-Kernel-features/02-Queues-mutexes-and-semaphores/04-Mutexes" target="_blank" rel="noopener"&#10; &gt;FreeRTOS Documentation — Mutexes&lt;/a&gt;）&lt;/p&gt;&#10;&lt;p&gt;這個差別看起來還是有點抽象，所以我們直接來看一個嵌入式很常見的例子吧&lt;/p&gt;&#10;&lt;p&gt;假設今天有一顆 Sensor 不斷取樣，並且把資料暫存在自己的 FIFO，當 FIFO 裡的資料到達某個 Threshold，Sensor 就拉起 Interrupt，通知 MCU：「我這邊累積了一些資料，記得來讀取喔」&lt;/p&gt;&#10;&lt;p&gt;MCU 進入 ISR 之後，當然可以直接讀 Sensor Register、搬出 FIFO 資料，再做 Filter 與後續運算，但通常不會想這樣做，因為 ISR 最好越短越好，比較常見的做法，是讓 ISR 只負責 &lt;code&gt;Give&lt;/code&gt; Semaphore，再由 Sensor Thread 執行真正耗時間的工作：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Sensor Interrupt -&amp;gt; ISR -&amp;gt; Give Semaphore -&amp;gt; Sensor Thread -&amp;gt; Read FIFO&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;概念上，ISR 只做很少的事情，下面是方便理解的通用寫法，實際使用時仍要換成該 RTOS 允許在 ISR 中呼叫的 API：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sensor_isr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;semaphore_give&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sensor_sem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;平常 Sensor Thread 則停在 &lt;code&gt;semaphore_take()&lt;/code&gt; 等待通知：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;span class="lnt"&gt;7&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;semaphore_take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;sensor_sem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;read_sensor_fifo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;process_sensor_data&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;沒有 Interrupt 時，Semaphore Count 是 &lt;code&gt;0&lt;/code&gt;，Sensor Thread 會保持在 Blocked State，不需要一直輪詢 Sensor 有沒有資料，當 Interrupt 發生後，ISR 將 Count 從 &lt;code&gt;0&lt;/code&gt; 加到 &lt;code&gt;1&lt;/code&gt;，等待中的 Sensor Thread 就會被喚醒；它 &lt;code&gt;Take&lt;/code&gt; 掉這個 Token，再去讀取 FIFO&lt;/p&gt;&#10;&lt;p&gt;這樣分工就很清楚：&lt;strong&gt;ISR 負責通知，Thread 負責真正做事情&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Zephyr 官方文件也把這列為 ISR Offload Work 的典型方法：ISR 可以透過 Semaphore 等 Kernel Object 去 Signal 一個 Helper Thread，把比較耗時間的工作留到 Thread Context 執行（&lt;a class="link" href="https://docs.zephyrproject.org/latest/kernel/services/interrupts.html" target="_blank" rel="noopener"&#10; &gt;Zephyr Project Documentation — Interrupts&lt;/a&gt;）&lt;/p&gt;&#10;&lt;p&gt;所以從這個例子就很容易看出 Mutex 與 Semaphore 的語意差別：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Mutex 比較像：「廁所有人了，你先不要進來」&lt;/li&gt;&#10;&lt;li&gt;Semaphore 比較像：「現在有一件事情要處理」&lt;/li&gt;&#10;&lt;li&gt;Counting Semaphore 則可以表示：「現在累積有三件事情要處理」&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;這邊還有個細節是，如果使用 Binary Semaphore，它的 Count 只會是 &lt;code&gt;0&lt;/code&gt; 或 &lt;code&gt;1&lt;/code&gt;，假設 Sensor Thread 還沒開始處理，Sensor 又連續觸發了三次 Interrupt，最後的 Count 可能仍然只有 &lt;code&gt;1&lt;/code&gt;，也就是說，Binary Semaphore 比較接近「有事情發生了」，而不是精確記錄「事情發生了三次」&lt;/p&gt;&#10;&lt;p&gt;如果你的系統需要每一次 Event 都不能遺失，就要考慮使用 Counting Semaphore 並設定足夠的 Count 上限，甚至改用 Queue，Semaphore 可以代表可用的 Resource 數量，也可以代表等待處理的 Event 數量，會比 Mutex 更泛用&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="spinlock反正應該馬上就好了我站在這邊等"&gt;Spinlock：反正應該馬上就好了，我站在這邊等&#10;&lt;/h2&gt;&lt;p&gt;最後來看看 Spinlock 吧，前面說過，Thread 拿不到 Mutex 時可以進入 Blocked State，讓 RTOS 的調度策略去把 CPU 交給其他 Thread，這聽起來很合理對吧，但 &lt;strong&gt;Context Switch 其實不是免費的&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;系統需要先保存目前 Thread 的 Register、Program Counter、Stack Pointer 等 Execution Context，再載入另一個 Thread 的 Context，這些動作當然都有成本，假設 CPU 1 上的 Thread B 正拿著 Lock，但它只剩下 &lt;code&gt;shared_data++&lt;/code&gt; 這種非常短的操作，可能再幾個 CPU Cycle 就會釋放，這時如果 CPU 0 上的 Thread A 立刻 Block、儲存 Context、重新排程，忙了一圈之後，Thread B 搞不好早就把 Lock 放掉了&lt;/p&gt;&#10;&lt;p&gt;所以另外一個想法就出現了：&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;既然我猜它馬上就會用完，那我乾脆站在這裡等&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;這就是 Spinlock，概念上它會不斷檢查 Lock 是否已經被釋放，也就是直接佔住 CPU 做 Busy Waiting：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock_is_taken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Busy waiting&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;等待期間 Thread 不會進入 Sleep State，它所在的 CPU Core 也不會去執行其他 Thread，而是一直嘗試取得 Lock，它一直在原地「轉」，所以才叫 &lt;strong&gt;Spin Lock&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Linux Kernel 對 Spinlock 的描述也是這個概念：拿不到 Spinlock 就持續嘗試；相較之下，拿不到 Mutex 則可以 Suspend，讓 CPU 去做其他事情（&lt;a class="link" href="https://docs.kernel.org/next/kernel-hacking/locking.html" target="_blank" rel="noopener"&#10; &gt;Linux Kernel Documentation — Locking&lt;/a&gt;）&lt;/p&gt;&#10;&lt;p&gt;那 Spinlock 不是超級浪費 CPU 嗎？沒錯，所以 Spinlock 有一個非常重要的前提：&lt;strong&gt;Critical Section 必須非常短&lt;/strong&gt;，否則就會讓等待這把 Lock 的 CPU Core 白白浪費在那邊&lt;/p&gt;&#10;&lt;p&gt;例如下面這種很快就結束的操作，才適合使用 Spinlock：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;spin_lock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;shared_state&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;update_pointer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;spin_unlock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;如果持有 Spinlock 時去 Sleep、執行很慢的 I/O，或等待 Hardware，其他正在等待同一把 Lock 的 CPU Core 就只能一直空轉，除了浪費 CPU，在許多 Kernel Context 中，持有 Spinlock 時 Sleep 本身就是不允許的&lt;/p&gt;&#10;&lt;p&gt;所以可以很粗略地記成：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;等待時間可能比較長，而且允許 Sleep：考慮 Mutex&lt;/li&gt;&#10;&lt;li&gt;Critical Section 非常短，而且不能或不適合 Sleep：考慮 Spinlock&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;這就是 Spinlock 最基本的 Trade-off：&lt;strong&gt;拿 CPU Time 換掉 Context Switch 與 Sleep / Wakeup 的 Overhead&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;看到這邊通常就會覺得，哦，所以 Spinlock 是只會在多核心系統上出現對吧&lt;/p&gt;&#10;&lt;p&gt;當然 Spinlock 在多核心系統上特別好理解，假設 CPU 1 正在修改 Shared Data，而 CPU 0 也想修改，CPU 0 即使原地 Spin，也不會妨礙 CPU 1 繼續完成手上的 Critical Section；只要 CPU 1 很快 Unlock，CPU 0 就能接著取得 Lock&lt;/p&gt;&#10;&lt;p&gt;不過這邊有個很容易誤會的地方：&lt;strong&gt;Spinlock 並不是只有 Multicore 才有意義&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Linux Kernel 裡還有 Thread Context、SoftIRQ、Hard IRQ 等不同的 Execution Context，其中有些 Context 根本不能 Sleep，如果 Interrupt Handler 拿不到 Lock，我們不能叫它先睡一下，因此 Mutex 這種 Sleeping Lock 就不適合，反而需要 Spinlock 或其他不能 Sleep 的 Synchronization Primitive，Linux Kernel 也因此提供了 &lt;code&gt;spin_lock_irq()&lt;/code&gt;、&lt;code&gt;spin_lock_irqsave()&lt;/code&gt; 等不同形式（&lt;a class="link" href="https://docs.kernel.org/next/kernel-hacking/locking.html" target="_blank" rel="noopener"&#10; &gt;Linux Kernel Documentation — Locking&lt;/a&gt;）&lt;/p&gt;&#10;&lt;p&gt;所以更完整地說，Spinlock 常見的使用條件是：&lt;strong&gt;Critical Section 很短，而且目前的 Execution Context 不適合或不能 Sleep&lt;/strong&gt;，Multicore 只是這個需求最直覺的一種情境，另外，在某些單核心 Linux Kernel Configuration 中，Spinlock 甚至不一定需要真的 Spin，例如系統沒有 SMP，也沒有 Preemption 時，就不存在另一個同時執行的 Task 可以搶走這個 Critical Section；Linux Kernel 會依 Configuration 對 Lock 做不同處理（&lt;a class="link" href="https://docs.kernel.org/next/kernel-hacking/locking.html" target="_blank" rel="noopener"&#10; &gt;Linux Kernel Documentation — Locking&lt;/a&gt;）&lt;/p&gt;&#10;&lt;p&gt;補充一下，Linux 的 PREEMPT_RT 會改變 &lt;code&gt;spinlock_t&lt;/code&gt; 的實作語意，真正會在所有 Kernel Configuration 中維持 Spinning Lock 語意的是 &lt;code&gt;raw_spinlock_t&lt;/code&gt;，這篇先用一般 Spinlock 的概念理解就好（&lt;a class="link" href="https://docs.kernel.org/locking/locktypes.html" target="_blank" rel="noopener"&#10; &gt;Linux Kernel Documentation — Lock Types&lt;/a&gt;）&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="所以-mutexsemaphorespinlock-到底差在哪"&gt;所以 Mutex、Semaphore、Spinlock 到底差在哪？&#10;&lt;/h2&gt;&lt;p&gt;前面講了一大堆，最後其實可以用三句話快速記：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Mutex&lt;/strong&gt;：「這個東西現在是我的，你們先不要碰」主要用來保護 Motor、I2C Bus、Shared Data、Driver State 或 File 等 Shared Resource，而拿不到時通常可以 Block / Sleep&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Semaphore&lt;/strong&gt;：「現在有幾個 Event 要處理？」主要用來做 Synchronization、Signaling 或 Resource Counting，而且不一定有 Owner&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Spinlock&lt;/strong&gt;：「它應該馬上就好了，我站在這邊等」主要用來保護非常短的 Critical Section；拿不到時不會 Sleep，而是持續佔用所在的 CPU Core 等待&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;當然現實世界沒有這麼絕對，不同作業系統對搶佔的實作機制也可能有差異，調度策略可能也不太一樣，但如果只是想快速判斷，可以先從下面三個方向想：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;我要保護一個 Resource 嗎？先想到 Mutex&lt;/li&gt;&#10;&lt;li&gt;我要通知另一個 Thread「有事情發生」嗎？先想到 Semaphore&lt;/li&gt;&#10;&lt;li&gt;我要保護一個非常短的 Critical Section，而且這裡不能 Sleep 嗎？考慮 Spinlock&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 2&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 3&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 4&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 5&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 6&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 7&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 8&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 9&#10;&lt;/span&gt;&lt;span class="lnt"&gt;10&#10;&lt;/span&gt;&lt;span class="lnt"&gt;11&#10;&lt;/span&gt;&lt;span class="lnt"&gt;12&#10;&lt;/span&gt;&lt;span class="lnt"&gt;13&#10;&lt;/span&gt;&lt;span class="lnt"&gt;14&#10;&lt;/span&gt;&lt;span class="lnt"&gt;15&#10;&lt;/span&gt;&lt;span class="lnt"&gt;16&#10;&lt;/span&gt;&lt;span class="lnt"&gt;17&#10;&lt;/span&gt;&lt;span class="lnt"&gt;18&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 要解決什麼問題？&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +---------------+---------------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 保護 Shared Resource Event / Resource Count&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v v&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Mutex Semaphore&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 拿不到 Lock 怎麼辦？&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; +-----+------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;可以 Sleep 不能/不想 Sleep&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v v&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Mutex Spinlock&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h2 id="結語"&gt;結語&#10;&lt;/h2&gt;&lt;p&gt;總之大概就是這樣了，Mutex、Semaphore 跟 Spinlock 第一次看的時候，很容易覺得三個東西長得差不多，反正都在避免多個 Execution Context 互相干擾，但真正去看它們想解決的問題，思考當初發明這些東西的人的核心思想，就會覺得還真是精妙啊，各自對應不同 Thread 之間的互動情境&lt;/p&gt;&#10;&lt;p&gt;Mutex 比較偏向 &lt;strong&gt;Ownership / Mutual Exclusion&lt;/strong&gt; 強調資源的唯一性，Semaphore 比較偏向 &lt;strong&gt;Count / Signaling&lt;/strong&gt; 更適合當有事件發生時的提醒信號，Spinlock 則是在 Mutual Exclusion 的問題上選擇 &lt;strong&gt;Busy Waiting&lt;/strong&gt;，用 CPU Time 換掉 Context Switch 可能造成的額外成本&lt;/p&gt;&#10;&lt;p&gt;這些經典機制會被廣泛運用在不同的作業系統中，而它們背後其實都在處理不同的問題，理解這些機制以及它們背後想解決的問題，也能讓我們在設計系統時靈活運用這些作業系統提供的功能&lt;/p&gt;&#10;&lt;p&gt;希望這篇對大家有幫助啦&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="reference"&gt;Reference&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://docs.zephyrproject.org/latest/kernel/services/synchronization/mutexes.html" target="_blank" rel="noopener"&#10; &gt;Zephyr Project Documentation — Mutexes&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://docs.zephyrproject.org/latest/kernel/services/synchronization/semaphores.html" target="_blank" rel="noopener"&#10; &gt;Zephyr Project Documentation — Semaphores&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://docs.zephyrproject.org/latest/kernel/services/interrupts.html" target="_blank" rel="noopener"&#10; &gt;Zephyr Project Documentation — Interrupts / Offloading ISR Work&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://www.freertos.org/Documentation/02-Kernel/02-Kernel-features/02-Queues-mutexes-and-semaphores/04-Mutexes" target="_blank" rel="noopener"&#10; &gt;FreeRTOS Documentation — Mutexes and Semaphores&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://docs.kernel.org/next/kernel-hacking/locking.html" target="_blank" rel="noopener"&#10; &gt;Linux Kernel Documentation — Locking / Spinlocks / Mutexes&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://docs.kernel.org/locking/locktypes.html" target="_blank" rel="noopener"&#10; &gt;Linux Kernel Documentation — Lock Types and PREEMPT_RT&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>關於 Aligned Memory Allocation</title><link>https://www.hugoshih.com/zh-tw/p/%E9%97%9C%E6%96%BC-aligned-memory-allocation/</link><pubDate>Thu, 30 Oct 2025 21:48:32 +0800</pubDate><guid>https://www.hugoshih.com/zh-tw/p/%E9%97%9C%E6%96%BC-aligned-memory-allocation/</guid><description>&lt;img src="https://i.imgur.com/WVmjgMd.jpeg" alt="Featured image of post 關於 Aligned Memory Allocation" /&gt;&lt;h2 id="起源"&gt;起源&#10;&lt;/h2&gt;&lt;p&gt;由於在這個東西上吃了大悶虧，至於是什麼虧就不細究了，總之是我當初在看到這東西的時候沒有認真摸透，就趁著這個機會好好還債了&#10;我想要用一個超級白話的方式讓人快速可以理解，下次遇到這個議題再回來看就能快速回憶起來&lt;/p&gt;&#10;&lt;h2 id="什麼是-aligned-memory-allocation"&gt;什麼是 Aligned Memory Allocation？&#10;&lt;/h2&gt;&lt;p&gt;設想一個情況，今天程式運行到一個地方，如果我需要一些額外的記憶體來儲存新的資料，在 C 語言中我們一定會用到 &lt;code&gt;malloc()&lt;/code&gt; 來跟系統要額外的記憶體空間&lt;/p&gt;&#10;&lt;p&gt;舉個例子，我現在突然需要 32 bytes 的空間，所以我就使出 &lt;code&gt;malloc()&lt;/code&gt;，然後系統就翻啊翻找啊找，最後在記憶體中找到了 32 bytes 的空間，並把起始的記憶體位置傳回來。這個起始的位置可能很隨機，像是 &lt;code&gt;0x08004321&lt;/code&gt;，不過這也無傷大雅，總之我現在有了 32 bytes 空間，我現在想對他幹嘛就幹嘛。&lt;/p&gt;&#10;&lt;p&gt;然而然而，在某些情況（至於是哪些情況後面再講），我們會希望我們得到的記憶體的起始位置是「對齊」的。那什麼叫做對齊呢？&lt;/p&gt;&#10;&lt;p&gt;舉個例子，如果你用一些 Hex 檔案的預覽工具，你會看到左邊的起始位置很整齊以 32 bytes 間隔，像下面這樣：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;span class="lnt"&gt;7&#10;&lt;/span&gt;&lt;span class="lnt"&gt;8&#10;&lt;/span&gt;&lt;span class="lnt"&gt;9&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;一般隨機分配 (未對齊):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0x08004321 | Data...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ^ 起始位置很隨意&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;32-byte Alignment (對齊):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0x08000000 | Data...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0x08000020 | Data... &amp;lt;-- 0x20 就是 32&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0x08000040 | Data... &amp;lt;-- 0x40 就是 64&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ^ 起始位置都可以被 32 整除&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;&lt;img alt="image" class="gallery-image" data-flex-basis="833px" data-flex-grow="347" height="632" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://i.imgur.com/hr8QdrG.jpeg" srcset="https://www.hugoshih.com/hr8QdrG_7110454563738595381_hu_a5c2d5ab0cb0c0df.jpg 800w, https://www.hugoshih.com/hr8QdrG_7110454563738595381_hu_a1571e98593b37a3.jpg 1600w, https://i.imgur.com/hr8QdrG.jpeg 2196w" width="2196"&gt;&lt;/p&gt;&#10;&lt;p&gt;所謂的 &lt;strong&gt;Aligned Memory Allocation&lt;/strong&gt;，就是我在要新的記憶體空間時，想要拿到一個&lt;strong&gt;起始位置可以被 32（或是其他數字）整除的記憶體空間&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;OK，所以說到這邊應該對這個 Aligned Memory Allocation 是什麼應該有點概念了，這樣就成功了一半了。因為大多數人第一次聽到應該都比較難想到這是什麼，又或者知道這是什麼，但不知道有什麼用&lt;/p&gt;&#10;&lt;p&gt;所以，這個有什麼用呢？&lt;/p&gt;&#10;&lt;h2 id="為什麼需要-alignment"&gt;為什麼需要 Alignment？&#10;&lt;/h2&gt;&lt;p&gt;會需要 Alignment 大多是受到硬體設計的限制，例如：&lt;/p&gt;&#10;&lt;p&gt;向量指令集 (如 AVX)，當執行這類指令集的時候，會要求目標的記憶體位置是對齊的，沒有對齊的話得要拆成兩步去處理，或是直接出現錯誤，那為什麼不設計成指令集可以讀取任意位置起始的記憶體呢？我覺得大多是成本考量，如果能從驅動程式端去解決，又何必浪費昂貴的硬體空間呢&lt;/p&gt;&#10;&lt;p&gt;其他像是 CUDA 中 GPU 透過 PCIe 抓取資料時，使用 DMA 也會要求記憶體要對齊，又或者 GPU 指令抓取他自己記憶體的資料也會要求對齊，另外在 STM32 這類單晶片上使用 DMA 時，buffer 的記憶體位置也會要求對齊，才能夠被 DMA 正確寫入或讀取&lt;/p&gt;&#10;&lt;p&gt;這時候一定有人跳出來說：「我平常用 CUDA 或是在 STM32 上用 DMA 都沒遇到這問題啊！」&lt;/p&gt;&#10;&lt;p&gt;那是因為 CUDA driver 或是 HAL library 之類的會幫你處理好，所以當然沒感覺，反過來說，如果你今天寫的是 driver 或是 bare metal 開發，那一定要小心記憶體對齊這東西，不然可能連動都動不起來&lt;/p&gt;&#10;&lt;h2 id="所以該怎麼做到-alignment"&gt;所以該怎麼做到 Alignment？&#10;&lt;/h2&gt;&lt;p&gt;前面說了這麼多，那要怎麼才能做到記憶體分配時 Alignment 呢？&lt;/p&gt;&#10;&lt;p&gt;設想今天我們目標要一塊新的 64 bytes 記憶體，並且要求 32-byte 對齊。&#10;如果今天 &lt;code&gt;malloc&lt;/code&gt; 給的起始位置剛剛好就是 &lt;code&gt;0x08000040&lt;/code&gt;，是 alignment block 的起始位置，這樣就中大獎了，但實際上當然不會那麼幸運，他可能給你的起始位置是 &lt;code&gt;0x08000041&lt;/code&gt;（偏移了 1 個 byte），也可能起始是 &lt;code&gt;0x0800005F&lt;/code&gt;（偏移了 31 個 bytes）&lt;/p&gt;&#10;&lt;p&gt;但沒關係，這時候就會想到，只要&lt;code&gt;malloc&lt;/code&gt;的記憶體夠長，從中找到一個 Alignment Block 的起點並從那邊開始存資料，這樣也算對齊吧&lt;/p&gt;&#10;&lt;p&gt;那麼問題就回到，我們該要多長的記憶體呢？&#10;觀察前面的例子，如果做 32 bytes 的 alignment，偏移的量最多就是 &lt;code&gt;0 ~ 31&lt;/code&gt; bytes。所以我們要 &lt;code&gt;malloc()&lt;/code&gt; 的空間至少要是 &lt;code&gt;size + alignment - 1&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;等等，好像少了什麼？&#10;這樣的話要怎麼才能 &lt;code&gt;free&lt;/code&gt; 掉 alignment 起始位址前面的那些空間呢？我們需要紀錄&lt;strong&gt;真正原本 &lt;code&gt;malloc&lt;/code&gt; 出來的起始位址&lt;/strong&gt;，所以還需要再多一個位置去存這個指標，這個需要的空間是 &lt;code&gt;sizeof(void*)&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;總結來說，我們真正要 &lt;code&gt;malloc()&lt;/code&gt; 的長度是：&#10;&lt;code&gt;size + alignment - 1 + sizeof(void*)&lt;/code&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;記憶體結構示意圖：&lt;/strong&gt;&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 2&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 3&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 4&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 5&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 6&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 7&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 8&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 9&#10;&lt;/span&gt;&lt;span class="lnt"&gt;10&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;實際 malloc 得到的區塊 (Original)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;|&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;v&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+-------------+---------------------+-------------------------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;| padding ... | 原始指標 (Stored) | 使用者拿到的指標 (Aligned)|&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+-------------+---------------------+-------------------------+&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ^ ^&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; | |&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 這格存著 original 符合 32-byte 對齊&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 的地址 我們回傳這個地址&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;然後我們只要找到這裡面 alignment 的起始位址，並且把「原始的起始位址」存在 alignment 起始的前一格。這樣在 &lt;code&gt;free&lt;/code&gt; memory 時，只要從 alignment 位址&lt;strong&gt;往前找一格&lt;/strong&gt;，就可以找到我們應該要釋放的記憶體起點。&lt;/p&gt;&#10;&lt;p&gt;原理概念上就是這樣了，那麼就來看看要怎麼實作吧。&lt;/p&gt;&#10;&lt;h2 id="c-語言實作"&gt;C 語言實作&#10;&lt;/h2&gt;&lt;p&gt;以下用 32-byte alignment 為例子，可以把 32 換成任意想要的正整數 alignment（通常是 2 的次方）。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 2&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 3&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 4&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 5&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 6&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 7&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 8&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 9&#10;&lt;/span&gt;&lt;span class="lnt"&gt;10&#10;&lt;/span&gt;&lt;span class="lnt"&gt;11&#10;&lt;/span&gt;&lt;span class="lnt"&gt;12&#10;&lt;/span&gt;&lt;span class="lnt"&gt;13&#10;&lt;/span&gt;&lt;span class="lnt"&gt;14&#10;&lt;/span&gt;&lt;span class="lnt"&gt;15&#10;&lt;/span&gt;&lt;span class="lnt"&gt;16&#10;&lt;/span&gt;&lt;span class="lnt"&gt;17&#10;&lt;/span&gt;&lt;span class="lnt"&gt;18&#10;&lt;/span&gt;&lt;span class="lnt"&gt;19&#10;&lt;/span&gt;&lt;span class="lnt"&gt;20&#10;&lt;/span&gt;&lt;span class="lnt"&gt;21&#10;&lt;/span&gt;&lt;span class="lnt"&gt;22&#10;&lt;/span&gt;&lt;span class="lnt"&gt;23&#10;&lt;/span&gt;&lt;span class="lnt"&gt;24&#10;&lt;/span&gt;&lt;span class="lnt"&gt;25&#10;&lt;/span&gt;&lt;span class="lnt"&gt;26&#10;&lt;/span&gt;&lt;span class="lnt"&gt;27&#10;&lt;/span&gt;&lt;span class="lnt"&gt;28&#10;&lt;/span&gt;&lt;span class="lnt"&gt;29&#10;&lt;/span&gt;&lt;span class="lnt"&gt;30&#10;&lt;/span&gt;&lt;span class="lnt"&gt;31&#10;&lt;/span&gt;&lt;span class="lnt"&gt;32&#10;&lt;/span&gt;&lt;span class="lnt"&gt;33&#10;&lt;/span&gt;&lt;span class="lnt"&gt;34&#10;&lt;/span&gt;&lt;span class="lnt"&gt;35&#10;&lt;/span&gt;&lt;span class="lnt"&gt;36&#10;&lt;/span&gt;&lt;span class="lnt"&gt;37&#10;&lt;/span&gt;&lt;span class="lnt"&gt;38&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdint.h&amp;gt; // 為了使用 uintptr_t&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;aligned_malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 1. 計算需要的總空間：&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// size + (alignment - 1) 的調整空間 + 存放原始指標的空間&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 2. 算出 alignment 的起始位置&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 先預留一個 pointer 的空間給我們存地址&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uintptr_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 利用 Bitwise 操作進行對齊&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 原理：(address + mask) &amp;amp; ~mask&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 注意：這裡要用 ~(32 - 1) 也就是 ~31 才能遮掉後五位達到 32 對齊&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;uintptr_t&lt;/span&gt; &lt;span class="n"&gt;aligned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 其實也可以用(void*)(original - (original % 32));去算&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 只是用bit manipulation感覺比較優雅&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 3. 把原本 malloc() 的起始位址存到 alignment 位址的前一格&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;aligned&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 4. 最後回傳 alignment 的起始&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;aligned&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;aligned_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// free 的時候，去找到 alignment 位址前一格存的原來 malloc 的位址&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 然後把它釋放就行了&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h2 id="結語"&gt;結語&#10;&lt;/h2&gt;&lt;p&gt;總之就是這樣，Aligned Memory Allocation這個東西重要又有一些實作小細節，藉由這個機會算是有搞懂他了，以後忘了也可以快速回來複習一下&lt;/p&gt;&#10;&lt;h2 id="reference"&gt;Reference&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://bclin.tw/2020/03/15/aligned-malloc/" target="_blank" rel="noopener"&#10; &gt;如何實作一個 aligned_alloc () ？&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://medium.com/howsofcoding/memory-management-aligned-malloc-and-free-9273336bd4c6" target="_blank" rel="noopener"&#10; &gt;Designing aligned Memory Allocator&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://linux.die.net/man/3/aligned_alloc" target="_blank" rel="noopener"&#10; &gt;aligned_alloc(3) - Linux man page&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://github.com/theEmbeddedGeorge/theEmbeddedNewTestament.github.io/blob/master/Embedded_C/Aligned_Memory_Allocation.md" target="_blank" rel="noopener"&#10; &gt;Aligned Memory Allocation&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>常見 Real-Time OS 介紹與比較</title><link>https://www.hugoshih.com/zh-tw/p/%E5%B8%B8%E8%A6%8B-real-time-os-%E4%BB%8B%E7%B4%B9%E8%88%87%E6%AF%94%E8%BC%83/</link><pubDate>Mon, 06 Jan 2025 15:32:31 +0800</pubDate><guid>https://www.hugoshih.com/zh-tw/p/%E5%B8%B8%E8%A6%8B-real-time-os-%E4%BB%8B%E7%B4%B9%E8%88%87%E6%AF%94%E8%BC%83/</guid><description>&lt;img src="https://i.imgur.com/7SJYZ3b.jpg" alt="Featured image of post 常見 Real-Time OS 介紹與比較" /&gt;&lt;h2 id="介紹"&gt;介紹&#10;&lt;/h2&gt;&lt;p&gt;即時作業系統（Real-Time Operating System, RTOS）是一種專門為即時（real-time）應用而設計的作業系統，強調「在特定時間限制內（deadline）對事件進行回應」，試想一個情境在工業的領域若機器某部分突然產生故障，需要在極短的時間內停止所有馬達的運作，若此時使用的是傳統的作業系統如Linux，依OS內核的調度策略，最終馬達停止的動作會有些許延遲，相較之下RTOS則可以有較彈性的執行緒搶佔，讓緊急情況的處理可以更快被執行&lt;/p&gt;&#10;&lt;p&gt;本質上來說RTOS的內核使用起來的效果是大同小異的，基本上能做到多個執行緒的建立、輪詢執行、優先序搶佔、互斥鎖、內核級的中斷等功能，進階一點的則有可以動態記憶體管理、多核心的支援，這些RTOS主要的差異大多是在外圍模組的支援、周邊的開發生態系等，以下就分別介紹目前主流常見的幾種RTOS:&lt;/p&gt;&#10;&lt;h3 id="freertos"&gt;&lt;a class="link" href="https://github.com/FreeRTOS/FreeRTOS" target="_blank" rel="noopener"&#10; &gt;FreeRTOS&lt;/a&gt;&#10;&lt;/h3&gt;&lt;p&gt;FreeRTOS 是由 Real Time Engineers Ltd. 及 Amazon 維護，完全開源並且在全球廣受使用，FreeRTOS 可以說是最最最常見的一種RTOS，成功之道大概在於早早就開源，早早就被各家IDE整合，各家晶片的SDK也都可以看到他的影子，我最早接觸他是在Arduino IDE，FreeRTOS早早就出現在Arduino library list中，使用起來也夠簡單，沒有什麼複雜的設定，加一兩行code就可以實現多執行緒，讓初學者很容易就可以踏入RTOS的世界。他背後的金主爸爸是 Amazon，後來也整合了不少 AWS 上IoT相關的功能，由於早早就開源，周圍的模組也不少。&lt;/p&gt;&#10;&lt;h3 id="azurertos-threadx"&gt;&lt;a class="link" href="https://github.com/eclipse-threadx/threadx" target="_blank" rel="noopener"&#10; &gt;AzureRTOS (Threadx)&lt;/a&gt;&#10;&lt;/h3&gt;&lt;p&gt;ThreadX 是由 Express Logic 開發的即時作業系統（RTOS），後續被 Microsoft 收購並推出更名版本 AzureRTOS 並開源，從名字就可以看出來未來應該是要跟微軟的 Azure 搭配做一些IoT的功能，但目前好像還沒看到什麼殺手級的應用。由於開源的時間比較晚，周圍的套件比較少，像BLE、Wifi這類的Stack能用的東西好像比較少，但相信有微軟原生的支援應該可以成長的很快，我會碰到 AzureRTOS 是因為 STM32 官方原生支援 AzureRTOS，很有趣的是 STM32 的開發環境也有支援 FreeRTOS，但支援度是遠遜於 AzureRTOS 的。&lt;/p&gt;&#10;&lt;h3 id="zephyr"&gt;&lt;a class="link" href="https://github.com/zephyrproject-rtos/zephyr" target="_blank" rel="noopener"&#10; &gt;Zephyr&lt;/a&gt;&#10;&lt;/h3&gt;&lt;p&gt;Zephyr 是由 Linux 基金會（Linux Foundation）主導的開源即時作業系統（RTOS），Zephyr雖然出現很久了，但算是這幾年才突然紅起來，主打一個跨平台的相容性和完整的開發工具鏈，有像是West這種很好用的工具、也有像是DeviceTree者種為跨平台設計的架構，可以說要用Zephyr就要連著這一整串工具鏈一起用才會好用，不過好像也是因為綁著工具鏈跟相容性的原因，好像各家晶片雖然說有支援，但好像又支援的沒那麼好，特別是牽涉到很底層的硬體設定(像時鐘樹設定)就會變的特別麻煩，如果沒有這種需求就蠻好用的，很多設定、初始化都幫你省略了。會碰到Zephyr是因為使用 Nordic 的藍牙 SoC，最新的的 nrf connect SDK 基本上是建立在 Zephyr 上的，而 Zephyr 通訊相關的套件也很完整，很適合給 Nordic 的晶片用。&lt;/p&gt;&#10;&lt;h3 id="rt-thread"&gt;&lt;a class="link" href="https://github.com/RT-Thread/rt-thread" target="_blank" rel="noopener"&#10; &gt;RT-Thread&lt;/a&gt;&#10;&lt;/h3&gt;&lt;p&gt;RT-Thread 是一款由中國團隊主導開發的開源即時作業系統（RTOS），在中國的嵌入式系統開源社群算蠻熱門的，對中國製MCU(特別是一些RISC-V的晶片)支援度很好，但其他地方就比較少看到有人在用，跟Zephyr一樣也有蠻多工具可以使用，我自己還沒有用過，但讓我印象深刻的是RT-Thread社群有一個可以一鍵生成 STM32 bootloader 的工具，不過要使用要註冊帳號，而且還限定用中國的電話才能註冊&amp;hellip;，就對中國以外的開發者不是很友好。&lt;/p&gt;&#10;&lt;h2 id="比較"&gt;比較&#10;&lt;/h2&gt;&lt;h3 id="幾種rtos的差別"&gt;幾種RTOS的差別&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;名稱&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;AzureRTOS (ThreadX)&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;FreeRTOS&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;Zephyr&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;RT-Thread&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;維護者&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 原為 Express Logic 開發，現由 Azure (Microsoft) 管理 &lt;br/&gt; - 被微軟購買後開源&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- AWS（Amazon）及大社群共同維護 &lt;br/&gt; - 開源專案，有廣大社群支援&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 由 Linux Foundation 托管 &lt;br/&gt; - 眾多主要產業贊助商與貢獻者（如 Intel、Nordic 等）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 中國嵌入式廠商與RT-Thread 社群&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;授權&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- MIT 授權 (開源版本)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- MIT 授權 &lt;br/&gt; - 完全開源、商業可使用&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- Apache 2.0 授權 &lt;br/&gt; - 完全開源&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- LGPL 授權 &lt;br/&gt; - 可商業使用，但需遵守 LGPL 條款&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;主要特色&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 小巧的記憶體佔用 &lt;br/&gt; - 高效能可搶先式多工 &lt;br/&gt; - 整合除錯功能、其他外設控制模組&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 模組化設計，可高度客製化內核 &lt;br/&gt; - 協同式及可搶先式多工 &lt;br/&gt; - 詳細文件與巨大社群，支援度高&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 跨平台相容性好 &lt;br/&gt; - 追蹤與分析工具完善 &lt;br/&gt; - 與 Percepio Tracealyzer 整合&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 輕量化設計與微核心架構 &lt;br/&gt; - 效率高，啟動速度快 &lt;br/&gt; - 著重模組化與易用性&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;使用門檻&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 門檻中 &lt;br/&gt; - 提供專門的 API 與工具鏈 &lt;br/&gt; - 原生支援STM32，其他晶片支援則較少&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 門檻低 &lt;br/&gt; - 文件全面且社群龐大 &lt;br/&gt; - 新手上手相對容易，維護成本低&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 門檻高 &lt;br/&gt; - 須熟悉成套的開發工具鏈 &lt;br/&gt; - 社群與企業資源豐富，難度取決於應用深度&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 門檻中 &lt;br/&gt; - 設計較為精簡易懂 &lt;br/&gt; - 模組化讓功能可彈性擴充，但仍需對 RTOS 原理有基礎理解&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;社群與生態&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 因 Microsoft Azure 加持，工業界支持度高 &lt;br/&gt; - 開源時間較晚，社群支援相較少&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 擁有極為廣泛的使用者社群 &lt;br/&gt; - 豐富的第三方套件與論壇資源&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 企業和開源社群雙管齊下 &lt;br/&gt; - 大型廠商投入資源開發並提供硬體支援 &lt;br/&gt; - 文件與套件工具逐漸成熟&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 亞洲市場擁有極大用戶基礎 &lt;br/&gt; - 社群不斷壯大，第三方資源也逐年提升&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;適用範圍&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 消費電子、工業設備 &lt;br/&gt; - 高度資源受限或需要高效能的應用&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 廣泛用於各類微控制器與嵌入式裝置 &lt;br/&gt; - 適合中小型專案或原型開發&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 高度跨平台相容性的產品 &lt;br/&gt; - 需要整合多種通訊協定的裝置&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 小型物聯網裝置、家電產品、工業控制等 &lt;br/&gt; - 適合資源有限且需高即時性的情境&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;範例&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 高度整合 SoC 平台 &lt;br/&gt; - 網路裝置、醫療器械、消費性電子設備 &lt;br/&gt; - 資源特別有限但需穩定度的系統&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 物聯網邊緣設備 &lt;br/&gt; - 小型感測器、可攜式產品 &lt;br/&gt; - 工業自動化控制&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 高速通訊設備 &lt;br/&gt; - 需要完整追蹤與調試能力的工業設備 &lt;br/&gt; - 精密儀器及高可靠度應用&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 嵌入式微控制器 &lt;br/&gt; - 智能家居與可穿戴式裝置 &lt;br/&gt; - 需要快速啟動、高效率排程的場景&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h3 id="內核功能層面的差異"&gt;內核功能層面的差異&#10;&lt;/h3&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;名稱&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;AzureRTOS (ThreadX)&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;FreeRTOS&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;Zephyr&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;strong&gt;RT-Thread&lt;/strong&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;內核架構&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 輕量級、模組化設計 &lt;br/&gt; - 單核心即時系統 &lt;br/&gt; - 提供 preemption-threshold&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 單核心設計 &lt;br/&gt; - 透過設定檔自由啟用/停用功能 &lt;br/&gt; - 針對 MCU 移植方便&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 類微核心（microkernel）概念 &lt;br/&gt; - Kconfig 編譯時可裁剪/擴充 &lt;br/&gt; - 可支援多核心 (SMP)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 微核心 + 模組化 &lt;br/&gt; - 通過 Packages 擴充 &lt;br/&gt; - 預設核心輕量、啟動速度快&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;排程機制&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 可搶先式多工 &lt;br/&gt; - 優先順序排程 &lt;br/&gt; - preemption-threshold 降低不必要的搶先&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 可搶先式或協同式多工 &lt;br/&gt; - 時間片 (time slicing) 可選 &lt;br/&gt; - 任務通知提高效能&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 可搶先式/協同式混合 &lt;br/&gt; - 支援多核心下的任務搬移 &lt;br/&gt; - 彈性設定時間片與搶先策略&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 以優先順序為基礎的可搶先式 &lt;br/&gt; - 切換開銷低 &lt;br/&gt; - 適合時限嚴苛的應用&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;中斷管理&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 提供快速中斷響應 &lt;br/&gt; - ISR 中可使用 IPC 原語 &lt;br/&gt; - 建議複雜邏輯放在 Thread&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 有完整的中斷優先級設定規範 &lt;br/&gt; - 提供「FromISR」API 與核心互動 &lt;br/&gt; - 避免高優先級中斷封鎖 RTOS 服務&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 針對不同架構有對應方法 &lt;br/&gt; - 允許在 ISR 後半段做較複雜處理 &lt;br/&gt; - 支援 &lt;code&gt;irq_offload&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 中斷入口/出口簡潔 &lt;br/&gt; - 可在 ISR 裡做任務喚醒或 IPC &lt;br/&gt; - 建議將重任務放在中斷後處理或 Task&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;任務通訊與同步&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 提供 Semaphore、Mutex、Event Flags、Message Queue &lt;br/&gt; - Block Pool / Byte Pool 作為底層資源&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 有 Queue、Semaphore、Mutex、Event Group、Task Notification &lt;br/&gt; - Queue 是核心溝通管道&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 提供 Queue、FIFO、LIFO、Pipe、Mailbox、Semaphore、Mutex 等多樣化機制 &lt;br/&gt; - 信號（Signals）/事件等高階通訊&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 提供 Semaphore、Mutex、Event、Mailbox、Message Queue &lt;br/&gt; - 模組化 API，易於擴充&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;記憶體管理&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- Block Pool（固定大小） &lt;br/&gt; - Byte Pool（彈性分配） &lt;br/&gt; - 適合小型系統&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 多種 heap 實作 (heap1~heap5) &lt;br/&gt; - 依需求選擇靜態或動態配置 &lt;br/&gt; - 須注意配置 Semaphore / Mutex 以確保多任務安全&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- slab、mempool、heap 等多種分配策略 &lt;br/&gt; - Kconfig 可細調 &lt;br/&gt; - 需在編譯階段做良好配置&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- mempool、heap 二分法 &lt;br/&gt; - 預設輕量動態分配算法 &lt;br/&gt; - 可搭配自訂或高階 alloc 演算法&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;除錯與追蹤&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 可使用 TraceX 等工具 &lt;br/&gt; - 整合度高，針對 Thread、Event、IPC 做可視化&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- FreeRTOS+Trace、第三方 IDE 追蹤工具多 &lt;br/&gt; - 與 Percepio Tracealyzer 深度整合&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 統一的 Tracing 子系統 &lt;br/&gt; - 可連接 Tracealyzer、LTTng、SystemView 等 &lt;br/&gt; - 追蹤支援度豐富&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- RT-Thread Studio 或其他插件 &lt;br/&gt; - 提供簡單 log 系統與線上除錯機制 &lt;br/&gt; - 社群亦有類似 FreeRTOS Trace 的追蹤方案&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;可移植性與擴充性&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 官方主要支援多數 ARM、RISC-V、MIPS &lt;br/&gt; - Microsoft 收購後社群逐漸擴大&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 幾乎所有 MCU 都有移植 &lt;br/&gt; - 大量第三方套件與驅動 &lt;br/&gt; - 可透過 AWS FreeRTOS 延伸雲端功能&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 原生支援多家大廠硬體 (Intel、NXP、Nordic&amp;hellip;) &lt;br/&gt; - Kconfig + Devicetree 與 Linux 類似 &lt;br/&gt; - 企業支援與社群並重&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;- 在亞洲（特別是中國）用得廣泛 &lt;br/&gt; - 與國產 MCU、RISC-V 平台整合度高 &lt;br/&gt; - Packages 方式讓功能彈性擴充&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="總結"&gt;總結&#10;&lt;/h2&gt;&lt;p&gt;總結一下前面提到的四種RTOS：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;AzureRTOS (ThreadX)&lt;/strong&gt;：以高效能與小巧佔用聞名，擅長在嚴苛的資源受限環境中執行，另有良好的除錯工具與商業案例，對STM32的支援度好。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;FreeRTOS&lt;/strong&gt;：擁有巨大開源社群與靈活的擴充性，適用於廣泛的微控制器與嵌入式應用，在學術或商業專案皆常見，初學者適合。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Zephyr&lt;/strong&gt;：受到 Linux 基金會與多家企業的大力推動，硬體支援廣泛，並有強大的追蹤與分析工具，適合需要高可靠度及詳細監控的中大型嵌入式應用&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;RT-Thread&lt;/strong&gt;：架構輕量化、模組化明確，啟動速度快，適合物聯網及其他對即時性與資源效率要求高的場景。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;</description></item><item><title>Zephyr RTOS 兩種建立Thread的方式</title><link>https://www.hugoshih.com/zh-tw/p/zephyr-rtos-%E5%85%A9%E7%A8%AE%E5%BB%BA%E7%AB%8Bthread%E7%9A%84%E6%96%B9%E5%BC%8F/</link><pubDate>Sun, 05 Jan 2025 22:20:31 +0800</pubDate><guid>https://www.hugoshih.com/zh-tw/p/zephyr-rtos-%E5%85%A9%E7%A8%AE%E5%BB%BA%E7%AB%8Bthread%E7%9A%84%E6%96%B9%E5%BC%8F/</guid><description>&lt;img src="https://i.imgur.com/V5OSNcL.jpg" alt="Featured image of post Zephyr RTOS 兩種建立Thread的方式" /&gt;&lt;h1 id="zephyr-rtos-兩種建立thread的方式"&gt;Zephyr RTOS 兩種建立Thread的方式&#10;&lt;/h1&gt;&lt;h2 id="thread-執行緒"&gt;Thread (執行緒)&#10;&lt;/h2&gt;&lt;p&gt;在 Zephyr OS中，每個獨立的功能或任務可以放在不同的執行緒中執行，內部使用了輕量級的排程器，可以根據不同執行緒的優先級，以及執行緒本身的狀態（ready, running, pending 等），來決定哪一個執行緒能夠先被 CPU 執行，執行緒可以有以下幾個性質：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;執行緒優先級（Priority）：數值越小表示優先級越高（Preemptive scheduling 的情況下）。&lt;/li&gt;&#10;&lt;li&gt;堆疊大小（Stack Size）：每個執行緒必須設定自己的堆疊空間，Zephyr 會在執行緒切換時儲存或恢復執行緒的上下文。&lt;/li&gt;&#10;&lt;li&gt;執行緒生命週期（Lifecycle）：可以在編譯期就被預先配置好，或在執行期動態配置與建立。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="zephyr-建立執行緒的兩種常見方法"&gt;Zephyr 建立執行緒的兩種常見方法&#10;&lt;/h2&gt;&lt;h3 id="在編譯期間compile-time建立執行緒"&gt;在編譯期間（Compile Time）建立執行緒&#10;&lt;/h3&gt;&lt;p&gt;Zephyr 提供了一個巨集 &lt;code&gt;K_THREAD_DEFINE()&lt;/code&gt; 用於在編譯期就宣告並建立執行緒。只要程式一開啟並初始化 RTOS 後，這些執行緒就會自動被建立並進入排程器的管理。&lt;/p&gt;&#10;&lt;h4 id="k_thread_define"&gt;K_THREAD_DEFINE()&#10;&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#define K_THREAD_DEFINE(name, stack_size, entry_fn, p1, p2, p3, \&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt; prio, options, delay)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;ul&gt;&#10;&lt;li&gt;name：此執行緒的識別名稱，同時會生成一個 k_tid_t 型態的變數。&lt;/li&gt;&#10;&lt;li&gt;stack_size：此執行緒的堆疊大小（以 byte 為單位）。&lt;/li&gt;&#10;&lt;li&gt;entry_fn：此執行緒進入點函式（thread function），開始執行時要跑哪個函式。&lt;/li&gt;&#10;&lt;li&gt;p1, p2, p3：最多可傳入三個參數給進入點函式使用（都為 void* 型態）。&lt;/li&gt;&#10;&lt;li&gt;prio：優先級（數值越小，優先級越高）。&lt;/li&gt;&#10;&lt;li&gt;options：執行緒選項，通常先給 0 表示沒有特別的額外設定。&lt;/li&gt;&#10;&lt;li&gt;delay：執行緒開始之前的延遲時間（ticks 或毫秒），若填 0 表示不延遲，立即就緒。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="在執行期間run-time建立執行緒"&gt;在執行期間（Run Time）建立執行緒&#10;&lt;/h3&gt;&lt;p&gt;另一種是在程式運行期間建立執行緒，使用 API 函數&lt;code&gt;k_thread_create()&lt;/code&gt; 建立執行緒。這種方法在有些情況下會更具彈性，例如依據狀態或條件，動態啟動或終止執行緒，以免靜態建立過多浪費資源。&lt;/p&gt;&#10;&lt;h4 id="k_thread_create"&gt;k_thread_create()&#10;&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 2&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 3&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 4&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 5&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 6&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 7&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 8&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 9&#10;&lt;/span&gt;&lt;span class="lnt"&gt;10&#10;&lt;/span&gt;&lt;span class="lnt"&gt;11&#10;&lt;/span&gt;&lt;span class="lnt"&gt;12&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;k_tid_t&lt;/span&gt; &lt;span class="nf"&gt;k_thread_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;k_thread&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;new_thread&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;k_thread_stack_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;stack_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;k_thread_entry_t&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;prio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;k_timeout_t&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;ul&gt;&#10;&lt;li&gt;new_thread：指向使用者自行宣告的 struct k_thread 物件，用來儲存此執行緒的控制區塊。&lt;/li&gt;&#10;&lt;li&gt;stack：指向此執行緒對應的堆疊空間（需先以 &lt;code&gt;K_THREAD_STACK_DEFINE()&lt;/code&gt; 靜態或動態配置）。&lt;/li&gt;&#10;&lt;li&gt;stack_size：此堆疊的大小。&lt;/li&gt;&#10;&lt;li&gt;entry：執行緒執行的進入點函式。&lt;/li&gt;&#10;&lt;li&gt;p1, p2, p3：最多可傳入三個參數到執行緒函式。&lt;/li&gt;&#10;&lt;li&gt;prio：優先級。&lt;/li&gt;&#10;&lt;li&gt;options：執行緒選項，常用為 0。&lt;/li&gt;&#10;&lt;li&gt;delay：執行緒開始前是否要延遲。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="範例"&gt;範例&#10;&lt;/h2&gt;&lt;p&gt;以下兩段程式碼，分別示範用 &lt;code&gt;K_THREAD_DEFINE()&lt;/code&gt;（在編譯期間）或 &lt;code&gt;k_thread_create()&lt;/code&gt;（在執行期間）建立兩個執行緒，分別執行 LED 漸亮漸暗（fade_led） 與 LED 閃爍（toggle_led） 的功能&#10;&lt;a class="link" href="https://github.com/h920032/Zephyr-blinky-toggle-demo" target="_blank" rel="noopener"&#10; &gt;github repo&lt;/a&gt;&#10;以下是在Nucleo-F303K8的執行結果，左邊LED執行toggle，右邊執行fading&#10;&lt;img class="gallery-image" data-flex-basis="135px" data-flex-grow="56" height="768" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://imgur.com/V0jJ9bs.gif" width="432"&gt;&lt;/p&gt;&#10;&lt;h3 id="在編譯期間compile-time動態建立執行緒範例"&gt;在編譯期間（Compile Time）動態建立執行緒範例&#10;&lt;/h3&gt;&lt;ol&gt;&#10;&lt;li&gt;&lt;code&gt;K_THREAD_DEFINE(fade_tid, 512, fade_led, NULL, NULL, NULL, 5, 0, 0);&lt;/code&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;建立一個名為 fade_tid 的執行緒，堆疊大小 512 bytes，執行函式為 fade_led，優先級 5。&lt;/li&gt;&#10;&lt;li&gt;p1, p2, p3 都填 NULL，表示不需要傳遞參數。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;K_THREAD_DEFINE(toggle_tid, 512, toggle_led, NULL, NULL, NULL, 5, 0, 0);&lt;/code&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;同樣建立名為 toggle_tid 的執行緒，stack大小、優先級參數與上面一致，執行函式為 toggle_led。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;這些執行緒在編譯的時候就已經被「靜態配置」了，所以當系統啟動並執行到 main() 時，它們也就由排程器自動啟動並不斷執行。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 2&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 3&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 4&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 5&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 6&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 7&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 8&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 9&#10;&lt;/span&gt;&lt;span class="lnt"&gt;10&#10;&lt;/span&gt;&lt;span class="lnt"&gt;11&#10;&lt;/span&gt;&lt;span class="lnt"&gt;12&#10;&lt;/span&gt;&lt;span class="lnt"&gt;13&#10;&lt;/span&gt;&lt;span class="lnt"&gt;14&#10;&lt;/span&gt;&lt;span class="lnt"&gt;15&#10;&lt;/span&gt;&lt;span class="lnt"&gt;16&#10;&lt;/span&gt;&lt;span class="lnt"&gt;17&#10;&lt;/span&gt;&lt;span class="lnt"&gt;18&#10;&lt;/span&gt;&lt;span class="lnt"&gt;19&#10;&lt;/span&gt;&lt;span class="lnt"&gt;20&#10;&lt;/span&gt;&lt;span class="lnt"&gt;21&#10;&lt;/span&gt;&lt;span class="lnt"&gt;22&#10;&lt;/span&gt;&lt;span class="lnt"&gt;23&#10;&lt;/span&gt;&lt;span class="lnt"&gt;24&#10;&lt;/span&gt;&lt;span class="lnt"&gt;25&#10;&lt;/span&gt;&lt;span class="lnt"&gt;26&#10;&lt;/span&gt;&lt;span class="lnt"&gt;27&#10;&lt;/span&gt;&lt;span class="lnt"&gt;28&#10;&lt;/span&gt;&lt;span class="lnt"&gt;29&#10;&lt;/span&gt;&lt;span class="lnt"&gt;30&#10;&lt;/span&gt;&lt;span class="lnt"&gt;31&#10;&lt;/span&gt;&lt;span class="lnt"&gt;32&#10;&lt;/span&gt;&lt;span class="lnt"&gt;33&#10;&lt;/span&gt;&lt;span class="lnt"&gt;34&#10;&lt;/span&gt;&lt;span class="lnt"&gt;35&#10;&lt;/span&gt;&lt;span class="lnt"&gt;36&#10;&lt;/span&gt;&lt;span class="lnt"&gt;37&#10;&lt;/span&gt;&lt;span class="lnt"&gt;38&#10;&lt;/span&gt;&lt;span class="lnt"&gt;39&#10;&lt;/span&gt;&lt;span class="lnt"&gt;40&#10;&lt;/span&gt;&lt;span class="lnt"&gt;41&#10;&lt;/span&gt;&lt;span class="lnt"&gt;42&#10;&lt;/span&gt;&lt;span class="lnt"&gt;43&#10;&lt;/span&gt;&lt;span class="lnt"&gt;44&#10;&lt;/span&gt;&lt;span class="lnt"&gt;45&#10;&lt;/span&gt;&lt;span class="lnt"&gt;46&#10;&lt;/span&gt;&lt;span class="lnt"&gt;47&#10;&lt;/span&gt;&lt;span class="lnt"&gt;48&#10;&lt;/span&gt;&lt;span class="lnt"&gt;49&#10;&lt;/span&gt;&lt;span class="lnt"&gt;50&#10;&lt;/span&gt;&lt;span class="lnt"&gt;51&#10;&lt;/span&gt;&lt;span class="lnt"&gt;52&#10;&lt;/span&gt;&lt;span class="lnt"&gt;53&#10;&lt;/span&gt;&lt;span class="lnt"&gt;54&#10;&lt;/span&gt;&lt;span class="lnt"&gt;55&#10;&lt;/span&gt;&lt;span class="lnt"&gt;56&#10;&lt;/span&gt;&lt;span class="lnt"&gt;57&#10;&lt;/span&gt;&lt;span class="lnt"&gt;58&#10;&lt;/span&gt;&lt;span class="lnt"&gt;59&#10;&lt;/span&gt;&lt;span class="lnt"&gt;60&#10;&lt;/span&gt;&lt;span class="lnt"&gt;61&#10;&lt;/span&gt;&lt;span class="lnt"&gt;62&#10;&lt;/span&gt;&lt;span class="lnt"&gt;63&#10;&lt;/span&gt;&lt;span class="lnt"&gt;64&#10;&lt;/span&gt;&lt;span class="lnt"&gt;65&#10;&lt;/span&gt;&lt;span class="lnt"&gt;66&#10;&lt;/span&gt;&lt;span class="lnt"&gt;67&#10;&lt;/span&gt;&lt;span class="lnt"&gt;68&#10;&lt;/span&gt;&lt;span class="lnt"&gt;69&#10;&lt;/span&gt;&lt;span class="lnt"&gt;70&#10;&lt;/span&gt;&lt;span class="lnt"&gt;71&#10;&lt;/span&gt;&lt;span class="lnt"&gt;72&#10;&lt;/span&gt;&lt;span class="lnt"&gt;73&#10;&lt;/span&gt;&lt;span class="lnt"&gt;74&#10;&lt;/span&gt;&lt;span class="lnt"&gt;75&#10;&lt;/span&gt;&lt;span class="lnt"&gt;76&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;zephyr/kernel.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;zephyr/sys/printk.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;zephyr/device.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;zephyr/drivers/pwm.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;zephyr/drivers/gpio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fade_led&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;toggle_led&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 利用 DT_ALIAS(pwm_led0) 與 DT_ALIAS(led1) 取得裝置樹中的設備訊息&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;pwm_dt_spec&lt;/span&gt; &lt;span class="n"&gt;pwm_led0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;PWM_DT_SPEC_GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;DT_ALIAS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwm_led0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;gpio_dt_spec&lt;/span&gt; &lt;span class="n"&gt;led1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GPIO_DT_SPEC_GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;DT_ALIAS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;led1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;gpios&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 這裡定義一些常數用於控制漸亮漸暗的速度或節拍&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#define NUM_STEPS 256U&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#define SLEEP_MSEC 5U&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#define SLEEP_TIME_MS 500&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 在編譯期間靜態建立兩個執行緒&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;K_THREAD_DEFINE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fade_tid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fade_led&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;K_THREAD_DEFINE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toggle_tid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;toggle_led&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// fade_led 會透過 PWM 設定脈寬來控制 LED 的亮度，製造 &amp;#34;呼吸燈&amp;#34; 效果&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fade_led&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;pulse_width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0U&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pwm_led0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;NUM_STEPS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1U&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 設定當前的脈寬&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;pwm_set_pulse_dt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pwm_led0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pulse_width&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 調整脈寬，控制 LED 往漸亮或漸暗方向&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pulse_width&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pulse_width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;pwm_led0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pulse_width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pwm_led0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0U&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pulse_width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pulse_width&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pulse_width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1U&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;k_sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;K_MSEC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SLEEP_MSEC&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// toggle_led 會透過 GPIO pin toggle 的方式，使 LED 閃爍&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;toggle_led&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;gpio_pin_toggle_dt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;led1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;k_sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;K_MSEC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SLEEP_TIME_MS&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 檢查 PWM 裝置是否 ready&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;pwm_is_ready_dt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pwm_led0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;printk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Error: PWM device %s is not ready&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pwm_led0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 設定 GPIO pin 為輸出&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;gpio_pin_configure_dt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;led1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GPIO_OUTPUT_ACTIVE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// main 函式結束後，兩個 K_THREAD_DEFINE 建立的執行緒依然會持續執行&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h3 id="在執行期run-time動態建立執行緒範例"&gt;在執行期（Run Time）動態建立執行緒範例&#10;&lt;/h3&gt;&lt;p&gt;若想要在程式執行的過程中（例如：由某個事件觸發）再建立執行緒，可以使用以下的做法：&#10;1.&#9;用 &lt;code&gt;K_THREAD_STACK_DEFINE(my_stack_area, STACK_SIZE)&lt;/code&gt;; 定義stack。&#10;2.&#9;定義一個 struct k_thread my_thread_data; 作為執行緒控制區塊（Thread Control Block, TCB）。&#10;3.&#9;呼叫 k_thread_create()，傳入上述的stack、控制區塊、進入點函式及其他參數來完成建立。&lt;/p&gt;&#10;&lt;p&gt;以下是一個範例，展示如何在 main() 函式中動態建立執行緒（此示例僅顯示與執行緒有關的部分，省略週邊初始化等重複程式碼）：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 2&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 3&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 4&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 5&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 6&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 7&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 8&#10;&lt;/span&gt;&lt;span class="lnt"&gt; 9&#10;&lt;/span&gt;&lt;span class="lnt"&gt;10&#10;&lt;/span&gt;&lt;span class="lnt"&gt;11&#10;&lt;/span&gt;&lt;span class="lnt"&gt;12&#10;&lt;/span&gt;&lt;span class="lnt"&gt;13&#10;&lt;/span&gt;&lt;span class="lnt"&gt;14&#10;&lt;/span&gt;&lt;span class="lnt"&gt;15&#10;&lt;/span&gt;&lt;span class="lnt"&gt;16&#10;&lt;/span&gt;&lt;span class="lnt"&gt;17&#10;&lt;/span&gt;&lt;span class="lnt"&gt;18&#10;&lt;/span&gt;&lt;span class="lnt"&gt;19&#10;&lt;/span&gt;&lt;span class="lnt"&gt;20&#10;&lt;/span&gt;&lt;span class="lnt"&gt;21&#10;&lt;/span&gt;&lt;span class="lnt"&gt;22&#10;&lt;/span&gt;&lt;span class="lnt"&gt;23&#10;&lt;/span&gt;&lt;span class="lnt"&gt;24&#10;&lt;/span&gt;&lt;span class="lnt"&gt;25&#10;&lt;/span&gt;&lt;span class="lnt"&gt;26&#10;&lt;/span&gt;&lt;span class="lnt"&gt;27&#10;&lt;/span&gt;&lt;span class="lnt"&gt;28&#10;&lt;/span&gt;&lt;span class="lnt"&gt;29&#10;&lt;/span&gt;&lt;span class="lnt"&gt;30&#10;&lt;/span&gt;&lt;span class="lnt"&gt;31&#10;&lt;/span&gt;&lt;span class="lnt"&gt;32&#10;&lt;/span&gt;&lt;span class="lnt"&gt;33&#10;&lt;/span&gt;&lt;span class="lnt"&gt;34&#10;&lt;/span&gt;&lt;span class="lnt"&gt;35&#10;&lt;/span&gt;&lt;span class="lnt"&gt;36&#10;&lt;/span&gt;&lt;span class="lnt"&gt;37&#10;&lt;/span&gt;&lt;span class="lnt"&gt;38&#10;&lt;/span&gt;&lt;span class="lnt"&gt;39&#10;&lt;/span&gt;&lt;span class="lnt"&gt;40&#10;&lt;/span&gt;&lt;span class="lnt"&gt;41&#10;&lt;/span&gt;&lt;span class="lnt"&gt;42&#10;&lt;/span&gt;&lt;span class="lnt"&gt;43&#10;&lt;/span&gt;&lt;span class="lnt"&gt;44&#10;&lt;/span&gt;&lt;span class="lnt"&gt;45&#10;&lt;/span&gt;&lt;span class="lnt"&gt;46&#10;&lt;/span&gt;&lt;span class="lnt"&gt;47&#10;&lt;/span&gt;&lt;span class="lnt"&gt;48&#10;&lt;/span&gt;&lt;span class="lnt"&gt;49&#10;&lt;/span&gt;&lt;span class="lnt"&gt;50&#10;&lt;/span&gt;&lt;span class="lnt"&gt;51&#10;&lt;/span&gt;&lt;span class="lnt"&gt;52&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;前面的程式碼相同&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;*&lt;/span&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 宣告執行緒stack size&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;K_THREAD_STACK_DEFINE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fade_stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;K_THREAD_STACK_DEFINE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toggle_stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 宣告執行緒控制區塊&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;k_thread&lt;/span&gt; &lt;span class="n"&gt;fade_thread_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;k_thread&lt;/span&gt; &lt;span class="n"&gt;toggle_thread_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 這兩個函式和之前範例雷同&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fade_led&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;toggle_led&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 在執行期動態建立 fade_led thread&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;k_tid_t&lt;/span&gt; &lt;span class="n"&gt;fade_tid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;k_thread_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;fade_thread_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;my_fade_stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;K_THREAD_STACK_SIZEOF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_fade_stack&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fade_led&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;K_NO_WAIT&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 在執行期動態建立 toggle_led thread&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;k_tid_t&lt;/span&gt; &lt;span class="n"&gt;toggle_tid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;k_thread_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;toggle_thread_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;my_toggle_stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;K_THREAD_STACK_SIZEOF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_toggle_stack&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;toggle_led&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;K_NO_WAIT&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// k_thread_create 回傳 k_tid_t，可用於後續管理 thread&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 例如 k_thread_suspend(fade_tid), k_thread_resume(fade_tid), k_thread_abort(fade_tid)...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h2 id="比較"&gt;比較&#10;&lt;/h2&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;建立方式&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;特點&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;典型使用時機&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Compile Time 建立&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;K_THREAD_DEFINE()&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;一開始已知需要的執行緒，以及系統資源充足時&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Run Time 動態建立&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;k_thread_create()&lt;/code&gt;，在程式執行時期再分配資源&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;不確定需要多少執行緒，或需要彈性創建／釋放&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;如果確定在整個系統的生命週期中，執行緒數量固定，或是系統比較單純（例如一個簡單的多任務控制），用編譯期方式能減少程式碼的複雜度，也能讓程式一啟動就快速就緒。&lt;/li&gt;&#10;&lt;li&gt;如果系統中會動態產生／取消某些任務（如：偵測到新裝置才開新任務等），就可以使用動態建立的方式，提高系統彈性。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="總結"&gt;總結&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;靜態（編譯期）建立執行緒：&#10;&lt;ul&gt;&#10;&lt;li&gt;使用 &lt;code&gt;K_THREAD_DEFINE()&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;簡單、快速、程式架構清晰&lt;/li&gt;&#10;&lt;li&gt;適用於固定且少數的長期執行緒&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;動態（執行期）建立執行緒：&#10;&lt;ul&gt;&#10;&lt;li&gt;使用 &lt;code&gt;k_thread_create()&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;需要自行管理堆疊、控制區塊等&lt;/li&gt;&#10;&lt;li&gt;適用於需要彈性創建和銷毀大量或不定數量執行緒的應用場景&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>嵌入式系統常用的幾種MCU</title><link>https://www.hugoshih.com/zh-tw/p/%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%B3%BB%E7%B5%B1%E5%B8%B8%E7%94%A8%E7%9A%84%E5%B9%BE%E7%A8%AEmcu/</link><pubDate>Mon, 21 Aug 2023 19:32:31 +0800</pubDate><guid>https://www.hugoshih.com/zh-tw/p/%E5%B5%8C%E5%85%A5%E5%BC%8F%E7%B3%BB%E7%B5%B1%E5%B8%B8%E7%94%A8%E7%9A%84%E5%B9%BE%E7%A8%AEmcu/</guid><description>&lt;img src="https://i.imgur.com/xEqWwbo.jpg" alt="Featured image of post 嵌入式系統常用的幾種MCU" /&gt;&lt;h1 id="嵌入式系統常用的幾種mcu"&gt;嵌入式系統常用的幾種MCU&#10;&lt;/h1&gt;&lt;p&gt;最近在實作嵌入式系統時因為系統需要更強的運算功能，決定捨棄原本常用的Atmega328p，尋找其他32位元MCU開發平台，選擇平台是件大事，每個平台的適用場景跟開發環境都不同，藉此機會來分析一下。&lt;/p&gt;&#10;&lt;h2 id="atmel系列-如atmega328p"&gt;ATMEL系列 (如ATMEGA328P)&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;Atmega328p是ATMEL公司(美國)生產的8位元AVR架構MCU，常用於Arduino UNO板上&lt;/li&gt;&#10;&lt;li&gt;優點:容易入門且文檔完整、社群支援完整(Arduino生態系建立的很好)、良好的功耗效率(這點沒實測過但聽說很不錯)&lt;/li&gt;&#10;&lt;li&gt;缺點:與其他32位元MCU相比，處理能力有限、較少的內置記憶體和RAM、以他的性能來說小貴(35元上下)&lt;/li&gt;&#10;&lt;li&gt;適用場景: 初學者入門的專案(例如Arduino教學、簡單的嵌入式專案)、低功耗低成本的家用電子裝置&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="esp系列-如esp32-c3"&gt;ESP系列 (如ESP32-C3)&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;ESP32-C3是Espressif Systems公司(中國)的產品，基於32位元RICS-V或Xtensa架構，自帶WiFi和藍牙功能&lt;/li&gt;&#10;&lt;li&gt;優點:直接包含WiFi和藍牙不用外接模組所以CP很高(一顆35-50元)、也是很多人在使用社群支援算完整、有內涵SPI flash的版本很方便&lt;/li&gt;&#10;&lt;li&gt;缺點:較高的功耗(雖然有深度睡眠功能)、系統穩定性聽說不佳&lt;/li&gt;&#10;&lt;li&gt;適用場景: IoT (物聯網) 需要WiFi或藍牙連接的設備、智慧家電、智慧穿戴裝置、不是很關鍵的東西。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="stm系列-如stmf103c8t6"&gt;STM系列 (如STMF103C8T6)&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;STMF103C8T6是STMicroelectronics公司(瑞士)的32位元ARM Cortex-M3微控制器&lt;/li&gt;&#10;&lt;li&gt;優點:運算功能強、具有多種接口、有低功耗模式的版本，具有較高的性價比(範圍大20-60元都有)、有很完整的開發除錯工具、在工業產品中比較常用&lt;/li&gt;&#10;&lt;li&gt;缺點:開發環境較為複雜、較少的RAM和Flash&lt;/li&gt;&#10;&lt;li&gt;適用場景: 複雜的嵌入式專案、需要強大的處理能力、工業自動化、無人機、高階的感測器系統&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="stc系列-如stc89c52"&gt;STC系列 (如STC89C52)&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;STC89C52是STC Micro公司(中國)生產的8位元8051微控制器。&lt;/li&gt;&#10;&lt;li&gt;優點: 8051架構非常的古老、具有廣泛的應用和大量的資料支援、價格超便宜(10-50元)、開發環境算簡單。&lt;/li&gt;&#10;&lt;li&gt;缺點:超舊的架構，處理能力有限、較少的內存和RAM、可以看作是人弱化版的Atmel系列、但因為功能單純，家電之類的很常用到&lt;/li&gt;&#10;&lt;li&gt;適用場景: 傳統的嵌入式系統，如家用電器控制、教育用途，學習8051微控制器的原理和程式設計、簡單的LED顯示、鍵盤輸入等基本IO操作&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="raspberry系列-如rp2040"&gt;RASPBERRY系列 (如RP2040)&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;RP2040是Raspberry Pi基金會(英國)開發的32位元ARM Cortex-M0+微控制器&lt;/li&gt;&#10;&lt;li&gt;優點:高效的處理性能、RAM算大、開源資源和社群支援、簡單的開發環境(Micropython)、CP值也很高(35元)&lt;/li&gt;&#10;&lt;li&gt;缺點: 較新的產品，可能存在與硬體不穩定的問題有些Bug可能還沒有解法&lt;/li&gt;&#10;&lt;li&gt;適用場景: 要求高性能處理的嵌入式專案、影音運算、影像處理、複雜的演算法運算、大數據處理、跑TinyML模型等等&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item></channel></rss>