「利益揭露: 本文英文書籍連接使用 Amazon Associates Program.」

我相信絕大部分 Linux 開發者都會告訴你,Driver 的開發比 Linux Application 容易許多,即便寫驅動程式聽起來莫名偉大,其實也不過是一段 C/assembly 的組合程式碼。有別於桌面應用程式,Linux kernel API 較少因爲不同的新軟硬體規格,而進行大幅度 API 更動 (參數的簡化倒是十分常見),且由傑出的軟體開發者撰寫的核心架構,穩定性已經十分可靠。

相較與 userland 高度複雜的設定機制,Linux Kernel 暴露的界面十分簡單,在硬體穩定的前提之下,你也難得碰到 API 反應與預期不符合的處境。一般開發者對於核心驅動程式上手的時間,應該不會比開發桌面軟體來的更久。花費時間較多應該是研讀硬體手冊,以及不嚴謹的開發習慣造成臭蟲而所需的除錯時間。

對於深具經驗的開發者,在學習開發 Linux kernel driver 時,最快的方法莫過於直接解開 Kernel tarball, 切進欲開發的 subsystem 目錄,拿出 global, vim, LXR 直接把現成程式碼當作範例學習,很快就可以理解程式結構。不過,偶爾還是需要參考書來驗證對於架構得理解是否正確,另外接觸新的 subsystem 時先閱讀入門文章也可以減少無謂的撞牆期。

所幸,幾位 Linux kernel hacker 也是傑出的文件作者。在 Linux kernel sourc tree 中已有一些各子系統的架構、操作參考文件,涵蓋了基本的 coding style、設計哲學等。另外,像是 Robert Love, Greg Kroah-Hartman (PCI, USB maintainer) 等開發者也出版了完整的書籍,很值得參考。

不過市面上針對 Linux kernel 開發的書籍也不少,那一本是適合你的呢?這類的技術書籍,通常設定不同的讀者羣來設定內容,有的偏重知識,有的偏重操作實務。且出版版次也會影響所介紹的 API 差異,造成無法編譯其範例,但並非舊書,所談之理論就不正確。

以下分享不才對於市面上 2005 年之後出版的核心開發書籍的評論,希望對於想擴充團隊圖書館的朋友提供些參考。

Linux Kernel in a Nutshell 是 2006 年年底發行,作者是 GregKH,使用核心為 2.6.18,部分操作方式或指令已經略有更改。GregKH 基於讓更多新手參與開發行列,針對的讀者是從未編譯過 Linux kernel,想瞭解下載、設定、編譯需求等等細節,適合剛從其他平臺進入 Linux 核心開發的朋友,可以較快熟悉核心編譯的操作程序。書內主要介紹通用性知識,因此未提各 distro 間安裝 kernel 的細節 (如 initrd 建制方式)。

LKN 已採 CC BY-SA 2.5 授權。電子書可於 GregKH 的網頁下載

Understanding the Linux Kernel, Third Edition 這本由兩位博士 Daniel P. Bovet 與 Marco Cesati 所撰寫,從 2000 年底出版之後,到 2005 已經是第三版,介紹的核心是 2.6.11。有中譯版

此書結構以流水帳方式帶過各個子系統,但稍嫌膚淺的僅僅介紹表面的細節,未能給予概觀性的理論說明,也未能直指程式核心。篇幅常用於註記資料結構或函式用途,適合想尋著麵包屑理解 Linux kernel 運作的探險家使用。

Professional Linux Kernel Architecture 在2008 年出版,作者是 Wolfgang Mauerer,作者的背景是量子物理學家。在沒有社群內知名開發者的背書與協助下,他完成了一本巨大的書籍,篇幅高達 1368 頁。

有別於 UTLK,也許是為了非科班出生的讀者,作者試著詳盡的敘述作業系統的基本概念,另外一方面也以程式碼告訴讀者 Linux 的運作模式。

如果你讀不下純粹理論導向的作業系統教科書,而想透過 Linux理解一個作業系統的設計原理,這是適合你的書。本書基於 Linux kernel 2.6.24.

身為知名的 kernal hacker, Robert LoveLinux Kernel Development (3rd Edition) 一書中為讀者拆解 Linux kernel source tree, 直接從設計理念切入,酌以程式碼輔助,讀者需要有作業系統理論素養以及 Linux 開發經驗,才能消化理解筆者的解剖。最新第三版發表於 2010 年初版,更新到 2.6.34.

簡體中譯版 Linux 内核设计与实现 翻譯自 Linux Kernel Development 第二版。正體中文版有維科圖書有限公司出版沈中庸, 沈彥男翻譯的 Linux 核心開發指南, 2/e

Linux Network Internals 的作者是 Christian Benvenuti,發表於 2005 年。少數專談 Linux Network stacks 的書籍,作者循序的從設定工具、核心啟動開始,逐一介紹 封包傳送接受、Bridging、IPv4、Neighboring Subsystem 與 Routing。本書基於 2.6.12.

書中涵蓋了 Layer 2, Layer 3 等協定, 可惜遺漏了 IPv6, IGMP, PIM, Traffic Control, Netfilter, Virtual devices (802.1Q, bonding, IPIP, GRE) 等等重要原件。讀者需要基本開發能力與網路協定常識。此書有中譯版

The Linux TCP/IP Stack: Networking for Embedded Systems 第一版發表與 2004 年,最新第二版 2006 年,針對的版本是 2.6.16,作者是 Thomas F. Herbert。此書對於讀者的定位不明。雖然意圖以一個章節討論嵌入式系統中的 TCP/IP Stack,但除了說明一般嵌入式系統需求外,缺乏實際有用資訊。

書籍想涵蓋各種 TCP/IP Stack 所涵蓋的項目,但章節設計雜亂,從基本的 Network Stack 開始介紹,對於 API 部分又缺乏系統性描述。既無法瞭解網路協定,或撰寫網路程式或作業系統核心架構。

書中時常夾雜敘述與程式碼,令讀者難以連貫消化,讀者需要開啟原始程式碼才能領會作者的。這是一本關於網路協定的原始碼註記,適合已具核心開發經驗的開發者參考使用,考量其版本日期,書籍的功能可能比自行追蹤程式碼的效用還差。另外,若你想瞭解嵌入式系統,這也不是你該買的書。

Linux Device Drivers, 3rd Edition 的作者是 Jonathan Corbet (LWN創辦人)、Alessandro RubiniGreg Kroah-Hartman。即便 LDD3 已經出版許久,還是所有想寫 Linux kernel driver 的第一優先入門參考書阿。此書有中譯版

LDD3 務實的從實做範例開始,帶領讀者理解各種 subsystem,含括了入門操作與基本觀念,對於初次開發 lkm 的開發者提供了燈塔般的指引。

LDD3 授權採 CC BY-SA 2.0,線上版可於此下載 http://lwn.net/Kernel/LDD3/。但由於書籍年代較久,針對的核心版本為 2.6.10,書中範例需要一點調整才能正常運作。已有同好改了幾份擺在  github (jesstess, martinezjavier).

Essential Linux Device Drivers 的作者是長期在 IBM 工作的 Sreekrishnan Venkateswaran,參與 Linux Watch, PDA, Nurse Call Systems, Merlin Patient Care System 等等開發專案。有正體中譯版 Linux驅動程式開發實戰 以及簡體中譯版 精通Linux驅動程序開發

這本書是作者的實務工程筆記,出版於 2008 年,針對核心為 2.6.23/2.6.24,透過此書新手可以從中漫遊一個深具經驗的開發者,如何從原始碼迷霧之中理解 Linux device  driver,老手或可從雜亂的描述中再次驗證自己的理解。

雖然篇幅高達 744 頁,卻被引用程式碼佔了許多頁面。這本書不足以提供開發者撰寫驅動程式的基本觀念,也無法協助理解作業系統概觀。

作者另有一小冊 Debugging Linux Systems 電子書短短九十頁,帶過幾個常見的核心除錯工具與技巧,很有實務參考價值。

《The Linux(R) Kernel Primer: A Top-Down Approach for x86 and PowerPC Architectures》 出版於 2005 年,作者是 Claudia Salzberg Rodriguez, Gordon Fischer, Steven Smolski。有中文版,但評價頗差

書名讓人非常期待總算有一本核心介紹書籍 x86 外的硬體平臺,畢竟 RISC vs CISC 架構的不同, endianness, alignment, calling convention 等,應當有許多寫核心驅動程式應該注意得事項。但是整本書只在 2.2 節稍微說一下寫 Assembly 時,PowerPC, x86 的指令差異,剩下的細節根本沒提!

整本書還是著重在一般核心的結構介紹。

而書中除了少量的插圖之外,根本沒有沒有多少邏輯上的說明跟描述。通篇拆解程式碼,對資料結構作註解。這些資訊任何有點基礎的工程師都可自行閱讀程式碼及程式碼註解。新入門工程師還可能因爲書中解釋而疑惑。

除非你想寫沒有價值的書評,否則不建議購買。

Linux(R) Debugging and Performance Tuning: Tips and Techniques 出版於 2005, 作者是 Steve Best。此書少見的從除了應用程式之外,還從核心切入的除錯、效能測試書籍,因爲這方面的技術資訊總是一下就超過保鮮期。

作者試著含括基本的 Profiling 實務開始,介紹 gdb, 應用程式記憶體管理,再講核心的各種資訊界面。很可惜,以一本專講除錯與效能測試的書來說,範例與介紹過於粗淺,以第十二章 Dynamic Probes 為例,其介紹深度可能還比不上 Documents/kprobes.txt 中的概念介紹與 IBM developerWorks 的範例介紹

適合剛切換到 Linux 的開發者,可概略學得各種基本開發工具者的入門資訊。

2011-03-17 18:00 更新

增列相關中譯版本連接,感謝 ansoncat 告知資訊。

2011-05-15 18:00 更新

修正 UTLK 版本為 2.6.11, 補充 ULNI 版本為 2.6.12. 感謝讀者 Wayling 指出錯誤。

經過幾年發展,DBus 已經取代早年 Linux 桌面環境所用的 GNOME Bonobo, KDE DCOP,用於許多應用程式,成為主流 IPC 系統。隨著軟體原件逐漸成熟,眾多程式語言都已經支援 DBus APIs,DBus daemon 的 footprint 也逐漸能夠被嵌入式所接受,而被行動裝置作業系統如 MeeGo, WebOS 所採用。

Linux 開發者難免因爲介接需求,需要測試或使用 DBus 除錯。這裡分享幾個常用的小技巧。DBus 使用物件導向的 API 界面,所有 ServicesObject 函式都是以 Method, Signals, Properties 的概念揭露給外界存取,配合 Introspectable API,很容易讓第三方介接。

最常用到的工具之一是 dbus-send,它可以用來從指令列測試接傳 DBus messages,像是列出系統上所有註冊在 Session BusServices

$ dbus-send --session --print-reply --reply-timeout=2000 \
--type=method_call --dest=org.freedesktop.DBus /org/freedesktop/DBus  \
org.freedesktop.DBus.ListActivatableNames

有了 Services 名稱,接下來你就可以用 Service 為名以 Introspection 界面去查詢其所開放之 API,如

$ dbus-send --session --print-reply --reply-timeout=2000 --type=method_call \
--dest=org.gnome.ScreenSaver / org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.7256 -> dest=:1.7286 reply_serial=2
 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
 <interface name="org.freedesktop.DBus.Introspectable">
 <method name="Introspect">
 <arg name="data" direction="out" type="s"/>
 </method>
 </interface>
 <interface name="org.gnome.ScreenSaver">
 <method name="Lock">
 </method>
 <method name="Cycle">
 </method>
 <method name="SimulateUserActivity">
 </method>
 <method name="Inhibit">
 <arg name="application_name" direction="in" type="s"/>
 <arg name="reason" direction="in" type="s"/>
 <arg name="cookie" direction="out" type="u"/>
 </method>
 <method name="UnInhibit">
 <arg name="cookie" direction="in" type="u"/>
 </method>
 <method name="GetInhibitors">
 <arg name="list" direction="out" type="as"/>
 </method>
 <method name="Throttle">
 <arg name="application_name" direction="in" type="s"/>
 <arg name="reason" direction="in" type="s"/>
 <arg name="cookie" direction="out" type="u"/>
 </method>
 <method name="UnThrottle">
 <arg name="cookie" direction="in" type="u"/>
 </method>
 <method name="GetActive">
 <arg name="value" direction="out" type="b"/>
 </method>
 <method name="GetActiveTime">
 <arg name="seconds" direction="out" type="u"/>
 </method>
 <method name="SetActive">
 <arg name="value" direction="in" type="b"/>
 </method>
 <method name="ShowMessage">
 <arg name="summary" direction="in" type="s"/>
 <arg name="body" direction="in" type="s"/>
 <arg name="icon" direction="in" type="s"/>
 </method>
 <signal name="ActiveChanged">
 <arg name="new_value" type="b"/>
 </signal>
 </interface>
</node>
"
$ dbus-send --session --print-reply --reply-timeout=2000 \
--type=method_call --dest=org.gnome.Tomboy \
/org/gnome/Tomboy/RemoteControl org.freedesktop.DBus.Introspectable.Introspect

 string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- NDesk.DBus 0.6.0 -->
<node>
 <interface name="org.freedesktop.DBus.Introspectable">
 <method name="Introspect">
 <arg name="data" direction="out" type="s" />
 </method>
 </interface>
 <interface name="org.gnome.Tomboy.RemoteControl">
 <method name="Version">
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="DisplayNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="HideNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="DisplayNoteWithSearch">
 <arg name="uri" direction="in" type="s" />
 <arg name="search" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="FindNote">
 <arg name="linked_title" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="FindStartHereNote">
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="CreateNote">
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="CreateNamedNote">
 <arg name="linked_title" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="CreateNamedNoteWithUri">
 <arg name="linked_title" direction="in" type="s" />
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="DeleteNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="DisplaySearch" />
 <method name="DisplaySearchWithText">
 <arg name="search_text" direction="in" type="s" />
 </method>
 <method name="NoteExists">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="ListAllNotes">
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="GetNoteContents">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="GetNoteTitle">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="GetNoteCreateDate">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="x" />
 </method>
 <method name="GetNoteChangeDate">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="x" />
 </method>
 <method name="GetNoteContentsXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="GetNoteCompleteXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="SetNoteContents">
 <arg name="uri" direction="in" type="s" />
 <arg name="text_contents" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="SetNoteContentsXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="xml_contents" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="SetNoteCompleteXml">
 <arg name="uri" direction="in" type="s" />
 <arg name="xml_contents" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="GetTagsForNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="AddTagToNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="tag_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="RemoveTagFromNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="tag_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="GetAllNotesWithTag">
 <arg name="tag_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="GetNotebookForNote">
 <arg name="uri" direction="in" type="s" />
 <arg name="ret" direction="out" type="s" />
 </method>
 <method name="AddNoteToNotebook">
 <arg name="uri" direction="in" type="s" />
 <arg name="notebook_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="GetAllNotesInNotebook">
 <arg name="notebook_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <method name="AddNotebook">
 <arg name="notebook_name" direction="in" type="s" />
 <arg name="ret" direction="out" type="b" />
 </method>
 <method name="SearchNotes">
 <arg name="query" direction="in" type="s" />
 <arg name="case_sensitive" direction="in" type="b" />
 <arg name="ret" direction="out" type="as" />
 </method>
 <signal name="NoteDeleted">
 <arg name="uri" direction="out" type="s" />
 <arg name="title" direction="out" type="s" />
 </signal>
 <signal name="NoteAdded">
 <arg name="uri" direction="out" type="s" />
 </signal>
 <signal name="NoteSaved">
 <arg name="uri" direction="out" type="s" />
 </signal>
 </interface>
</node>"

注意其中 object name 因各 Service 定義不同,首次查詢可從 / 開始查,Dbus 會答覆其子路徑。

有了 API 界面之後,就可以直接用 dbus-send 送些指令給這些 Services 啦。如下啟動螢幕保護程式。

dbus-send --session --dest=org.gnome.ScreenSaver \
--type=method_call --print-reply --reply-timeout=20000 \
/ org.gnome.ScreenSaver.SetActive boolean:true

參數的格式可參考 DBus 規格中的 Type Signatures

如果你是在 Desktop 環境想查詢測試 DBus APIs, 比較容易的工具是使用 d-feet,這是 Python/GTK 所寫的工具,只消滑鼠點點就可以查詢各種 API 與送出指令訊息。(以下圖片出自 d-feet)

知道了基本的測試工具後,測試期間通常需要觀測訊息的傳送與反應是否正確,此時可以利用 dbus-monitor。它可以用來監測系統中所有的 Dbus messages,方便查詢軟體是否運作正常。

由於安全性的考量,dbus-monitor 預設只能監錄 Session Bus 中的訊息,意即在每個 Login session 中使用者所開啟的軟體。至於 System Bus,像是 Network Manager 等預設是無法存取的,這是避免惡意軟體竊取隱私,如密碼等資訊。

必須手動開啟權限,設定方法是更改以下設定檔,開啟 eavesdrop policy,並重啟 Dbus daemon. 這樣才能竊聽相關通訊。

cat > /etc/dbus-1/system-local.conf
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>

  <policy context="default">
    <!-- All messages may be received by default -->
    <allow receive_requested_reply="false" receive_type="method_call" eavesdrop="true"/>
    <allow receive_requested_reply="false" receive_type="method_return" eavesdrop="true"/>
    <allow receive_requested_reply="false" receive_type="error" eavesdrop="true"/>
    <allow receive_requested_reply="false" receive_type="signal" eavesdrop="true"/>
    <allow eavesdrop="true"/>
  </policy>
  <policy user="root">
      <allow send_destination="*" eavesdrop="true"/>
      <allow receive_sender="*" eavesdrop="true"/>
  </policy>
</busconfig>

詳盡資訊請參考

很久一段時間沒有寫 blog,Wordpress 也跟著年久失修。順手升級到 WordPress 3.0.5 後,發現網頁載入時間長達五、六秒。緩慢的 wordpress 除了翻資料很慢,也很可能被搜尋引擎降級

於是乎,翻出 Firebug, YSlow, Page Speed 出來診斷一番。主要有幾個問題

  • WordPress PHP 網頁產生很慢
  • WordPress 用了太多 js/css file, 瀏覽器要抓超過十個檔案
  • 靜態檔案沒有作 cache control

首先是 WordPress 生頁面超過一秒,這顯然是因爲資料庫太慢的緣故。目前 Blog 跑在某臺稍微有點負載的機器,MySQL 的速度頗慢,調查了一下 WordPress 的快取模組如 Super Cache 等,最後選裝了 Hyper Cache

至於 WordPress 用了一堆 js/css ,偷懶的方法則是裝 WP Minify,它整合了 Minify。簡單講就是把 js/css 程式碼合併在一起,移掉註解並壓縮起來,所以原本超過十個 Http Request 會被簡化成 2 個 (CSS, Javascript)。如果你像我一樣在 UserDir 中裝 wordpress,要關掉設定中的 Automatically set your Minify base per siteurl setting,並手動在 wp-minify/min/config.php 中設定 $min_documentRoot 參數。

若網站除了 WordPress 外,還有其他網站,Google 也出了一套 mod_pagespeed,可以在 Apache2 模組中動態修改輸出的 CSS/Script 檔案。

至於最後一項靜態檔案的 Cache control,則把 mod_expires 開起來,時間設一設就搞定了。

其他還可以做的除了把機器搬到快一點的網路或檔案丟到 CDN 上外。還可以用 HeadJS Loader (HeadJS) 讓舊款不會卡在逐一載入 Javascript 的程序上。也可以裝 jQuery Image Lazy Load 讓網頁先顯示,再慢慢下載圖片。

參考資料

遠端測試工具

閱聞 tsung 分享的Debian / Ubuntu Linux 空間釋放 – 清除沒用的檔案一文,也來分享一下自己清理作業系統環境的方法。

自己是 Debian GNU/Linux 的長年使用者。Debian 使用者有個習慣,由於系統實在太穩定,裝好之後終年難得重灌,久而久之系統就會留下徒然佔用空間的廢棄檔案。這些檔案除了使用者個人資料外,不外乎快取、歷史記錄、設定等等,此外就是曾經安裝、編譯軟體時所裝的開發函式庫。這裡分享幾個常用的清理檔案方法。

Personal data

最後是個人檔案,你有許多目錄,需要先找出最佔用磁碟空間的目錄,你可以透過指令列用 du 來看 disk usage. 常用的參數會像如下

du --max-depth=1 -x | sort -n

這個指令的意思是查詢當下目錄所佔用的大小,並依照小到大排列,-x 的意思是只查詢一個檔案系統,方便你找出單一檔案系統中最大的目錄。你也可以用 -h 讓 du 列出易讀格式

如果你不是 geek, 我推薦使用 baobao,圖形化操作界面,加上磁碟佔用派圖,很容易抓出佔用最多空間的檔案。KDE 使用者可以用 Filelight

Image source: Disk Usage Analyzer by Fabio Marzocca

依照我個人而言,佔用最多空間的往往是電子郵件,信件中常常夾了簡報檔、文件等等。如果已經歸檔結案,我常常會把夾檔刪掉,只留下郵件本文存檔。若你用 Evolution,我推薦自己寫得 evolution-remove-attachments

apt-tools cache

從最簡單的系統安裝設定開始,系統就已經裝了許多你用不著得檔案。第一個常見的就是各國語系的說明文件與翻譯,決大多數使用者從來不會去看其他語言的文件,像是我從來沒讀過法文、甚至日文、韓文的 man pages,也不會使用這些語系的使用界面。而大部分軟體因為國際化的關係,往往裝了數國翻譯,如果是 OpenOffice 或 GNOME, KDE 這些大型軟體計劃,佔用的空間還不少呢。你可以安裝 localepurge 來移除不需要的語系。第一次安裝時,系統會問你要保留哪些語系,回答過一次,未來每次透過 apt/aptitude 安裝套件時,它都會自動清理一次。

下載後的 deb 安裝檔常常也佔用很大空間,特別是 OpenOffice, Xorg, kernel 等等,如果你常常升級系統,不知不覺系統就會存下很多暫存的 deb 安裝檔。你可以用 apt-get 來清除它們。

# apt-get autoclean
# apt-get clean

其中 autoclean 是清掉舊版的 deb,系統會保留一份最新版本安裝檔。我個人都使用 autoclean,以免發生意外的時候,沒有最新版本的 deb 檔可以重新裝。一般使用者可以直接以 clean 清除快取。

apt-tools dependency

使用 Linux 時,或多或少會自己編譯一些軟體,需要編譯軟體時,就會需要相關的函式庫。但常常裝完砍掉後,這些相關的開發函式庫還留在系統中變成軟體孤兒。

你除了應該儘量用 apt-get autoremove 或 aptitude 外,也可以用 deborphan 來找軟體孤兒。最常見 (也很危險) 的用法是

# aptitude remove $(deborphan --nice-mode --guess-all)

這樣deborphan 會推估所有系統中不需要的程式,並全部移除。你也可以用以下指令找出最佔用空間的套件。

$ deborphan -a -n|sort -n

懶惰的話,你也可裝 gtkorphan,以圖形化界面管理。

bleachbit (ccleaner for Linux)

雖然新的 Linux 軟體,已經逐漸使用 .local .cache, .config 等目錄設定方式,但是還是有許多軟體會把快取存在 .config 或自己的目錄中,像是 .gnome2, .firefox 等。結果是使用者很難找到快取檔案,若使用 UbuntuOne, Dropbox 等等雲端備份工具,常常連不重要的快取都一起備份了,費時費錢。

上述或指令或工具,看似十分繁複,而且只能管理系統檔案。從 Windows 跳船過來的使用者一定想,是否有類似 CCleaner 的工具,點一點就可以把垃圾檔案清乾淨?

你可以使用 BleachBit (跨平臺),BleachBit 設計了一套相當有彈性的設定描述語言 – CleanerML,支援了許多常見軟體的清除功能,非常類似 CCleaner,軟體會自動偵測可清楚的軟體列表,只消點選要清除的檔案類型,BleachBit 就會自動刪除並計算所騰出空間。沒有 X 圖形界面時,也可以用 bleachbit_cli 指令列。

References

如果你很 Geek, 可以參考 Ubuntu Wiki 上的 Reducing Disk Footprint,上面分享了許多縮減系統大小的奇技淫巧。

另外 Ubuntu 正有計劃要削減安裝光碟大小,未來也會影響系統安裝後的大小。

作為 NoSQL 潮流中的一份子,很容易被拿來與被貼上一樣標籤 (buzzwords) 的資料庫作為比較。各個專案分別被貼上 key-value stores, tuple stores, BigTable Clones, Document store, Graph database 等等標籤

這些標籤分類有時很容易幫助使用者瞭解大概的後段實做技術屬性,但很多時候,也會把使用者帶來偏頗預設看法與錯誤理解。我們可以說這些新技術都是想解決 CAP theorem 問題,但其實這些不同的資料庫計劃各有其想要解決的問題與開發哲學,如果以概括性的功能特徵來作為選擇技術的方式,很多時候會發現專案發展方向適合自己需求。使用者應該先定義自己的問題後,再依照選擇最適合的專案屬性。

Mikeal Rogers (CouchOne 的開發者) 上個月就,這波產業浪潮掩蓋了各專案實際要解決的問題,取而代之是對於 Big Data 的想像與期待。Mikeal 那麼就讓其他人 (與舊資料庫業者) 去追尋 NoSQL 的浪潮吧,CouchDB 可以專注與處理 Mobile Database

CouchDB 特色

試用了 CouchDB 一陣子,幾個特別欣賞的特色功能:

  • Implemention for ACID Properties
  • Schema-Free document-oriented database
  • RESTful JSON API
  • View model / JavaScript View Functions
  • Replication (Peer-based distributed databases), Distributed, featuring robust, incremental replication with bi-directional conflict detection and management.

CouchDB 使用 MVCC model,所有的資料庫寫入 (add, update, delete) 都不需要先 lock 資料庫,因此可以保證可以即時讀出資料。索引則是靠 B-Tree 大量存於硬碟,Document 一份索引,每一份 View (利用 MapReduce pattern 設計的查詢界面) 也會存有一份索引,因此你的資料庫會佔用有大量硬碟空間來換取速度。

另外則是資料可以是隨意的 Json document, 所以愛存什麼就存什麼。此外 API 界面是 RESTful,加上內建 Javascript 的 View Server,你可以用內建的 Show 功能 Render Document, CouchApp 很方便配合瀏覽器使用 (也支援權限功能)。

最重要的特色是 Replication 功能,CouchDB 支援雙向衝突偵測,很方便長期離線的 Client 上線後快速同步資料庫內容,平時離綫狀態,也可以存取已經拉回的資料。

CouchDB 目前限制

雖說 CouchDB 使用 MVCC Model,但 CouchDB 的讀寫速度遠遠慢於 in-memory key-value,與 MySQL 評測也未能夠勝出。加上 CouchDB 只支援 Replication,要作 Horizontal partitioning 要靠 CouchDB Lounge,同等於手動作 Sharding. 效能跟彈性都未能夠勝過其他資料庫系統。

此外,作為存取 unstructured data 的代價,你若想要查詢特別資料,必須依賴 View Model,每組 View 會建出獨立的 b-tree index,這種索引的代價頗高,特別是你想做 ad hoc query 時,使用 _temp_view 會浪費你大量的磁碟與 CPU。因此你必須設計軟體時與鍵入資料前先建好 View。

雖然 View functions 用了 MapReduce pattern, 但礙於 CouchDB View API 的限制,目前很難作多次的資料處理,基本上一次 View 倒出來的資料僅限制一次查詢,你無法方便快速的做第二次 Map/Reduce 處理,如此缺乏彈形讓 MapReduce 反而像是限制,而非一種特色。

除了 Big Data 以外相關應用

捨去上述限制不談,CouchDB 的特色仍然可以作很多應用,特別是 JSON API 與 Peer-based distributed 這兩個功能,很方便在 Laptop 或 Mobile devices 上提供一些應用,特別是你需要在時常離綫的裝置上處理伺服器與本地端資料時。

除了像是 CouchOne 推出相容 CouchDB 的 CouchOne Mobile for Android。在 Linux 上則有 Desktop Couch,目前整合了 Tomboy, Evolution, Bindwood for Firefox 等,可以用來管理 PIM, Bookmarks 等等。理論上可以整合 UbuntuOneMidgard

我讀過兩本 CouchDB 得書籍CouchDB: The Definitive Guide 1st Edition 與 Beginning CouchDB,比較推薦 Beginning CouchDB,但 CouchDB: The Definitive Guide 的下一版,內容已經有改善。

本文是個人使用經驗與看法,如果錯誤,歡迎指教。

使用 git 改完一段程式碼後,你總想回傳給上游開發者。一般是利用 git-format-patch 來產生 patchset,然後搭配郵件軟體軟體寄出。可以配合 git-format-patch 使用的寄信軟體有 git-send-email, git-imap-send 等等,或你也可用 formail 將郵件轉為你的郵件軟體認得的格式。

個人喜歡 git-imap-send,它會將 patch 透過 IMAP 協定存在郵件伺服器上的草稿目錄,如此你可以稍候再透過郵件軟體編修或決定是否寄出。

GMail 設定方式

git-imap-send 使用時,需要先設定 IMAP 伺服器位置與帳號密碼。你可以直接編輯 .gitconfig ,寫入如下設定

[imap]
# Folder = "[Gmail]/Drafts"
Folder = "[Gmail]/&g0l6Pw-"
Host = "imaps://imap.gmail.com"
User = "[email protected]"
port = 993
sslverify = false

你如果看網路上的大部份說明文件,會告訴你 Folder 應該填 [Gmail]/Drafts。很神秘的是,大約我是中文使用者,我的草稿目錄就叫做「草稿」。若用 [Gmail]/Drafts 會出現 “(NO) – [TRYCREATE] Folder doesn’t exist. (Failure)” 錯誤訊息。但是 git-imap-send 不會自行編碼,因此你必須先自行依照 RFC2060 轉碼後,寫入設定,”[Gmail]/草稿” 即為”[Gmail]/&g0l6Pw-“。

附帶一提,Debian 的 git 由於 openssl 的授權問題,並沒有編譯入 SSL 支援。因此你得自行編譯一次,如想使用 Gmail 的 IMAP 寄信的話。

相關 Patch 格式、回覆地址設定

若你想透過 Gmail 送 patch,另外一個需要注意的是 Gmail 會弄爛內文的換行,因此請將 patch 以 attachment 的方式寄送,夾檔 Disposition 是 inline 或 attach 都可以。做法如下

git format-patch –stdout –inline –keep-subject origin | git-imap-send

為了方便別人閱讀,你也可以將夾檔副檔名改為 .txt,以便大部分郵件軟體可以直接閱讀辨識。

[format]
suffix = .txt
inline

另外你也可以在郵件表頭中指定回覆的地址。例如你可以指定請大家回信到 mailing list,以便可以在公開場合進行 patch review。你可以在 git config 中加入如下設定,由於你可能參與很多計劃,最好是加在 project/.git/config 中,而非 ${HOME}/.gitconfig.

[format]
        headers = "To: patches <[email protected]>\nReply-To: devel <[email protected]>\n"

其他方便開發者閱讀的參數如 –signoff, –thread, –numbered 等, 相關細節請見 git-format-patch (1) man page.