August 20, 2008
「泛 EeePC 產品的軟體設計考量」簡報上線

八月 4 日應某公司的邀請,進行主題為「汎 EeePC 產品的軟體設計考量」的演講,簡報檔案已上線,可參見 [
think-smaller.pdf]。在這個議程中,談及 Eee PC 一類所謂的「小筆電」在軟體設計的議題,我探討了以下三個問題:
- 泛 EeePC 的成功是軟體發展的退步?
- 將 GNU/Linux 推入消費性電子產品的迷思
- 準狠快 -- GNU/Linux 的系統整合與客製化
我們可見,這一年內的「小筆電」風潮好似對低靡的筆記型電腦產業,注入強心針,只不過,藍海很快就成了全面廝殺的紅海,而這之中有太多值得身處於台灣的我們,該認真思考的議題。電子資訊大國的幻滅,或許不是傾刻發生之事,但若坐視不理會,後果很難想像。
就個人愚見與觀察,本該賦予這類新產品生命的軟體,比重卻不甚明朗,GNU/Linux 在此的客制化好似在 Home Screen 打轉,這又意味著什麼?當然,瞬息萬變的大千世界,往往可能在短期內改觀,或許僅是庸人自擾,但就如簡報檔最後幾頁提及的文字「為了我們的子孫,請把產品做好」所言,不單為了環保,也為了產業的永續發展,衷心期盼這類的小筆電,能用心導入更多該做的設計。
請多指教,謝謝!
由 jserv 發表於
08:23 AM
|
迴響 (6)
August 16, 2008
「Clutter -- 釋放 OpenGL 威力的新途徑」簡報上線

今天下午應酷學園之邀,至台南崑山科技大學給予主題為 [
Clutter -- 釋放 OpenGL 威力的新途徑] 的演講,簡報檔案已上線,可參考 [
clutter-overview.pdf],以及議程中提及的 [
影音展示]。
作為一個概念性的介紹,本議程探討以 OpenGL 為基礎、活躍開發的開放原始碼函式庫 -- [
Clutter toolkit] 的應用,包含 MID (Mobile Internet Device)、NetBook、GPS/GND、Media center 等等。過去開發 OpenGL 應用程式時,往往專注於繁複的數學運算與模型表示,而很難兼顧「物件」本身的行為表現,而 Clutter 獨到處,就是讓開發者得以快速建構場景 (Stage) 與物件「演員」(Actor) 的互動,進而開發具有科技質感的使用介面。
感謝與會朋友的指教,未來則會針對 2D/3D 嶄新的視覺呈現與新的開發模式作經驗分享。
由 jserv 發表於
09:21 PM
|
迴響 (0)
August 03, 2008
當感覺已麻木,告訴 Linux 說 reboot

這標題是前幾日在 twitter 上意外聯想到,對於 Linux reboot() 系統呼叫的 geek 笑話,為了解釋箇中的幽默,決定寫一篇短文。
當我們查閱 reboot(2) 的 manpage 時,可發現其函式宣告如下:
#include <unistd.h>
#include <sys/reboot.h>
int reboot(int cmd);
至於參數的 "cmd",就是以 RB_ 開頭巨集表示的數值,可為以下:
- RB_AUTOBOOT - 系統會列印 "Restarting system.",並立即重開
- RB_HALT_SYSTEM - 系統會列印 "System halted.",接著將控制權交與 ROM monitor (在非 x86 的系統很常見)
- RB_ENABLE_CAD - 驅使 CAD (Ctr-Alt-Del 組合按鍵) 生效,亦即按下 CAD 後,就執行前述 RB_AUTOBOOT 的動作
- RB_DISABLE_CAD - 使 CAD 失效,這意味著 CAD 不會讓系統重新啟動,但會觸發 SIGINT signal 並遞送給 init (process 1)
這是 glibc 考量到支援的眾多平台,做了一些包裝,所以 reboot() 在 glibc 是以接受單一參數的函式存在。在早先的版本,就是直接提供 Linux kernel 的系統呼叫 wrapper,其描述如下:
#include <unistd.h>
#include <linux/reboot.h>
int reboot(int magic, int magic2, int cmd, void *arg);
看到參數的 magic 與 magic2 字樣,很自然就會想到,就是 magic number,並且背後大有文章。在資訊技術的領域中,不乏會有透過有限空間,表達獨特趣味的手法,比方說 Microsoft COM 技術裡頭,不時會有蹦蹦跳跳的貓咪,證據就是 "MEOW packet",據某個權威者的說法 MEOW 代表 "Microsoft Extensible Object Wire" 之意,但不免啟人疑竇。又,從 Oak (橡樹) 更名為 Java 的知名程式語言,其 classfile 開頭就是 Cafe Babe (表示法為 0xCAFEBABE,亦即「咖啡寶貝」),這一類的案例更是豐富,凡十六進位表示法恰好能對應到英文某些詞彙者,就稱為 [
Hexspeak]。Linux kernel 內自然也有,而且不只一處,本文的主題 reboot() 就堪稱其中經典。
前述的函式宣告中,前兩個參數 (magic 與 magic2) 必須同時符合某個條件,才真正會觸發系統重新啟動或設定 CAD 行為的能力,以避免不當地使用。明確來說,magic 必須為 LINUX_REBOOT_MAGIC1 ("0xfee1dead"),而 magic2 的值要為以下其中一個: (定義於標頭檔 include/linux/reboot.h 之中)
- LINUX_REBOOT_MAGIC2 = 672274793
- LINUX_REBOOT_MAGIC2A = 85072278 (Linux 2.1.17 追加)
- LINUX_REBOOT_MAGIC2B = 369367448 (Linux 2.1.97 追加)
- LINUX_REBOOT_MAGIC2C = 537993216 (Linux 2.5.71 追加)
LINUX_REBOOT_MAGIC1 的值實在有意思,"0xfee1dead" 不就形如 "feel dead" 嗎?差別是前者是數字 "1",後者是小寫字母 "l",英文中 dead 的意思可解釋為「麻木不仁」一類,這也是本文標題的來源,因為,這會帶入 reboot() 作呼叫的動作,當然,看倌要發揮一些 geek 的幽默。
不過,趣味處不只如此,magic2 四個數字貌似平凡,但實際上述說了 Linux Kernel 發展的歷史,怎麼說呢?咱們用比較技巧性的方式分析,首先是 LINUX_REBOOT_MAGIC2 = 672274793,看看其十六進位表示法:
$ printf "%x\n" 672274793
28121969
稍微作切割,就是 28/12/1969,也就是 Linus Torvalds 生日的 DD/MM/YYYY 寫法。也可以這樣解讀:
$ perl -e 'print localtime(672274793). "\n";'
Mon Apr 22 06:59:53 1991
透過 localtime 得知筆者在 GMT+0800 (台北時區) 的日期表示,這個 1991 年四月 22 日大致是 Linus Torvalds 當年在芬蘭赫爾辛基大學撰寫 Linux Kernel 的日期。令人拍案叫絕之處,還不只如此,咱們繼續看下一個 LINUX_REBOOT_MAGIC2A = 85072278:
$ printf "%x\n" 85072278
5121996
同樣的手法,得知日期是 1996 年十二月 5 日,這天是 Linus 千金 Patricia Miranda Torvalds 出生的日子,也被稱為 "Linus v2.0",同理可推論下個數值 LINUX_REBOOT_MAGIC2B = 369367448:
$ printf "%x\n" 369367448
16041998
日期 1998 年四月 16 日,也就是二千金 Daniela Yolanda Torvalds,或 "Linus 3.0" 誕生的日子,當時甚至有網友異想天開問說「Linus 3.0 是否支援 IPv6 」或「Linus 是否 GPL 自己 的 DNA 序列」等令人莞爾的問句,女兒出生隔日則推出 Linux 2.1.97。
「最新」的一個家庭成員,表示為 LINUX_REBOOT_MAGIC2C = 537993216::
$ printf "%x\n" 537993216
20112000
小女兒 Celeste Amanda Torvalds 誕生於 2000 年十一月 20 日,陪伴著服務於幼稚園的夫人,一家五口,應該頗熱鬧。看來沈迷電腦技術的高人,不僅軟體生產力強大,真實世界也不遑多讓。該不會過一段時間,又追加一個 magic number 呢? *笑*
在 uClibc 中,關於 reboot() 函式的實做,可窺知如何使用系統呼叫。檔案 libc/sysdeps/linux/common/reboot.c 的內容如下:
#include <sys/syscall.h>
#include <sys/reboot.h>
#define __NR__reboot __NR_reboot
static inline _syscall3(int, _reboot, int, magic, int, magic2, int, flag);
int reboot(int flag)
{
return (_reboot((int) 0xfee1dead, 672274793, flag));
}
reboot() 系統呼叫要能呼叫,其成立條件除了前述的 magic 與 magic2 要符合外,系統得有 SYS_BOOT capability (參見 include/linux/capability.h 標頭檔) 以及 BKL (Big Kernel Lock,就最新的發展來說,已逐漸移去 BKL)。除了字面上 "reboot" 的「重新啟動」意思,事實上,這個系統呼叫 (核心實做名稱為 "sys_reboot") 提供以下功能選項:
- 指定方式關機:restart (重新啟動/暖開機)、halt (系統停止運作)、poweroff (關閉電源)
- 觸發某些硬體架構上特定的「關機指令」,比方說回到 watchdog 或 ROM monitor
- 切換 CAD (control-alt-delete) 的處理
為了讓核心的驅動或運作得以正常結束,reboot 系統呼叫也會逐一觸發已註冊的 reboot_notifier callbacks。
談及 reboot 系統呼叫時,我們也會提到 Linux 上的 [
SysRq],透過這個神奇的機制,我們可要求系統作些特別的動作,如重新啟動系統或關機。首先得先使其生效:
# echo 1 > /proc/sys/kernel/sysrq
強制重新開機 (也叫做 fast reboot) 就給予以下指令:
# echo b > /proc/sysrq-trigger
至於強制關機,就是以下:
echo o > /proc/sysrq-trigger
由 jserv 發表於
07:27 AM
|
迴響 (4)
August 02, 2008
演講:Clutter -- 釋放 OpenGL 威力的新途徑

八月 16 日,將應 [
酷!學園] 的邀請,在台南崑山科技大學作主題為「Clutter -- 釋放 OpenGL 威力的新途徑」的演講,以下摘錄 [
討論區公告] 的資訊:
簡介:OpenGL 作為工業的繪圖標準,獲得巨大的成功,而我們身處的數位資訊社會,也廣泛引入影音多媒體技術的創新。甚至,移動數位裝置使用的硬體加速器效能日臻改善,已能在兼顧省電的情況下,透過 OpenGL(ES) 給予我們豐富的體驗,遑論資源豐富的 GNU/Linux 開放平台。本議程試圖探討 GNU/Linux 上,以 OpenGL 為基礎、活躍開發的開放原始碼函式庫 -- Clutter toolkit 的應用,透過其精簡的
API,去思量如何開發具有科技質感的使用介面。
- 預計涵蓋以下項目:
- 2D/3D 繪圖基礎概念
- GNU Linux 的 OpenGL / OpenGL/ES 概況
- Clutter 的中心思想
- clutter 核心概念: Actor tree - stage, parent/child, timelines, events 等
- 快速建構具有科技質感的使用介面
- 時間:2008 年八月 16 日 13:30~17:00
- 地點:台南崑山科技大學 資訊科技大樓 5F I3502 教室 (台南縣永康市大灣路 949 號)
- 地理位置/交通路線:
- 注意事項:
- 建議聽眾有 C 語言及圖形處理的基礎概念,方可得心應手
- 預計於八月 12 日提供本議程的範例程式碼,請參與者報名時,務必提供正確的 email 聯絡資訊,以利工作人員事先寄送作參考
- 講者本於知識無價的理念,無償作技術分享,但願能廣結善緣,歡迎招待當地名產
- 報名網頁:http://registrano.com/events/satn080801
稍早在 [
許我們一個 Keroro 的桌面] 的演講已提過 Clutter/OpenGL,也做了些零星的技術展示,此則是較全面地探討設計上若干概念。而 GNU/Linux 發展至今,已非昔日阿蒙,在自由軟體活躍發展下,更是不斷激盪新的火花,我們該思考的是,該如何有效展現與實做出預期的 2D/3D 視覺風貌,進而作整個場景的布局。
期待您的指教,謝謝!
由 jserv 發表於
09:16 AM
|
迴響 (1)
August 01, 2008
以 C 語言模擬 Lisp/Scheme 語法

傍晚與一位六月份見過面的朋友通訊,我們聊到機器人設計,他問說為何不考慮用 Lisp 來建構系統平台,問題一出,讓我這個「慣 C 魔人」想到新題目:
筆者選定 Scheme 作為主要的模擬對象,發展於 1975 年的 MIT 人工智慧實驗室 (是的,就是 Richard M. Stallman 早年服務所在)、衍生自 Lisp,作為一種 functional programming languages,以 lambda calculus 為理論基礎。現有 Scheme 語言的標準,依據 2007 年制訂 Scheme 語法規則的第六次修正,特稱 [
R6RS] (Revised(6) Report on the algorithmic language Scheme),基本上就是 Lisp 的方言 (dialect),伴隨豐富的函式庫資源。[
朱孝國的筆記本] 有一份簡要的 [
中文簡介],引述其中關於語法的段落:
- 整個 scheme 可以說是 read-eval-print loop 的運作方式:即「讀取、計算,印出」的過程
- scheme 沒有大小寫之分
- 由函數組合所構成,可以巢狀組合,沒有 main 這個主函數進入點,以小括號將運算式括起來,函數名稱或運算元在左括號的右邊,運算子彼此以空白為間隔,如 3+4*5 這個運算式,以 Sheme 語法撰寫如: (+ 3 (* 4 5)) ,類似資料結構中的前序運算式
- 基本的資料型態為原子 (atom) 及字串 (list):
- 原子 (atom) 包含符號 (symbol) 及數值 (number)
- 串列 (list) 則是以小括號括起來的一串資料
Wikipedia 的 [
Scheme] 詞目給了一個遞迴式階層運算的範例:
(define (fact n)
(if (= n 0)
1
(* n (fact (- n 1)))))
考量到 C 語言的關鍵字,我們「模仿」成以下的風格: (factorial.c)
define(int, factorial, (int n),
if(eq(n, 0),
1,
mul(n, factorial(sub(n, 1)))))
define(int, main, (void),
(printf("10! = %d\n", factorial(10)), EXIT_SUCCESS))
由上可見,「形式」上,保有括號與原子 (atom) 的呈現,不過,這就不是 C 語言了呀?沒關係,只要在編譯器那邊動點手腳即可,以下是編撰的 Makefile 內容:
CFLAGS = -Wall \
-D'define(ret, name, args, block) = ret name args { return block; }' \
-D'if(expr, block1, block2) = expr ? block1 : block2' \
-D'eq(a, b) = a == b' \
-D'sub(a, b) = a - b' \
-D'mul(a, b) = a * b' \
-include "stdio.h" -include "stdlib.h"
TARGET=factorial
all:
gcc -o $(TARGET) $(TARGET).c $(CFLAGS)
clean:
rm -f $(TARGET)
編譯並執行:
$ make >/dev/null && ./factorial
10! = 3628800
結果符合我們預期。由上述 macro 定義 (即 -D'name(args...)=definition' 的那五行),特意將 Scheme 語法的 atom 轉變成 C 語言的語法,原本 Scheme 的語法是:
define(型別 函數名稱 (引數串列), (函數 ...))
做了小量的挪移,恰好符合。同樣地,以上僅示範「模擬」語法的可能性,實際尚須考量到 Scheme/Lisp 在處理運算,本質上採用 prefix (前序運算式) 的設計。
取得本文打包的程式碼 [
scheme-in-c.tar.bz2]。
由 jserv 發表於
08:54 PM
|
迴響 (1)
以 C 語言實做 Javascript 的 prototype 特性
自從撰文 [
以 C 語言實做 Functional Language 的 Currying] 與 Thinker 的指教後,又認真思索「以 C 語言模擬其他程式語言的關鍵機制」的議題,何也?在筆者淺薄的認知中,語言只是一種手段,語法不過是彰顯某些動機與概念,而本文則試著由 C 語言「模仿」Javascript 的 prototype 特性,雖不過是東施效顰,但不妨可視為一個切入點,對 Javascript 這個兼具 prototype inheritance 與 functional programming 特性的動態語言。
JavaScript 這個具有十多年歷史的程式語言,雖然普遍的認知是,語言本身跟 Java 無關,但兩者發展的背景卻值得玩味。過去任職於 Netscape 的 Brendan Eich (現為 Mozilla Corporation 的 CTO),為訂於 1995 年發佈的 Netscape Navigator 2.0 設計了嶄新的語言,即 LiveScript,目標為同時能運作於客戶端 (如 Navigator 這樣的網頁瀏覽器) 與伺服器端 (Netscape 主打自家的解決方案 LiveWare)。這樣的概念與打著 "The Network is The Computer" 旗幟的 Sun Microsystems 不謀而合,於是,雙方進行了合作,這樣的因素使得 LiveScript 被更名為 Javascript,考量到同一年發佈的 Java,可在市場概念上作強化。經過多次改版的 Javascript,最後由 Netscape 交付予 ECMA (European Computer Manufacturers Association) 制定為新標準,也就是 ECMA-262,也被規範為 ECMAscript、與實現廠商無關的腳本程式語言的語法和語意,稍後整合為 ISO/IEC-16262。
Javascript 程式語言最令初學者費解的兩項特性就是 Currying 與 prototype,前者來自 functional programming,後者來自物件導向的一個分枝:prototype-based,與傳統程序語言如 C 或 Pascal 有很大的出入。簡單來說,在 prototype-based 的思維中,任何物件都是 "instance",但卻沒有傳統 class 的概念,這些 "instance" 由特定的 prototype 去複製 (clone) 而生。godfat 給了一個簡要的範例,探討 ECMAscript 中操作 prototype 的方式,以下引述作參考: [
出處]
// 產生一個 function object, 會輸出「我是 ooo」
function say() { print('I am ' + this) }
// 產生一個 function object, 拿這當 Duck 的 prototype
function Duck() { }
// 讓 Duck prototype 產生一個成員,也就是讓 say 變成他的 method
Duck.prototype.say = say;
// 定義 toString 讓 say 使用
Duck.prototype.toString = function(){ return 'Duck' }
// 假設現在有一個讓某東西說話的 function
function say_hello(who){ who.say() }
// 於是我們可以這樣呼叫 say_hello
say_hello(new Duck)
在如此的語意中,"new Duck" 意味著 ECMAscript 得先去尋找 Duck 的 prototype,然後 clone 一份該 prototype 後傳回物件的 instance,所以 function say_hello(who) 的 "who" 被帶入一份 Duck 的複製,也因此,執行 say 則會輸出:
I am Duck
而回到 Javascript 的物件導向設計,其 prototype inheritance 的特性落實於 this, new, prototype 等關鍵字的使用,再巧妙地摻入 lexical scope (execution context 和 scope chain)、匿名函數、function object 等 functional programming 特性,組合表現出物件導向的多個面向。在 [
石頭閒語] 有若干篇值得一讀的好文,與此主題相關者可參見:
鋪陳許久,本文終於能切入主題了,是的,掌握 prototype-base 的思維,一句話來說就是:
「"instance" 由特定的 prototype 去 clone 而生」
筆者預想的情境是,環境中存有若干個矩形 (Rect) 物件,對 prototype 作操作,給定如 getter() 這類的 method 動作予矩形物件,並觀察其執行的表現。下列的程式碼,將展現 C 語言實做 prototype 的手法: [
js-prototype.c]
#include <stdio.h>
#include <stdlib.h>
struct _rect {
int x, y;
int (**prototype)(struct _rect *);
};
typedef struct _rect Rect;
typedef int (*Rect_method)(Rect *);
enum { GET_X, GET_Y, END_OF_NAME_TABLE };
Rect_method *rect_prototype;
Rect *rect_new(int x, int y)
{
Rect *p = (Rect *) malloc(sizeof(Rect));
p->x = x, p->y = y;
p->prototype = rect_prototype;
return p;
}
void rect_delete(Rect *p) { free(p); }
int rect_get_x(Rect *p) { return p->x; }
int rect_get_y(Rect *p) { return p->y; }
void rect_init_prototype(void)
{
rect_prototype = (Rect_method *) malloc((sizeof(Rect_method)) *
END_OF_NAME_TABLE);
rect_prototype[GET_X] = rect_get_x;
rect_prototype[GET_Y] = rect_get_y;
}
void rect_delete_prototype(void) { free(rect_prototype); }
static void test_suite(void)
{
Rect *r1 = rect_new(1, 2);
Rect *r2 = rect_new(3, 4);
printf("r1 = (%d, %d), r2 = (%d, %d)\n",
r1->prototype[GET_X](r1), r1->prototype[GET_Y](r1),
r2->prototype[GET_X](r2), r2->prototype[GET_Y](r2));
rect_delete(r1);
rect_delete(r2);
}
int main(void)
{
rect_init_prototype();
test_suite();
rect_delete_prototype();
return 0;
}
試著編譯並執行:
$ gcc -o js-prototype{,.c} && ./js-prototype
r1 = (1, 2), r2 = (3, 4)
顯然,我們的 prototype 操作已發揮功能,當然,這僅是一個模擬的手法,但可從中窺見 Javascript 在此機制的特性。
由 jserv 發表於
01:35 PM
|
迴響 (3)