<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linux on 施星宇</title><link>https://www.hugoshih.com/zh-tw/tags/linux/</link><description>Recent content in Linux 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/tags/linux/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>Build U-boot for QEMU</title><link>https://www.hugoshih.com/zh-tw/p/build-u-boot-for-qemu/</link><pubDate>Thu, 30 Jan 2025 20:56:58 +0800</pubDate><guid>https://www.hugoshih.com/zh-tw/p/build-u-boot-for-qemu/</guid><description>&lt;img src="https://i.imgur.com/mzEAvua.jpg" alt="Featured image of post Build U-boot for QEMU" /&gt;&lt;h4 id="install-dependencies"&gt;Install Dependencies&#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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bc build-essential git libncurses5-dev &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lzop perl libssl-dev bison flex &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;swig libyaml-dev pkg-config python3-dev&#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;h4 id="clone-u-boot-source"&gt;Clone U-boot Source&#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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/u-boot/u-boot.git&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; u-boot&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make distclean&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make qemu_arm64_defconfig &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make -j8&#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;&lt;code&gt;make distclean&lt;/code&gt;&#10;This cleans the repository by removing any previous build artifacts, generated configuration files, or object files. It ensures you have a completely fresh start, especially useful if you built U-Boot for a different target earlier.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;make qemu_arm64_defconfig&lt;/code&gt;&#10;This sets the default configuration for building U-Boot specifically for a QEMU ARM64 (AArch64) virtual environment. Defconfigs are baseline config files that initialize a bunch of standard options for a given architecture or board in U-Boot.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;make -j8&lt;/code&gt;&#10;Builds U-Boot with 8 parallel jobs, leveraging multiple cores to speed up compilation. The output of this step will typically produce u-boot.bin and other binaries needed to run U-Boot (like u-boot.elf or u-boot.map).&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 id="run-u-boot-with-qemu"&gt;Run U-boot with QEMU&#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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qemu-system-aarch64 &lt;span class="se"&gt;\ &lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-M virt &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-cpu cortex-a57 &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-m &lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-nographic &lt;span class="se"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-bios u-boot.bin&#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;&lt;code&gt;-M virt:&lt;/code&gt;&#10;Specifies the QEMU “virt” machine model, a generic machine typically used for virtualizing ARM systems.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;-cpu cortex-a57:&lt;/code&gt;&#10;Defines the virtual CPU core type. Cortex-A57 is a common 64-bit ARM core.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;-m 1024:&lt;/code&gt;&#10;Allocates 1024 MB of memory (i.e., 1 GB) to the virtual machine.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;-nographic:&lt;/code&gt;&#10;Disables graphical output and directs all console I/O to the terminal.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;-bios u-boot.bin:&lt;/code&gt;&#10;Instructs QEMU to use the u-boot.bin file as the “BIOS” or firmware image. Essentially, QEMU treats this binary as the initial code that runs at system reset, simulating the environment where U-Boot is the first piece of software to execute.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h4 id="output"&gt;Output&#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;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;/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;U-Boot 2025.04-rc1-00020-g021baf7b08cc (Jan 30 2025 - 14:41:56 +0000)&#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;DRAM: 1 GiB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Core: 51 devices, 14 uclasses, devicetree: board&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Flash: 64 MiB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Loading Environment from Flash... *** Warning - bad CRC, using default environment&#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;In: serial,usbkbd&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Out: serial,vidconsole&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Err: serial,vidconsole&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;No USB controllers found&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Net: eth0: virtio-net#32&#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;starting USB...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;No USB controllers found&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Hit any key to stop autoboot: 0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Scanning for bootflows in all bootdevs&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Seq Method State Uclass Part Name Filename&#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;Scanning global bootmeth &amp;#39;efi_mgr&amp;#39;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Cannot persist EFI variables without system partition&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Missing TPMv2 device for EFI_TCG_PROTOCOL&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Missing RNG device for EFI_RNG_PROTOCOL&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Scanning bootdev &amp;#39;fw-cfg@9020000.bootdev&amp;#39;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fatal: no kernel available&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;No USB controllers found&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;scanning bus for devices...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Scanning bootdev &amp;#39;virtio-net#32.bootdev&amp;#39;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;BOOTP broadcast 1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;DHCP client bound to address 10.0.2.15 (13 ms)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;*** Warning: no boot file name; using &amp;#39;0A00020F.img&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Using virtio-net#32 device&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TFTP from server 10.0.2.2; our IP address is 10.0.2.15&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Filename &amp;#39;0A00020F.img&amp;#39;.&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Load address: 0x40400000&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Loading: *&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TFTP error: &amp;#39;Access violation&amp;#39; (2)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Not retrying...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;No more bootdevs&#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;(0 bootflows, 0 valid)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;=&amp;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;</description></item><item><title>Docker使用小筆記</title><link>https://www.hugoshih.com/zh-tw/p/docker%E4%BD%BF%E7%94%A8%E5%B0%8F%E7%AD%86%E8%A8%98/</link><pubDate>Mon, 30 May 2022 14:56:58 +0800</pubDate><guid>https://www.hugoshih.com/zh-tw/p/docker%E4%BD%BF%E7%94%A8%E5%B0%8F%E7%AD%86%E8%A8%98/</guid><description>&lt;img src="https://i.imgur.com/5vmDYCZ.jpg" alt="Featured image of post Docker使用小筆記" /&gt;&lt;h1 id="docker使用小筆記"&gt;Docker使用小筆記&#10;&lt;/h1&gt;&lt;h2 id="前言"&gt;前言&#10;&lt;/h2&gt;&lt;p&gt;Docker可以說是雲端技術中非常重要的一項專案，這個專案實現了Container技術，使得軟體的執行可以不在受系統環境限制，更加速了雲端技術中Microservice概念的發展，以下就簡單介紹常用的Docker指令。&lt;/p&gt;&#10;&lt;h2 id="基本操作"&gt;基本操作&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;離開但不關閉container：&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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;crtl+p crtl+q&#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;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;查看container列表&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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo docker container ls&#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;進入container&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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo docker attach &amp;lt;ID&amp;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;關閉container&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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo container stop &amp;lt;ID&amp;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="docker-image操作"&gt;Docker Image操作&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;查看image列表&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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo docker image ls&#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;下載image&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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo docker pull &amp;lt;hub path&amp;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;Run&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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo docker run &amp;lt;--gpus all&amp;gt; &amp;lt;-it&amp;gt; &amp;lt;-p 80&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:80&lt;span class="o"&gt;(&lt;/span&gt;docker&lt;span class="o"&gt;)&lt;/span&gt;&amp;gt; &amp;lt;-v /data&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:/data&lt;span class="o"&gt;(&lt;/span&gt;docker&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;images&amp;gt; &amp;lt;command&amp;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;image commit&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;/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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo docker commit &amp;lt;ID&amp;gt; &amp;lt;images name&amp;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="reference"&gt;Reference&#10;&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://larrylu.blog/step-by-step-dockerize-your-app-ecd8940696f4" target="_blank" rel="noopener"&#10; &gt;https://larrylu.blog/step-by-step-dockerize-your-app-ecd8940696f4&lt;/a&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;在沒有sudo權限使用docker：&#10;&lt;a class="link" href="https://docs.docker.com/engine/security/rootless/" target="_blank" rel="noopener"&#10; &gt;https://docs.docker.com/engine/security/rootless/&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;docker儲存：&#10;&lt;a class="link" href="https://peihsinsu.gitbooks.io/docker-note-book/content/docker-save-image.html" target="_blank" rel="noopener"&#10; &gt;https://peihsinsu.gitbooks.io/docker-note-book/content/docker-save-image.html&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;docker run 操作：&#10;&lt;a class="link" href="https://noob.tw/docker-basic/" target="_blank" rel="noopener"&#10; &gt;https://noob.tw/docker-basic/&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;docker port：&#10;&lt;a class="link" href="https://www.runoob.com/docker/docker-run-command.html" target="_blank" rel="noopener"&#10; &gt;https://www.runoob.com/docker/docker-run-command.html&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;nvidia docker：&#10;&lt;a class="link" href="https://gitlab.com/nvidia/container-images/cuda/blob/master/doc/supported-tags.md" target="_blank" rel="noopener"&#10; &gt;https://gitlab.com/nvidia/container-images/cuda/blob/master/doc/supported-tags.md&lt;/a&gt;&#10;&lt;a class="link" href="https://www.celantur.com/blog/run-cuda-in-docker-on-linux/" target="_blank" rel="noopener"&#10; &gt;https://www.celantur.com/blog/run-cuda-in-docker-on-linux/&lt;/a&gt;&#10;&lt;a class="link" href="https://docs.docker.com/engine/reference/commandline/run/#access-an-nvidia-gpu" target="_blank" rel="noopener"&#10; &gt;https://docs.docker.com/engine/reference/commandline/run/#access-an-nvidia-gpu&lt;/a&gt;&#10;error解法：&#10;&lt;a class="link" href="https://github.com/NVIDIA/nvidia-docker/issues/1243" target="_blank" rel="noopener"&#10; &gt;https://github.com/NVIDIA/nvidia-docker/issues/1243&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;x11 firefox docker：&#10;&lt;a class="link" href="https://stackoverflow.com/questions/16296753/can-you-run-gui-applications-in-a-docker-container" target="_blank" rel="noopener"&#10; &gt;https://stackoverflow.com/questions/16296753/can-you-run-gui-applications-in-a-docker-container&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;指令大全：&#10;&lt;a class="link" href="https://github.com/docker-library/php/issues/359" target="_blank" rel="noopener"&#10; &gt;https://github.com/docker-library/php/issues/359&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;沒有中文的解法：&#10;&lt;a class="link" href="https://github.com/docker-library/php/issues/359" target="_blank" rel="noopener"&#10; &gt;https://github.com/docker-library/php/issues/359&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Linux終端機視窗管理工具tmux筆記</title><link>https://www.hugoshih.com/zh-tw/p/linux%E7%B5%82%E7%AB%AF%E6%A9%9F%E8%A6%96%E7%AA%97%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7tmux%E7%AD%86%E8%A8%98/</link><pubDate>Mon, 30 May 2022 13:32:31 +0800</pubDate><guid>https://www.hugoshih.com/zh-tw/p/linux%E7%B5%82%E7%AB%AF%E6%A9%9F%E8%A6%96%E7%AA%97%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7tmux%E7%AD%86%E8%A8%98/</guid><description>&lt;img src="https://i.imgur.com/s6DO9W0.jpg" alt="Featured image of post Linux終端機視窗管理工具tmux筆記" /&gt;&lt;h1 id="linux終端機視窗管理工具tmux筆記"&gt;Linux終端機視窗管理工具tmux筆記&#10;&lt;/h1&gt;&lt;h2 id="前言"&gt;前言&#10;&lt;/h2&gt;&lt;p&gt;一般在操作遠端的Linux主機時通常會使用SSH連線進去操作，然而若遇到需要執行一個不可中斷的指令但又不得不中斷SSH連線時，一般情況下會將該Process拉到背景執行或是使用視窗管理工具&lt;code&gt;screen&lt;/code&gt;，而&lt;code&gt;tmux&lt;/code&gt;則是另一個非常強大的視窗管理工具，可以在終端機中模擬視窗操作，包括視窗的切換、視窗的切割等等，以下就列出一些&lt;code&gt;tmux&lt;/code&gt;常用到的一些指令。&lt;/p&gt;&#10;&lt;h2 id="基本"&gt;基本&#10;&lt;/h2&gt;&lt;p&gt;進入tmux&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-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux&#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;列出tmux視窗列表&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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux ls&#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;連接tmux使窗&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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux attach -t 0&#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;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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux attach -t database&#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;建立一個名為database的視窗&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-Bash" data-lang="Bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux new -s database&#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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tmux rename-session -t 0 database&#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;/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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ctrl+b&#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;垂直切割&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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ctrl+b&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;&#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;/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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ctrl+b&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;c&#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;/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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ctrl+b&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;p&#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;/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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ctrl+b&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;n&#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;/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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ctrl+b&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;number&amp;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;視窗選單&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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ctrl+b&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;s&#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;/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;/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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;crtl+b [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;q (離開)&#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;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;/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-fallback" data-lang="fallback"&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;$ tmux&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# OR&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tmux new -s &amp;lt;your_session_name&amp;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;# session 列表&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tmux ls&#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;# 重新連線 session&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tmux a -t 0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# OR&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tmux a -t &amp;lt;session_name&amp;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;# 刪除 session&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tmux kill-session -t 0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# OR&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tmux kill-session -t &amp;lt;session_name&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# OR&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tmux kill-session -a # 全部&#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;# 刪除 tmux server&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tmux kill-server&#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;# 重新命名 session&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ tmux rename-session -t 0 &amp;lt;new_session_name&amp;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;# C-b ? Help&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b c 新增視窗&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b， 視窗命名&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b w 視窗列表&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b f 尋找視窗&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b &amp;amp; 刪除視窗&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b % 垂直分割區塊&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b “ 水平分割區塊&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b &amp;lt;方向鍵&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b p 上一個視窗&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b n 下一個視窗&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b &amp;lt;number&amp;gt; 依照編號直接切換（編號顯示於狀態列）&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b d 離開 session&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b x 關閉 Pane&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-d 關閉 Pane&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# C-b z 讓一個 Pane 變成全螢幕，在輸入一次則回到剛剛的尺寸&#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="reference"&gt;Reference&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;ssh自動連進tmux&#10;&lt;a class="link" href="https://stackoverflow.com/questions/27613209/how-to-automatically-start-tmux-on-ssh-session" target="_blank" rel="noopener"&#10; &gt;https://stackoverflow.com/questions/27613209/how-to-automatically-start-tmux-on-ssh-session&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;與iterm結合&#10;&lt;a class="link" href="https://medium.com/@wancw/iterm2-ssh-tmux-73d3fcf9dac6" target="_blank" rel="noopener"&#10; &gt;https://medium.com/@wancw/iterm2-ssh-tmux-73d3fcf9dac6&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item></channel></rss>