玩 FreeDOS 1.0
FreeDOS 在今年 9 月時釋出了重大的里程(big milestone)版本 "1.0"。FreeDOS 是一個 "complete", "free", "100% MS-DOS compatible" 的 operating system,今天心血來潮,把她裝起來玩了一下,其實還真有點懷念呢。
懷念的滋味:西冒號斜線大於。
DOOM!
« October 2006 | (回到Blog入口) | December 2006 » November 2006 歸檔November 1, 2006玩 FreeDOS 1.0FreeDOS 在今年 9 月時釋出了重大的里程(big milestone)版本 "1.0"。FreeDOS 是一個 "complete", "free", "100% MS-DOS compatible" 的 operating system,今天心血來潮,把她裝起來玩了一下,其實還真有點懷念呢。
Ragel:狀態機編譯器Ragel 是一個 "State Machine Compiler",Ragel 可以把狀態機(regular expression)編譯成 C/C++/Objective-C/D 程式碼。Ragel 除了可以用來快速撰寫分析文件的程式外,用來學習 Automata 也是很好的工具。 Ragel 的官方網站:http://www.cs.queensu.ca/~thurston/ragel/ November 2, 2006Preemptive Process Scheduling 的觀念先前我們在介紹 sys_getppid 時,跳掉了一段 code 如下: #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) { ... } #endif 這段 code 在判斷 kernel 是否有 'CONFIG_SMP'(多處理器)與 'CONFIG_PREEMPT'(可搶先的 process 排程)。目前 kernel 2.6 在各種處理器平臺已經支援 preemptive process scheduler,當然也包含了 i386 處理器。 這個 kernel 的設定位於 arch/i386/Kconfig,我們節錄 Kconfig 內容如下: config PREEMPT bool "Preemptible Kernel" help This option reduces the latency of the kernel when reacting to real-time or interactive events by allowing a low priority process to be preempted even if it is in kernel mode executing a system call. This allows applications to run more reliably even when the system is under load. 這段 code 與 CONFIG_PREEMPT 的用意非常清楚:提供 preemptive process scheduling 的能力,我們對 Linux kernel preemptive process scheduling 的處理方法相當感興趣。 在進一步研究與 scheduling 有關的 system call 與 'CONFIG_PREEMPT' 前,有必要先來複習一下什麼是 "preemptvie"。 Preemption 一個重要的工作是了解「preemptive」的觀念。 當 process 進入執行狀態(running state)時,排程器(scheduler)會去檢查進入該 process 優先序(priority),若該 process 的 priority 比目前執行中的 process priority 高,Linux 便會搶先(preemptive)執行該 process,因此原本的 process 便被中斷(interrupt)。 這種 high priority process 把 low priority process 執行權了活生生搶走的機制便是 preemptive process scheduling;因此我們說,Linux 的 process 是 "preemptive"!Preemption(搶奪)的排程,目前在 Linux 2.6 的核心裡已經有不錯的支援了。 更多 Linux Preemptive Scheduling 1. Linux 的 scheduler 包含 preemptive 的實作。 Linux/Unix 作業系統核心是 nonpreemptive 的實作,這樣的作業系統設計較簡單,並且可免除許多核心同步(kernel synchronization)的問題。 Preemptive Scheduling 以下是節錄自作業系統教科書裡,對於可搶先班程的說明[1]: CPU scheduling decisions may take place when a process -
[1] A. Silberschatz, P. Galvin, and G. Gagne, Applied Operating System Concepts, First Edition, John Wiley & Sons, Inc. (2000). KVM 驅動程式的 HOWTO之前提到過 Kernel 2.6 的 KVM 驅動程式現身了,今天看到 KVM 驅動程式的 HOWTO 已經上線了。KVM 的官方網站是:http://kvm.sourceforge.net/ 裡頭除了有 HOWTO 外,也有 mailing list 可以加入了。KVM 的驅動程式除了有 kernel-space 的 driver 外,也包含一個 user-space 的 library,以及給 QEMU 的 patch。 November 4, 2006目前的「Linux System Calls' Forum」與「Jollen 的 Linux 核心分享包」專欄進度報告。「Linux System Calls' Forum」與「Jollen 的 Linux 核心分享包」是二個相互配合的專欄,因此相關的日記彼此之間是具備相依性與次序的。建議的閱讀順序是「依照日記的時間(即發佈順序)」來閱讀「Linux System Calls' Forum」與「Jollen 的 Linux 核心分享包」。這是這二個系統日記的閱讀建議。 目前的狀況報告 對於 Linux system call 的討論,我們已經來到了 scheduler 的門口了,要深入了解 Linux scheduler 的設計原理與相關理論前,Jollen 還必須多做一點功課才行。到目前為止,我們所討論過的幾個 system call 如下。
「Jollen 的 Linux 核心分享包」已發佈二則日記如下。
另外,Jollen 在每一個 system call 的討論都點出幾個基本的主題,希望大家能大致了解這目前「Linux System Calls' Forum」裡出現的幾個主題後,再繼續往下看「Linux System Calls' Forum」論壇。 後續進度 這篇日記完成後,緊接著的就是「Linux System Calls' Forum, #7:(第157號系統服務)sys_sched_getscheduler」,後然緊接的是「Jollen 的 Linux 核心分享包,#3」了,請大家多多指正! November 5, 2006Linux System Calls' Forum, #7:(第157號系統服務)sys_sched_getscheduler「作業系統的排程器(scheduler)提供好幾種排程演算法,並在不同的應用場合,或是不同的 process 採取適合的排程策略(policy)。」 Linux 的 process descriptor 資料結構為 struct task_struct,我們最早開始談論與 process 有關的 system call 時,就看過這個用來描述 process 的資料結構。struct task_struct 裡頭的 policy 欄位便是用來描述該 process 的排程策略。 與排程有關的系統呼叫 整理與 scheduling 有關的幾個 system call 如下(可參考「Linux 2.6 的 System Call:12 大類」),另外 Jollen 也把到目前為止已介紹過的 system call 加上紅色註記。
「'sys_sched_getscheduler' 便是用來取得 process 排程策略(policy)的 system call。」 「'struct task_struct->policy' 是今天要了解的主題。」 我們想要思考一個問題「作業系統的教科書提到的排程演算法中,Round Robin 是我們知道現在的分時作業系統(eg. Linux)所採用的策略,所以我想了解 Linux 的 process descriptor 裡,是哪一個 field 在紀錄這件事情。」 struct task_struct 的 policy 欄位 這個問題還挺簡單的,因為直接看 struct task_struct 幾乎就能猜到是哪一個 field,再用谷歌找資料來印證我們的設假;另外,看「Understanding the Linux Kernel」也寫的很清楚。 struct task_struct { ... unsigned long policy; ... }; 接著再回頭來看今天的主題,sys_sched_getscheduler 的實作(Linux 2.6.11+): /** * sys_sched_getscheduler - get the policy (scheduling class) of a thread * @pid: the pid in question. */ asmlinkage long sys_sched_getscheduler(pid_t pid) { int retval = -EINVAL; task_t *p; // jollen: 'typedef struct task_struct task_t;' if (pid < 0) goto out_nounlock; retval = -ESRCH; read_lock(&tasklist_lock); p = find_process_by_pid(pid); // 參照說明 1. if (p) { retval = security_task_getscheduler(p); if (!retval) retval = p->policy; // 參照說明 2. } read_unlock(&tasklist_lock); out_nounlock: return retval; } 取出重點部位來看:
很簡單的將 sys_sched_getscheduler 看了一下,並且了解到 policy 欄位的用途。 find_process_by_pid 把 find_process_by_pid() 的實作拿出來看: /** * find_process_by_pid - find a process with a matching PID value. * @pid: the pid in question. */ static inline task_t *find_process_by_pid(pid_t pid) { return pid ? find_task_by_pid(pid) : current; } 發現一件很重要的是,也是之前都沒提過的。 「若 PID 為 0,則傳回 current,否則傳回 PID 為 pid 的 process 之 process descriptor。」 別忘了,current 的 data type 也是 'struct task_struct'。 Linux 排程策略:policy 欄位的值 Linux 2.6 提供的排程策略如下: /* * Scheduling policies */ #define SCHED_NORMAL 0 #define SCHED_FIFO 1 #define SCHED_RR 2 #define SCHED_BATCH 3 定義在 include/linux/sched.h。順帶把 Linux 2.4 提供的排程策略也節錄一下: /* * Scheduling policies */ #define SCHED_OTHER 0 #define SCHED_FIFO 1 #define SCHED_RR 2 November 8, 2006「Mobile 2.0 的思考」與第一隻採用 OpenMoko 的 Linux Smartphone這隻手機是由台灣的大眾電腦所開發(真是一個好消息),原始的報導在 LinuxDevices.com 上,詳情可點閱這篇文章「Cheap, hackable Linux smartphone due soon」。 Neo1973(代號:FIC-GTA001) 是「completely open, Linux-based, GPS-equipped, quad-band GSM/GPRS phone」,沒錯,這是一隻「PHS 手機」。預計上市的時間是今年(2006)的 12 月中,並在明年的 1 月份銷售。 Neo1973 採用 Samsung S3C2410 平臺,並且是第一隻採用「OpenMoko」平臺的 Linux 智慧型手機,此外,更採用「apt-get-lke」的 software manager。不管正著看還是反著看,都很符合 Linux 玩家的習慣(與期望)。 非常期待他的問世,到時一定要去帶一隻回家「hack hack」一番。
突然發現到,我好像已經加入 Moss-Pultz 的 ecosystem 一員了,顯然我是這個系統的 end user。這個互動來自於「open hacking」的思維邏輯,近年來似乎被不少的國際大廠巧妙地應用著。(突然一個唸頭閃過,昨天參加的一場 roadshow 不就也是如此!)
(圖片來源:http://www.linuxdevices.com/news/NS2986976174.html) Neo1973 其實是採取「dual-OS」的產品策略,因此她也能執行 Microsoft Windows Mobile。不過大家最期待的「Open Source」節錄一小段原始報導的說明如下:
還有更棒,Neo1973 採用 OpenEmbedded 來讓使用者增加、移除與更新套件,OpenEmbedded 裡頭已經包含了數以「千」計的套件,透過 bitbake 來建立 Linux distribution,讓愛好者與玩家都能透過簡單的指令來實做「Embedded Linux to Devices」。 Neo1973 的 hardware 規格也是很棒的,整理如下: 1. Samsung S3C2410 SoC 2. 128MB RAM 3. 64M flash 4. 64MB MicroSD 5. 2.8" touchscreen 6. Assisted GPS 7. quad-band GSM/GPRS module Moss-Pultz 的 presentation 可以在這裡找到:http://www.linuxdevices.com/files/article072/sld002.html OpenMoko 的「official site」是:http://www.openmoko.com/press/index.html。在這上面有完整的新聞稿與簡報 PDF 檔可以下載。 Revision, 2006/11/09, 01:13PM. November 9, 2006Jollen 的 Linux 核心分享包,#3: fork_init()《講義6》我們知道,在 start_kernel() 的開機過程中,會呼叫許多初始化核心的常式。其中 fork_init() 是用來初始化 kernel 的"fork" 環境的。"fork()" 是用來產生 child process 的 system call,當 parent process 想要執行外部程式時,會先 fork child process,接著 child process 再利用 exec system call 自己的空間取代為外部程式。 從講義第 5 頁開始,我們把問題做分割,找出與 Scheduling 有關的初始化流程來做研讀,因為我們想要以 Scheduling 做為深入 Linux kernel 的第一門課。 講解 fork_init() 1. Kernel 能 fork 的 process 數是依據 physical memory 的大小來決定的,我們看到 fork_init(unsigned long mempages) 的 mempages 即是用來計算 max_threads 的參數。 2. 看到 mempages 參數,這個參數是由 start_kernel() 呼叫 fork_init() 時傳入的: asmlinkage void __init start_kernel(void) { ... fork_init(num_physpages); ... } 3. 找一下 num_physpages,原來是 kernel 的 global symbol 啊,這個地方在 mm/memory.c: unsigned long num_physpages; ... EXPORT_SYMBOL(num_physpages); 我們的想法是,目前還沒讀到有關 Memory Manager 與 mm 初始化的主題,所以還不如先暫時把這個地方當黑盒子,再做一次分割。因此,num_physpages 是怎麼來的就先放到「TODO」清單裡了。 4. 計算 max_threads 需要以下二個常數: a. THREAD_SIZE:dependent on architecture b. PAGE_SIZE:dependent on architecture 這二個常數都是平臺相依的,我把他們都標示在投影片上。這是在 processor-level 的 porting 就要處理掉的。 5. 再來,如同程式碼所述:至少要能產生 20 個 thread 系統才能開機。所以限定 max_threads 的最小值是 20。 另外,我們會發現有些與 "memory" 或 "signal" 有關的 code,目前比較好的做法是先放到「TODO」。希望我們這裡的思考邏輯與陳述方式對大家「閱讀核心」能有一點幫助。
November 12, 2006Linux Link TEch Show 的訪談:理查史都曼談 GPLv3November 13, 2006Novell 宣佈釋出 Mono 1.2:Linux 執行 .NET 程式的解決方案前幾天大家或許都看到 Microsoft/Novell 宣佈要作作開發 Linux 軟體的新聞了。就在 2 天前,Novell 在 Microsoft 的 TechEd Developers Conference & Expo(Barcelona Spain)宣佈推出 Mono 1.2。 Mono 是一套可以讓 Linux 與 Unix 使用者執行 Microsoft .NET 程式的軟體,在 LinuxWatch 的報導中提出 1.2 版是一個重要的里程碑,節錄原始報導如下: Mono 1.2 enables Linux and Unix users to use Microsoft .NET code and applications. This new version, which brings performance improvements and support for Windows Forms, is seen as an important milestone toward compatibility with the .NET Framework 2.0. Other enhancements in the latest release include virtual machine upgrades, enhanced Java support, better memory consumption, stability improvements, and support for more .NET 2.0 features. Mono 1.2 的新突破是可以讓 Microsoft 的 UI(user interface)更容易轉移到 Linux 平臺上。Mono Project 的官方網站是:http://www.mono-project.com/Main_Page,現在已經可以下載到 Mono 1.2 了。 對 Linux 應用軟體開發的朋友,可以了解一下這個重要的專案計畫。 November 14, 2006Sun Microsystem 釋出 GPLv2 Java 實作:與我的有感而發Sun Microsystems 在今天宣佈,將要以 GPLv2 釋出他的 Java 實作。 這二年可以看到非常多的廠商或是專案加入 open source community 的陣營,而 Sun 的 Java 實作超過 10 年的發展,過程真是非常的精采,最終也提出 GPLv2 的版本,有點小意外,但我想可能也不出大家所預期的可能結果。 Sun 的 Java 實作最終還是選擇以 GPLv2 釋出新版本,並且也不意外地引發「Is Java's move to GPL too late?」的討論。在這 10 幾年的精采發展過程中,Java 也曾回拒 open source 的想法與建議,最終仍然無法抵擋一個潮流與趨勢。 與其說 Sun 是在無計可施的局面下選擇 open source,我覺得還不如說這是 open source 這個「生態環境(ecosystem)」的成形、成熟與狀大,讓人「不可不為」。當然這是我自己的看法。 對 open source 生態系統最早「覺醒」,並且也因為認可這個 ecosystem 而得好處的經典案例就是「IBM」了,而今年也能嗅到 Linux mobile phone 生態環境的成形,想必未來 2 年 Linux mobile phone 是大有可為的。因此,假如 Sun 不想在 Linux mobile phone 的歷史缺席,就算不解或無奈,也只能聰明的加入已成形的生態系統裡。 IBM 擁有許多作業系統的專利,透過智財保護,並進行授權或 royalty 似乎是天經地義的做法,但是這位老大哥選擇並且認真加入 open source 生態環境的運作,最後得到許多好處與回饋,也讓人見識到 ecosystem 讓人不可不為的威力。IBM 的 PowerPC 也選擇了 open source community 的系統,只不過 IBM 比 Sun Microsystems 更有智慧,我想這是未來將會被更熱烈討論的主題(ecosystem of open source community)。 把這個好消息看待得太嚴肅了,對 developer 來說,又多了一項新奇有趣的東西,值得期待! 以下是這則新聞的主要閱讀列表: Sun GPLs Java, http://www.linux-watch.com/news/NS4348361333.html. November 15, 2006深入淺出 insmod, #1作者/陳俊宏
|
Also See |
|
Embedded Linux 的開發工具一日千里,主意不錯而且發展速度快,顯現的是未來「bring Linux to devices」的工作將更快速(快但是不見得輕鬆),這也將會構成 embedded Linux 將來市佔率(使用 Linux 做為 device OS 解決方案的比率)能大幅並快速提升的要素之一:「快速平臺與軟硬解決方案」 (rapid prototyping platform and SW/HW solutions)。
由此可大膽推測,embedded Linux 後期的生態環境正在快速形成當中,或許再過 2~3 年,就會有殺手級的 embedded Linux 的開發工具(IDE)出現。倘若如此,developer 現在一個很重要的工作就是僅快去學習研究 embedded Linux 的基礎原理;未來才能善用工具,而不是只會用工具。
embedded Linux 的開發工具逐漸成形了,會這麼說是因為今天看到一則新聞:
「MontaVista launches Dev Rocket 5 beta」
試想一個情境:怎麼讓「自己寫好的 code」透過 IDE 環境「咚!」一聲就被整合到 target device 的 image 檔(root filesysetm)裡?(像是 TimeSys 的服務,很快,但仍要自己「整」一堆東西到他提供的 RFS 裡。)
推敲一下未來可能的 killer application 身影。
首先引用報導裡關於 Dev Rocket 5 的說明如下:
MontaVista Software has invited current customers to join an open beta program for its next-generation Eclipse-based embedded Linux development toolset.
很好,這是一個 Eclipse-based 的 IDE(如果您不知道 Eclipse 的重要性,網路上有很多前輩發表許多關於他的看法,非常精采)。
根據報導說明,我的想法是,Dev Rocket 能支援「MontaVista Linux Edition Management」,但是如果能把這個地方做成是比較萬用(一般化)的一個 feature,那麼會是比較有彈性而且有殺氣的。要產生 root filesystem image 檔,就要有一個好用的 Linux distribution "build" 工具;目前為特定 target device 建立 Linux distribution 的一套重量級工具是 OpenEmbedded。
OpenEmbedded 是一個舉足輕重的工具,他能方便地建立 embedded Linux distribution。例如之前講到的 Neo1973 Linux mobile phone 就用到 OpenEmbedded。現在好多人在玩的 Linksys NSLU-2 也都是 OpenEmbedded 的產物。
Dev Rocket 5 也支援「One-click Debugging」與「Platform Image Creation and Configuration」的功能;Dev Rocket 5 也有 plug-ins 的功能,能整入像是「UML modeling」這樣的模組進來。
MontaVista 對 embedded Linux 的貢獻良多,未來若是能將這些 IDE open 出來,發散一陣子再收斂起來,這所謂的 killer application 已經不遠了。
Also See |
|
ELF(Executable and Linking Format)是 object file 的檔案格式,其主要結構是以 section(節區)為主,我們可以利用 GNU binutils 套件的 objdump 工具來列出執行檔的 section 與其內容。例如,我想把 ls 命令的 ELF section 列印出來:
# objdump -x /bin/ls |more ... Idx Name Size VMA LMA File off Algn 0 .interp 00000013 08048114 08048114 00000114 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .note.ABI-tag 00000020 08048128 08048128 00000128 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .hash 0000028c 08048148 08048148 00000148 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .dynsym 000005e0 080483d4 080483d4 000003d4 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .dynstr 000003ea 080489b4 080489b4 000009b4 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .gnu.version 000000bc 08048d9e 08048d9e 00000d9e 2**1 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .gnu.version_r 00000070 08048e5c 08048e5c 00000e5c 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 7 .rel.dyn 00000028 08048ecc 08048ecc 00000ecc 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 8 .rel.plt 00000278 08048ef4 08048ef4 00000ef4 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 9 .init 00000017 0804916c 0804916c 0000116c 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 10 .plt 00000500 08049184 08049184 00001184 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 11 .text 0000ab4c 08049690 08049690 00001690 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 12 .fini 0000001b 080541dc 080541dc 0000c1dc 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 13 .rodata 00003760 08054200 08054200 0000c200 2**5 CONTENTS, ALLOC, LOAD, READONLY, DATA 14 .eh_frame_hdr 0000002c 08057960 08057960 0000f960 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 15 .eh_frame 0000010c 0805798c 0805798c 0000f98c 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 16 .data 00000114 08058000 08058000 00010000 2**5 CONTENTS, ALLOC, LOAD, DATA 17 .dynamic 000000d0 08058114 08058114 00010114 2**2 CONTENTS, ALLOC, LOAD, DATA 18 .ctors 00000008 080581e4 080581e4 000101e4 2**2 CONTENTS, ALLOC, LOAD, DATA 19 .dtors 00000008 080581ec 080581ec 000101ec 2**2 CONTENTS, ALLOC, LOAD, DATA 20 .jcr 00000004 080581f4 080581f4 000101f4 2**2 CONTENTS, ALLOC, LOAD, DATA 21 .got 00000150 080581f8 080581f8 000101f8 2**2 CONTENTS, ALLOC, LOAD, DATA 22 .bss 00000368 08058360 08058360 00010360 2**5 ALLOC ...
如果要印出指定 section 的內容,例如 .data section:
# objdump -j .data -s /bin/ls
/bin/ls: file format elf32-i386
Contents of section .data: 8058000 00000000 00000000 f0810508 00000000 ................ 8058010 00000000 00000000 00000000 00000000 ................ 8058020 00000080 ffffffff 01000000 01000000 ................ 8058030 00000000 00000000 00000000 00000000 ................ 8058040 02000000 5c420508 01000000 5f420508 ....\B......_B.. 8058050 00000000 00000000 01000000 53720508 ............Sr.. 8058060 01000000 53720508 05000000 61420508 ....Sr......aB.. 8058070 05000000 67420508 02000000 76420508 ....gB......vB.. 8058080 05000000 6d420508 05000000 73420508 ....mB......sB.. 8058090 05000000 73420508 00000000 00000000 ....sB.......... 80580a0 00000000 00000000 05000000 79420508 ............yB.. 80580b0 05000000 6d420508 05000000 7f420508 ....mB.......B.. 80580c0 05000000 85420508 05000000 8b420508 .....B.......B.. 80580d0 05000000 91420508 00000000 97420508 .....B.......B.. 80580e0 a1420508 01000000 ffffffff 01000000 .B.............. 80580f0 90110508 01000000 01000000 00010000 ................ 8058100 a0850508 fc800508 e0750508 01000000 .........u...... 8058110 00000000 ....
Object file 主要有 3 種類別:
1. relocatable file
2. executable file
3. shared object file
relocatable file 即副檔名為 .o 的檔案、executable file 為一般的執行檔、shared object file 則是 *.so(shared libraries)檔案。ELF 是 object file 的檔案格式,其檔案格式如下圖。
圖:ELF View(Execution View v.s. Linking View)
Linking view 指的是經由 assembler 或 linkage editor 編譯過,可被 CPU 執行的檔案格式,也就是儲存在儲存裝置上的程式格式(stored programs)。Execution view 指的是由 loader 載入後,程式執行時的格式,也就是存在於記憶體上的程式格式(process)。
當載入器(loader)將程式載入記憶體後,object file 就會是 execution view 的格式。了解 ELF 格式與學會如何讀取 ELF 內容,對於深入研究 Linux 內部的程式實作相當有幫助。例如,未來我們將會討論的 sys_init_module 實作,便需要對「如何讀取 ELF object」有一定程度的了解。
從事 Embedded Linux(GNU/Linux systems on devices)工作的朋友,除了日常的讀書功課外,另外一個重要的工作就是「隨時注意 Linux kernel 的發展狀況」。要能與 Linux kernel 的發展同步,嚴格來說,已經是一件吃重的工作了,不過還是可以列出幾個基本的工作原則如下:
1. 每天閱讀 linux-kernel 郵遞論壇(mailing-list)的「標題」。
2. 隨時上 kernel.org 看看最新釋出的 kernel 版本(或留意 linux-kernel-announce mailing-list)。
3. 閱讀釋出版本的 Changelog。
4. 不要與 git 系統的距離太遠,定時看看 git 系統,保持一定的「短距離」。
Mailing List
linux-kernel 上的 posts 每天的量大約在 200~350 篇左右,數量並不算少,要把每一篇都看過是不太可能的,因此以我自己的閱讀心態來說,我會建議以下的閱讀方式:
1. 看標題,如果是自己有興趣或正在留意的更新,我就會標記下來持續追蹤。
2. 如果有 Bugfix 的 patch 出現,我會看看這項修正的起因與原理,但為了不讓自己花費太多時間,如果我對這項 patch 的修正「原理」不甚熟悉,我便會跳過此 post。
以下是閱讀 linux-kernel list 必須知道的幾件事:
1. 如果有新的修正,第一時間都會發佈在此 list 上,並且標題的起頭一定是 "[PATCH n/m] subject" 這樣的格式。PATCH 表示這是一個 patch 的發佈,由於一個 patch 會以多篇 post 發佈,因此就用「n/m」來表示「這是第幾篇 patch,總共有幾篇。」,例如:
[PATCH 0/7] KVM: Kernel-based Virtual Machine
[PATCH 1/7] KVM: userspace interface
[PATCH 2/7] KVM: Intel virtual mode extensions definitions
[PATCH 3/7] KVM: kvm data structures
[PATCH 5/7] KVM: mmu virtualization
[PATCH 6/7] KVM: x86 emulator
[PATCH 7/7] KVM: plumbing
2. 不能在這裡詢問與 kernel 發展無關的問題,例如:工具的使用、系統設定、詢問是否有XXX驅動程式、請求協助測試等等,這些都是不能張貼的文章。另外,原本就該留意的非成文禮節一定要注意,像是 FAQ 能找到的東西,就不要去麻煩人家。
3. list 裡大部份都是 device driver 的討論,並且很多都是架構面或觀念面的討論與修正建議,所以當您參與討論時,千萬不要用「個人的主觀看法,或是沒有事實與理論根據」的角度發表意見;由於「觀念」的修正是 kernel 2.6 驅動程式的大討論方向,所以必須先把主題相關的東西先看懂看熟後再參與討論。
kernel.org
我會不定時來看看最新發佈的 kernel 版本,因此我們必須知道目前仍在維護的 kernel 版本與其分支狀況,大致說明如下:
1. 2.6 與 2.4 都是目前仍持續積極維護中的版本。以本文寫作的時間為例(2006/11/20),目前最新的版本分別是 2.6.18.3(2006/11/19 釋出)與 2.4.33.4(2006/11/19 釋出)。
2. 2.4 的釋出版本有「stable」與「prepatch」二個分支。
3. 2.6 的釋出版本有「stable」、「prepatch」、「snapshot」與「-mm patch」四個分支。
其中 '-mm' 系列的 2.6 kernel 是由 Andrew Morton 所釋出的 Linux 分支,主要性質以「實驗」與「新功能」為主 ,所以常常可以在這個分支的 kernel 裡找到不久前才剛發佈在 mailing-list 上的 patch。如果您不太知道怎麼處理 mailing-list 上的 patch 發佈,也可以等 -mm patch 的發佈。
另外,prepatch 就是所謂的 '-rc' 發佈,所以檔名會接 '-rc?' 字串,例如:linux-2.6.19-rc6。
Changlog
每個在 kernel.org 上所釋出的 kernel 都會有一份變更紀錄(change logs)的檔案,可以了解這個版本與分支的釋出做了什麼變更。
上到 kernel.org 後,每個釋出版本的後面,會有 4 個選項(視版本不同):
把「C」給按下去就會進到所謂的「git 系統」。
git 系統
git 是 kernel 用來維護發展中版本的系統,也就是所謂的「snapshot 」版本,snapshot 版本都是當天新鮮送達的 kernel,釋出時會在後面加上「-git?」字串。
我會不定時點「C」到 kernel 的 git 去看看 kernel 的最新(最近)動態,說不定有我正要找的 patch。git 系統裡也能看到 kernel 的 "commit" 訊息,包含 commit 的作者、時間與差異比較(diff),而且都可以很方便地瀏覽。
結語
隨時與 kernel 的發展同步,有幾個動機:
1. 學習,kernel developers 會討論與多觀念面的議題,很好的學習機會。
2. 新的 kernel 加入了哪些新驅動程式、修正掉哪些臭蟲與增強了哪些驅動程式的功能?
3. 留意我所使用的平臺(architecture)是否有重要的修正(eg. for i386, for ARM...etc)。
4. 如果新 kernel 有加入重要的 feature 或 Bugfix,我會把自己玩耍的 code patch 到新 kernel。
測試這些有趣的新版本,建議使用 QEMU 或是 User-Mode Linux 來進行。
本文接續之前的日記「ELF(Executable and Linking Format)格式教學文件, #1: ELF 簡介」,在了解 ELF 的用途後,再來我們先由「ELF Header」的部份開始看起,我們的目標是寫一個可以將 ELF 執行檔裡所有 section 讀取出來的程式。未來將繼續朝 user-space 的 ELF 處理前進。
如果您是 Jollen 的讀者,或許曾經在書上讀到這個章節,本系統的內容與書上內容大致相仿,但仍有一些小差異,建議您可再讀一次。我希望能以其它的出版形式,將這些內容都與大家分享。
ELF 於 SysV ABI 標準中定義,其中 ELF header 的結構如下表:
Field | Description |
e_ident |
用來辨別檔案是否為ELF,並包含一些machine independent 的資料。 |
e_type |
檔案的類型 |
e_machine |
檔案的平臺 |
e_version |
版本資訊 |
e_entry |
程式的起始位址(process virtual address) |
e_phoff |
program header table的檔案偏移值(offset),單位是bytes。如果沒有program header table則此值為0。 |
e_shoff |
section header table的檔案偏移值(offset),單位是bytes。如果沒有section header table則此值為0。 |
e_flags |
與processor有關的旗標值 |
e_ehsize |
ELF header的長度,單位是bytes。 |
e_phentsize |
program header table每個entry的長度(bytes),每個entry的長度都相等。 |
e_phnum |
program header table的entry個數,若無program header table 則此欄的值為0。 |
e_shentsize |
section header table每個entry的長度(bytes),每個entry的長度都相等。 |
e_shnum |
section header table的entry個數,若無program header table則此欄的值為0。 |
e_shstrndx |
section header table的index值,索引至section name string table entry。如果檔案沒有section name string table,則此欄的值為SHN_UNDEF。 |
ELF 檔案格式最基本的就是它的檔頭(header)部份,ELF header 儲存 object file 的各種資訊。ELF header 的讀取方式非常簡單,我們將會實作讀取 ELF header 的程式,以強化我們所讀到的觀念。
ELF header 的資料結構定義在 elf.h 裡,如下:
/* The ELF file header. This appears at the start of every ELF file. */ #define EI_NIDENT (16) typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf32_Half e_type; /* Object file type */ Elf32_Half e_machine; /* Architecture */ Elf32_Word e_version; /* Object file version */ Elf32_Addr e_entry; /* Entry point virtual address */ Elf32_Off e_phoff; /* Program header table file offset */ Elf32_Off e_shoff; /* Section header table file offset */ Elf32_Word e_flags; /* Processor-specific flags */ Elf32_Half e_ehsize; /* ELF header size in bytes */ Elf32_Half e_phentsize; /* Program header table entry size */ Elf32_Half e_phnum; /* Program header table entry count */ Elf32_Half e_shentsize; /* Section header table entry size */ Elf32_Half e_shnum; /* Section header table entry count */ Elf32_Half e_shstrndx; /* Section header string table index */ } Elf32_Ehdr;
Section 與 section header table 的讀取與處理方式,會在介紹完 ELF header 後再做說明。
本系列日記共有 5 個範例程式,其功能差異整理如下表。
讀取檔頭 | 分析檔頭 | 判斷ELF | 處理Section | 讀取Strtab | 列印節區名稱 | |
loader-0.1.c | ● | ● | ||||
loader-0.2.c | ● | ● | ● | |||
loader-0.3.c | ● | ● | ● | ● | ||
loader-0.4.c | ● | ● | ● | ● | ● | ● |
loader-0.5.c | ● | ● | ● | ● | ● | ● |
為了能了解ELF格式,我們將會實作 6 大項功能如下:
1. 讀取檔頭:讀取 ELF 檔案的檔頭資訊。
2. 分析檔頭:分析讀取的檔頭資訊,例如分析 ELF 執行檔的編碼平臺。
3. 判斷 ELF:判斷所讀取的檔案是否為標準 ELF 格式的檔案。
4. 處理 Section:可以讀取所有 section 的資訊,並做簡單處理,例如找出是 string table 的 section。
5. 讀取 StrTab:讀取 section name string table 裡的資訊。
6. 列印節區名稱:處理 section name string table,可以根據 section name string table 來列印所有 section的名稱(ASCII string)。
我們將會以漸進式的方式來慢慢完成所有的功能,不同版本範例程式間的主要差異將會特別做說明,並詳細解釋新功能的程式實作。
另外,loader-0.4.c 與 loader-0.5.c 看似功能相同,但 loader-0.5.c 主要是為了討論 loader-0.4.c 裡幾個奇怪的地方。我們會在講解loader-0.5.c 程式時再做更詳細的說明。
Loader 在載入 object file 前,必須先由 ELF header 取得 object file 的資訊並且判斷 object file 是否為 ELF 格式的執行檔,然後才能將 object file 載至記憶體。
Also See |
以 Perl 寫的簡單 script,可以用來產生 Linux 的系統呼叫表(system call's table)。雖然程式笨笨的,跑一次也要一段時間,不過還是幫了我許多忙。由於 Jollen 網站上的 Linux System Calls' Table 並不會針對每個版本做更新,所以我就把我用的 Perl script 提供出來給大家自行使用。
不過,2.4/2.6 不同版本間的 system call 都是一樣的,除了注意是否有新增的 system call 外,也不會有什麼差別。而且,也不常看到 kernel 在新增 system call。
這個 Perl Script 的使用方法請參考檔案內的 README 說明。下載檔案 [lsct_gen-0.1.tar.bz2(10 KBytes)]。另外,這個 script 也會把 system call 所在的檔案印出來。
這個版本只適合用來處理 2.6 系列的程式碼,無法處理 2.4 系列的 kernel source code。
ELF 的第一個範例:loader v0.1
ELF header 儲存執行檔的重要資訊,我們必須先知道如何讀取 ELF 檔案的檔頭資訊(header),才能處理個別節區(section)。在 GNU/Linux 系統下,程式可以直接引用 elf.h 標頭檔。完整的程式範例 loader-0.1.c 列表如下。
/* * Copyright (C) 2003 www.jollen.org * * ELF programming. ver 0.1 * */ #include <stdio.h> #include <unistd.h> #include <elf.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> void parse_machine(Elf32_Half machine) { printf("Machine: "); switch (machine) { case EM_NONE: printf("No machine\n"); break; case EM_M32: printf("AT&T WE 32100\n"); break; case EM_SPARC: printf("SPARC\n"); break; case EM_386: printf("Intel 80386\n"); break; case EM_68K: printf("Motorola 68000\n"); break; case EM_88K: printf("Motorola 88000\n"); break; case EM_860: printf("Intel 80860\n"); break; case EM_MIPS: printf("MIPS RS3000 Big-Endian\n"); break; default: printf("Unknow\n"); } } int main(int argc, char *argv[]) { int fd; Elf32_Ehdr f_header; if (argc != 2) { printf("Usage: loader [filename]\n"); return -1; } fd = open(argv[1], S_IRUSR); if (fd < 0) { printf("\nfile open error\n"); return -1; } /* Read ELF Header */ read(fd, &f_header, sizeof(Elf32_Ehdr)); /* Parse header information */ parse_machine(f_header.e_machine); }
範例說明
程式 loader-0.1.c 首先宣告變數 f_header 用存放所讀取的檔頭資料:
Elf32_Ehdr f_header;
Elf32_Ehdr 的宣告在 elf.h 裡,這個 data type 是 SysV ABI 裡的 ELF object files 檔頭的標準資料結構。讀者的方式如同傳統的 C 語言一般,只要利用低階 I/O 函數將檔頭的部份讀出即可:
read(fd, &f_header, sizeof(Elf32_Ehdr));
目前我們只讀取 ELF header 的部份。ELF header 裡存放許多重要的 object file 資訊,其中一項為 e_machine 成員,因此程式接著來判斷此欄位的內容,並且列印出 object file 所支援的硬體平臺名稱。
在 parse_machine() 裡頭,我們判斷 e_machine 欄位的值,並且列印出相對應的硬體平臺名稱,例如:若 e_mahcine 的值為 EM_386,則印出 "Intel 80386" 字串。EM_386 與其它相關的常數都定義在 SysV ABI 標準裡,可在 elf.h 裡看到。這部份就留給大家當功課了。
執行結果
先將上面的程式碼 copy 下來存成 loader-0.1.c 後編譯成執行檔:
$ gcc -o loader-0.1 loader-0.1.c
接著再用 loader-0.1 來分析 ELF object file:
# ./loader-0.1 /bin/vi Machine: Intel 80386
Also See |
ELF Identification
ELF header 資料結構裡的 e_ident 欄位用來判斷檔案是否為ELF格式的欄位,e_ident 欄位是一個陣列資料結構,其長度由 SysV ABI所定義的常數 EI_NIDENT指定。
EI_NIDENT 的值為 16 代表 e_ident 欄位有 16 個元素,SysV ABI 定義了 8 個常數來索引 e_ident 陣列的元素。
表 e_ident[] 索引值定義
Name | Value | 說明 |
EI_MAG0 | 0 | ELF 識別字元 |
EI_MAG1 | 1 | ELF 識別字元 |
EI_MAG2 | 2 | ELF 識別字元 |
EI_MAG3 | 3 | ELF 識別字元 |
EI_CLASS | 4 | 檔案類別 (class) |
EI_DATA | 5 | 資料編碼方式 (Data encoding) |
EI_VERSION | 6 | 檔案版本 |
EI_PAD | 7 | padding bytes 的開頭 |
loader-0.2.c主要的改變在於加入了判斷檔案是否為 ELF 格式的程式碼。要判斷檔案是否為 ELF 格式,必須根據 e_ident[] 裡的識別字元來做判斷:
e_ident[EI_MAG0] 必須等於 ELFMAG0
e_ident[EI_MAG1] 必須等於 ELFMAG1
e_ident[EI_MAG2] 必須等於 ELFMAG2
e_ident[EI_MAG3] 必須等於 ELFMAG3
表 ELF識別字元(magic number)定義
Name |
Value |
說明 |
ELFMAG0 |
0x7f |
e_ident[EI_MAG0] 之值 |
ELFMAG1 |
‘E’ |
e_ident[EI_MAG1] 之值 |
ELFMAG2 |
‘L’ |
e_ident[EI_MAG2] 之值 |
ELFMAG3 |
‘F’ |
e_ident[EI_MAG3] 之值 |
判斷是否為 ELF 格式
設計一個 elf_ident() 函數來判斷檔案是否為 ELF 格式,其程式碼如下:
int elf_ident(char *ident) { if (*(ident+EI_MAG0) != ELFMAG0) return 0; if (*(ident+EI_MAG1) != ELFMAG1) return 0; if (*(ident+EI_MAG2) != ELFMAG2) return 0; if (*(ident+EI_MAG3) != ELFMAG3) return 0; return -1; }
e_ident[] 裡還存放許多 ELF 的資訊。以下再舉一例,我們新增一個函數 parse_ident() 來分析 e_ident[] 的 "CLASS" 資訊:
void parse_ident(char *ident) { printf("ELF Identification\n"); printf(" Class: "); switch (*(ident+EI_CLASS)) { case ELFCLASSNONE: printf("Invalid class\n"); break; case ELFCLASS32: printf("32-bit objects\n"); break; case ELFCLASS64: printf("64-bit objects\n"); break; } }
程式列表:loader-0.2.c
/* * Copyright(c) 2003,2006 www.jollen.org * * ELF programming. ver 0.2 * */ #include#include #include #include #include #include int elf_ident(char *ident) { if (*(ident+EI_MAG0) != ELFMAG0) return 0; if (*(ident+EI_MAG1) != ELFMAG1) return 0; if (*(ident+EI_MAG2) != ELFMAG2) return 0; if (*(ident+EI_MAG3) != ELFMAG3) return 0; return -1; } void parse_ident(char *ident) { printf("ELF Identification\n"); printf(" Class: "); switch (*(ident+EI_CLASS)) { case ELFCLASSNONE: printf("Invalid class\n"); break; case ELFCLASS32: printf("32-bit objects\n"); break; case ELFCLASS64: printf("64-bit objects\n"); break; } } void parse_machine(Elf32_Half machine) { printf("Machine: "); switch (machine) { case EM_NONE: printf("No machine\n"); break; case EM_M32: printf("AT&T WE 32100\n"); break; case EM_SPARC: printf("SPARC\n"); break; case EM_386: printf("Intel 80386\n"); break; case EM_68K: printf("Motorola 68000\n"); break; case EM_88K: printf("Motorola 88000\n"); break; case EM_860: printf("Intel 80860\n"); break; case EM_MIPS: printf("MIPS RS3000 Big-Endian\n"); break; default: printf("Unknow\n"); } } int main(int argc, char *argv[]) { int fd; Elf32_Ehdr f_header; if (argc != 2) { printf("Usage: loader [filename]\n"); return -1; } fd = open(argv[1], S_IRUSR); if (fd < 0) { printf("\nfile open error\n"); return -1; } /* Read ELF Header */ read(fd, &f_header, sizeof(Elf32_Ehdr)); /* Parse header information */ if (elf_ident(f_header.e_ident)) { parse_ident(f_header.e_ident); parse_machine(f_header.e_machine); } else { printf("not a ELF binary file\n"); } }
Also See |
椅子坐太久除了手痛、腰酸與腳麻外,頭腦也會變的極度渾沌。所以在計畫讓屁股離開椅子前,就來看了一下「王建民」。有趣的是,我找了一下「王建民」跟「embedded linux」在 Google 的搜尋量比較,結果還挺有趣的。先來看一下「embedded linux」跟「wince」、「embedded system」關鍵字的搜尋關係:
embedded linux wince embedded system
結果 embedded linux 跟 embedded system 的關係相當密切,幾乎是貼在一起走的。至於跟 wince 做比較的話,則是保持一定的差距,看起來跟幾年前相差不遠。
再來是「embedded linux」PK「王建民」:
embedded linux 王建民
2005 年 Q1 以前,「王建民」幾乎是躺平的,大概只有 2004 Q3 亞洲盃時凸起來一下下。然後 2005 年 Q1 上大聯盟後是上市密月期,連拉數日長紅,接著到 2005 Q4 ~ 2005 Q1 是 MLB 休兵期間,再度躺平。
今年隨著 Wang 40 的精采表現而呈現的多頭走勢,沒想到在 Wang 40 回國後進入主升段,連爆長紅。雖然在 Wang 40 上市後,這期間有幾次紅線超越「Emebdded Linux」,不過這次 Wang 40 的主升段展開後,已經把「Embedded Linux」遠遠抛在腦後了。
如果再看下半部的新參考頁面「量」,Wang 40 老早就不把 embedded Linux 看在眼裡了。另外,如果你在 Google 找「王建民」,會發現右半部很「台灣味」:
王建民,加油!
今天的內容「讀取 ELF section」是這系列 ELF 文章的重點戲。因為我們終於進入 ELF 的核心議題「節區的觀念」了。在 loader v0.5 以前的範例,都是屬於靜態的討論(linking view);在 loader v0.6 開始的討論中,我們將會開始提到動態的執行行為(execution view)。
如何讀取 ELF Section
我們分 2 個步驟來讀取 ELF 的 section 資訊:
1. 如圖一,一開始先讀取 section header table 的資訊。section header table 是所有 section 的紀錄表格,所有的 section 都要透過 section header table 才能得知其在檔案中的偏移位置(offset),如此一來才能讀取 section 的內容。
2. 如圖二,接著再根據 section header table 讀取檔案裡的每一個 section。
section header table 裡的 section 個數(section entries)紀錄於 ELF header 裡的 e_shnum 欄位,每個 section entry 的長度則是紀錄在 ELF header 的 e_shentsize 欄位,單位是bytes。
圖一:Section Header Table
ELF header 裡的 e_shoff 欄位,是 section header table 開始的檔案偏移位置 (file offset)。因此,我們只要由 e_shoff 偏移值開始讀取 e_shnum 個單位,每個單位為 e_shentsize(bytes),即可將整個 section header table 讀取出來。
圖二:Section Entries
SysV ABI 定義 section entry 的資料結構如下:
typedef struct { Elf32_Word sh_name; Elf32_Word sh_type; Elf32_Word sh_flags; Elf32_Addr sh_addr; Elf32_Off sh_offset; Elf32_Word sh_size; Elf32_Word sh_link; Elf32_Word sh_info; Elf32_Word sh_addralign; Elf32_Word sh_entsize; } Elf32_Shdr;
Section header table 是 Elf32_Shdr data type 的陣列,其元素個數為 e_shnum,我們可以透過索引 Elf32_Shdr 陣列來讀取所有的 section,如圖二所示。
Section header(Elf32_Shdr)的欄位用途說明如下表。
Field | Description |
sh_name | section的名稱,需由string table查表取得。 |
sh_type | section的類型。 |
sh_flags | section的屬性。 |
sh_addr | section在記憶體裡的起始位址,但並非所有的section都會被載入至記憶體。 |
sh_offset | section在objct file裡的開始偏移值(offset),程式必須根據此欄位來讀取section的本文。 |
sh_size | section的長度(bytes)。 |
sh_link | 可用來存放section header table的index link。 |
sh_info | 可用來存放section的額外資訊。 |
sh_addralign | 紀錄section的address alignment,例如有些section的 alignment為DWORD(dobuleword)。 |
sh_entsize | 有些section的內容為entry長度固定的table,例如symbol table。此欄用來紀錄entry的長度,單位是bytes。 |
範例程式:loader v0.3
接著說明 loader v0.2 需要改寫的功能。
在主程式新增一個 parse_sections() 函數來讀取 ELF object file 的 section header table:
parse_sections(&f_header, fd);
parse_sections() 負責做2件工作:
1. 讀取 Section Header Table
2. 找出 Section Name String Table
同時,在主程式也要加入以下 2 個變數:
Elf32_Shdr header[40];
Elf32_Shdr *strtab; /* point to string table */
header[] 用來存放由 section header table 所讀取出來的所有 section entry,其型別為 Elf32_Shdr;strtab 指標用來指向 section name string table,其型別為 Elf32_Shdr。Section name string table 是一個特殊的 section,下一個範例再來處理這個 section。
將檔案讀寫指標移到 e_shoff 的地方,準備開始讀取 section header table:
lseek(fd, hdr->e_shoff, SEEK_SET);
然後再利用最簡單的方式,一次一個將所有的section entry讀取出來,並且判斷該section entry是否為string table:
for (i = 0; i < hdr->e_shnum; i++) { read(fd, &header[i], sizeof(Elf32_Shdr)); /* find out string table ! */ if (header[i].sh_type == SHT_STRTAB) strtab = &header[i]; }
最後,section 用途是根據他的類型來區分的,這個部份留待下次再做說明。
Also See |
Debian ARM 已成為 Debian 數十種平臺的 distribution 中的第三大。由 Debian 的 popularity-contest 統計資料來看,Debian ARM 已經超越 PowerPC 成為第三大的 Debian 架構分支。詳細報導在此。
讓 Debian ARM 成為第三熱門的 Debian 版本的功臣就是今年大大有名的 NSLU2 計畫,如果您還不曉得 NSLU2 的名號,可以到NSLU2 的官方網站瞧瞧。當初 NSLU2 計畫發起時,便立下這樣的志願(節錄自 LinuxDevices.com 的報導):
to have the Linksys NSLU2 become a fully supported mainstream Linux distribution device.
這個目標達成了!Debian ARM 從第七名竄升到第三名,只花了九個月的時間;並且,有 90% 的 Debian ARM 使用者都是將 Debian ARM 安裝在 Linksys 的 NSLU2 device 上(節錄自 LinuxDevices.com 的報導):
Ninety percent of those ARM installations are NSLU2 devices.
NSLU2 是今年 open source community 發展模式中,具代表性的成功案例之一。Jollen 認為,當中最關鍵的因素除了眾所矚目的開放源碼社群模式外,有二項關鍵因素是值得推敲思考的:
1. 將 Linksys NSLU2 當做是「device」來推廣,而不是「product」。如果你到 NSLU2 的首頁去,一定能看到這句話:「Purchase a Linksys NSLU2 and join our community if you are interested in doing the same.」。
東西當做 device 來賣,顧客是 community,賣了東西也得到不少東西;再說,賣給 community 的數量也不見得比賣 product 給 end-user 少。
2. NSLU2 device 拿來給社群做發展與「把玩」並建立自己的 open source project(與 community)後,才能得到其他(、更多) community 的支持,更多社群跟你互補後,不但能加快本身的產品發展速度與進行更有效的推廣,也能吸引許多「end-user」的關注。對於「product」的長期發展絕對是正面的。我們可以看到,許多社群的開發者(developer)都是熱血的死忠擁護者,這些人也是很好的 promoter。
Debian ARM(或 NSLU2)之所以成功,在 LinuxDevices.com 上的報導裡已經講出重點了:
This is, of course, the outcome of a lot of hard work by a lot of people, including (but not limited to) the NSLU2-Linux core team and developers, the OpenEmbedded developers, the Debian-arm porters, and the Debian-installer and Debian-kernel teams.
可見閉門造車並不是好事,因為除了要不停的「重造車輪」外,也無法讓不同的社群響應你的計畫,或是跟你互補。
最近有些朋友在問 PReP / CHRP / OpenFirmware 的關係,老實說這真的有點混亂,特別是 PReP / CHRP 是歷史所留下的遺跡,所以就用最簡潔的方式大概說明一下吧。
PReP(PowerPC Reference Platform)是最早的 PowerPC reference design,PReP 規格並沒有風行,原因是 IBM 是以「一言堂」的模式來制定這項規格。PReP 之後則是 CHRP(Common Hardware Reference Platform)的第二代 PowerPC reference design 規格,CHRP reference design 制定了作業系統支援的規格。CHRP 的另外一個重要內容則是定義 OpenFirmware 的標準。
OpenFirmware(也稱為 OpenBoot)的角色等於 PC 的 BIOS,也就是開機時期的軟體。OpenFirmware 相當強大,因為 OpenFirmware 採用的是 Forth 語言,因此撰寫 firmware 的模式是「Forth-based shell script」。
OpenFirmware 只是一個規格,目前有 open source 的 OpenFirmware 專案,也就是知名的 OpenBIOS。當然也有商業性質的 OpenFirmware 實作,不過既然 IBM 也貢獻了 FCODE 給 OpenBIOS,那麼我想未來 OpenBIOS 絕對是首選的 OpenFirmware 實作了。
最後要說明的是,PReP 與 CHRP 都不是目前 Power Architecture 的 reference design 規格了。早先 Jollen 在「RISC 嵌入式平臺 (PowerPC) 的 VGA 解決方案」的日記裡也大略提到過 OpenBIOS 與 OpenFirmware(FCODE suite),大家也可以參考一下。
新一代的 Power Architecture 規格叫做 PAPR,這是由 Power.org 組織所制定的新規格,主要是針對 Power 架構的 workstation 與 server 所制定的(no PC...),目前的最新版本是 v2.0。
Top | 授權條款 | Jollen's Forum: Blog 評論、討論與搜尋
Copyright(c) 2006 www.jollen.org