2013年3月21日 星期四

比 soft link 更好用的 mount --bind

當目錄 a 所在的 partition 沒空間但又得放東西在 a 下面時, 通常會另建 partition, 然後 soft link 新 partition 下某個目錄過來, 比方說從新 partition 用 soft link 指向 a/b。

這個作法有個缺點, 若 a/b/ 下的程式想透過 ../ 存取 a 目錄下的東西, 比方說用 ../c 取得 a/c。在 soft link 的情況會找不到 c, 因為 b 其實在另一個 partition, b/.. 並不是 a。

這時可用 mount --bind 解, 作法很簡單, 就直接抄 man mount 的內容過來備忘:

Since Linux 2.4.0 it is possible to remount part of the file hierarchy somewhere else. The call is
     mount --bind olddir newdir
or shortoption
     mount -B olddir newdir
or fstab entry is:
     /olddir /newdir none bind

和 C 前置處理器相關的小技巧

在 gdb 內查詢 macro

需要在編譯時加上 -g3, info macro 等指令才會有用。

參考資料: http://sourceware.org/gdb/onlinedocs/gdb/Macros.html

列出 include 的內容

$ gcc -E a.c

編譯錯誤指出缺少某個 symbol 時, 可用這招確定到底有沒有引入目標的宣告。有時 #ifdef 太多, 看原始碼不易確認這件事。

列出 define 的內容

比方說若想知道 errno 111 代表什麼意思, 可這麼做:
$ echo "#include <errno.h>" | gcc -dM -E - | grep " 111$"
#define ECONNREFUSED 111
再來 man errno 查詢 ECONNREFUSED 即可得到答案。
另外, 可用以下的方法得知系統預先 define 的東西
gcc -dM -E - < /dev/null
參考資料: http://stackoverflow.com/questions/2224334/gcc-dump-preprocessor-defines

2013年3月20日 星期三

python memory leak

由於 CPython 使用 reference counting 管理記憶體, reference count 到 0 時會立即釋放記憶體, 遇到 memory leak 時, 至少可以用 del 稍微自救一番。

import sys

a = []
print sys.getrefcount(a)  # 2
b = a
print sys.getrefcount(a)  # 3
del b
print sys.getrefcount(a)  # 2

如上所示, 可在程式中用 sys.getrefcount() 檢查 referece count 是否符合預期, 盡量在不用以後立即呼叫 del, 當 reference count 變為 0 時, 會提前釋放記憶體。

注意 python 的 while、for 沒有形成新的 scope, 有可能因此誤判一些物件的生命週期:

for i in range(10):
    pass
print i  # 9, 並非 NameError

備註

附上 CPython 2.7.3 Include/object.h 內關於減 reference 相關的程式:

769 #define Py_DECREF(op)                                   \
770     do {                                                \
771         if (_Py_DEC_REFTOTAL  _Py_REF_DEBUG_COMMA       \
772         --((PyObject*)(op))->ob_refcnt != 0)            \
773             _Py_CHECK_REFCNT(op)                        \
774         else                                            \
775         _Py_Dealloc((PyObject *)(op));                  \
776     } while (0)

可看出在 reference count 為 0 時, 確實會呼叫 deallocator。

今日休閒時間已用盡, 改天再來看看為什麼初始數值為 2。

2013年3月3日 星期日

Core Data lightweight migration

上份工作需要階段性處理大量資料, 當時有不少 database schema 與 data migration 的經驗, 雖然我不怎麼喜歡 Django 的 ORM, 不過配合 South 做 migration 到是滿方便的。

最近用 Core Data 不幸地在發佈測試程式後發覺需要增加欄位, 查了一下作法, 所幸 Core Data 有針對簡單的情況提供自動化 migration, 相當輕鬆, 簡記用法如下:

說明瀏覽器運作相關的文章

備忘一些不錯的概念文章:

  • WebKit for Developers - Paul Irish: 說明各家使用 WebKit 的瀏覽器到底共用的 WebKit 是什麼。另有附一份 slide 說明 "How WebKit Works", 不過 slide 講的內容就很少了。
  • How browsers work, 2011 年的文章, 說明瀏覽器運作的每個步驟

iOS delegation vs. notification

初始使用 iOS 的 NSNotificationCenter 覺得頗不錯的。UI 物件需要從一個遙遠的物件裡取得資料改變的狀態, 當初沒考慮好這個需求, 不太方便傳遞資料。這時用 notification 解套就容易多了。但是隱約覺得 notification 的用法不太妙, 查到這篇 When to use Delegation, Notification, or Observation in iOS 分析得很清楚, 摘要重點如下。
使用 notification 的缺點 (或說特色) 是:
  • notification 是非同步呼叫, 發送 notification 和接受 notification 在不同的 runloop iteration 裡, 不易除錯。
  • notification 的發送和註冊採用字串做為 key, 無法在編譯時期找到錯誤。執行時才會察覺「好像沒呼叫到」
另一方面, delegation 透過定義使用端的介面 (iOS 術語為 protocol), 運作的特點為:
  • 透過函式呼叫通知事件, 方便除錯
  • 可在編譯時期確保提供服務者和用戶端有正確串在一起
  • 除通知發生事件外, 可讓客戶提供實作供提供服務者使用
原文有提及更多項目, 不過對我來說最要緊的是「是否容易除錯」。從這個角度來看, 使用 delegation 遠比 notification 安全。所以可以的話, 盡量避免使用 notification 較好。

2013-03-03 更新

經 Sam 指正, 發覺 notification 預設是同步呼叫, 在 backtrace 裡可找到發出點和接收點。這樣就沒那麼難除錯了。重看一遍該篇文章, 作者提的除錯困難, 可能是指用太多 notification 混在一起的情況。

兩者的主要差別應該是介面的強度。介面規範強, 編譯時期自然抓到較多錯誤, 寫起來也比較麻煩一些。其它的小細節如「少了 observer 訊息、一對多 (多對一) 架構」等, 則是設計介面或使用時可補救的。

2013年2月11日 星期一

GUI 架構的考量

( 和 Scott 聊過後, 決定改寫《非同步程式新手心得: 為什麼要用單一 thread + task queue 的方式設計架構》的前半段。比較完整地描述 GUI 架構的入門心得。 )

GUI 需要低延遲的反應速度, 隨時能接收使用者輸入和並提供畫面回饋。這表示要在不互相阻檔的前提下應付三種工作:

  • 更新畫面
  • 偵測使用者輸入
  • 處理使用者輸入

甚至第四種情況:

  • 網路輸入

若是使用 multi-thread 處理上述情況 (比方說有三個 thread 分別專職處理前述三件工作), 必須留意 thread 之間有共享資料的情況。為了避免 race condition 必須使用 lock; 使用 lock 後又要擔心 dead lock。

使用 global lock 有可能成效不彰, 比方說更新畫面太久, 而沒有即時回饋使用者的輸入。另一方面, 切許多細小的 lock 處理各塊共享資料, 容易出錯造成 dead lock, 或誤以為有處理好卻仍存在 race condition。

反過來說, 若每個工作 (#1) 都切得很細, 執行時間不長, 但可能需要多個工作單元才能完成目標。這樣就能在一個 thread 內以類似 queue consumer 的方式執行全部的工作。有新的需求時, 放入工作到 queue 裡, 大致上可確保相依工作之間的執行順序 (#2)。由於全部工作都在同一 thread 執行, 自然不用擔心 dead lock 或 race condition。

於是可以看到, 像 Gtk、iOS、Android 的平台, 都將更新畫面、偵測事件和處理事件三者整合在 main thread 處理, 並且假設程式以 single thread 的方式執行 (#3)。

我不清楚其它平台的情況, iOS 有提供 NSURLRequest 應付常見的網路需求。讀寫資料和事件的 callback 也都在 main thread 內執行。

這麼一來, 框架開發者省事許多, 使用框架的開發者也可以安心地實作 callback。

備註

#1 可想像每個工作是一個函式, 在 iOS 或 Android 上, 整合這種「工作 queue」的概念到基本框架裡, 用起來很直覺, 希望盡量避免開發者從 thread 的角度去處理事情。

#2 不行的時候只好加入狀態變數了解目前該做什麼, 至少不用擔心狀態變數本身會有 race condition。

#3 若開發者有特別需求, 比方涉及檔案或網路 I/O, 或是需要大量 CPU 計算, 而無法在短時間內完成單一工作的話, 開發者需要自行另外處理。使用新的 thread 的話, 自然需要使用 lock 保護和 main thread 共享的資料, 但需要煩惱的範圍已降致最小。

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...