Farseerfc的小窩 - linux//farseerfc.me/2019-02-07T02:14:00+09:00用 usbip 轉發 raspberry pi 的 USB 鍵盤鼠標給 Arch Linux 的 PC2019-02-07T02:14:00+09:002019-02-07T02:14:00+09:00farseerfctag:farseerfc.me,2019-02-07:/usbip-forward-raspberrypi.html<p>惠狐 <a class="reference external" href="/links.html#megumifox">megumifox</a> 寫了篇 <a class="reference external" href="https://blog.megumifox.com/public/2019/02/06/%E7%94%A8pulseaudio%E5%B0%86%E7%94%B5%E8%84%91%E7%9A%84%E5%A3%B0%E9%9F%B3%E7%94%A8%E6%89%8B%E6%9C%BA%E6%94%BE%E5%87%BA%E6%9D%A5/">用PulseAudio將電腦的聲音用手機放出來</a>
,文末提到想知道我怎麼用樹莓派轉發 USB 的,於是寫篇文章記錄一下。</p>
<div class="section" id="id1">
<h2>起因</h2>
<p>家裏有個裝了 Arch Linux ARM 的樹莓派3B 閒置着,裝了 Arch Linux ARM 偶爾上電更新一下,
不過因爲性能實在不適合做別的事情於是一直在吃灰。某日 <del>給老婆安利幻想萬華鏡</del><ins>和老婆看片</ins>
的時候, <del>老婆不吃安利於是遷怒鍵盤鼠標</del><ins>鍵盤鼠標被長長的 USB 線扯着感覺很難受</ins>
,於是偶發奇想,能不能利用一下樹莓派的多達 4 個 USB 2.0 端口接鼠標鍵盤呢,
這樣鼠標鍵盤就可以跟着樹莓派來回走,不用拖着長長的 USB 線了。</p>
<p>上網搜了一下, Linux 環境有個 usbip 工具正好能做到這個。原理也很直觀, usbip 能把 USB …</p></div><p>惠狐 <a class="reference external" href="/links.html#megumifox">megumifox</a> 寫了篇 <a class="reference external" href="https://blog.megumifox.com/public/2019/02/06/%E7%94%A8pulseaudio%E5%B0%86%E7%94%B5%E8%84%91%E7%9A%84%E5%A3%B0%E9%9F%B3%E7%94%A8%E6%89%8B%E6%9C%BA%E6%94%BE%E5%87%BA%E6%9D%A5/">用PulseAudio將電腦的聲音用手機放出來</a>
,文末提到想知道我怎麼用樹莓派轉發 USB 的,於是寫篇文章記錄一下。</p>
<div class="section" id="id1">
<h2>起因</h2>
<p>家裏有個裝了 Arch Linux ARM 的樹莓派3B 閒置着,裝了 Arch Linux ARM 偶爾上電更新一下,
不過因爲性能實在不適合做別的事情於是一直在吃灰。某日 <del>給老婆安利幻想萬華鏡</del><ins>和老婆看片</ins>
的時候, <del>老婆不吃安利於是遷怒鍵盤鼠標</del><ins>鍵盤鼠標被長長的 USB 線扯着感覺很難受</ins>
,於是偶發奇想,能不能利用一下樹莓派的多達 4 個 USB 2.0 端口接鼠標鍵盤呢,
這樣鼠標鍵盤就可以跟着樹莓派來回走,不用拖着長長的 USB 線了。</p>
<p>上網搜了一下, Linux 環境有個 usbip 工具正好能做到這個。原理也很直觀, usbip 能把 USB
端口上的數據封裝成 IP 協議通過網絡轉發出去,從而兩個網絡間相互聯通的電腦就可以遠程轉發 USB 了。
設置好的話,就像是一臺 PC 多了幾個位於樹莓派上的 USB 端口,插上樹莓派的 USB 設備統統作爲 PC
的設備。</p>
<p>這篇文章假設有一個裝了 Arch Linux 的 PC ,和一個裝了 Arch Linux ARM 的樹莓派,
並且兩者間能通過網絡互相訪問到。別的發行版上大概也可以這麼做,只是我沒有試過。 usbip
工具似乎普遍被發行版打包了,除此之外需要的也只是 Linux 內核提供好的功能而已。</p>
</div>
<div class="section" id="arch-linux-arm">
<h2>設置 Arch Linux ARM 的樹莓派端</h2>
<p>假設樹莓派上面網絡已經設置妥當,開機插電就能自動聯網。接下來安裝 usbip 工具:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> sudo pacman -Syu usbip</span>
</pre></div>
<p>然後需要記錄一下樹莓派的 IP 地址:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> ip addr</span>
<span class="code-line"><span class="go">3: wlan0: ......</span></span>
<span class="code-line"><span class="go">inet 192.168.0.117/24 brd 192.168.0.255 scope global noprefixroute wlan0</span></span>
<span class="code-line"><span class="go">......</span></span>
</pre></div>
<p>接下來給 udev 添加一個規則,當插入 usb 設備的時候,執行我的腳本 usbipall.sh
把 usb 設備通過 usbip 共享出去:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> cat /etc/udev/rules.d/usbipall.rules</span>
<span class="code-line"><span class="go">ACTION=="add", SUBSYSTEM=="usb", RUN+="/usr/bin/bash /usr/local/bin/usbipall.sh"</span></span>
</pre></div>
<p>這個 rules 文件 <a class="reference external" href="https://github.com/farseerfc/dotfiles/blob/master/usbiprpi/usbipall.rules">可以在我的 dotfiles 裏面找到</a> 。</p>
<p>然後規則調用的 usbipall.sh 我這麼寫的, <a class="reference external" href="https://github.com/farseerfc/dotfiles/blob/master/usbiprpi/usbipall.sh">文件同樣在我的 dotfiles 裏面</a> :</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="ch">#!/bin/sh</span></span>
<span class="code-line"><span class="o">(</span></span>
<span class="code-line"><span class="nv">allusb</span><span class="o">=</span><span class="k">$(</span>usbip list -p -l<span class="k">)</span></span>
<span class="code-line"><span class="k">for</span> usb in <span class="nv">$allusb</span></span>
<span class="code-line"><span class="k">do</span></span>
<span class="code-line"> <span class="nv">busid</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$usb</span><span class="s2">"</span> <span class="p">|</span> sed <span class="s2">"s|#.*||g;s|busid=||g"</span><span class="k">)</span></span>
<span class="code-line"> <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$busid</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"1-1.1"</span> <span class="o">]</span></span>
<span class="code-line"> <span class="k">then</span></span>
<span class="code-line"> <span class="c1"># ignoring usb ethernet</span></span>
<span class="code-line"> <span class="k">continue</span></span>
<span class="code-line"> <span class="k">fi</span></span>
<span class="code-line"> <span class="nb">echo</span> <span class="s2">"</span><span class="k">$(</span>date -Iseconds<span class="k">)</span><span class="s2">: Exporting </span><span class="nv">$busid</span><span class="s2">"</span></span>
<span class="code-line"> usbip <span class="nb">bind</span> --busid<span class="o">=</span><span class="s2">"</span><span class="nv">$busid</span><span class="s2">"</span></span>
<span class="code-line"><span class="k">done</span></span>
<span class="code-line"><span class="o">)</span> >>/var/log/usbipall.log <span class="m">2</span>><span class="p">&</span><span class="m">1</span></span>
</pre></div>
<p>這個腳本做了這樣幾件事。</p>
<ol class="arabic simple">
<li>調用 <code class="code">
usbip list --local</code>
列出本地所有 usb 設備。</li>
<li>針對每個設備<ol class="arabic">
<li>取出它的 busid</li>
<li>判斷是不是樹莓派的 USB 以太網卡,不是的話繼續</li>
<li>通過 <code class="code">
usbip bind --busid=</code>
命令把這個 usb 設備導出到網上</li>
</ol>
</li>
<li>最後把所有輸出記錄到 /var/log/usbipall.log 日誌裏面</li>
</ol>
<p>樹莓派這邊設置就完成了。從此之後插入的 usb 設備就會統統導出出去。</p>
<p>這裏需要注意一下,啓用了 udev 規則之後,就沒法插鍵盤鼠標到樹莓派上控制它了……我都是從另一端 ssh
上樹莓派操作的。如果有什麼地方設置錯誤,可能需要把樹莓派的 SD 卡拔下來插到電腦上,刪除掉 rules
文件……</p>
<p>仔細檢查設置正確了之後,重新載入 udev 規則,或者重啓樹莓派:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">#</span> systemctl restart systemd-udevd</span>
</pre></div>
<p>這樣樹莓派這邊就設置好了。</p>
</div>
<div class="section" id="arch-linux-pc">
<h2>設置 Arch Linux 的 PC 端</h2>
<p>同樣假設 PC 這邊也已經聯網。接下來同樣安裝 usbip 工具:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> sudo pacman -Syu usbip</span>
</pre></div>
<p>然後我寫了個小腳本去鏈接樹莓派端, <a class="reference external" href="https://github.com/farseerfc/dotfiles/blob/master/usbiprpi/usbiprpi3.sh">這個文件 usbiprpi3.sh 也在我的 dotfiles</a>:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="ch">#!/bin/sh</span></span>
<span class="code-line"><span class="nv">rpi3</span><span class="o">=</span><span class="s2">"192.168.0.117"</span></span>
<span class="code-line"></span>
<span class="code-line">modprobe vhci-hcd</span>
<span class="code-line"></span>
<span class="code-line"><span class="nv">allusb</span><span class="o">=</span><span class="k">$(</span>usbip list -p -r <span class="nv">$rpi3</span> <span class="p">|</span> cut -d<span class="s2">":"</span> -f1 -s <span class="p">|</span> sed <span class="s1">'s|^[ \t]*||;/^$/d'</span><span class="k">)</span></span>
<span class="code-line"><span class="k">for</span> busid in <span class="nv">$allusb</span></span>
<span class="code-line"><span class="k">do</span></span>
<span class="code-line"> <span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$busid</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"1-1.1"</span> <span class="o">]</span></span>
<span class="code-line"> <span class="k">then</span></span>
<span class="code-line"> <span class="c1"># ignoring usb ethernet</span></span>
<span class="code-line"> <span class="k">continue</span></span>
<span class="code-line"> <span class="k">fi</span></span>
<span class="code-line"> <span class="nb">echo</span> <span class="s2">"Attaching </span><span class="nv">$busid</span><span class="s2">"</span></span>
<span class="code-line"> usbip attach --remote<span class="o">=</span><span class="nv">$rpi3</span> --busid<span class="o">=</span><span class="s2">"</span><span class="nv">$busid</span><span class="s2">"</span></span>
<span class="code-line"><span class="k">done</span></span>
</pre></div>
<p>其中腳本第一行填入上面記錄下來的樹莓派的 IP 地址,接下來腳本做了這麼幾件事:</p>
<ol class="arabic simple">
<li>用 modprobe 確認加載 vhci-hcd 通用虛擬鍵鼠驅動</li>
<li>用 <code class="code">
usbip list --remote=</code>
列出遠程設備上已經導出了的 USB 設備,取出他們的 busid</li>
<li>對每個設備用 <code class="code">
usbip attach</code>
接上該設備</li>
</ol>
<p>然後就已經準備妥當,接下來是見證奇蹟的時刻:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> sleep <span class="m">10</span><span class="p">;</span> sudo ./usbiprpi3.sh</span>
<span class="code-line"><span class="go">Attaching 1-1.4.3</span></span>
<span class="code-line"><span class="go">Attaching 1-1.4.1</span></span>
</pre></div>
<p>因爲只有一套鍵盤鼠標,所以先 sleep 個 10 秒,在此期間快速把鍵鼠拔下來插到樹莓派的 USB 口上去。
如果對自己手速沒自信也可以把時間設長一點。然後用 root 權限執行 usbiprpi3.sh 。</p>
<p>一切正常的話,先能觀測插上樹莓派的鍵盤鼠標被樹莓派初始化了一下,比如鍵盤燈會亮,
然後這些設備會被導出出去,從而鍵盤燈滅掉,然後 10 秒等待結束後他們被遠程接到了 PC 端,
又會被初始化一下,同時 PC 端這邊會有上述 Attaching 的輸出。然後鍵盤鼠標就能像平常一樣用啦。</p>
</div>
<div class="section" id="id3">
<h2>使用體驗</h2>
<p>因爲就是通過 IP 轉發 USB 嘛,所以就和普通地接 USB 的體驗差不多,當然前提是網絡環境足夠穩定。
在我家間隔 5 米到無線路由器的環境下,基本感覺不到網絡延遲的影響。
通過這種方式聊天上網應該和直接接 USB 設備完全一樣。本文就是在通過樹莓派轉發的前提下用鍵盤打字寫的。</p>
<p>不過如果網絡負載本身就很大的話,可能會一些延遲,比如我開着 OBS 直播打東方的時候,原本就手殘
的我感覺更加手殘了……</p>
<p>試過拿着樹莓派在房間到處走,走到無線信號覆蓋不到的地方, usbip 會斷掉,PC 上的現象就像是 USB
設備被拔下來了……所以如果無線網絡不穩的話,可能需要對上面腳本做個循環?不過那樣可能會用起來很彆扭吧。</p>
<p>以及,上述操作 usbip 是走 TCP 3240 端口,數據包大概完全沒有加密,所以考慮安全性的話,
最好還是在內網環境使用。不過轉念一想,萬一有別人接上了我導出出去的 USB ,也就是截獲我的鍵盤,
PC 這邊沒法 attach 設備了,應該馬上會發現吧。我敲打 sudo 之類命令的時候 shell 裏面沒有回顯,
就不會再繼續敲密碼了。而且似乎對攻擊者也沒有什麼好處?要是他 usb attach 到了我的設備上,
我就能控制他的鍵盤了耶~</p>
</div>
【譯】使用 GNU stow 管理你的點文件2018-12-08T03:35:00+09:002018-12-08T03:35:00+09:00farseerfctag:farseerfc.me,2018-12-08:/using-gnu-stow-to-manage-your-dotfiles.html<div class="section" id="id1">
<h2>譯註</h2>
<!-- PELICAN_BEGIN_SUMMARY -->
<p>這篇是翻譯自 <a class="reference external" href="http://brandon.invergo.net/news/2012-05-26-using-gnu-stow-to-manage-your-dotfiles.html">Brandon Invergo 的博客的英文文章 Using GNU Stow to manage your dotfiles</a> 。
Brandon Invergo 的博客採用 <a class="reference external" href="https://creativecommons.org/licenses/by-sa/3.0/deed.en_US">CC-BY-SA 3.0</a>
授權,因此本文也同樣採用 <a class="reference external" href="https://creativecommons.org/licenses/by-sa/3.0/deed.en_US">CC-BY-SA 3.0</a>
,不同於其它我寫的文章是 <a class="reference external" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC-BY-NC-SA 4.0</a>
授權。</p>
<p>我自己已經使用此文中介紹的方案管理 <a class="reference external" href="https://git.io/fcdots">我自己的 dotfiles</a>
快 3 年了。最早想採用這樣的管理方案是爲了方便在多臺 Arch Linux 系統之間同步配置,
後來逐漸主力系統也更新換代了一次,又同步到了自己的 vps 上去,目前管理多個 Arch Linux
上都多少都有這套配置。甚至裝好 Arch Linux 添加好用戶最初做的事情就是安裝 …</p></div><div class="section" id="id1">
<h2>譯註</h2>
<!-- PELICAN_BEGIN_SUMMARY -->
<p>這篇是翻譯自 <a class="reference external" href="http://brandon.invergo.net/news/2012-05-26-using-gnu-stow-to-manage-your-dotfiles.html">Brandon Invergo 的博客的英文文章 Using GNU Stow to manage your dotfiles</a> 。
Brandon Invergo 的博客採用 <a class="reference external" href="https://creativecommons.org/licenses/by-sa/3.0/deed.en_US">CC-BY-SA 3.0</a>
授權,因此本文也同樣採用 <a class="reference external" href="https://creativecommons.org/licenses/by-sa/3.0/deed.en_US">CC-BY-SA 3.0</a>
,不同於其它我寫的文章是 <a class="reference external" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">CC-BY-NC-SA 4.0</a>
授權。</p>
<p>我自己已經使用此文中介紹的方案管理 <a class="reference external" href="https://git.io/fcdots">我自己的 dotfiles</a>
快 3 年了。最早想採用這樣的管理方案是爲了方便在多臺 Arch Linux 系統之間同步配置,
後來逐漸主力系統也更新換代了一次,又同步到了自己的 vps 上去,目前管理多個 Arch Linux
上都多少都有這套配置。甚至裝好 Arch Linux 添加好用戶最初做的事情就是安裝 stow git
然後 clone 了我自己的 dotfiles repo 下來,然後按需取想要的配置,快捷方便有效。</p>
<!-- PELICAN_END_SUMMARY -->
<p>廢話不多說,下面是原文和翻譯。與之前的翻譯一樣,正文部分給出原文引用以便對照參考。</p>
</div>
<div class="section" id="id2">
<h2>使用 GNU stow 管理你的點文件</h2>
<a aria-controls="8b1882fc" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#8b1882fc" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="8b1882fc">
<blockquote>
I accidentally stumbled upon something yesterday that I felt like sharing,
which fell squarely into the "why the hell didn't I know about this before?"
category. In this post, I'll describe how to manage the various
configuration files in your GNU/Linux home directory
(aka "dotfiles" like .bashrc) using GNU Stow.</blockquote>
</div>
<p>我昨天偶然間發現一些我覺得值得分享的經驗,就是那種「爲毛我沒有早點知道這個?」那一類的。
我將在這篇文章中介紹如何使用 GNU Stow 管理你的 GNU/Linux 系統中位於用戶家目錄裏的各種配置文件
(通常又叫「點文件(dotfiles)」比如 .bashrc)。</p>
<a aria-controls="71af1930" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#71af1930" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="71af1930">
<blockquote>
The difficulty is that it would be helpful to manage one's configuration
files with a version control system like Git, Mercurial or Bazaar, but
many/most dotfiles reside at the top-level of your home directory,
where it wouldn't be a good idea to initialize a VCS repository.
Over time I've come across various programs which aim to manage this
for you by keeping all the files in a subdirectory and then installing or
linking them into their appropriate places. None of those programs ever
really appealed to me. They would require a ton of dependencies
(like Ruby and a ton of libraries for it) or they would require me to
remember how to use them, which is difficult when really for such a task
you rarely use the program.</blockquote>
</div>
<p>這件事的困難之處在於,如果能用版本管理系統(VCS, Version Control System)比如
Git, Mercurial(hg), Bazaar(bzr)
管理點文件的話會非常方便,但是這些點文件大部分都位於家目錄的頂級目錄下,
在這個位置不太適合初始化一個版本管理倉庫。這些年下來我試過很多程序,設計目的在於解決這個問題,
幫你把這些配置文件安置在某個下級目錄中,然後安裝或者鏈接這些文件到它們應該在的位置。
嘗試下來這些程序沒有一個真正能打動我。它們要麼有很多依賴(比如 Ruby 和一大坨庫),
要麼需要我記住如何用它,考慮到同步配置這種不算經常使用的場合,要記住用法真的挺難。</p>
<a aria-controls="f79eff90" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#f79eff90" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="f79eff90">
<blockquote>
Lately I've been using GNU Stow to manage programs I install from source to
/usr/local/. Basically, in this typical usage, you install locally built
packages to /usr/local/stow/${PKGNAME}-{PKGVERSION} and then
from /usr/local/stow/ you run # stow ${PKGNAME}-${PKGVERSION} and the
program generates symbolic links to all the programs' files into the
appropriate places under /usr/local/. Then, when you uninstall a program
via Stow, you don't have to worry about any stray files that you or a
provide Makefile may have missed. It also makes handling alternate versions
of a program quite easy (i.e. when I'm experimenting with different
configurations of dwm or st).</blockquote>
</div>
<p>最近我在用 GNU Stow 來管理我從源代碼在本地編譯安裝到 <code class="code">
/usr/local/</code>
中的一些程序。
基本上說,在這種常見用法下,是你把這些本地編譯的包配置安裝到
<code class="code">
/usr/local/stow/${PKGNAME}-{PKGVERSION}</code>
這樣的位置,然後在
<code class="code">
/usr/local/stow/</code>
目錄中執行 <code class="code">
# stow ${PKGNAME}-${PKGVERSION}</code>
,然後它就會爲程序所有的文件創建符號鏈接放在 <code class="code">
/usr/local</code>
中合適的地方。然後當你想用 Stow 卸載這個程序的時候,就不必再考慮會留下什麼垃圾文件,
或者找不到安裝時用的 Makefile 了。這種安裝方式下也可以非常容易地切換一個程序的不同版本
(比如我想嘗試不同配置選項下的 <a class="reference external" href="https://dwm.suckless.org/">dwm</a> 或者
<a class="reference external" href="https://st.suckless.org/">st</a> 的時候)。</p>
<a aria-controls="de09a7d8" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#de09a7d8" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="de09a7d8">
<blockquote>
Some time ago I happened across a mailing list posting where someone
described using Stow to manage the installation of their dotfiles.
I didn't pay much attention to it but my brain must have filed it away
for later. Yesterday I decided to give it a try and I have to say that
it is so much more convenient than those other dedicated dotfile-management
programs, even if it wasn't an immediately obvious option.</blockquote>
</div>
<p>前段時間在我掃郵件列表的時候,看到某個帖子中某人在說使用 Stow 管理安裝他的點文件。
當時我沒特別在意這個帖子,但是大概我大腦潛意識把它歸檔保存爲今後閱讀了。
昨天我想起來試試這種用法,試過後我不得不說,這比那些專門設計用來做這任務的點文件管理器要方便太多了,
雖然表面上看起來這種用法沒那麼顯而易見。</p>
<a aria-controls="2e827562" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#2e827562" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="2e827562">
<blockquote>
The procedure is simple. I created the ${HOME}/dotfiles directory and then
inside it I made subdirectories for all the programs whose cofigurations
I wanted to manage. Inside each of those directories, I moved in all the
appropriate files, maintaining the directory structure of my home directory.
So, if a file normally resides at the top level of your home directory,
it would go into the top level of the program's subdirectory.
If a file normally goes in the default ${XDG_CONFIG_HOME}/${PKGNAME}
location (${HOME}/.config/${PKGNAME}), then it would instead go in
${HOME}/dotfiles/${PKGNAME}/.config/${PKGNAME} and so on.
Finally, from the dotfiles directory, you just run $ stow $PKGNAME and
Stow will symlink all the package's configuration files to the appropriate
locations. It's then easy to make the dotfiles a VCS repository so you can
keep track of changes you make (plus it makes it so much easier to share
configurations between different computers, which was my main reason to
do it).</blockquote>
</div>
<p>方法很簡單。我建了個 <code class="code">
${HOME}/dotfiles</code>
文件夾,然後在裏面爲我想管理的每個程序配置都
創建一個子文件夾。然後我把這些程序的配置從原本的家目錄移動到這每一個對應的子文件夾中,
並保持它們在家目錄中的文件夾結構。比如,如果某個文件原本應該位於家目錄的頂層文件夾裏,
那它現在應該放在這個程序名子目錄的頂層文件夾。如果某個配置文件通常應該位於默認的
<code class="code">
${XDG_CONFIG_HOME}/${PKGNAME}</code>
位置 (<code class="code">
${HOME}/.config/${PKGNAME}</code>
),
那麼現在它應該放在 <code class="code">
${HOME}/dotfiles/${PKGNAME}/.config/${PKGNAME}</code>
,如此類推。然後在那個 dotfiles 文件夾裏面,直接運行 <code class="code">
$ stow $PKGNAME</code>
命令,
Stow 就會爲你自動創建這些配置文件的符號鏈接到合適的位置。接下來就很容易爲這個 dotfiles
目錄初始化版本管理倉庫,從而記錄你對這些配置文件做的修改(並且這也可以極度簡化在不同電腦之間
共享配置,這也是我想要這麼做的主要原因)。</p>
<a aria-controls="5e0e831d" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#5e0e831d" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="5e0e831d">
<blockquote>
For example, let's say you want to manage the configuration for Bash,
VIM and Uzbl. Bash has a couple files in the top-level directory;
VIM typically has your .vimrc file on the top-level and a .vim directory;
and Uzbl has files in ${XDG_CONFIG_HOME}/uzbl and ${XDG_DATA_HOME}/uzbl.
So, your home directory looks like this:</blockquote>
</div>
<p>舉個例子,比如說你想管理 Bash, VIM, Uzbl 這三個程序的配置文件。Bash 會在家目錄的頂層文件夾
放幾個文件; VIM 通常會有在頂層文件夾的 .vimrc 文件和 .vim 目錄;然後 Uzbl 的配置位於
<code class="code">
${XDG_CONFIG_HOME}/uzbl</code>
以及 <code class="code">
${XDG_DATA_HOME}/uzbl</code>
。於是在遷移配置前,你的家目錄的文件夾結構應該看起來像這樣:</p>
<div class="highlight"><pre><span class="code-line"><span></span>home/</span>
<span class="code-line"> brandon/</span>
<span class="code-line"> .config/</span>
<span class="code-line"> uzbl/</span>
<span class="code-line"> [...some files]</span>
<span class="code-line"> .local/</span>
<span class="code-line"> share/</span>
<span class="code-line"> uzbl/</span>
<span class="code-line"> [...some files]</span>
<span class="code-line"> .vim/</span>
<span class="code-line"> [...some files]</span>
<span class="code-line"> .bashrc</span>
<span class="code-line"> .bash_profile</span>
<span class="code-line"> .bash_logout</span>
<span class="code-line"> .vimrc</span>
</pre></div>
<a aria-controls="8c2a2e91" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#8c2a2e91" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="8c2a2e91">
<blockquote>
You would then create a dotfiles subdirectory and move all the files there:</blockquote>
</div>
<p>然後遷移配置的方式是,應該建一個 dotfiles 子目錄,然後像這樣移動所有配置文件:</p>
<div class="highlight"><pre><span class="code-line"><span></span>home/</span>
<span class="code-line"> /brandon/</span>
<span class="code-line"> .config/</span>
<span class="code-line"> .local/</span>
<span class="code-line"> .share/</span>
<span class="code-line"> dotfiles/</span>
<span class="code-line"> bash/</span>
<span class="code-line"> .bashrc</span>
<span class="code-line"> .bash_profile</span>
<span class="code-line"> .bash_logout</span>
<span class="code-line"> uzbl/</span>
<span class="code-line"> .config/</span>
<span class="code-line"> uzbl/</span>
<span class="code-line"> [...some files]</span>
<span class="code-line"> .local/</span>
<span class="code-line"> share/</span>
<span class="code-line"> uzbl/</span>
<span class="code-line"> [...some files]</span>
<span class="code-line"> vim/</span>
<span class="code-line"> .vim/</span>
<span class="code-line"> [...some files]</span>
<span class="code-line"> .vimrc</span>
</pre></div>
<a aria-controls="a3ea20b7" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#a3ea20b7" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="a3ea20b7">
<blockquote>
Then, perform the following commands:</blockquote>
</div>
<p>然後執行以下命令:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> <span class="nb">cd</span> ~/dotfiles</span>
<span class="code-line"><span class="gp">$</span> stow bash</span>
<span class="code-line"><span class="gp">$</span> stow uzbl</span>
<span class="code-line"><span class="gp">$</span> stow vim</span>
</pre></div>
<a aria-controls="6a03f8ee" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#6a03f8ee" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="6a03f8ee">
<blockquote>
And, voila, all your config files (well, symbolic links to them) are all
in the correct place, however disorganized that might be, while the actual
files are all neatly organized in your dotfiles directory, which is easily
turned into a VCS repo. One handy thing is that if you use multiple
computers, which may not have the same software installed on them,
you can pick and choose which configurations to install when you need them.
All of your dotfiles are always available in your dotfiles directory,
but if you don't need the configuration for one program, you simply don't
Stow it and thus it does not clutter your home directory.</blockquote>
</div>
<p>然後,瞬間,所有你的配置文件(的符號鏈接)就安安穩穩地放入了它們該在的地方,無論原本這些目錄結構
有多麼錯綜複雜,這樣安排之後的 dotfiles 文件夾內的目錄結構立刻整理得有條有理,
並且可以很容易地轉換成版本控制倉庫。非常有用的一點是,如果你有多臺電腦,可能這些電腦並沒有
安裝完全一樣的軟件集,那麼你可以手選一些你需要的軟件配置來安裝。在你的 dotfiles 文件夾中總是
可以找到所有的配置文件,但是如果你不需要某個程序的某份配置,那你就不對它執行 stow
命令,它就不會擾亂你的家目錄。</p>
<a aria-controls="68a93e12" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#68a93e12" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="68a93e12">
<blockquote>
Well, that's all there is to it. Hopefully someone else out there finds
this useful! I know I've found it to be a huge help.</blockquote>
</div>
<p>嗯,以上就是整個用法介紹。希望能有別人覺得這個用法有用!我知道對我來說這個非常有幫助。</p>
</div>
爲什麼 Linus Torvalds 不願意將 Linux 變成 GPLv3 授權?2016-08-08T16:15:00+09:002016-08-08T16:15:00+09:00farseerfctag:farseerfc.me,2016-08-08:/why-linus-torvalds-undermine-gplv3.html<p>從 <a class="reference external" href="https://www.zhihu.com/question/48884264/answer/113454129">知乎</a> 轉載</p>
<p>和上篇文章一樣,這篇也是來自一個知乎上我回答的問題。</p>
<p>原問題:为什么 Linus Torvalds 不愿意将 Linux 变成 GPLv3 授权?</p>
<div class="panel panel-default">
<div class="panel-heading">
DebConf 14: Q&A with Linus Torvalds</div>
<div class="panel-body">
<div class="well" style="padding: 0">
<div class="tab-content" id="youtubeku">
<div class="tab-pane fade active in" id="youtube_1Mg5_gxNXTo">
<div align="left" class="youtube embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" src="https://www.youtube.com/embed/1Mg5_gxNXTo"></iframe> </div>
</div>
<div class="tab-pane fade" id="youku_XMTY3NjIzNDU0NA">
<div align="left" class="youku embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" height="498" src="https://player.youku.com/embed/XMTY3NjIzNDU0NA" width="510"></iframe> </div>
</div>
</div>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#youtube_1Mg5_gxNXTo">Youtube</a></li>
<li><a data-toggle="tab" href="#youku_XMTY3NjIzNDU0NA">Youku</a></li>
</ul>
</div>
</div>
</div>
<p>我的回答:</p>
<blockquote>
<p>這裏有段 Linus Torvalds 在 DebConf 14 上的 Q&A:
<a class="reference external" href="https://youtu.be/1Mg5_gxNXTo?t=47m20s">https://youtu.be/1Mg5_gxNXTo?t=47m20s</a></p>
<p>其中關於 GPLv3 和協議的那一段在47:20開始到57:00左右。
裏面 Linus 對自己的觀點澄清得很清楚了。
看u2b或者聽英語有困難的請留評論,我抽空可以試着翻譯一下。</p>
</blockquote>
<div class="section" id="id2">
<h2>然後接下來就是我承諾的翻譯了 …</h2></div><p>從 <a class="reference external" href="https://www.zhihu.com/question/48884264/answer/113454129">知乎</a> 轉載</p>
<p>和上篇文章一樣,這篇也是來自一個知乎上我回答的問題。</p>
<p>原問題:为什么 Linus Torvalds 不愿意将 Linux 变成 GPLv3 授权?</p>
<div class="panel panel-default">
<div class="panel-heading">
DebConf 14: Q&A with Linus Torvalds</div>
<div class="panel-body">
<div class="well" style="padding: 0">
<div class="tab-content" id="youtubeku">
<div class="tab-pane fade active in" id="youtube_1Mg5_gxNXTo">
<div align="left" class="youtube embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" src="https://www.youtube.com/embed/1Mg5_gxNXTo"></iframe> </div>
</div>
<div class="tab-pane fade" id="youku_XMTY3NjIzNDU0NA">
<div align="left" class="youku embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" height="498" src="https://player.youku.com/embed/XMTY3NjIzNDU0NA" width="510"></iframe> </div>
</div>
</div>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#youtube_1Mg5_gxNXTo">Youtube</a></li>
<li><a data-toggle="tab" href="#youku_XMTY3NjIzNDU0NA">Youku</a></li>
</ul>
</div>
</div>
</div>
<p>我的回答:</p>
<blockquote>
<p>這裏有段 Linus Torvalds 在 DebConf 14 上的 Q&A:
<a class="reference external" href="https://youtu.be/1Mg5_gxNXTo?t=47m20s">https://youtu.be/1Mg5_gxNXTo?t=47m20s</a></p>
<p>其中關於 GPLv3 和協議的那一段在47:20開始到57:00左右。
裏面 Linus 對自己的觀點澄清得很清楚了。
看u2b或者聽英語有困難的請留評論,我抽空可以試着翻譯一下。</p>
</blockquote>
<div class="section" id="id2">
<h2>然後接下來就是我承諾的翻譯了</h2>
<a aria-controls="ebbd22ff" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#ebbd22ff" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="ebbd22ff">
<blockquote>
Q: Do you agree that you undermine GPLv3? and ...</blockquote>
</div>
<p>問:你是否同意說你貶低了 GPLv3 ? 以及……</p>
<a aria-controls="505c14ba" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#505c14ba" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="505c14ba">
<blockquote>
L: Yes</blockquote>
</div>
<p>L: 是的</p>
<a aria-controls="97e91d0f" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#97e91d0f" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="97e91d0f">
<blockquote>
Q: How can we get you to stop?</blockquote>
</div>
<p>問:我們如何纔能讓你別這麼做?</p>
<a aria-controls="77f7155c" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#77f7155c" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="77f7155c">
<blockquote>
L: What?</blockquote>
</div>
<p>L: 什麼?</p>
<a aria-controls="d3591d99" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#d3591d99" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="d3591d99">
<blockquote>
Q: ...How can we get you to stop?</blockquote>
</div>
<p>問:……我們如何纔能讓你別這麼做?</p>
<a aria-controls="7b4e5b8a" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#7b4e5b8a" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="7b4e5b8a">
<blockquote>
L: Oh I hate GPLv3. I undermined it on purpose.
I actually thought the GPLv3 extensions were horrible.
I understand why people would want to do them but I think it should have
been a completely new license.</blockquote>
</div>
<p>L: 哦我討厭 GPLv3 ,我是在故意貶低它。實際上我覺得 GPLv3 的擴展非常可怕。
我能理解爲什麼人們想要做這個,但是我覺得它本應是一個全新的協議。</p>
<a aria-controls="20696190" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#20696190" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="20696190">
<blockquote>
Emm my argument for liking version 2, and I still think version 2 is a
great license, was that, "I give you source code, you give me your
changes back, we are even." Right? That's my take on GPL version 2, right,
it's that simple.</blockquote>
</div>
<p>嗯我喜歡版本 2 的那些理由,並且我仍然覺得版本 2 是一個非常棒的協議,
理由是:「我給你源代碼,你給我你對它的修改,我們就扯平了」
對吧?這是我用 GPL 版本 2 的理由,就是這麼簡單。</p>
<a aria-controls="7565a2bc" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#7565a2bc" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="7565a2bc">
<blockquote>
And version 3 extended that in ways that I personally am really
uncomfortable with, namely "I give you source code, that means that if
you use that source code, you can't use it on your device unless you
follow my rules." And to me that's, that's a violation of everything
version 2 stood for. And I understand why the FSF did it because I know
what the FSF wants. But to me it's not the same license at all.</blockquote>
</div>
<p>然後版本 3 的擴展在某些方面讓我個人覺得非常不舒服,也就是說「我給你源代碼,
這意味着你必須服從我的一些規則,否則你不能把它用在你的設備上。」
對我來說,這是違反了版本 2 協議所追求的所有目的。然而我理解爲什麼 FSF 要這麼做,
因爲我知道 FSF 想要達成什麼,但是對我來說這完全是不同的協議了。</p>
<a aria-controls="f0b4e1fb" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#f0b4e1fb" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="f0b4e1fb">
<blockquote>
So I was very upset and made it very clear, and this was months before
version 3 was actually published. There was a discussion about this
long before... There was an earlier version of version 3, years before
actually, where I said "No, this is not gonna fly."
And during that earlier discussion I had already added to the kernel that,
"Hey, I don't have the version 2 or later". And there was no...
And I was really happy then when version 3 came out, that I have done that
something like 5 years before, because there was ever never any question
about what the license for the kernel was.</blockquote>
</div>
<p>所以我當時非常不安,並且表明了自己的觀點,並且這是在版本 3 發佈的數月之前。
在那很久之前曾經有過一場討論……在版本 3 之前有一個早期的版本,
事實上幾年之前,那時我就說過:「不,這不可能工作」。
並且在那個早期的討論階段我已經在內核裏寫好了「嘿,我可沒有寫過版本 2
或者更高版本」。所以之後也沒有過(爭議)……隨後版本 3 出來的時候我非常開心,
因爲我早在大概 5 年前做了預防,之後也就再也沒有過關於內核的協議究竟是哪個
版本的討論。</p>
<a aria-controls="32e9bcca" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#32e9bcca" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="32e9bcca">
<blockquote>
But I actually thought that version 3 is ... Uh, no ... I actually think
version 3 is a <strong>FINE</strong> license, right. I'm a firm believer in,
"If you write your code, it is your choice to pick a license."
And version 3 is a fine license. Version 3 was not a good ...
"Here we give you version 2, and then we tried to sneak in these new rules,
and tried to force everybody to upgrade." That was the part I disliked.
And the FSF did some really sneaky stuff, downright immoral in my opinion.</blockquote>
</div>
<p>不過事實上我覺得版本 3 是……呃不……我事實上覺得版本 3 是個 <strong>不錯</strong> 的協議,
對吧。我堅定地相信「如果是你寫的代碼,那麼你有權利決定它應該用什麼協議」。
並且版本 3 是個不錯的選擇。版本 3 不好的地方在……「我們給你了版本 2
,然後我們試圖偷偷混入這些新的規則,並且想逼着所有人都跟着升級」這是我不喜歡版本
3 的地方。並且 FSF 在其中做了很多見不得人的事情,我覺得做得很不道德。</p>
<a aria-controls="318f390b" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#318f390b" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="318f390b">
<blockquote>
Q: So you are talking about <a class="reference external" href="https://en.wikipedia.org/wiki/Tivoization">Tivoization</a>?</blockquote>
</div>
<p>問:所以你在說 <a class="reference external" href="https://en.wikipedia.org/wiki/Tivoization">Tivoization</a> 的事情麼?</p>
<div class="panel panel-default">
<div class="panel-heading">
譯註: 關於 <a class="reference external" href="https://en.wikipedia.org/wiki/Tivoization">Tivoization</a></div>
<div class="panel-body">
Tivoization 是 FSF 發明的一個詞,表示 TiVo 的做法。 TiVo
是一個生產類似電視機頂盒之類的設備的廠商,他們在他們的設備中用到了 Linux
內核和很多別的開源組件,並且他們根據 GPLv2 協議開放了他們使用的組件的源代碼。
然而他們在他們出售的設備中增加了數字簽名,驗證正在執行的系統和軟件是他們自己
編制的軟件,從而限制了用戶修改運行軟件的自由。這種做法在 FSF 看來是鑽了 GPLv2
的法律上的空子,所以 FSF 提出了 GPLv3 封堵這種做法。</div>
</div>
<a aria-controls="db80a74e" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#db80a74e" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="db80a74e">
<blockquote>
L: Ehmm, yeah the Tivoization is always my main, eh dislike of version 3.
And, the FSF was being very dishonest thing. "Hey, we actually allow you
to invalidate the Tivoization clause" and they tried to, they literally
lied to people, and say "Hey, so that means that you can use GPLv3 without
the Tivoization part", right. This is ... How many people heard this
particular statement from the FSF? (Please raise your hands)</blockquote>
</div>
<p>L: 沒錯,Tivoization 的事情一直是我反對版本 3 的主要根據。並且,FSF
在這件事上表現得極不誠實。「嘿,其實我們允許你無效化 Tivoization 條款」,這樣他們試圖,
應該說他們是在明白着欺騙別人,並且說「嘿,這意味着你可以使用除去 Tivoization 部分的 GPLv3」。
這很……在場的諸位中有誰從 FSF 那兒聽過這個說法?(請舉手)</p>
<a aria-controls="d4a2f0a6" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#d4a2f0a6" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="d4a2f0a6">
<blockquote>
Ok, maybe they only tried to convince me with that one.
But they did try. And it was like, "I'm not stupid", right. Yes, you can
... The GPLv3 allows you to say "Ok, Tivoization is not an issue for us".
But it allows somebody else to take the project, and say "Hey, I ... The
GPLv3 without Tivoization is compatible with the full GPLv3, so I will now
make my own fork of this, and I will start doing drivers that use the full
version of version 3" And where am I stuck then? I am stuck saying "Hey I
give you the source code, and now I can't take it back your changes".
That's completely against the whole point of the license in the first
place.</blockquote>
</div>
<p>好吧,或許他們只試過對我用這套說辭,但是他們真的試過。我的反應是「我可不傻」,對吧。是的,
的確你可以…… GPLv3 允許你說「好, Tivoization 的事情對我們來說不是問題」,
但是它同時又允許別人接過這個項目,並且說「嘿,我覺得……去掉了 Tivoization 的 GPLv3
是兼容完整的 GPLv3 的,所以我可以 fork 這個項目,然後我將在自己的 fork 上用完整的
GPLv3 寫驅動。」然後我就囧了。我的困境在於說「嘿,我給了你我的源代碼,現在我卻不能拿回你對它
的修改了」。這是徹底違背了我用這個協議最初的目的了。</p>
<a aria-controls="2f77aec5" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#2f77aec5" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="2f77aec5">
<blockquote>
So the FSF was, I mean the kind of stuff that was going on behind the
scenes, ah, made me once and for all to decide to never had any thing to
do with the FSF again. So if you wanted to give money to an organization
that does good? Give it to the EFF. The FSF is full of crazy bittered
people. That's just mine opinion. Uh, actually I have ... Ah ...
I overstated that a bit, right. The FSF has a lot of nice people in it,
but some of them are bit too extreme.</blockquote>
</div>
<p>所以 FSF 是,我是說那時他們暗地裏做的那些事情,讓我當下決定永遠不再和 FSF 有任何瓜葛。
所以如果你想捐錢給一個行善的組織,那就捐給 EFF 吧。FSF 充滿了瘋狂難處的人。這只是我的觀點。
呃其實我……嗯……我說得有點過分了。FSF 裏有很多不錯的人,不過其中有些人有點過激。</p>
<a aria-controls="51874275" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#51874275" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="51874275">
<blockquote>
Q: Well I wish the EFF care more about software freedom. But, uh,
can you ... Do you think that Tivoization benefits me as a user somehow?</blockquote>
</div>
<p>問: 嗯我也希望 EFF 能更多的關注於軟件的自由方面。但是你能……你覺得 Tivoization
這種行爲也能在某種方式上讓我作爲用戶獲益麼?</p>
<a aria-controls="489be787" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#489be787" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="489be787">
<blockquote>
L: No, no I don't. I mean that ... But that was never my argument. That
was not why I selected the GPLv2. This is my whole point. It's not that
I think Tivoization is necessarily something that you should strive for.
But it is something that in my world view, it's your decision.
If you make hardware that locks down the software, that's your decision
as a hardware maker. That has no impact on my decision as a software maker
to give you the software. Do you see where I am coming from? I don't like
the locked down hardware, but at the same time that was never the social
contract I intended with Linux.</blockquote>
</div>
<p>L: 不,我不覺得。我的意思是……這從來都不是我的論據,這不是我選擇了 GPLv2 的理由。
並不是說我覺得 Tivoization 是某種值得你去爭取的權利,而是說在我的世界觀中,這是你的決定。
如果你生產硬件去鎖住了其中的軟件,這是你作爲一個硬件提供者的決定。
這完全不影響我作爲一個軟件提供者給你軟件的決定。你能看出我的立場在哪兒了麼?
我不喜歡上鎖的硬件,但是同時這也從來不是我想要給 Linux 加上的的社會契約。</p>
<a aria-controls="d4934e2e" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#d4934e2e" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="d4934e2e">
<blockquote>
To me, umm, I mean, people may or may not
realize GPLv2 wasn't even the first license for Linux.
To me the important part was always "I give you software, you can do
whatever you want with it. If you making improvements, you have to give
them back." That was the first version of the license. It also had a
completely broken clause which was completely insane and I was stupid.
Hey it happened. My origin license says that you can't make money
change hands. And that was a mistake. That was clearly just wrong and bad
because it really didn't have anything to do with what I wanted. But I
was young, I was poor, I didn't realize that the whole money thing wasn't
the important part. And I have saw the errors in my ways, I saw the GPLv2
and said "Hey, that's the perfect license". And I saw the GPLv3 and I said
"No, that's overreaching a lot, that's not what I wanted". And so I made
Linux GPLv2 only, right.</blockquote>
</div>
<p>對我來說,呃我想說,大家可能知道或者不知道, GPLv2 並不是 Linux 的最初的協議。
對我來說重要的部分一直是「我給你軟件,你可以用它做任何你想要做的事情。如果你做了任何改進,
你需要把它交還給我。」這是協議最初的樣子。最早的協議還有一條完全錯誤的條款,寫得完全不合理,
那時我很傻。嘿我也傻過。我最初的協議說你不能用它賺錢。這是失策,這明顯是不對的不好的,
因爲它和我真正想要做的事情沒有任何關係。但是那時我很傻很天真,
我沒意識到錢的事情在其中完全不重要。然後我發現了其中的問題,我看到了 GPLv2 然後說「嘿,
這是個完美的協議」。然後我看到了 GPLv3 我說「不,這做得過分了,這不是我想要的」
所以我讓 Linux 成爲了僅限 GPLv2 ,對吧。</p>
<a aria-controls="9e4f3a4f" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#9e4f3a4f" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="9e4f3a4f">
<blockquote>
Q: So do you think getting the patches back is as useful even if you can't
modify the device that it is used on?</blockquote>
</div>
<p>問: 所以你是否認爲,即使你不能修改跑着這個軟件的設備,拿回對軟件的修改也還是同樣重要的?</p>
<a aria-controls="1eafb32a" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#1eafb32a" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="1eafb32a">
<blockquote>
L: Yeah, absolutely. And I mean TiVo itself is actually an example of this.
Their patches were kind of crafty but I mean they were basically running
on a, originally a fairly standard MIPS thing. And their patches were
working around bugs in the chipsets they used. And they were valid patches.
The fact that they then felt that their hardware had to be locked down
someway. I didn't like it. But as I have mentioned, I felt that that was
their decision.</blockquote>
</div>
<p>L: 是的,當然。我想說 TiVo 它自己實際上就是一個例子。他們的修改有點複雜,但是我想說他們基本
是,一開始基本是運行在一套相當標準的 MIPS 設備上。然後他們的修改是想繞開他們用到的芯片上的
一些問題,並且這些是合格的修改。之後的事情是他們覺得他們需要鎖住他們的硬件,我不喜歡這個。
但是就像我已經說的,我覺得這是他們的決定。</p>
<a aria-controls="93f9460e" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#93f9460e" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="93f9460e">
<blockquote>
And they had real reasons for that. That's something people sometimes
missed. There are sometimes reasons to do what TiVo did. Sometimes it's
imposed on you by, wireless carriers. Sometimes it's imposed on you by
Disney. Uh sometimes it's imposed on you by laws. The GPLv3 actually
accepts the last one when it comes to things like medical equipment
I think. But the point is that the whole Tivoization thing is, sometimes
it's, there is a reason for it. And if you make ... I mean I am not a
hardware designer. I think FPGA and stuff like that is really cool.
But I always ... I mean I really don't want to impose my world view on
anybody else. You don't have to use Linux. If you do use Linux, the only
thing I asked for is source code back. And there is all these other
verbiages in the GPLv2 about exact details, those aren't important.
And that was always my standpoint.</blockquote>
</div>
<p>並且他們有真正的理由去這麼做。這是有時人們忽視的地方。有時是真的有理由去做 TiVo
他們做的事情。有時強加給你這種限制的是,無線運營商。有時強加給你的是迪士尼。
有時強加給你限制的甚至是法律。 GPLv3 在醫療設備之類的場合其實允許最後一種情況,我記得。
我的觀點是,整個 Tivoization 的事情有時是有理由去這麼做的。如果你生產……
我是說我不是硬件設計者,我覺得 FPGA 之類的東西很酷,但是我……我的意思是我真的不想把我對世界的
看法強加給別人。你不是非得要用 Linux ,如果你想要用 Linux
,那麼我唯一要求你做的事情是把源代碼(變更)還給我。然後在 GPLv2
中還有很多繁文縟節規定了詳細的細節,這些都不重要。這是我一直以來的觀點。</p>
<a aria-controls="1bed26a3" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#1bed26a3" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="1bed26a3">
<blockquote>
Q: Ok, well I will stop my non-point of making noise now.</blockquote>
</div>
<p>問: 好吧那我就不浪費時間了。</p>
<div class="panel panel-default">
<div class="panel-heading">
譯註: 關於 <a class="reference external" href="https://zh.wikipedia.org/wiki/ISC%E8%A8%B1%E5%8F%AF%E8%AD%89">ISC 協議</a></div>
<div class="panel-body">
ISC 協議是一個開源軟件協議,和兩句的 BSD 協議功能相同。OpenBSD 項目選擇儘量用 ISC
協議公開他們新寫的代碼。</div>
</div>
<a aria-controls="a8a95a78" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#a8a95a78" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="a8a95a78">
<blockquote>
L: I mean don't get me ... I mean I like other licenses too. I have used
like the four, emmm... Which BSD license is the acceptable one?
One of the BSD license is actually really nice. And it's actually the...
What?</blockquote>
</div>
<p>L: 我的意思是別誤解……我也喜歡別的協議。我用過……到底是哪個 BSD 協議是可以接受的?
有一個 BSD 協議實際上非常不錯。它實際上是……什麼?</p>
<a aria-controls="4624145d" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#4624145d" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="4624145d">
<blockquote>
A: ISC</blockquote>
</div>
<p>觀衆: ISC</p>
<a aria-controls="b1cbe308" aria-expanded="false" class="translate-collapse-btn" data-toggle="collapse" href="#b1cbe308" role="button">
<span class="badge badge-pill badge-light"><i class="fa fa-language"></i> </span></a><div class="collapse" id="b1cbe308">
<blockquote>
L: ISC? And I actually encourage people who don't care about the giving
code back but care about the "Hey, I did something cool, please use it".
I encourage people to use the BSD license for that. And I mean the BSD
license is wonderful for that. It so happens that I thought that for my
project the giving back is equally important so I, for me BSD is bad.
But the point is <strong>for me</strong>. The GPLv3 maybe the perfect license for what
you guys want to do. And that's fine. And then it's the license you should
use. It's just that when somebody else wrote the code you don't get that
choice.</blockquote>
</div>
<p>L: ISC?並且事實上我在鼓勵那些不在意拿回修改但是在意「嘿,我做了一個很酷的東西,請用它」。
我鼓勵這些人去用 BSD 協議做這些事情。我想說 BSD 協議在這種場合是完美的。
只是碰巧我覺得對於我的項目,拿回修改也同樣重要,所以對我而言 BSD 不好。但是重點是
<strong>對我而言</strong> 。 GPLv3 可能對你們想要做的事情而言是完美的協議,這很好,並且這時你就應該去用
GPLv3 。只是當代碼是別人寫的時候,你沒有這個選擇權。</p>
</div>
PacVis: 可視化 pacman 本地數據庫2016-07-31T03:52:00+09:002016-07-31T03:52:00+09:00farseerfctag:farseerfc.me,2016-07-31:/pacvis.html
<div class="panel panel-default">
<div class="panel-heading">
PacVis</div>
<div class="panel-body">
<img alt="Demo of PacVis" class="img-responsive" src="//farseerfc.me/images/pacvis-first.png"/>
</div>
</div>
<div class="section" id="pacvis">
<h2><a class="toc-backref" href="#id11">我爲什麼要做 PacVis</a></h2>
<p>我喜歡 Arch Linux ,大概是因爲唯有 Arch Linux 能給我對整個系統「瞭如指掌」的感覺。
在 Arch Linux 裏我能清楚地知道我安裝的每一個包,能知道系統裏任何一個文件是來自哪個包,
以及我爲什麼要裝它。或許對 Debian/Fedora/openSUSE 足夠熟悉了之後也能做到這兩點,
不過他們的細緻打包的結果通常是包的數量比 Arch 要多個 3 到 10 倍,並且打包的細節也比 Arch
Linux 簡單的 PKGBUILD 要複雜一個數量級。</p>
<p>每一個裝過 Arch Linux 的人大概都知道,裝了 Arch Linux 之後得到的系統非常樸素,按照
ArchWiki 上的流程一路走下來的話,最關鍵的一條命令就是 <code class="code">
pacstrap /mnt …</code></p></div>
<div class="panel panel-default">
<div class="panel-heading">
PacVis</div>
<div class="panel-body">
<img alt="Demo of PacVis" class="img-responsive" src="//farseerfc.me/images/pacvis-first.png"/>
</div>
</div>
<div class="section" id="pacvis">
<h2><a class="toc-backref" href="#id11">我爲什麼要做 PacVis</a></h2>
<p>我喜歡 Arch Linux ,大概是因爲唯有 Arch Linux 能給我對整個系統「瞭如指掌」的感覺。
在 Arch Linux 裏我能清楚地知道我安裝的每一個包,能知道系統裏任何一個文件是來自哪個包,
以及我爲什麼要裝它。或許對 Debian/Fedora/openSUSE 足夠熟悉了之後也能做到這兩點,
不過他們的細緻打包的結果通常是包的數量比 Arch 要多個 3 到 10 倍,並且打包的細節也比 Arch
Linux 簡單的 PKGBUILD 要複雜一個數量級。</p>
<p>每一個裝過 Arch Linux 的人大概都知道,裝了 Arch Linux 之後得到的系統非常樸素,按照
ArchWiki 上的流程一路走下來的話,最關鍵的一條命令就是 <code class="code">
pacstrap /mnt base</code>
,
它在 <code class="code">
/mnt</code>
裏作爲根調用 <code class="code">
pacman -S base</code>
裝上了整個 base 組,
然後就沒有然後了。這個系統一開始空無一物,你需要的任何東西都是後來一點點用
<code class="code">
pacman</code>
手動裝出來的,沒有累贅,按你所需。</p>
<p>然而時間長了,系統中難免會有一些包,是你裝過用過然後忘記了,
然後這些包就堆在系統的角落裏,就像家裏陳年的老傢俱,佔着地,落着灰。雖然
<code class="code">
pacman -Qtd</code>
能方便地幫你找出所有
<strong>曾經作爲依賴被裝進來,而現在不被任何包依賴</strong> 的包,但是對於那些你手動指定的包,
它就無能爲力了。</p>
<p>於是我就一直在找一個工具能幫我梳理系統中包的關係,方便我:</p>
<ol class="arabic simple">
<li>找出那些曾經用過而現在不需要的包</li>
<li>找出那些體積大而且佔地方的包</li>
<li>釐清系統中安裝了的包之間的關係</li>
</ol>
<div class="figure">
<img alt="Android System Architecture" class="img-responsive" src="//farseerfc.me/images/Android-System-Architecture.jpg"/>
<p class="caption"><a class="reference external" href="https://en.wikipedia.org/wiki/Android_(operating_system)">Android 系統架構</a></p>
</div>
<p>關於最後一點「釐清包的關係」,我曾經看到過
<a class="reference external" href="https://en.wikipedia.org/wiki/Architecture_of_OS_X">macOS 系統架構圖</a>
和 Android 的系統架構圖,對其中的層次化架構印象深刻,之後就一直在想,是否能畫出現代
Linux 桌面系統上類似的架構圖呢?又或者 Linux 桌面系統是否會展現完全不同的樣貌?
從維基百科或者別的渠道能找到 Linux 內核、或者 Linux 圖形棧,
或者某個桌面環境的架構,但是沒有找到覆蓋一整個發行版的樣貌的。
於是我便想,能不能從包的依賴關係中自動生成這樣一張圖呢。</p>
</div>
<div class="section" id="id1">
<h2><a class="toc-backref" href="#id12">PacVis的老前輩們</a></h2>
<p>在開始寫 PacVis 之前,我試過一些類似的工具,他們都或多或少能解決一部分我的需要,
又在某些方面有所不足。這些工具成爲了 PacVis 的雛形,啓發了 PacVis
應該做成什麼樣子。</p>
<div class="section" id="pactree">
<h3><a class="toc-backref" href="#id13">pactree</a></h3>
<p>pactree 曾經是一個
<a class="reference external" href="https://bbs.archlinux.org/viewtopic.php?id=51795">獨立的項目</a> ,現在則是
<a class="reference external" href="https://www.archlinux.org/pacman/pactree.8.html">pacman 的一部分</a> 了。
從手冊頁可以看出, pactree 的輸出是由某個包開始的依賴樹。
加上 <code class="code">
--graph</code>
參數之後 pactree 還能輸出
<a class="reference external" href="http://www.graphviz.org/">dot</a> 格式的矢量圖描述,然後可以用 dot 畫出依賴圖:</p>
<div class="panel panel-default">
<div class="panel-heading">
<code class="code">
pactree pacvis-git -d3 --graph | dot -Tpng >pacvis-pactree.png</code>
</div>
<div class="panel-body">
<img alt="pactree --graph" class="img-responsive" src="//farseerfc.me/images/pacvis-pactree.png"/>
</div>
</div>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> pactree pacvis-git -d3</span>
<span class="code-line"><span class="go">pacvis-git</span></span>
<span class="code-line"><span class="go">├─python-tornado</span></span>
<span class="code-line"><span class="go">│ └─python</span></span>
<span class="code-line"><span class="go">│ ├─expat</span></span>
<span class="code-line"><span class="go">│ ├─bzip2</span></span>
<span class="code-line"><span class="go">│ ├─gdbm</span></span>
<span class="code-line"><span class="go">│ ├─openssl</span></span>
<span class="code-line"><span class="go">│ ├─libffi</span></span>
<span class="code-line"><span class="go">│ └─zlib</span></span>
<span class="code-line"><span class="go">├─pyalpm</span></span>
<span class="code-line"><span class="go">│ ├─python</span></span>
<span class="code-line"><span class="go">│ └─pacman</span></span>
<span class="code-line"><span class="go">│ ├─bash</span></span>
<span class="code-line"><span class="go">│ ├─glibc</span></span>
<span class="code-line"><span class="go">│ ├─libarchive</span></span>
<span class="code-line"><span class="go">│ ├─curl</span></span>
<span class="code-line"><span class="go">│ ├─gpgme</span></span>
<span class="code-line"><span class="go">│ ├─pacman-mirrorlist</span></span>
<span class="code-line"><span class="go">│ └─archlinux-keyring</span></span>
<span class="code-line"><span class="go">└─python-setuptools</span></span>
<span class="code-line"><span class="go"> └─python-packaging</span></span>
<span class="code-line"><span class="go"> ├─python-pyparsing</span></span>
<span class="code-line"><span class="go"> └─python-six</span></span>
<span class="code-line"><span class="gp"> $</span> pactree pacvis-git -d3 --graph <span class="p">|</span> dot -Tpng >pacvis-pactree.png</span>
</pre></div>
<p>從畫出的圖可以看出,因爲有共用的依賴,所以從一個包開始的依賴關係已經不再是一棵
<a class="reference external" href="https://zh.wikipedia.org/wiki/%E6%A8%B9%E7%8B%80%E7%B5%90%E6%A7%8B">圖論意義上的樹(Tree)</a>
了。最初嘗試做 PacVis 的早期實現的時候,就是試圖用 bash/python 腳本解析 pactree 和
pacman 的輸出,在 pactree 的基礎上把整個系統中所有安裝的包全都包含到一張圖裏。
當然後來畫出的結果並不那麼理想,首先由於圖非常巨大,導致 dot
的自動佈局要耗費數小時,最後畫出的圖也過於巨大基本上沒法看。</p>
<p>然而不得不說沒有 pactree 就不會有 PacVis ,甚至 pacman 被分離出 alpm
庫也和 pactree 用 C 重寫的過程有很大關係,而 PacVis 用來查詢 pacman 數據庫的庫
pyalpm 正是 alpm 的 Python 綁定。因爲 pactree 的需要而增加出的 alpm 庫奠定了 PacVis
實現的基石。</p>
</div>
<div class="section" id="pacgraph">
<h3><a class="toc-backref" href="#id14">pacgraph</a></h3>
<div class="panel panel-default">
<div class="panel-heading">
pacgraph 的輸出</div>
<div class="panel-body">
<img alt="pacgraph" class="img-responsive" src="//farseerfc.me/images/pacvis-pacgraph.png"/>
</div>
</div>
<p><a class="reference external" href="http://kmkeen.com/pacgraph/index.html">pacgraph</a> 是一位 Arch Linux 的
Trusted User <a class="reference external" href="http://kmkeen.com/">keenerd</a> 寫的程序,和
PacVis 一樣也是用 Python 實現的。
比起 pactree , pacgraph 明顯更接近我的需求,它默認繪製整個系統的所有安裝包,
並且用聰明的佈局算法解決了 dot 佈局的性能問題。</p>
<p>pacgraph 的輸出是一個富有藝術感的依賴圖,圖中用不同的字體大小表示出了每個包佔用
的磁盤空間。通過觀察 pacgraph 的輸出,我們可以清楚地把握系統全局的樣貌,
比如一眼看出這是個桌面系統還是個服務器系統,並且可以很容易地發現那些佔用磁盤空間
巨大的包,考慮清理這些包以節約空間。</p>
<p>更棒的是 pacgraph 還提供了一個交互性的 GUI 叫做 pacgraph-tk ,顯然通過 tk 實現。
用這個 GUI 可以縮放觀察整幅圖的細節,或者選中某個包觀察它和別的包的依賴關係。</p>
<p>pacgraph 還支持通過參數指定只繪製個別包的依賴關係,就像 pactree 那樣。</p>
<p>不過 pacgraph 也不是完全滿足我的需要。如我前面說過,我希望繪製出的圖能反應
<strong>這個發行版的架構面貌</strong> ,而 pacgraph 似乎並不區別「該包依賴的包」和「依賴該包的包」
這兩種截然相反的依賴關係。換句話說 pacgraph 畫出的是一張無向圖,
而我更想要一張有向圖,或者說是 <strong>有層次結構的依賴關係圖</strong> 。</p>
</div>
</div>
<div class="section" id="id4">
<h2><a class="toc-backref" href="#id15">於是就有了 PacVis</a></h2>
<div class="panel panel-default">
<div class="panel-heading">
PacVis 剛打開的樣子</div>
<div class="panel-body">
<img alt="PacVis on startup" class="img-responsive" src="//farseerfc.me/images/pacvis-second.png"/>
</div>
</div>
<p>總結了老前輩們的優勢與不足,我便開始利用空餘時間做我心目中的 PacVis 。
前後斷斷續續寫了兩個月,又分爲兩個階段,第一階段做了基本的功能和雛形,
第二階段套用上 <a class="reference external" href="https://getmdl.io/">https://getmdl.io/</a> 的模板,總算有了能拿得出手給別人看的樣子。</p>
<p>於是乎前兩天在 AUR 上給 pacvis 打了個
<a class="reference external" href="https://aur.archlinux.org/packages/pacvis-git/">pacvis-git</a>
包,現在想在本地跑 pacvis 應該很方便了,用任何你熟悉的 aurhelper
就可以安裝,也可以直接從 aur 下載 PKGBUILD 打包:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="go">~$ git clone aur@aur.archlinux.org:pacvis-git.git</span></span>
<span class="code-line"><span class="go">~$ cd pacvis-git</span></span>
<span class="code-line"><span class="go">~/pacvis-git$ makepkg -si</span></span>
<span class="code-line"><span class="go">~/pacvis-git$ pacvis</span></span>
<span class="code-line"><span class="go">Start PacVis at http://localhost:8888/</span></span>
</pre></div>
<p>按照提示說的,接下來打開瀏覽器訪問 <a class="reference external" href="http://localhost:8888/">http://localhost:8888/</a> 就能看到 PacVis
的樣子了。僅僅作爲嘗試也可以直接打開跑在我的服務器上的 demo:
<a class="reference external" href="https://pacvis.farseerfc.me/">https://pacvis.farseerfc.me/</a> ,這個作爲最小安裝的服務器載入速度大概比普通的桌面系統快一點。</p>
<div class="panel panel-default">
<div class="panel-heading">
在 Windows msys2 跑 PacVis</div>
<div class="panel-body">
<img alt="PacVis on Windows msys2" class="img-responsive" src="//farseerfc.me/images/pacvis-msys2.png"/>
</div>
</div>
<p>另外補充一下,因爲 PacVis 只依賴 pyalpm 和 tornado ,所以在別的基於 pacman
的系統上跑它應該也沒有任何問題,包括
<a class="reference external" href="https://msys2.github.io/">Windows 上的 msys2</a> 裏(儘管在 msys2 上編譯
tornado 的包可能要花些功夫)。</p>
</div>
<div class="section" id="id5">
<h2><a class="toc-backref" href="#id16">PacVis 的圖例和用法</a></h2>
<p>操作上 PacVis 仿照地圖程序比如 Google Maps 的用法,可以用滾輪或者觸摸屏的手勢
縮放、拖拽,右上角有個側邊欄,不需要的話可以點叉隱藏掉,右下角有縮放的按鈕和
回到全局視圖的按鈕,用起來應該還算直觀。</p>
<div class="figure">
<img alt="PacVis showing pacvis-git" class="img-responsive" src="//farseerfc.me/images/pacvis-pacvis-git.png"/>
<p class="caption">pacvis-git 包的依賴</p>
</div>
<p>先解釋圖形本身,整張圖由很多小圓圈的節點,以及節點之間的箭頭組成。
一個圓圈就代表一個軟件包,而一條箭頭代表一個依賴關係。縮放到細節的話,
能看到每個小圓圈的下方標註了這個軟件包的名字,鼠標懸浮在圓圈上也會顯示相應信息。
還可以點開軟件包,在右側的邊欄裏會有更詳細的信息。</p>
<p>比如圖例中顯示了 pacvis-git 自己的依賴,它依賴 pyalpm, python-tornado 和
python-setuptools ,其中 pyalpm 又依賴 pacman 。圖中用
<span class="label label-primary">紫色</span> 表示手動安裝的包,
<span class="label label-warning">橙色</span> 表示被作爲依賴安裝的包,
箭頭的顏色也隨着包的顏色改變。</p>
<p>值得注意的是圖中大多數箭頭都是由下往上指的,這是因爲 PacVis 按照包的依賴關係做
了拓撲排序,並且給每個包賦予了一個拓撲層級。比如 pacvis-git 位於 39
層,那麼它依賴的 pyalpm 就位於 38 層,而 pyalpm 依賴的 pacman 就位於 37
層。根據層級關係排列包是 PacVis 於 pacgraph 之間最大的不同之處。</p>
<p>除了手動縮放, PacVis 還提供了搜索框,根據包名快速定位你感興趣的包。
以及在右側邊欄中的 Dep 和 Req-By 等頁中,包的依賴關係也是做成了按鈕的形式,
可以由此探索包和包之間的關聯。</p>
<p>最後稍微解釋一下兩個和實現相關的參數:</p>
<div class="label label-info">
Max Level</div>
<p>這是限制 PacVis 載入的最大拓撲層。系統包非常多的時候 PacVis
的佈局算法會顯得很慢,限制層數有助於加快載入,特別是在調試 PacVis 的時候比較有用。</p>
<div class="label label-info">
Max Required-By</div>
<p>這是限制 PacVis 繪製的最大被依賴關係。稍微把玩一下 PacVis 就會發現系統內絕大多數
的包都直接依賴了 glibc 或者 gcc-libs 等個別的幾個包,而要繪製這些依賴的話會導致
渲染出的圖中有大量長直的依賴線,不便觀察。於是可以通過限制這個值,使得 PacVis
不繪製被依賴太多的包的依賴關係,有助於讓渲染出的圖更易觀察。</p>
</div>
<div class="section" id="id6">
<h2><a class="toc-backref" href="#id17">從 PacVis 能瞭解到的一些事實</a></h2>
<div class="panel panel-default">
<div class="panel-heading">
一個 KDE 桌面的 PacVis 結果全圖, <a class="reference external" href="//farseerfc.me/images/pacvis-16384.png">放大(17M)</a></div>
<div class="panel-body">
<img alt="A normal KDE desktop in PacVis" class="img-responsive" src="//farseerfc.me/images/pacvis-4096-anno.png"/>
</div>
</div>
<p>稍微玩一下 PacVis 就能發現不少有趣現象,上述「絕大多數包依賴 glibc 」就是一例。
除此之外還有不少值得玩味的地方。</p>
<div class="section" id="id7">
<h3><a class="toc-backref" href="#id18">依賴層次</a></h3>
<p>系統中安裝的包被明顯地分成了這樣幾個層次:</p>
<ul class="simple">
<li>glibc 等 C 庫</li>
<li>Bash/Perl/Python 等腳本語言</li>
<li>coreutils/gcc/binutils 等核心工具</li>
<li>pacman / systemd 等較大的系統工具</li>
<li>gtk{2,3}/qt{4,5} 等 GUI toolkit</li>
<li>chromium 等 GUI 應用</li>
<li>Plasma/Gnome 等桌面環境</li>
</ul>
<p>大體上符合直觀的感受,不過細節上有很多有意思的地方,比如 zsh 因爲 gdbm
間接依賴了 bash,這也說明我們不可能在系統中用 zsh 完全替代掉 bash。
再比如 python (在 Arch Linux 中是 python3)和 python2 和 pypy
幾乎在同一個拓撲層級。</p>
<div class="figure">
<img alt="zsh depends on bash because of gdbm" class="img-responsive" src="//farseerfc.me/images/pacvis-zsh-bash.png" style="width: 45%;"/>
<p class="caption">zsh 因爲 gdbm 間接依賴了 bash</p>
</div>
<p>不過偶爾顯示的依賴層級不太符合直觀,比如 qt5-base < qt4 < gtk2 < gtk3 。
qt5 因爲被拆成了數個包所以比 qt4 更低級這可以理解,而 gtk 系比 qt
系更高級這一點是很多人(包括我)沒有預料到的吧。</p>
</div>
<div class="section" id="id8">
<h3><a class="toc-backref" href="#id19">循環依賴</a></h3>
<p>有些包的依賴關係形成了循環依賴,一個例子是 freetype2 和 harfbuzz,freetype2
是繪製字體的庫,harfbuzz 是解析 OpenType 字形的庫,兩者對對方互相依賴。
另一個例子是 KDE 的 kio 和 kinit,前者提供類似 FUSE 的資源訪問抽象層,
後者初始化 KDE 桌面環境。</p>
<div class="figure">
<img alt="freetype2 harfbuzz" class="img-responsive" src="//farseerfc.me/images/pacvis-freetype2-harfbuzz.png" style="width: 45%;"/>
<p class="caption">freetype2 和 harfbuzz 之間的循環依賴</p>
</div>
<p>因爲這些循環依賴的存在,使得 PacVis 在實現時不能直接拓撲排序,我採用環探測
算法找出有向圖中所有的環,並且打破這些環,然後再使用拓撲排序。
因此我在圖中用紅色的箭頭表示這些會導致環的依賴關係。</p>
</div>
<div class="section" id="id9">
<h3><a class="toc-backref" href="#id20">有些包沒有依賴關係</a></h3>
<div class="figure">
<img alt="PacVis Level 0" class="img-responsive" src="//farseerfc.me/images/pacvis-level0.png" style="width: 45%;"/>
<p class="caption">man-pages 和 licenses 沒有依賴關係</p>
</div>
<p>有些包既不被別的包依賴,也不依賴別的包,而是孤立在整張圖中,比如
man-pages 和 licenses 。這些包在圖中位於最頂端,拓撲層級是 0 ,我用
<span class="label label-info">藍色</span> 正方形特別繪製它們。</p>
</div>
<div class="section" id="linux">
<h3><a class="toc-backref" href="#id21">只看依賴關係的話 Linux 內核完全不重要</a></h3>
<p>所有用戶空間的程序都依賴着 glibc ,而 glibc 則從定義良好的 syscall 調用內核。
因此理所當然地,如果只看用戶空間的話, glibc 和別的 GNU 組件是整個 GNU/Linux
發行版的中心,而 Linux 則是位於依賴層次中很深的位置,甚至在我的 demo 服務器上
Linux 位於整個圖中的最底端,因爲它的安裝腳本依賴 mkinitcpio
而後者依賴了系統中的衆多組件。</p>
</div>
<div class="section" id="pacman-qtd">
<h3><a class="toc-backref" href="#id22">pacman -Qtd 不能找到帶有循環依賴的孤兒包</a></h3>
<div class="figure">
<img alt="pacman -Qtd cannot find packages with circle dependency" class="img-responsive" src="//farseerfc.me/images/pacvis-circledeps-Qtd.png" style="width: 45%;"/>
<p class="caption">msys2 中帶有循環依賴的孤兒包</p>
</div>
<p>這是我在 msys2 上測試 PacVis 的時候發現的,我看到在渲染的圖中有一片羣島,
沒有連上任何手動安裝的包。這種情況很不正常,因爲我一直在我的所有系統中跑
<code class="code">
pacman -Qtd</code>
找出孤兒包並刪掉他們。放大之後我發現這些包中有一條循環依賴,
這說明 <code class="code">
pacman -Qtd</code>
不能像語言的垃圾回收機制那樣找出有循環依賴的孤兒包。</p>
</div>
</div>
<div class="section" id="id10">
<h2><a class="toc-backref" href="#id23">PacVis 的未來</a></h2>
<p>目前的 PacVis 基本上是我最初開始做的時候設想的樣子,隨着開發逐漸又增加了不少功能。
一些是迫於佈局算法的性能而增加的(比如限制層數)。</p>
<p>今後準備再加入以下這些特性:</p>
<ol class="arabic simple">
<li>更合理的 optdeps 處理。目前只是把 optdeps 關係在圖上畫出來了。</li>
<li>更合理的 <strong>依賴關係抉擇</strong> 。有時候包的依賴關係並不是直接根據包名,而是
<code class="code">
provides</code>
由一個包提供另一個包的依賴。目前 PacVis 用 alpm
提供的方式抉擇這種依賴,於是這種關係並沒有記錄在圖上。</li>
<li>目前的層級關係沒有考慮包所在的倉庫 (core/extra/community/...) 或者包所屬的組。
加入這些關係能更清晰地表達依賴層次。</li>
<li>目前沒有辦法只顯示一部分包的關係。以後準備加入像 pactree/pacgraph 一樣顯示部分包。</li>
</ol>
<p>如果你希望 PacVis 出現某些有趣的用法和功能,也
<a class="reference external" href="https://github.com/farseerfc/pacvis/issues">請給我提 issue</a> 。</p>
</div>
X 中的混成器與 Composite 擴展2015-03-19T17:45:00+09:002015-03-19T17:45:00+09:00farseerfctag:farseerfc.me,2015-03-19:/compositor-in-X-and-compositext.html
<p>在上篇文章 <a class="reference external" href="//farseerfc.me/brief-history-of-compositors-in-desktop-os.html">「桌面系統的混成器簡史」</a>
中我介紹了其它桌面系統中的混成器的發展史和工作原理,
話題回到我們的正題 Linux 系統上,來說說目前 X 中混成器是如何工作的。
這篇文章將比上一篇深入更多技術細節,不想看太多細節的可以直接跳過看 <a class="reference external" href="#id6">結論</a> 。</p>
<div class="section" id="x">
<h2><a class="toc-backref" href="#id8">原始的 X 的繪圖模型</a></h2>
<p>首先,沒有混成器的時候 X 是這樣畫圖的:</p>
<img alt="ditaa diagram" class="ditaa img-responsive" src="//farseerfc.me/uml/e06d011e.png"/>
<p>X 的應用程序沒有統一的繪圖 API 。GTK+ 在 3.0 之後統一用 <a class="reference external" href="http://cairographics.org/">Cairo</a> 繪圖,
而 Cairo 則是基於 PDF 1.4 的繪圖模型構建的,
GTK 的 2.0 和之前的版本中也有很大一部分的繪圖是用 Cairo 進行,
其餘則通過 xlib 或者 xcb 調用 X 核心協議提供的繪圖原語繪圖 …</p></div>
<p>在上篇文章 <a class="reference external" href="//farseerfc.me/brief-history-of-compositors-in-desktop-os.html">「桌面系統的混成器簡史」</a>
中我介紹了其它桌面系統中的混成器的發展史和工作原理,
話題回到我們的正題 Linux 系統上,來說說目前 X 中混成器是如何工作的。
這篇文章將比上一篇深入更多技術細節,不想看太多細節的可以直接跳過看 <a class="reference external" href="#id6">結論</a> 。</p>
<div class="section" id="x">
<h2><a class="toc-backref" href="#id8">原始的 X 的繪圖模型</a></h2>
<p>首先,沒有混成器的時候 X 是這樣畫圖的:</p>
<img alt="ditaa diagram" class="ditaa img-responsive" src="//farseerfc.me/uml/e06d011e.png"/>
<p>X 的應用程序沒有統一的繪圖 API 。GTK+ 在 3.0 之後統一用 <a class="reference external" href="http://cairographics.org/">Cairo</a> 繪圖,
而 Cairo 則是基於 PDF 1.4 的繪圖模型構建的,
GTK 的 2.0 和之前的版本中也有很大一部分的繪圖是用 Cairo 進行,
其餘則通過 xlib 或者 xcb 調用 X 核心協議提供的繪圖原語繪圖。
QT 的情況也是類似,基本上用 QPaint 子系統繪製成位圖然後交給 X 的顯示服務器。
顯示服務器拿到這些繪製請求之後,再在屏幕上的相應位置繪製整個屏幕。
當然還有很多老舊的不用 GTK 或者 QT 的程序,他們則直接調用 X 核心協議提供的繪圖原語。</p>
<p>值得注意一點是 X 上除了沒有統一的繪圖模型,也沒有統一的矢量圖格式。
X 核心協議的繪圖原語提供的是像素單位的繪圖操作,沒有類似 GDI+ 或者 Quartz
提供的 <ruby><rb>設備無關</rb><rp>(</rp><rt>Device Independence</rt><rp>)</rp></ruby> 的「點」的抽象。所以只用 X
的繪圖原語的話,我們可以把 (1,1) 這個像素點塗黑,但是不能把 (0.5, 0.5)
這個點塗黑,這一設計缺陷在
<a class="reference external" href="http://web.mit.edu/~simsong/www/ugh.pdf">Unix Hater's Handbook</a>
中已經被吐槽過了。因爲這個缺陷,所以直接用 X 繪圖原語繪製的圖像不能像
矢量圖那樣進行無損縮放。同樣的缺陷導致 X 繪圖原語繪製的字符不能做到
<ruby><rb>子像素級</rb><rp>(</rp><rt>subpixel-level</rt><rp>)</rp></ruby> <ruby><rb>抗鋸齒</rb><rp>(</rp><rt>anti-aliasing</rt><rp>)</rp></ruby>
(這解釋了默認配置下的 xterm 和
<a class="reference external" href="http://arch.acgtyrant.com/2015/01/05/I-do-not-recommend-urxvt-again-now/">urxvt 中的字體渲染爲什麼難看</a>
)。相比之下 GDI 有對應的 WMF 矢量圖格式, Quartz 有對應的 PDF 矢量圖格式,
而 X 中沒有這樣的格式對應。因爲沒有統一的矢量圖格式,所以無論是 Cairo 、QPaint
還是沒有用這些繪圖庫但是同樣在意字體和曲線渲染效果的程序(比如 Firefox 和
Chromium)都需要首先渲染到內部的 <a class="reference external" href="http://en.wikipedia.org/wiki/X_PixMap">XPixMap</a>
位圖格式,做好子像素渲染和矢量縮放,然後再把渲染好的位圖轉交給 X 圖形服務器。</p>
</div>
<div class="section" id="composite">
<h2><a class="toc-backref" href="#id9">通過 Composite 擴展重定向窗口輸出</a></h2>
<p>2004年發佈的 X11R6.8 版本的 Xorg 引入了
<a class="reference external" href="http://freedesktop.org/wiki/Software/CompositeExt/">Composite 擴展</a>
。這個擴展背後的動機以及前因後果在一篇文章
<a class="reference external" href="http://keithp.com/~keithp/talks/xarch_ols2004/xarch-ols2004-html/">The (Re)Architecture of the X Window System</a>
中有詳細的表述。Composite 擴展允許某個 X 程序做這幾件事情:</p>
<ol class="arabic simple">
<li>通過 <code class="code">
RedirectSubwindows</code>
調用將一個窗口樹中的所有窗口渲染重定向到
<ruby><rb>內部存儲</rb><rp>(</rp><rt>off-screen storage</rt><rp>)</rp></ruby> 。重定向的時候可以指定讓 X
自動更新窗口的內容到屏幕上或者由混成器手動更新。</li>
<li>通過 <code class="code">
NameWindowPixmap</code>
取得某個窗口的內部存儲。</li>
<li>通過 <code class="code">
GetOverlayWindow</code>
獲得一個特殊的用於繪圖的窗口,
在這個窗口上繪製的圖像將覆蓋在屏幕的最上面。</li>
<li>通過 <code class="code">
CreateRegionFromBorderClip</code>
取得某個窗口的邊界剪裁區域(不一定是矩形)。</li>
</ol>
<p>有了 Composite 擴展,一個 X 程序就可以調用這些 API 實現混成器。
這裏有篇 <a class="reference external" href="http://www.talisman.org/~erlkonig/misc/x11-composite-tutorial/">教學解釋如何使用 Composite 擴展</a> 。開啓了混成的 X 是這樣繪圖的:</p>
<img alt="ditaa diagram" class="ditaa img-responsive" src="//farseerfc.me/uml/18f7774d.png"/>
<p>整個 X 的混成器模型與 Mac OS X 的混成器模型相比,有如下幾點顯著的區別:</p>
<ol class="arabic simple">
<li>混成的部分是交由外部的程序完成的,對混成的繪製方式和繪製普通窗口一樣。
出於效率考慮,絕大多數 X 上的混成器額外使用了 XRender 擴展或者
OpenGL/EGL 來加速繪製貼圖。不過即使如此,還是不能避免同樣的位圖(內容不一定完全一致,
比如 X 可以在窗口交給它的位圖上加上邊框然後再返還給混成器) <strong>在不同的三個程序之間來回傳遞</strong> 。</li>
<li><code class="code">
RedirectSubwindows</code>
調用針對的是一個窗口樹,換句話說是一個窗口
及其全部子窗口,不同於 Mac OS X 中混成器會拿到全部窗口的輸出。
這個特點其實並不算是限制,因爲 X 中每個虛擬桌面都有一個根窗口,只要指定這個根窗口
就可以拿到整個虛擬桌面上的全部可見窗口輸出了。
反而這個設計提供了一定的自由度,比如我們可以用這個調用實現一個截圖程序,
拿到某個特定窗口的輸出,而不用在意別的窗口。</li>
<li>爲了讓窗口有輸出,窗口必須顯示在當前桌面上,不能處於最小化
狀態或者顯示在別的虛擬桌面,用 X 的術語說就是窗口必須處於 <ruby><rb>被映射</rb><rp>(</rp><rt>mapped</rt><rp>)</rp></ruby>
的狀態。因此直接用上述方法 <strong>不能得到沒有顯示的窗口的輸出</strong> ,比如不能對最小化的窗口
直接實現 Windows 7 中的 Aero Peak 之類的效果。這個限制可以想辦法繞開,
比如在需要窗口輸出的時候臨時把窗口映射到桌面上,拿到輸出之後再隱藏起來,
不過要實現這一點需要混成器和窗口管理器相互配合。</li>
<li>不像 Mac OS X 的基於 OpenGL Surface 的繪圖模型是 <ruby><rb>設備無關</rb><rp>(</rp><rt>device independent</rt><rp>)</rp></ruby>
的,這裏 X 的繪圖模型是 <ruby><rb>設備相關</rb><rp>(</rp><rt>device dependent</rt><rp>)</rp></ruby> 的。
這既是優點也是缺點。從缺點方面而言,顯示到 X 的位圖輸出因爲設備相關性,
所以嚴格對應顯示器的點陣,並不適合作爲文檔格式打印出來。當然無論是 Cairo
還是 QPaint 都提供了到 PostScript 或者 PDF 後端的輸出,所以實用層面這個並不構成問題。
設備相關這一點的優點在於,繪製到 XPM 位圖的時候,程序和繪圖庫是能拿到輸出設備(顯示器)
的特殊屬性的,從而繪圖庫能考慮不同的色彩、分辨率、 DPI 或者
<ruby><rb>子像素佈局</rb><rp>(</rp><rt>subpixel layout</rt><rp>)</rp></ruby> 這些屬性以提供最好的渲染效果。
Mac OS X 10.4 在設計的時候也曾考慮過提供無極縮放的支持,而這種支持到了 Mac OS X
10.5 中就縮水變成了 Retina 的固定 2 倍縮放。這種局面在 X
上沒有發生正是因爲 X 的繪圖模型的這種設備相關性,而 Mac OS X 的混成器採用的
OpenGL Surface 則無視了這些設備相關的屬性。</li>
</ol>
</div>
<div class="section" id="id5">
<h2><a class="toc-backref" href="#id10">輸入事件的重定向,這可能做到麼?</a></h2>
<p>通過上述 Composite 擴展提供的 API ,混成器可以把窗口的 <strong>輸出</strong> 重定向到自己的窗口上。
但是僅僅重定向輸出,整個 X 還不處於可用狀態,因爲 <strong>沒有重定向輸入</strong> 。
考慮一下用戶試圖用鼠標點擊某個按鈕或者文本框,這時鼠標處於的位置是在 OverlayWindow
上繪製的位置,這個鼠標事件會交給 OverlayWindow ,而用戶期待這個事件被發送給他看到的按鈕上。</p>
<p>需要重定向的事件主要有鍵盤和鼠標事件兩大類(暫時先不考慮觸摸屏之類的額外輸入)。
由於 Composite 擴展並沒有直接提供這方面的重定向 API ,這使得輸入事件處理起來都比較麻煩,</p>
<p>假設要重定向鍵盤事件,混成器需要效仿輸入法框架(fcitx, ibus, scim)
那樣處理一部分按鍵事件並把其餘事件轉給具有輸入焦點的程序。
看看現有的輸入法框架和諸多程序間的問題,我們就能知道這裏的坑有多深。
於是 <strong>大部分 X 的混成器都不處理鍵盤事件重定向</strong>
。再來看重定向鼠標事件,這邊的坑比重定向鍵盤事件的坑更多,
因爲不像重定向窗口輸出那樣只需要考慮 <ruby><rb>頂層</rb><rp>(</rp><rt>top-level</rt><rp>)</rp></ruby> 窗口,
重定向鼠標輸入的時候要考慮所有子窗口(它們有獨立的事件隊列),
以及要準確記錄輸入事件事件發生時的鍵盤組合鍵狀態,還要正確實現 ICCCM/EWMH
中描述的轉交窗口焦點的複雜規則,所有這些都已經在 X 中實現過的事情需要重新實現一遍。</p>
<p>由於坑太多難以實現,所以所有 X 下的混成器的實現方式都是直接忽略這個繁重的任務,
<strong>不重定向輸入事件</strong> 而把它交給 X 處理。具體的實現方式就是通過
<a class="reference external" href="http://freedesktop.org/wiki/Software/FixesExt/">XFixes</a>
擴展提供的 <code class="code">
SetWindowShapeRegion</code>
API 將 OverlayWindow 的 <strong>輸入區域</strong>
<code class="code">
ShapeInput</code>
設爲空區域,從而忽略對這個 OverlayWindow 的一切鼠標鍵盤事件。
這樣一來對 OverlayWindow 的點擊會透過 OverlayWindow 直接作用到底下的窗口上。</p>
<p>因爲選擇了不重定向輸入事件, X 下的混成器通常會處於以下兩種狀態:</p>
<ol class="arabic simple">
<li>選擇狀態下可以縮放窗口的大小,扭曲窗口的形狀,並且可以把窗口繪製在任意想要繪製的位置上
(並不是移動窗口的位置), <strong>但是不能讓用戶與窗口的內容交互</strong> 。</li>
<li>正常狀態下可以讓用戶與窗口的內容交互,但是
<strong>繪製的窗口位置、大小和形狀必須嚴格地和 X 記錄的窗口的位置、大小和形狀保持一致</strong>
。持續時間短暫的動畫效果可以允許位置和形狀稍有偏差,但是在動畫的過程中如果用戶點擊了
變形縮放過的窗口,那麼鼠標事件將發往錯誤的( X 記錄中的而非顯示出的)窗口元素上。</li>
</ol>
<p>可以發現這兩種狀態就直接對應了 Gnome 3 的普通狀態和縮略圖狀態(點擊 <ruby><rb>活動</rb><rp>(</rp><rt>Activity</rt><rp>)</rp></ruby>
或者戳畫面左上角之後顯示的狀態),這也解釋了爲什麼儘管 Gnome 3
的窗口有碩大的關閉按鈕,但是在縮略圖狀態下 Gnome 3 仍然需要給窗口加上額外的關閉按鈕:
<strong>因爲處於縮略狀態下的窗口只是一張畫而不能點</strong> 。</p>
<p>Composite 擴展的這些限制使得 X 下的混成器目前只能實現 Mac OS X 那樣的 Exposé
效果,而不能實現 <a class="reference external" href="//farseerfc.me/brief-history-of-compositors-in-desktop-os.html#project-looking-glass-3d">LG3D</a> 那樣直接在 3D 空間中操縱窗口內容。</p>
<p>解決重定向問題曾經的一縷曙光是 <ruby><rb>昇陽公司</rb><rp>(</rp><rt>Sun Microsystems</rt><rp>)</rp></ruby> 在開發 <a class="reference external" href="//farseerfc.me/brief-history-of-compositors-in-desktop-os.html#project-looking-glass-3d">LG3D</a> 的過程中同時提議過另一個 X
擴展叫做 Event Interception 或者簡稱 <a class="reference external" href="http://freedesktop.org/wiki/Software/XEvIE/">XEvIE</a> ,這個擴展的設計目的就是提供 API
讓某個程序接收並操縱全部的鍵盤和鼠標事件。可惜這個擴展隨着昇陽公司本身的隕落而
處於無人維護的狀態,這一點也在它的官方網頁上說明了:</p>
<blockquote>
It has been suggested that this extension should not be used
because it is broken and maintainerless.</blockquote>
</div>
<div class="section" id="id6">
<h2><a class="toc-backref" href="#id11">Composite 擴展的不足</a></h2>
<p>通過上面的介紹,我們就已經可以看到 Composite 擴展的不足之處了。
總結起來說,主要有兩大不足:</p>
<ol class="arabic">
<li><p class="first">繪圖效率低。因爲同樣的位圖從應用程序傳到 Xorg ,再從 Xorg 傳到混成器,
最後從混成器再繪製到屏幕上,繞了一個大彎。這就是爲什麼 Wayland 的開發者在他的slide
<a class="reference external" href="http://people.freedesktop.org/~daniels/lca2013-wayland-x11.pdf">the real story behind Wayland and X</a>
裏這麼說:</p>
<blockquote>
<p>and what's the X server? really bad IPC</p>
<p>那麼 X 服務器到底做了什麼呢? 非常糟糕的進程間通訊</p>
</blockquote>
</li>
<li><p class="first">沒有重定向輸入事件。如果我們要在 X 的混成器裏做這個事情,
基本上我們要全部重寫一遍 X 已經寫好的窗口事件分發邏輯。</p>
</li>
</ol>
<p>既然同樣要重寫,爲什麼不直接重寫一遍 X 呢,扔掉那些歷史負擔,扔掉那些無用的 API
,重新設計可擴展的 API ,做好快速安全的 IPC —— 嗯,重寫 X 就是 Wayland 的目的。</p>
<p>不過這麼重寫了的 Wayland 還是我們熟悉可愛的 X 麼?它有哪些地方變樣了?
這將是我下一篇文章的內容。</p>
</div>
<div class="section" id="id7">
<h2><a class="toc-backref" href="#id12">附錄:擴展閱讀</a></h2>
<p>我自己沒有寫過窗口管理器,沒有寫過混成器,沒有寫過 Wayland
程序,以上說的都是我從互聯網上看到的整理出來的內容。寫下本文的過程中我參考了這些文章:</p>
<p><a class="reference external" href="http://keithp.com/~keithp/talks/xarch_ols2004/xarch-ols2004-html/">The (Re)Architecture of the X Window System</a> 這篇2004年寫的文章描述了 Composite
擴展出現的動機和歷史,介紹了繪圖庫的實現情況,涉及了上面所說的那些 X 擴展被用到的情況和可能。
同時這篇文章還展望了很多現在的 X 已然實現了的功能,比如 OpenGL 和 X 的結合方面我們有了 <a class="reference external" href="http://en.wikipedia.org/wiki/GLX">GLX</a> 和 <a class="reference external" href="http://en.wikipedia.org/wiki/AIGLX">AIGLX</a>
,比如內核的顯卡支持方面我們有了 <a class="reference external" href="http://en.wikipedia.org/wiki/Direct_Rendering_Infrastructure">DRI</a> 和 <a class="reference external" href="http://en.wikipedia.org/wiki/Mode_setting">KMS</a> 。總之這是一篇描述 Linux
桌面未來的發展軌跡的非常有閱讀價值的歷史文獻。</p>
<p><a class="reference external" href="http://wingolog.org/archives/2008/07/26/so-you-want-to-build-a-compositor">so you want to build a compositor</a> 這是一篇 2008 年寫的博文,介紹如何用 Clutter
實現一個最簡單的混成器。</p>
<p><a class="reference external" href="http://www.talisman.org/~erlkonig/misc/x11-composite-tutorial/">Composite tutorial</a> 這是另一篇介紹如何實現一個簡單的混成器的博文,用 Qt 實現,但是同樣很底層。</p>
<p><a class="reference external" href="http://projects.mini-dweeb.org/projects/unagi">unagi</a> 這是一個可用的(但是已經長期沒有開發的)類似 xcompmgr 的混成器。這個項目貌似
是一位研究生的碩士畢業設計,同時他公開了碩士學位的畢業論文
<a class="reference external" href="http://projects.mini-dweeb.org/attachments/download/3/report.pdf">Master thesis: Writing an X compositing manager</a>
其中也對實現一個簡單的混成器做了詳盡描述,包括介紹了相關的 X 擴展和調用。</p>
</div>
archlinux 上用 chrome 實現 透明計算 遠程登錄2015-02-13T20:39:00+09:002015-02-13T20:39:00+09:00farseerfctag:farseerfc.me,2015-02-13:/arch-chrome-remote-desktop.html
<p><a class="reference external" href="http://news.sciencenet.cn/htmlnews/2015/1/311393.shtm">透明計算</a>
具體是什麼,因爲他們沒有公開技術細節所以我並不知道,只是看
<a class="reference external" href="http://v.qq.com/page/h/v/q/h0145ebh1vq.html">公開出來的演示視頻</a>
,感覺似乎只要能從手機上遠程登錄系統桌面,就能算是透明計算了。
如果透明計算真是這個意思,那麼我似乎已經用着這個技術很多年了嘛。</p>
<p>Xorg 上常用的遠程桌面工具有很多,基於 VNC 協議的、基於NX的和基於 RDP 協議的都能找到,
直接 ssh X forwarding 效果也不錯。只是這些方案的一個 <strong>不太易用</strong> 的地方在於,需要
通過 ip 訪問到遠程的電腦,所以在跨越 NAT 之類的情況下不太容易使用。</p>
<p>於是今天介紹一個使用方便設置也簡單的方法: 通過 chrome-remote-desktop 在 archlinux
上使用遠程桌面。這個方案的優勢在於,藉助 Google 的雲端服務器(內部貌似是XMPP協議下的握手)
方便地實現了 NAT 穿透,無論什麼網絡環境基本都能使用。當然,要支持遠程登錄,
位於遠端的登錄的計算機必須一直開着 …</p>
<p><a class="reference external" href="http://news.sciencenet.cn/htmlnews/2015/1/311393.shtm">透明計算</a>
具體是什麼,因爲他們沒有公開技術細節所以我並不知道,只是看
<a class="reference external" href="http://v.qq.com/page/h/v/q/h0145ebh1vq.html">公開出來的演示視頻</a>
,感覺似乎只要能從手機上遠程登錄系統桌面,就能算是透明計算了。
如果透明計算真是這個意思,那麼我似乎已經用着這個技術很多年了嘛。</p>
<p>Xorg 上常用的遠程桌面工具有很多,基於 VNC 協議的、基於NX的和基於 RDP 協議的都能找到,
直接 ssh X forwarding 效果也不錯。只是這些方案的一個 <strong>不太易用</strong> 的地方在於,需要
通過 ip 訪問到遠程的電腦,所以在跨越 NAT 之類的情況下不太容易使用。</p>
<p>於是今天介紹一個使用方便設置也簡單的方法: 通過 chrome-remote-desktop 在 archlinux
上使用遠程桌面。這個方案的優勢在於,藉助 Google 的雲端服務器(內部貌似是XMPP協議下的握手)
方便地實現了 NAT 穿透,無論什麼網絡環境基本都能使用。當然,要支持遠程登錄,
位於遠端的登錄的計算機必須一直開着 Chrome Remote Desktop 的後臺服務。</p>
<div class="panel panel-default">
<div class="panel-heading">
Chrome Remote Desktop 插件</div>
<div class="panel-body">
<img alt="Chrome Remote Desktop 插件" class="img-responsive" src="//farseerfc.me/images/chrome-remote-desktop-plugin.png"/>
</div>
</div>
<div class="section" id="chrome-remote-desktop">
<h2><a class="toc-backref" href="#id5">Chrome Remote Desktop 的客戶端</a></h2>
<p>雖然可能有很多人不知道,不過 Chrome 內包括遠程桌面的功能很久了。只是這個功能的界面默認
沒有提供界面,要使用它需要安裝 Google 官方出品的
<a class="reference external" href="https://chrome.google.com/webstore/detail/chrome-remote-desktop/gbchcmhmhahfdphkhkmpfmihenigjmpp">remote-desktop 插件</a> 。
裝好之後遠程桌面的客戶端就準備好,可以用來遠程訪問別的計算機桌面了(無論是 Windows/OS X
還是 Linux 都支持)。並且不光可以自己遠程訪問自己賬戶的桌面,還可以遠程協助朋友的桌面。</p>
</div>
<div class="section" id="archlinux">
<h2><a class="toc-backref" href="#id6">Archlinux 上設置遠程登錄的服務器</a></h2>
<p>有了客戶端之後還要設置一下纔能讓桌面作爲遠程登錄的服務器。Windows 和 OS X 上 Chrome
會自動下載需要的安裝包,無腦下一步就能裝好了。Linux上由於發行版衆多,桌面配置各異,
所以需要一點手動配置。官方的設置步驟記載在 <a class="reference external" href="https://support.google.com/chrome/answer/1649523">這裏</a>
其中給出了 debian 用的二進制包和 Ubuntu 12.10 上的設置方式,以下設置是參考官方步驟。</p>
<p>首先要安裝 chrome-remote-desktop 這個包,這個包實際上對應了 Windows/OS X 上用安裝程序
安裝的 Remote Desktop Host Controller。 archlinux 上開啓了
<a class="reference external" href="https://github.com/archlinuxcn/repo">[archlinuxcn]</a>
倉庫的話,可以直接安裝打好的包。或者可以從
<a class="reference external" href="https://aur.archlinux.org/packages/chrome-remote-desktop/">AUR</a> 裝。</p>
<pre><span class="code-line">$ pacman -Ss chrome-remote-desktop<br/><span style="color:purple;font-weight:bold;">archlinuxcn/</span><span style="font-weight:bold;">chrome-remote-desktop </span><span style="color:green;font-weight:bold;">40.0.2214.44-1</span><br/>Allows you to securely access your computer over the Internet through Chrome.</span></pre><p>裝好之後從會說這麼一段話:</p>
<blockquote>
<p>groupadd:无效的组 ID “chrome-remote-desktop”</p>
<p>Please create ~/.config/chrome-remote-desktop folder manually, if it doesn't exist, or else you can't use CRD.
The needed files are created by the Chrome app, inside the chrome-remote-desktop folder, after Enabling Remote Connections.
To {enable,start} the service use systemctl --user {enable,start} chrome-remote-desktop</p>
<p>You may need to create a ~/.chrome-remote-desktop-session file with commands to start your session</p>
<p>Go to <a class="reference external" href="https://support.google.com/chrome/answer/1649523">https://support.google.com/chrome/answer/1649523</a> for more information.</p>
</blockquote>
<p>那句報錯是 AUR 裏打的包還沒跟上上游 Google 的更改導致的錯誤,
首先我們需要把遠程登錄的用戶添加入 chrome-remote-desktop 這個用戶組裏。
新版本的 chrome remote desktop 提供了一個命令做這個事情,所以執行以下命令就可以了:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> /opt/google/chrome-remote-desktop/chrome-remote-desktop --add-user</span>
</pre></div>
<p>然後我們需要手動創建 <code class="code">
~/.config/chrome-remote-desktop</code>
這個文件夾,內容是空的
就好了,隨後 chrome 會往這裏面放 <code class="code">
host#.json</code>
文件用於身份驗證。</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> mkdir ~/.config/chrome-remote-desktop</span>
</pre></div>
<p>然後我們要創建一個 shell 腳本 <code class="code">
~/.chrome-remote-desktop-session</code>
,這是遠程
登錄時的 .xinitrc ,內容麼就是啓動你想在遠程登錄時用的桌面環境。
這裏可以指定一個和你正在登錄的 WM/DE 不同的桌面,比如我啓動 xfce4:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> cat ~/.chrome-remote-desktop-session</span>
<span class="code-line"><span class="gp">#</span>!/bin/bash</span>
<span class="code-line"><span class="go">startxfce4</span></span>
<span class="code-line"><span class="gp">$</span> chmod <span class="m">755</span> .chrome-remote-desktop-session</span>
</pre></div>
<p>接下來需要從 Chrome 的插件裏啓用遠程桌面。打開 Chrome 的 Remote Desktop 插件,這時
應該可以看到一個「啓用遠程鏈接」的按鈕。</p>
<div class="figure">
<img alt="Chrome Remote Desktop 插件中「啓用遠程鏈接」的按鈕" class="img-responsive" src="//farseerfc.me/images/chrome-remote-desktop-enable-button.png"/>
<p class="caption">Chrome Remote Desktop 插件中「啓用遠程鏈接」的按鈕</p>
</div>
<div class="alert alert-warning compound">
<p>在撰寫本文的時候, Archlinux 官方源裏的 chromium 的版本和 aur/google-chrome
的版本尚且還是 40.0.2214.111 ,而 Chrome Web Store 中提供的 Chrome Remote
Desktop 的插件的版本是 41.0.2272.41 。雖然通常並不要求兩者版本一致,不過貌似最近
Chrome 內部的 Remoting 功能更改了 API 導致可能出問題。如果你找不到
「啓用遠程鏈接」的按鈕,請嘗試一下新版本的 Chrome 比如 google-chrome-dev 。
在這一步啓用之後,老版本的 chrome 應該也就能使用遠程桌面了。</p>
</div>
<div class="alert alert-warning compound">
<p>在32位的 Linux 版本上,最近更新的 Chrome Remote Desktop 插件可能無法正確識別 Host
的版本,具體 <a class="alert-link reference external" href="https://code.google.com/p/chromium/issues/detail?id=332930">參考這個 bug</a> 。</p>
</div>
<p>點擊「啓用遠程鏈接」,設定一個 PIN 密碼(不需要很複雜,這裏首先有 Google 帳號驗證保證只有
你纔能訪問),然後就能看到這套電腦的 hostname 出現在「我的電腦」列表裏。</p>
<div class="figure">
<img alt="啓用遠程鏈接之後的樣子" class="img-responsive" src="//farseerfc.me/images/chrome-remote-desktop-after-enabled.png"/>
<p class="caption">啓用遠程鏈接之後的樣子</p>
</div>
<p>同時,啓用了遠程鏈接之後,可以在剛剛創建的 ~/.config/chrome-remote-desktop
文件夾中找到記錄了驗證信息的文件。</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> ls .config/chrome-remote-desktop</span>
<span class="code-line"><span class="go">chrome-profile host#8cfe7ecfd6bb17955c1ea22f77d0d800.json pulseaudio#8cfe7ecfd6</span></span>
</pre></div>
<p>然後就可以啓動對應的 systemd 用戶服務了,如果想自動啓動服務要記得 <code class="code">
systemctl --user enable</code>
:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> systemctl --user start chrome-remote-desktop.service</span>
</pre></div>
<p>如果上面的設置一切正常,就可以看到 chrome-remote-desktop 啓動了另外一個 Xorg 執行你
剛剛指定的桌面環境:</p>
<div class="figure">
<img alt="htop 中看到的 chrome-remote-desktop 啓動的另外一個 Xorg" class="img-responsive" src="//farseerfc.me/images/chrome-remote-desktop-htop.png"/>
<p class="caption">htop 中看到的 chrome-remote-desktop 啓動的另外一個 Xorg</p>
</div>
<p>然後就可以試着通過 Remote Desktop 插件登錄到這個新開的 Xorg 了:</p>
<div class="figure">
<img alt="「遠程」登錄到新的 XFCE4" class="img-responsive" src="//farseerfc.me/images/chrome-remote-desktop-xfce4.png"/>
<p class="caption">「遠程」登錄到新的 XFCE4</p>
</div>
</div>
<div class="section" id="linux-chrome-windows-os-x">
<h2><a class="toc-backref" href="#id7">Linux 版本的 Chrome遠程桌面 和 Windows/ OS X 上的區別</a></h2>
<p>通過上面的設置步驟也可以看出,Linux版本的遠程桌面會在後臺開一個獨立的 X 會話,而不能
復用現在已有的 X 會話。對遠程登錄的用法而言這還能接受,對遠程協助的功能而言有點問題,
因爲正在使用的人不能觀察協助者做了什麼,協助者也不能繼續請求協助的人的操作。</p>
<p>當然目前 Chrome 遠程桌面的 Linux Host Controller 還只是 beta 版本,官方只測試支持
Ubuntu 12.04 和 12.10 (14.04之後似乎有
<a class="reference external" href="https://code.google.com/p/chromium/issues/detail?id=366432">Bug</a>
),所以不能要求太多。希望以後能改善吧。</p>
</div>
<div class="section" id="bonus">
<h2><a class="toc-backref" href="#id8">Bonus: 手機遠程登錄</a></h2>
<div class="panel panel-default">
<div class="panel-heading">
手機上的 Chrome 遠程桌面 App</div>
<div class="panel-body">
<img alt="手機上的 Chrome 遠程桌面 App" class="img-responsive" src="//farseerfc.me/images/chrome-remote-desktop-android.png"/>
</div>
</div>
<p>通過上面的設置就可以從任何一個 Chrome 遠程桌面客戶端登錄剛剛設置的這臺電腦了。
因爲 Chrome 在三大桌面系統 Windows / OS X / Linux 上都有,所以應該能覆蓋大多數桌面
系統了。</p>
<p>除了桌面的 Chrome 之外還有一個客戶端是 Android 上的
<a class="reference external" href="https://play.google.com/store/apps/details?id=com.google.chromeremotedesktop">Chrome 遠程桌面 App</a> 經過上面的設置之後,從這個 App 也能看到並登錄:</p>
<div class="figure">
<img alt="手機遠程登錄" class="img-responsive" src="//farseerfc.me/images/chrome-remote-desktop-android-logined.png"/>
<p class="caption">手機遠程登錄</p>
</div>
<p>好啦,開始享受國家自然科學一等獎的透明計算技術吧!</p>
</div>
從非緩衝輸入流到 Linux 控制檯的歷史2014-12-12T17:06:00+09:002014-12-12T17:06:00+09:00farseerfctag:farseerfc.me,2014-12-12:/from-unbuffered-stdin-to-history-of-linux-tty.html
<p>這篇也是源自於水源C板上板友的一個問題,涉及Linux上的控制檯的實現方式和歷史原因。因爲內容比較長,所以在這裏再排版一下發出來。
<a class="reference external" href="http://bbs.sjtu.edu.cn/bbstcon,board,C,reid,1418138991,file,M.1418138991.A.html">原帖在這裏</a> 。</p>
<div class="section" id="id2">
<h2><a class="toc-backref" href="#id7">可以設置不帶緩衝的標準輸入流嗎?</a></h2>
<p>WaterElement(UnChanged) 於 2014年12月09日23:29:51 星期二 問到:</p>
<blockquote>
<p>請問對於標準輸入流可以設置不帶緩衝嗎?比如以下程序</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp"></span></span>
<span class="code-line"><span class="cp">#include</span> <span class="cpf"><unistd.h></span><span class="cp"></span></span>
<span class="code-line"></span>
<span class="code-line"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span></span>
<span class="code-line"> <span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fdopen</span><span class="p">(</span><span class="n">STDIN_FILENO</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span></span>
<span class="code-line"> <span class="n">setvbuf</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">_IONBF</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span></span>
<span class="code-line"> <span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">20</span><span class="p">];</span></span>
<span class="code-line"> <span class="n">buffer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"> <span class="n">fgets</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span></span>
<span class="code-line"> <span class="n">printf</span><span class="p">(</span><span class="s">"buffer …</span></span></pre></div></blockquote></div>
<p>這篇也是源自於水源C板上板友的一個問題,涉及Linux上的控制檯的實現方式和歷史原因。因爲內容比較長,所以在這裏再排版一下發出來。
<a class="reference external" href="http://bbs.sjtu.edu.cn/bbstcon,board,C,reid,1418138991,file,M.1418138991.A.html">原帖在這裏</a> 。</p>
<div class="section" id="id2">
<h2><a class="toc-backref" href="#id7">可以設置不帶緩衝的標準輸入流嗎?</a></h2>
<p>WaterElement(UnChanged) 於 2014年12月09日23:29:51 星期二 問到:</p>
<blockquote>
<p>請問對於標準輸入流可以設置不帶緩衝嗎?比如以下程序</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp"></span></span>
<span class="code-line"><span class="cp">#include</span> <span class="cpf"><unistd.h></span><span class="cp"></span></span>
<span class="code-line"></span>
<span class="code-line"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span></span>
<span class="code-line"> <span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fdopen</span><span class="p">(</span><span class="n">STDIN_FILENO</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span></span>
<span class="code-line"> <span class="n">setvbuf</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">_IONBF</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span></span>
<span class="code-line"> <span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">20</span><span class="p">];</span></span>
<span class="code-line"> <span class="n">buffer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"> <span class="n">fgets</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span></span>
<span class="code-line"> <span class="n">printf</span><span class="p">(</span><span class="s">"buffer is:%s"</span><span class="p">,</span> <span class="n">buffer</span><span class="p">);</span></span>
<span class="code-line"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"><span class="p">}</span></span>
</pre></div>
<p>似乎還是需要在命令行輸入後按回車纔會讓 <code class="code">
fgets</code>
返回,不帶緩衝究竟體現在哪裏?</p>
</blockquote>
<div class="section" id="id3">
<h3><a class="toc-backref" href="#id8">這和緩存無關,是控制檯的實現方式的問題。</a></h3>
<p>再講細節一點,這裏有很多個程序和設備。以下按 linux 的情況講:</p>
<ol class="arabic simple">
<li>終端模擬器窗口(比如xterm)收到鍵盤事件</li>
<li>終端模擬器(xterm)把鍵盤事件發給虛擬終端 pty1</li>
<li>pty1 檢查目前的輸入狀態,把鍵盤事件轉換成 stdin 的輸入,發給你的程序</li>
<li>你的程序的 c 庫從 stdin 讀入一個輸入,處理</li>
</ol>
<p>標準庫說的輸入緩存是在 4 的這一步進行的。而行輸入是在 3 的這一步被緩存起來的。</p>
<p>終端pty有多種狀態,一般控制檯程序所在的狀態叫「回顯行緩存」狀態,這個狀態的意思是:</p>
<ol class="arabic simple">
<li>所有普通字符的按鍵,會回顯到屏幕上,同時記錄在行緩存區裏。</li>
<li>處理退格( <kbd class="kbd">
BackSpace</kbd>
),刪除( <kbd class="kbd">
Delete</kbd>
)按鍵爲刪掉字符,左右按鍵移動光標。</li>
<li>收到回車的時候把整個一行的內容發給stdin。</li>
</ol>
<p>參考: <a class="reference external" href="http://en.wikipedia.org/wiki/Cooked_mode">http://en.wikipedia.org/wiki/Cooked_mode</a></p>
<p>同時在Linux/Unix下可以發特殊控制符號給pty讓它進入「raw」狀態,這種狀態下按鍵
不會被回顯,顯示什麼內容都靠你程序自己控制。
如果你想得到每一個按鍵事件需要用raw狀態,這需要自己控制回顯自己處理緩衝,
簡單點的方法是用 readline 這樣的庫(基本就是「回顯行緩存」的高級擴展,支持了
Home/End,支持歷史)或者 ncurses 這樣的庫(在raw狀態下實現了一個簡單的窗口/
事件處理框架)。</p>
<p>參考: <a class="reference external" href="http://en.wikipedia.org/wiki/POSIX_terminal_interface#History">http://en.wikipedia.org/wiki/POSIX_terminal_interface#History</a></p>
<p>除此之外, <kbd class="kbd">
Ctrl-C</kbd>
轉換到 SIGINT , <kbd class="kbd">
Ctrl-D</kbd>
轉換到 EOF 這種也是在 3 這一步做的。</p>
<p>以及,有些終端模擬器提供的 <kbd class="kbd">
Ctrl-Shift-C</kbd>
表示複製這種是在 2 這一步做的。</p>
<p>以上是 Linux/unix 的方式。 Windows的情況大體類似,只是細節上有很多地方不一樣:</p>
<ol class="arabic simple">
<li>窗口事件的接收者是創建 cmd 窗口的 Win32 子系統。</li>
<li>Win32子系統接收到事件之後,傳遞給位於 命令行子系統 的 cmd 程序</li>
<li>cmd 程序再傳遞給你的程序。</li>
</ol>
<p>Windows上同樣有類似行緩存模式和raw模式的區別,只不過實現細節不太一樣。</p>
</div>
<div class="section" id="strace">
<h3><a class="toc-backref" href="#id9">strace查看了下</a></h3>
<p>WaterElement(UnChanged) 於 2014年12月10日21:53:54 星期三 回復:</p>
<blockquote>
<p>感謝FC的詳盡解答。</p>
<p>用strace查看了下,設置標準輸入沒有緩存的話讀每個字符都會調用一次 <code class="code">
read</code>
系統調用,
比如輸入abc:</p>
<div class="highlight"><pre><span class="code-line"><span></span>read(0, abc</span>
<span class="code-line">"a", 1) = 1</span>
<span class="code-line">read(0, "b", 1) = 1</span>
<span class="code-line">read(0, "c", 1) = 1</span>
<span class="code-line">read(0, "\n", 1) = 1</span>
</pre></div>
<p>如果有緩存的話就只調用一次了 <code class="code">
read</code>
系統調用了:</p>
<div class="highlight"><pre><span class="code-line"><span></span>read(0, abc</span>
<span class="code-line">"abc\n", 1024) = 4</span>
</pre></div>
</blockquote>
</div>
<div class="section" id="raw-mode">
<h3><a class="toc-backref" href="#id10">如果想感受一下 raw mode</a></h3>
<p>沒錯,這個是你的進程內C庫做的緩存,tty屬於字符設備所以是一個一個字符塞給你的
程序的。</p>
<p>如果想感受一下 raw mode 可以試試下面這段程序(沒有檢測錯誤返回值)</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp"></span></span>
<span class="code-line"><span class="cp">#include</span> <span class="cpf"><unistd.h></span><span class="cp"></span></span>
<span class="code-line"><span class="cp">#include</span> <span class="cpf"><termios.h></span><span class="cp"></span></span>
<span class="code-line"></span>
<span class="code-line"><span class="k">static</span> <span class="kt">int</span> <span class="n">ttyfd</span> <span class="o">=</span> <span class="n">STDIN_FILENO</span><span class="p">;</span></span>
<span class="code-line"><span class="k">static</span> <span class="k">struct</span> <span class="n">termios</span> <span class="n">orig_termios</span><span class="p">;</span></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/* reset tty - useful also for restoring the terminal when this process</span></span>
<span class="code-line"><span class="cm"> wishes to temporarily relinquish the tty</span></span>
<span class="code-line"><span class="cm">*/</span></span>
<span class="code-line"><span class="kt">int</span> <span class="nf">tty_reset</span><span class="p">(</span><span class="kt">void</span><span class="p">){</span></span>
<span class="code-line"> <span class="cm">/* flush and reset */</span></span>
<span class="code-line"> <span class="k">if</span> <span class="p">(</span><span class="n">tcsetattr</span><span class="p">(</span><span class="n">ttyfd</span><span class="p">,</span><span class="n">TCSAFLUSH</span><span class="p">,</span><span class="o">&</span><span class="n">orig_termios</span><span class="p">)</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span></span>
<span class="code-line"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"></span>
<span class="code-line"><span class="cm">/* put terminal in raw mode - see termio(7I) for modes */</span></span>
<span class="code-line"><span class="kt">void</span> <span class="nf">tty_raw</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span></span>
<span class="code-line"><span class="p">{</span></span>
<span class="code-line"> <span class="k">struct</span> <span class="n">termios</span> <span class="n">raw</span><span class="p">;</span></span>
<span class="code-line"></span>
<span class="code-line"> <span class="n">raw</span> <span class="o">=</span> <span class="n">orig_termios</span><span class="p">;</span> <span class="cm">/* copy original and then modify below */</span></span>
<span class="code-line"></span>
<span class="code-line"> <span class="cm">/* input modes - clear indicated ones giving: no break, no CR to NL,</span></span>
<span class="code-line"><span class="cm"> no parity check, no strip char, no start/stop output (sic) control */</span></span>
<span class="code-line"> <span class="n">raw</span><span class="p">.</span><span class="n">c_iflag</span> <span class="o">&=</span> <span class="o">~</span><span class="p">(</span><span class="n">BRKINT</span> <span class="o">|</span> <span class="n">ICRNL</span> <span class="o">|</span> <span class="n">INPCK</span> <span class="o">|</span> <span class="n">ISTRIP</span> <span class="o">|</span> <span class="n">IXON</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"> <span class="cm">/* output modes - clear giving: no post processing such as NL to CR+NL */</span></span>
<span class="code-line"> <span class="n">raw</span><span class="p">.</span><span class="n">c_oflag</span> <span class="o">&=</span> <span class="o">~</span><span class="p">(</span><span class="n">OPOST</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"> <span class="cm">/* control modes - set 8 bit chars */</span></span>
<span class="code-line"> <span class="n">raw</span><span class="p">.</span><span class="n">c_cflag</span> <span class="o">|=</span> <span class="p">(</span><span class="n">CS8</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"> <span class="cm">/* local modes - clear giving: echoing off, canonical off (no erase with</span></span>
<span class="code-line"><span class="cm"> backspace, ^U,...), no extended functions, no signal chars (^Z,^C) */</span></span>
<span class="code-line"> <span class="n">raw</span><span class="p">.</span><span class="n">c_lflag</span> <span class="o">&=</span> <span class="o">~</span><span class="p">(</span><span class="n">ECHO</span> <span class="o">|</span> <span class="n">ICANON</span> <span class="o">|</span> <span class="n">IEXTEN</span> <span class="o">|</span> <span class="n">ISIG</span><span class="p">);</span></span>
<span class="code-line"></span>
<span class="code-line"> <span class="cm">/* control chars - set return condition: min number of bytes and timer */</span></span>
<span class="code-line"> <span class="n">raw</span><span class="p">.</span><span class="n">c_cc</span><span class="p">[</span><span class="n">VMIN</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="n">raw</span><span class="p">.</span><span class="n">c_cc</span><span class="p">[</span><span class="n">VTIME</span><span class="p">]</span> <span class="o">=</span> <span class="mi">8</span><span class="p">;</span> <span class="cm">/* after 5 bytes or .8 seconds</span></span>
<span class="code-line"><span class="cm"> after first byte seen */</span></span>
<span class="code-line"> <span class="n">raw</span><span class="p">.</span><span class="n">c_cc</span><span class="p">[</span><span class="n">VMIN</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">raw</span><span class="p">.</span><span class="n">c_cc</span><span class="p">[</span><span class="n">VTIME</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="cm">/* immediate - anything */</span></span>
<span class="code-line"> <span class="n">raw</span><span class="p">.</span><span class="n">c_cc</span><span class="p">[</span><span class="n">VMIN</span><span class="p">]</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="n">raw</span><span class="p">.</span><span class="n">c_cc</span><span class="p">[</span><span class="n">VTIME</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="cm">/* after two bytes, no timer */</span></span>
<span class="code-line"> <span class="n">raw</span><span class="p">.</span><span class="n">c_cc</span><span class="p">[</span><span class="n">VMIN</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">raw</span><span class="p">.</span><span class="n">c_cc</span><span class="p">[</span><span class="n">VTIME</span><span class="p">]</span> <span class="o">=</span> <span class="mi">8</span><span class="p">;</span> <span class="cm">/* after a byte or .8 seconds */</span></span>
<span class="code-line"></span>
<span class="code-line"> <span class="cm">/* put terminal in raw mode after flushing */</span></span>
<span class="code-line"> <span class="n">tcsetattr</span><span class="p">(</span><span class="n">ttyfd</span><span class="p">,</span><span class="n">TCSAFLUSH</span><span class="p">,</span><span class="o">&</span><span class="n">raw</span><span class="p">);</span></span>
<span class="code-line"><span class="p">}</span></span>
<span class="code-line"></span>
<span class="code-line"></span>
<span class="code-line"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span></span>
<span class="code-line"> <span class="n">atexit</span><span class="p">(</span><span class="n">tty_reset</span><span class="p">);</span></span>
<span class="code-line"> <span class="n">tty_raw</span><span class="p">();</span></span>
<span class="code-line"> <span class="kt">FILE</span> <span class="o">*</span><span class="n">fp</span> <span class="o">=</span> <span class="n">fdopen</span><span class="p">(</span><span class="n">ttyfd</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span></span>
<span class="code-line"> <span class="n">setvbuf</span><span class="p">(</span><span class="n">fp</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">_IONBF</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span></span>
<span class="code-line"> <span class="kt">char</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">20</span><span class="p">];</span></span>
<span class="code-line"> <span class="n">buffer</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"> <span class="n">fgets</span><span class="p">(</span><span class="n">buffer</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="n">fp</span><span class="p">);</span></span>
<span class="code-line"> <span class="n">printf</span><span class="p">(</span><span class="s">"buffer is:%s"</span><span class="p">,</span> <span class="n">buffer</span><span class="p">);</span></span>
<span class="code-line"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span></span>
<span class="code-line"><span class="p">}</span></span>
</pre></div>
</div>
</div>
<div class="section" id="id4">
<h2><a class="toc-backref" href="#id11">終端上的字符編程</a></h2>
<p>vander(大青蛙) 於 2014年12月12日08:52:20 星期五 問到:</p>
<blockquote>
<p>學習了!</p>
<p>進一步想請教一下fc大神。如果我在Linux上做終端上的字符編程,是否除了用ncurses庫
之外,也可以不用該庫而直接與終端打交道,就是你所說的直接在raw模式?
另外,終端類型vt100和linux的差別在哪裏?爲什麼Kevin Boone的KBox配置手冊裏面說必
須把終端類型設成linux,而且要加上terminfo文件,才能讓終端上的vim正常工作?term
info文件又是幹什麼的?</p>
</blockquote>
<div class="section" id="id5">
<h3><a class="toc-backref" href="#id12">Linux控制檯的歷史</a></h3>
<p>嗯理論上可以不用 ncurses 庫直接在 raw 模式操縱終端。</p>
<p>這裏稍微聊一下terminfo/termcap的歷史,詳細的歷史和吐槽參考
<a class="reference external" href="http://web.mit.edu/~simsong/www/ugh.pdf">Unix hater's Handbook</a>
第6章 Terminal Insanity。</p>
<p>首先一個真正意義上的終端就是一個輸入設備(通常是鍵盤)加上一個輸出設備(打印
機或者顯示器)。很顯然不同的終端的能力不同,比如如果輸出設備是打印機的話,顯
示出來的字符就不能刪掉了(但是能覆蓋),而且輸出了一行之後就不能回到那一行了
。再比如顯示器終端有的支持粗體和下劃線,有的支持顏色,而有的什麼都不支持。
早期Unix工作在電傳打字機(TeleTYpe)終端上,後來Unix被port到越來越多的機器上
,然後越來越多類型的終端會被連到Unix上,很可能同一臺Unix主機連了多個不同類型
的終端。由於是不同廠商提供的不同的終端,能力各有不同,自然控制他們工作的方式
也是不一樣的。所有終端都支持回顯行編輯模式,所以一般的面向行的程序還比較好寫
,但是那時候要撰寫支持所有終端的「全屏」程序就非常痛苦,這種情況就像現在瀏覽
器沒有統一標準下寫HTML要測試各種瀏覽器兼容性一樣。
通常的做法是</p>
<ol class="arabic simple">
<li>使用最小功能子集</li>
<li>假設終端是某個特殊設備,不管別的設備。</li>
</ol>
<p>水源的代碼源頭 Firebird2000 就是那樣的一個程序,只支持固定大小的vt102終端。</p>
<p>這時有一個劃時代意義的程序出現了,就是 vi,試圖要做到「全屏可視化編輯」。這在
現在看起來很簡單,但是在當時基本是天方夜譚。
vi 的做法是提出一層抽象,記錄它所需要的所有終端操作,然後有一個終端類型數據庫
,把那些操作映射到終端類型的具體指令上。當然並不是所有操作在所有終端類型上都
支持,所以會有一堆 fallback,比如要「強調」某段文字,在彩色終端上可能
fallback 到紅色,在黑白終端上可能 fallback 到粗體。</p>
<p>vi 一出現大家都覺得好頂讚,然後想要寫更多類似 vi 這樣的全屏程序。然後 vi 的作
者就把終端抽象的這部分數據庫放出來形成一個單獨的項目,叫 termcap (Terminal
Capibility),對應的描述終端的數據庫就是 termcap 格式。然後 termcap 只是一個
數據庫(所以無狀態)還不夠方便易用,所以後來又有人用 termcap 實現了 curses 。</p>
<p>再後來大家用 curses/termcap 的時候漸漸發現這個數據庫有一點不足:它是爲 vi 設
計的,所以只實現了 vi 需要的那部分終端能力。然後對它改進的努力就形成了新的
terminfo 數據庫和 pcurses 和後來的 ncurses 。 然後 VIM 出現了自然也用
terminfo 實現這部分終端操作。</p>
<p>然後麼就是 X 出現了, xterm 出現了,大家都用顯示器了,然後 xterm 爲了兼容各種
老程序加入了各種老終端的模擬模式。不過因爲最普及的終端是 vt100 所以 xterm 默
認是工作在兼容 vt100 的模式下。然後接下來各種新程序(偷懶不用*curses的那些)
都以 xterm/vt100 的方式寫。</p>
<p>嗯到此爲止是 Unix 世界的黑歷史。</p>
<p>知道這段歷史的話就可以明白爲什麼需要 TERM 變量配合 terminfo 數據庫纔能用一些
Unix 下的全屏程序了。類比一下的話這就是現代瀏覽器的 user-agent。</p>
<p>然後話題回到 Linux 。 大家知道 Linux 早期代碼不是一個 OS, 而是 Linus 大神想
在他的嶄新蹭亮的 386-PC 上遠程登錄他學校的 Unix 主機,接收郵件和逛水源(咳咳
)。於是 Linux 最早的那部分代碼並不是一個通用 OS 而只是一個 bootloader 加一個
終端模擬器。所以現在 Linux 內核裏還留有他當年實現的終端模擬器的部分代碼,而這
個終端模擬器的終端類型就是 linux 啦。然後他當時是爲了逛水源嘛所以 linux 終端
基本上是 vt102 的一個接近完整子集。</p>
<p>說到這裏脈絡大概應該清晰了, xterm終端類型基本模擬 vt100,linux終端類型基本模
擬 vt102。這兩個的區別其實很細微,都是同一個廠商的兩代產品嘛。有差別的地方差
不多就是 <kbd class="kbd">
Home</kbd>
/ <kbd class="kbd">
End</kbd>
/ <kbd class="kbd">
PageUp</kbd>
/ <kbd class="kbd">
PageDown</kbd>
/ <kbd class="kbd">
Delete</kbd>
這些不在 ASCII 控制字符表裏的按鍵的映射關係不同。</p>
<p>嗯這也就解釋了爲什麼在linux環境的圖形界面的終端裏 telnet 上水源的話,上面這些
按鍵會錯亂…… 如果設置終端類型是 linux/vt102 的話就不會亂了。在 linux 的
TTY 裏 telnet 也不會亂的樣子。</p>
<p>寫到這裏纔發現貌似有點長…… 總之可以參考
<a class="reference external" href="http://web.mit.edu/~simsong/www/ugh.pdf">Unix hater's Handbook</a>
裏的相關歷史評論和吐槽,那一段非常有意思。</p>
</div>
</div>
KDE5 Plasma 之跳動賣萌的活動按鈕2014-12-09T01:54:00+09:002014-12-09T01:54:00+09:00farseerfctag:farseerfc.me,2014-12-09:/jumping-kde5-plasma-activities-button.html<!-- PELICAN_BEGIN_SUMMARY -->
<p>今天嘗試 KDE5 Plasma 的活動的時候無意間發現這個現象。
只要把活動按鈕拖出桌面,它就會在桌面邊緣來回跳動。
視頻如下:</p>
<!-- PELICAN_END_SUMMARY -->
<div class="well" style="padding: 0">
<div class="tab-content" id="youtubeku">
<div class="tab-pane fade active in" id="youtube_SSbf97jGSpI">
<div align="left" class="youtube embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" src="https://www.youtube.com/embed/SSbf97jGSpI"></iframe> </div>
</div>
<div class="tab-pane fade" id="youku_XODQ0NjM2MzQ4">
<div align="left" class="youku embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" height="498" src="https://player.youku.com/embed/XODQ0NjM2MzQ4" width="510"></iframe> </div>
</div>
</div>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#youtube_SSbf97jGSpI">Youtube</a></li>
<li><a data-toggle="tab" href="#youku_XODQ0NjM2MzQ4">Youku</a></li>
</ul>
</div>
<!-- PELICAN_BEGIN_SUMMARY -->
<p>當然你可以把它再拖回來,所以這個問題還無傷大雅,只是賣萌。</p>
<p>比比之前 Gnome3 那個跳動的界面真是好太多了:</p>
<!-- PELICAN_END_SUMMARY -->
<div class="well" style="padding: 0">
<div class="tab-content" id="youtubeku">
<div class="tab-pane fade active in" id="youtube_TRQJdRHYwrw">
<div align="left" class="youtube embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" src="https://www.youtube.com/embed/TRQJdRHYwrw"></iframe> </div>
</div>
<div class="tab-pane fade" id="youku_XNjc4MjQ5NjE2">
<div align="left" class="youku embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" height="498" src="https://player.youku.com/embed/XNjc4MjQ5NjE2" width="510"></iframe> </div>
</div>
</div>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#youtube_TRQJdRHYwrw">Youtube</a></li>
<li><a data-toggle="tab" href="#youku_XNjc4MjQ5NjE2">Youku</a></li>
</ul>
</div>
<!-- PELICAN_BEGIN_SUMMARY -->
<p>順便,今天還看到一個賣萌的 KDE5 Plasma 靜音圖標的翻譯:</p>
<!-- PELICAN_END_SUMMARY -->
<blockquote class="twitter-tweet" lang="zh-tw"><p>KDE5のミュート画面の中国語翻訳、「静音」のはずだが「镜音」になっている。Vocaloidファンのネタだか、単なる入力ミスだか分からない。 <a href="http://t.co/ipyHjXMscR">pic.twitter.com/ipyHjXMscR</a></p>— Jiachen YANG (@farseerfc) <a href="https://twitter.com/farseerfc/status/541944351270518784">2014 12月 8日</a></blockquote>“…if we do this work … ” --Bill Gates2011-03-14T20:34:00+09:002011-03-14T20:34:00+09:00farseerfctag:farseerfc.me,2011-03-14:/if-we-do-this-work.html<p>導入自
<a class="reference external" href="http://blog.renren.com/blog/230263946/716517729">renren</a></p>
<p>From: Bill Gates</p>
<p>’-- Sent: Sunday, January 24, 1999 8:41 AM</p>
<p>Jeff Westorinon; Ben Fathi ;</p>
<p>TO: Carl Stork (Exchange); Nathan Myhrvofd; Eric Rudder</p>
<p>Subject: ACPI extensions</p>
<p>One thing I find myself wondering about is whether we shouldn’t try and
make the "ACPI" extensions somehow Windows specific.</p>
<p>It …</p><p>導入自
<a class="reference external" href="http://blog.renren.com/blog/230263946/716517729">renren</a></p>
<p>From: Bill Gates</p>
<p>’-- Sent: Sunday, January 24, 1999 8:41 AM</p>
<p>Jeff Westorinon; Ben Fathi ;</p>
<p>TO: Carl Stork (Exchange); Nathan Myhrvofd; Eric Rudder</p>
<p>Subject: ACPI extensions</p>
<p>One thing I find myself wondering about is whether we shouldn’t try and
make the "ACPI" extensions somehow Windows specific.</p>
<p>It seems unfortunate if we do this work and get our partners to do the
work and the result is that <strong>Linux works great without having to do the work</strong>.</p>
<p><strong>Maybe there is no way to avoid this problem but it does bother me.</strong></p>
<p>Maybe we could define the APIs so that they work well with NT and not
the others even if they are open.</p>
<p>Or maybe we could patent something relaled to this.</p>
<p>From:</p>
<p><a class="reference external" href="http://antitrust.slated.org/www.iowaconsumercase.org/011607/3000/PX03020.pdf">http://antitrust.slated.org/www.iowaconsumercase.org/011607/3000/PX03020.pdf</a></p>
<p>如果這就是我至今在Xen4.0上得不到ACPI 3.0的完善支持的原因,那麼我詛咒Bill Gates!</p>