<?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 WSLIN</title><link>https://wslin.dev/tags/linux/</link><description>Recent content in Linux 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/linux/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><item><title>【Ubuntu】Fail2Ban基本使用</title><link>https://wslin.dev/blog/ubuntu-fail2ban/</link><pubDate>Thu, 30 Apr 2026 13:48:45 +0800</pubDate><guid>https://wslin.dev/blog/ubuntu-fail2ban/</guid><description>&lt;p&gt;我們可以先用&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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo lastb&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;來看一下所有失敗的登入紀錄&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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;last&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;來看登入成功的紀錄&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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo grep &lt;span style="color:#e6db74"&gt;&amp;#34;Accepted&amp;#34;&lt;/span&gt; /var/log/auth.log | tail -n &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;或是顯示更詳細的登入資訊&lt;/p&gt;&#10;&lt;p&gt;基本上只要是公開的IP，有開啟22 port就很容易看到一堆登入失敗的資訊，因此我們要來用Fail2Ban把這些掃port的機器人ban掉。&lt;/p&gt;&#10;&lt;h2 id="基本使用"&gt;&#10; 基本使用&#10; &lt;a class="heading-link" href="#%e5%9f%ba%e6%9c%ac%e4%bd%bf%e7%94%a8"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;安裝&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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt-get update&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt-get install -y fail2ban&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;複製config檔，避免改到預設的&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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果想設置嘗試次數、Ban多久等參數的話，可以編輯他，這邊我就先不改，用預設的即可&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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo nano /etc/fail2ban/jail.local&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然後就可以啟動服務了&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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl enable fail2ban&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl start fail2ban&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;查看目前的封鎖資訊&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-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo fail2ban-client status sshd&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>【Linux Kernel】Process and Thread的關係</title><link>https://wslin.dev/blog/linux-kernel-process-and-thread/</link><pubDate>Wed, 10 Dec 2025 14:57:59 +0800</pubDate><guid>https://wslin.dev/blog/linux-kernel-process-and-thread/</guid><description>&lt;h2 id="前言"&gt;&#10; 前言&#10; &lt;a class="heading-link" href="#%e5%89%8d%e8%a8%80"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;Linux Kernel中的Process, Thread其實跟我們一般認知的定義有些不太一樣，因此這篇文章來記錄這些名詞上的差異。&lt;/p&gt;&#10;&lt;h2 id="歷史"&gt;&#10; 歷史&#10; &lt;a class="heading-link" href="#%e6%ad%b7%e5%8f%b2"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;最開始的Linux，並沒有follow POSIX的標準，且當初也沒有針對多執行緒去設計架構，都還是以單執行緒為主，因此最早期設計了task_struct這個資料結構當作Scheduler調度CPU資源的單位，一個task_struct就代表一個Process。&lt;/p&gt;&#10;&lt;h2 id="後來的設計"&gt;&#10; 後來的設計&#10; &lt;a class="heading-link" href="#%e5%be%8c%e4%be%86%e7%9a%84%e8%a8%ad%e8%a8%88"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;後來多核CPU逐漸成為主流後，多執行緒的設計就很有必要了，但Linux為了方便便延續了之前的設計，將task_struct定義改為Lightweight Process(LWP)，其實就是我們一般認知的Thread的概念，而Process這個名詞則變成了一組LWP的集合，Linux稱之為Thread Group。&lt;/p&gt;&#10;&lt;h2 id="task_struct"&gt;&#10; task_struct&#10; &lt;a class="heading-link" href="#task_struct"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;task_struct又被稱作Process descriptor(也就是OS中的PCB)，存放所有跟該LWP有關的資料，其中有兩個重要的欄位：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;pid ：這個LWP的Id，所有LWP的Id都是唯一的&lt;/li&gt;&#10;&lt;li&gt;tgid：同一組LWP(Thread)形成的Process的Id，如果是main thread(或稱Thread Group Leader)則跟pid一致，如果是其他thread則會記錄main thread的pid&lt;/li&gt;&#10;&lt;/ul&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;// task_struct中的欄位&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;pid_t&lt;/span&gt; pid;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;pid_t&lt;/span&gt; tgid;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以及另外兩個重要的function：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;getpid()：回傳tgid欄位&lt;/li&gt;&#10;&lt;li&gt;gettid()：回傳pid 欄位，也就是說pid其實實際上的意義是Thread Id，但task_struct中並沒有一個專屬的欄位紀錄tid，而是統一用pid&lt;/li&gt;&#10;&lt;/ul&gt;</description></item></channel></rss>