<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>DS on WSLIN</title><link>https://wslin.dev/tags/ds/</link><description>Recent content in DS on WSLIN</description><generator>Hugo</generator><language>zh-tw</language><lastBuildDate>Fri, 19 Jun 2026 14:57:59 +0800</lastBuildDate><atom:link href="https://wslin.dev/tags/ds/index.xml" rel="self" type="application/rss+xml"/><item><title>【Linux Kernel】侵入式鏈結串列 Intrusive Linked List</title><link>https://wslin.dev/blog/linux-kernel-linked-list/</link><pubDate>Fri, 19 Jun 2026 14:57:59 +0800</pubDate><guid>https://wslin.dev/blog/linux-kernel-linked-list/</guid><description>&lt;p&gt;一直以為Linked List是最簡單最基礎的資料結構之一，寫法就是很標準的struct裡面塞data跟指向下個節點的指標。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; Node {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; data; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; Node &lt;span style="color:#f92672"&gt;*&lt;/span&gt;next; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但這個作法有個小問題，就是直接把data的型別寫死在struct裡面，不能動態替換，如果哪天data要換型別，就要寫一個新的struct來處理，很顯然這是不太好的設計。但我也一直都認為這就是C的特性，沒有高階語言的泛型，就只能乖乖接受。&lt;/p&gt;&#10;&lt;p&gt;直到學習Linux後才發現，他們用了一個超聰明的方法來實現類似泛型的功能，就是把包裝的順序反過來，Node的struct裡不再包含data欄位，只留下很單純的下個節點的指標，然後將Node本身嵌入進原本data的struct裡面當作一個欄位。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; list_head {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; list_head &lt;span style="color:#f92672"&gt;*&lt;/span&gt;next;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; my_data {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; data;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; list_head list;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;更厲害的操作還在後面，就是當我們loop through List找到目標節點的時候，要如何才能拿到真正的data呢？Linux Kernel首先透過offsetof這個macro找出Node在data struct中的offset，再用container_of macro去把node的記憶體位址扣掉offset回推出實際data的指標，從而拿到真正的資料。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define offsetof(type, member) ((size_t) &amp;amp;((type *)0)-&amp;gt;member)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#define container_of(ptr, type, member) ((type *)( (char *)(ptr) - offsetof(type, member) ))&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; my_data instance1 &lt;span style="color:#f92672"&gt;=&lt;/span&gt; { .data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;42&lt;/span&gt;, .list &lt;span style="color:#f92672"&gt;=&lt;/span&gt; { .next &lt;span style="color:#f92672"&gt;=&lt;/span&gt; NULL } };&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; list_head &lt;span style="color:#f92672"&gt;*&lt;/span&gt;target_node &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;instance1.list;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; my_data &lt;span style="color:#f92672"&gt;*&lt;/span&gt;real_data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;container_of&lt;/span&gt;(target_node, &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; my_data, list);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Data: %d&amp;#34;&lt;/span&gt;, real_data&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;data);&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item></channel></rss>