<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
   <channel>
      <title>Jollen&apos;s Blog</title>
      <link>https://www.jollen.org/blog/</link>
      <description></description>
      <language>en</language>
      <copyright>Copyright 2020</copyright>
      <lastBuildDate>Wed, 11 Apr 2018 20:55:17 +0800</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

            <item>
         <title>另一個挖礦時代來臨了？關於 ERC 891 代幣的二件事</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2018/04/erc-891-ico.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="https://imgur.com/jCzAKdO.jpg">

不久前（2018 年 1 月），Ethereum 才剛針對 ERC 20 提出的 ERC 827 擴充標準；近期，ERC-891 代幣標準橫空出世。即使 ERC 891 仍只是一個 EIP（Ethereum Improvement Proposal），但筆者認為這是一個相當值得關注的提案。

![2018-04-11 6 56 07](https://user-images.githubusercontent.com/1126021/38612682-12376bb4-3dba-11e8-9269-46fda7881303.png)

第一、ERC 891 提案的重心，在於一個稱為 PPoW（Pseudo proof-of-work）的觀念。技術上，ERC 891 與 ERC 827 相同，都是 ERC 20 的延伸（Extension）；但 ERC 891 的目標，是希望「讓 ERC 20 成為 PoW 挖礦式代幣」。比較有趣的是，PPoW 打算採用異步式挖礦系統（Asynchronous mining system）。也就是說，礦工將能同時挖所有的 PPoW 代幣。

做為一個礦工，或者礦場的場主，更應該支持 ERC 891 的代幣標準。未來如果有 1,000 個 Token 採用 ERC 891 發行，表示一台挖礦機將能同時挖 1,000 種代幣。

第二、ERC 891 為 ERC 20 擴充了 ```function mine() public;``` 與 ```function checkReward() view public returns(uint256);``` 二個方法，原則上，這不影響原有的 ERC 20 智能合約實現。這二個方法充份表現了 ERC 891 的重點：挖礦與獎勵。挖掘 ERC 891 代幣，需要費用（Gas price），因此 ERC 891 希望 ```mine``` 方法是 *predictable*；也就是，礦工可能知道完成一個挖礦週期，需要花費多少 ETH。

## ICO 與 ERC 891

筆者認為，ERC 891 提案若能被廣為採行，將有助於打造更健康的 ICO 生態（ICO 2.0？）：

* 目前 ICO 採用 ERC 20 來發行代幣，代幣的初始供給總額（Total amount），是由智能合約的佈署者（Deployer）持有；採用 ERC 891 後，智能合約佈署者將不持有初始的所有代幣，而是由任何的礦工來 *mining*

![2018-04-11 7 50 11](https://user-images.githubusercontent.com/1126021/38614966-9b57363e-3dc1-11e8-9b16-6ecd75103e61.png)

* Token distribution 由智能銷售合約決定，有些 ICO 投資人認為這有潛在風險；將 token distribution 改採 PoW 的做法，有助於降低「人為掌握」所產生的投資風險

* 現有的 ICO model，其 token allocation 與 transfer 完全「可由人為」控制，但是我們都知道的，人性總是（永遠）無法戰勝誘惑，因此經常（一定）有非法行為產生；而 PoW 的方式來進行 token allocation 與 transfer，或許能部份防止此類的非法行為



</script>]]></description>
         <link>https://www.jollen.org/blog/2018/04/erc-891-ico.html</link>
         <guid>https://www.jollen.org/blog/2018/04/erc-891-ico.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Wed, 11 Apr 2018 20:55:17 +0800</pubDate>
      </item>
            <item>
         <title>P2P 物聯網論文發表：Advances in IoT Architecture and Systems，多倫多，加拿大 (2017)</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/10/devify-aiotas-isca-toronto-canada-june-2017.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="https://i.imgur.com/9h8vthIh.jpg">

![2](https://user-images.githubusercontent.com/1126021/31872573-aaf66a08-b782-11e7-8c39-da838ba47d8a.jpg)

這是今年（2017）第二篇有關 Blockchain 的論文。在加拿大多倫多 ISCA 2017 的 Advances in IoT Architecture and Systems (AIoTAS 2017) 研討會上，發表了一篇有關 P2P IoT 的研究文：Devify: Decentralized Internet of Things Software Framework for a Peer-to-Peer and Interoperable IoT Device。這篇論文也會出版在 2018 年的 ACM SIGBED Review 上。

![1](https://user-images.githubusercontent.com/1126021/31872572-aac05396-b782-11e7-83e8-352aa160a58e.jpg)

區塊鏈被視為是下一代的信息技術，它與目前的信息技術相較，有一個最大的差異就是去中心化應用（decentralized applications，Dapp）。單純從技術的角度來看，P2P（peer-to-peer）網路是 Dapp 的底層網路拓璞（topology）。

Devify 就是一個 P2P IoT 的軟體框架，它是 [Flowchain](https://flowchain.io) 研究的一個副產出（side project）；從區塊鏈與 Dapp 的角度來看，Devify 其實是最重要的基礎建議。因為沒有了 IoT P2P 軟體框架，IoT Blockchain 是無法實現的。

論文下載點：[https://flowchain.co/publication.html](https://flowchain.co/publication.html)

## Flowchain 計畫

Flowchain 目前處在 Proof-of-Concept 階段，後續計畫包含 TSDB（Time-Series Database）、去中心化的 IoT 通用框架（Decentralized and Generic IoT Programming Framework）以及混合區塊鏈的佈署案例（Hybrid Blockchain）。在商用化部份，目前已經開始搭配 Hyperledger Fabric 來提供一個 Edge Computing 與 IoT Blockchain 的實驗系統。所有的開源項目，以及最新進展，將會在 [https://flowchain.io](https://flowchain.io) 上發佈。

## 多倫多日常

![a](https://user-images.githubusercontent.com/1126021/31872580-b3abe95c-b782-11e7-926c-8963a668be37.jpg)
![b](https://user-images.githubusercontent.com/1126021/31872581-b3e7f050-b782-11e7-81d0-1c53ec0a0d27.jpg)
![c](https://user-images.githubusercontent.com/1126021/31872582-b446c454-b782-11e7-8169-238828ff278b.jpg)
![d](https://user-images.githubusercontent.com/1126021/31872583-b476ccda-b782-11e7-9119-de29b6df85fe.jpg)
![e](https://user-images.githubusercontent.com/1126021/31872584-b4af2e86-b782-11e7-9032-d9d82bc6c897.jpg)
![f](https://user-images.githubusercontent.com/1126021/31872585-b50ca098-b782-11e7-9564-f216317de2cf.jpg)
![g](https://user-images.githubusercontent.com/1126021/31872586-b542975c-b782-11e7-88a3-908d24a6a96b.jpg)
![h](https://user-images.githubusercontent.com/1126021/31872587-b5761a78-b782-11e7-8d0d-ba129e5c1d8f.jpg)
![i](https://user-images.githubusercontent.com/1126021/31872589-b5a4b176-b782-11e7-9047-5b01c89839c5.jpg)
![j](https://user-images.githubusercontent.com/1126021/31872590-b5d9ed5a-b782-11e7-89bf-1ce65a2ca213.jpg)



</script>]]></description>
         <link>https://www.jollen.org/blog/2017/10/devify-aiotas-isca-toronto-canada-june-2017.html</link>
         <guid>https://www.jollen.org/blog/2017/10/devify-aiotas-isca-toronto-canada-june-2017.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Mon, 23 Oct 2017 13:57:09 +0800</pubDate>
      </item>
            <item>
         <title>IoT Blockchain 論文發表：Linked Data and Distributed Ledgers 國際研討會，皮蘭，斯洛維尼亞 (2017)</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/10/flowchain-lddl-eswc-portoroz-slovenia-may-2017.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="https://i.imgur.com/9h8vthIh.jpg">

![](http://i.imgur.com/kY9P4vUh.jpg)

關心 Blockchain、更別忘了 Web 3.0。IoT Blockchain 近期在台灣產業界受到不少關注。許多報導與活動，都在討論物聯網區塊鏈的機會與方向；我則是在 ESWC 發表 IoT Blockchain 的具體成果與程式碼。

ESWC (Extended Semantic Web Conference) 是歐洲主要的 Semantic Web 學術會議，今年在 Piran (斯洛維尼亞) 舉辦的 ESWC 2017 已經邁入第 14 屆。這次參加 ESWC 2017 全程 5 天的會議，學習來自各界的研究成果分享，非常有收獲。

![](http://i.imgur.com/YMtxyXFh.jpg)

在與 ESWC 2017 共同舉辦的 International Workshop on 2nd Distributed Ledger and Linked Data (LDDL) 上，我有一篇關於物聯網區塊鏈 (IoT Blockchain) 的論文被 Accept，因此有了 20 分鐘的機會，向大家簡報 Flowchain 計畫的目標。

另人興奮的是，一位來自 Standford 的教授，對於 Flowchain 的想法感到興趣；Chord 不是一個新的 DHT 技術，過去也有許多 Chord 的 fixed 與 extended 研究被提出，但對於利用 Chord 的 Distributed Data Store 這項「天然特性」來實作 Distributed Ledger，Flowchain 很可能是第一個提出這項應用的研究計畫。

![](http://i.imgur.com/8ISFu6dh.jpg)

在經過這段時間的 paper review 與現場討論後，準備開始進行 beta release 的 commits 了。目前 alpha 版本，將保留為 Proof-of-Concept 的版本，目前已經將程式碼發佈在 Github 上：

https://github.com/flowchain/flowchain-ledger

這個版本沒有太多的雕飾與抽像資料結構，所以可以很容易看出 Flowchain 的架構。接下來，在 7 月底前，將進入 beta commits，陸續將 evaluation code 重寫並提交；同時，也會開始進行商業化。

![](http://i.imgur.com/AF6RJJWh.jpg)

Semantic Web 另一個耳熟能詳的名字，叫做 Web 3.0；這次在 ESWC 2017 上，我發現幾個 Web 3.0 的共同研究方向。總結一下 Web 3.0 接下來可能的研究熱點：第一、對於 Data Streams 的支援。第二個多次被出的議題是 Security 與 Privacy；第三則是 Decentralized。

關於 Data Streams 的部份，透過延伸 RDF 以支援 Data Streams，是這次 ESWC 2017 不約而同的一個主題；因為過去大多著重在 Document-Oriented Data 應用，因此 Streaming Data 的研究相對較少。這個主題很明顯，開始受到特別關注了。

![](http://i.imgur.com/a4pnv2Uh.jpg)
![](http://i.imgur.com/QFu5t6Oh.jpg)
圖：會議酒店的海岸

Data Streams 又分為 Time-Series Data (aka Point-based) 與 Interval-based Data 二類；以這次大會選出的 Best Paper 為例，該論文就是針對 Time-Series Data 進行研究，並提出一套方法論 (Ontology)，讓 Web 3.0 能分析即時交通資訊，以提升道路安全。

![](http://i.imgur.com/WWezrNdh.jpg)

Flowchain 也提出一個以 Semantic Web 支援 Time-Series Database (TSDB) 的方法；這部份的程式碼，預計在 Flowchain v2.0 開始發佈。

最後，讓 Web 3.0 支援 Data Streams 很重要嗎？現在的 Machine Learning 已經很擅長分析 big “history” data 了；如果讓 Data 能更好的語意化 (Semantic)，並打造一套好的 “RDF” Streams Store，就可以讓 Machine Learning 能更好地分析即時數據。這也是今年 ESWC 2017 的另一個重點。

## Flowchain 計畫

Flowchain 目前處在 Proof-of-Concept 階段，後續計畫包含 TSDB（Time-Series Database）、去中心化的 IoT 通用框架（Decentralized and Generic IoT Programming Framework）以及混合區塊鏈的佈署案例（Hybrid Blockchain）。在商用化部份，目前已經開始搭配 Hyperledger Fabric 來提供一個 Edge Computing 與 IoT Blockchain 的實驗系統。所有的開源項目，以及最新進展，將會在 [https://flowchain.io](https://flowchain.io) 上發佈。

</script>]]></description>
         <link>https://www.jollen.org/blog/2017/10/flowchain-lddl-eswc-portoroz-slovenia-may-2017.html</link>
         <guid>https://www.jollen.org/blog/2017/10/flowchain-lddl-eswc-portoroz-slovenia-may-2017.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Mon, 23 Oct 2017 13:51:37 +0800</pubDate>
      </item>
            <item>
         <title>P2P IoT software framework paper accepted at AIoTAS 2017 @ISCA&apos;17</title>
         <description><![CDATA[My paper titled “Devify: Decentralized Internet of Things Software Framework for a Peer-to-Peer and Interoperable IoT Device” has been accepted by the <i>Advances in IoT Architecture and Systems</i> (<a href="https://sites.google.com/view/aiotas2017/">AIoTAS 2017</a>), as co-located at ISCA 2017, taking in place in Toronto, Canada in June 2017. The paper proposes Devify, an open source software framework for peer-to-peer IoT networks. 

Also, the nature of the distributed ledger technology (DLT) has a large opportunity to toward a more secure and trusted IoT network. Therefore, this paper has already developed <a href="https://flowchain.co">Flowchain</a>, the blockchain for the IoT, to practically prove the concept of this work.
]]></description>
         <link>https://www.jollen.org/blog/2017/10/p2p-iot-software-framework-paper.html</link>
         <guid>https://www.jollen.org/blog/2017/10/p2p-iot-software-framework-paper.html</guid>
        
        
         <pubDate>Thu, 19 Oct 2017 14:03:02 +0800</pubDate>
      </item>
            <item>
         <title>區塊鏈演講 &amp; 講稿下載：LinuxCon + ContainerCon + CloudOpen (LC3), Beijing, China, 2017</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/10/flowchain-linuxcon-china-2017.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="https://i.imgur.com/9h8vthIh.jpg">


![](https://i.imgur.com/4Xei5MLl.jpg)

為什麼區塊鏈是 Web 3.0 的概念？因為 Web 3.0 有一個重要的概念，就是 P2P Internet。區塊鏈除了能為 Web 3.0 提供 P2P Internet 基礎建設外，也因為區塊鏈本身「天然特性」，還能提供 Web 3.0 所需的 Trusted Computing 與 Cryptography 技術。

Trusted computing 與 cryptography 在 IoT 領域，能提供絕佳的 Data security 與 Data privacy 環境。因此，一個建立在 Web 概念與 P2P 網路之上，並且能提供 Data security 與 Data privacy 環境的物聯網架構，就是物聯網區塊鏈（Blockchain IoT）的主要研究方向。

Flowchain 計畫就是為此生。由 Linux Foundation 主辦的 LinuxCon + ContainerCon + CloudOpen (LC3) 今年（2017）首次到北京舉辦，而且也設立了區塊鏈專場，利用這個難得的機會，投稿了今年 LC3 的 Blockchain Track；很幸運地得到 reviewers 的青睬，順利得到一個珍貴的演講機會。

## 議程與講稿下載

![](https://i.imgur.com/E7lRktbl.jpg)

* 官方議程（英文）：[LinuxCon + ContainerCon + CloudOpen China](https://goo.gl/c5zeaK)
* 講稿下載：[Flowchain: A Case Study on Building a Blockchain for the IoT](http://schd.ws/hosted_files/lc3china2017/43/Flowchain-LC3_2017_Beijing-20170614.pdf)
* [演讲内容中文介紹](https://www.bagevent.com/event/561769?sId=7411)
* [LC3大会技术亮点探秘之上篇](http://geek.csdn.net/news/detail/202037)

![](https://i.imgur.com/HDQ2MTJl.jpg)


</script>]]></description>
         <link>https://www.jollen.org/blog/2017/10/flowchain-linuxcon-china-2017.html</link>
         <guid>https://www.jollen.org/blog/2017/10/flowchain-linuxcon-china-2017.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Thu, 19 Oct 2017 13:36:06 +0800</pubDate>
      </item>
            <item>
         <title>Blockchain Developer - 初探 Distributed Ledger Technology (DLT)</title>
         <description><![CDATA[<p>DLT 有一個更耳熟能詳的名字，叫做區塊鏈...</p>

<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/09/blockchain-developer-distributed-ledger.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/eoxcQmh.jpg">

# Distributed Ledger Technology (DLT)

DLT 有一個更耳熟能詳的名字，叫做區塊鏈。簡單來說，根據 Wikipedia 上的定義 [1]，區塊鏈（Blockchain）就是一種 Distributed Ledger 的資料結構（Data Structure）：

```
a Blockchain is only one type of data structure considered to be a distributed ledger.
```

也就是說，DLT 可以延用 Blockchain 資料結構設計，也可以根據應用的不同，設計全新的資料結構；無論是採用 Blockchain 資料結構，或是設計新的資料結構，目標都是提供一個分散式資料儲存系統（distributed data storage）。一個 DLT Platform 除了要具備一個安全可信任的 distributed data storage，還具備以下 2 個元素：

* A peer-to-peer network
* Consensus algorithms

在 distributed data storage 網路上的電腦裝置，稱之為節點。由於 distributed data storage 需要考慮備援與錯誤修正的問題，因此會將一筆資料同步備援在多個節點上，這樣的技術就稱為 replication。當某一個節點上的該筆資料毀損時，扮演該筆資料備援角色的節點，就能協助該節點，回覆毀損的資料，這樣的技術就稱為 redundancy。

技術上，DLT 就是一個為 distributed data storage 提供 replication/redundancy 功能的通訊協定（Protocol）。這樣的 replication/redundancy 機制，就稱為 fault tolerance（容錯機制）。

## Transactions

DLT 與傳統資料庫系統，除了上述的差別外，另一個重要的區別是：交易。DLT 儲存的是交易（Transaction）；而傳統資料庫儲存，則是儲存「資料」（Data）。

交易與資料的一個區別是，交易需要被「驗證」（validation）；而資料卻是可以直接地，儲存進資料庫裡即可。交易會包含所要儲存的資料，因此，可以說交易是資料的「封裝」。

在軟體設計的領域裡，封裝有「打包」的意思；也就是說，這像是在寄掛號信，我先將文件（資料）放進信封打包好，再將郵件「提交」到郵局，最後由收件人簽收無誤後取出文件，再將文件歸檔（儲存）。

在一個 DLT Platoform 裡，交易的驗證過程，需要決定該筆交易的接受者與簽收方式，這就開始涉及 Broadcast 與 Consensus 技術了。最終，這筆交易會被節點儲存，而儲存交易的地方就稱為 Block（區塊）；這些區塊將不只一個，而是一個 chain of blocks 的結構，這些串接在一起的 blocks 就稱為 blockchain。

## Byzantine Fault Tolerance（拜占庭容錯算法）

DLT 是一個比 Blockchain 更整體的研究主題，例如知名的 Hyperledger 就是一個 DLT 平台。Fault tolerance 與 transactions 則是 DLT 的核心骨幹。像是 PBFT（Practical Byzantine Fault Tolerance ）就是一個知名的 fault tolerance 演算法，並且被 Hyperledger Fabric v0.6 所採用。

SBFT（Simplified Byzantine Fault Tolerance）也是一種 fault toerance 演算法，由 Hyperledger Fabric v1.0 實作；SBFT 演算法延續自 PBFT，目標是擴充 PBFT 以支援更大規模（large scale）的 p2p 網路。


拜占庭容錯算法，目前經常被稱為共識算法（consensus algorithms）；雖然二者是不同的技術，不過現在似乎也不會這麼嚴謹地區分了。

## References

[1] https://en.wikipedia.org/wiki/Distributed_ledger


</script>]]></description>
         <link>https://www.jollen.org/blog/2017/09/blockchain-developer-distributed-ledger.html</link>
         <guid>https://www.jollen.org/blog/2017/09/blockchain-developer-distributed-ledger.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Sat, 30 Sep 2017 14:24:58 +0800</pubDate>
      </item>
            <item>
         <title>[Flowchain 專欄] 一分鐘看 IoT Blockchain (Part 6)：使用 Fullstack JavaScript</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-fullstack">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/mwX8cwYh.jpg">

Heterogenous Hardware 的觀念非常簡單：各式各樣的硬體裝置。

## 各式各樣的硬體裝置

Heterogenous Hardware 的目標更為單純：「Write once, run everywhere」。對 IoT Blockchain 來說，是否能打造一套能在各式各樣硬體裝置上執行的軟體框架，會是一個關鍵議題。

使用 JavaScript 來實作 IoT 系統是一個流行，但更實質的原因，則是為了 Heterogenous Hardware。如圖一，Flowchain 以及它的底層通訊系統（Devify）都是 100% 的 JavaScript 實作，這可以解決基本的移植性問題。以現今的 IoT Device 硬體技術來說，Flowchain 能安裝在 Microcontroller、Microprocessor 與 Cloud Server 上。

## Flowchain 基礎架構

Flowchain 是一個從底層到上層，都使用 JavaScript 實作的軟體框架（Fullstack JavaScript）。不過，區塊鏈要能支援 Heterogenous Hardware，共識算法的研究與設計，才是真正的關鍵。

![flowchain-linux-foundation_r3 001](https://user-images.githubusercontent.com/1126021/27135860-40c355de-514c-11e7-8876-e6694b9823bc.jpg)

## 小結

原生 P2P 演算法要應用在 IoT Blockchain，需要處理演算法上的一些細節。

</script>]]></description>
         <link>https://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-fullstack.html</link>
         <guid>https://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-fullstack.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Thu, 15 Jun 2017 20:17:29 +0800</pubDate>
      </item>
            <item>
         <title>[Flowchain 專欄] 一分鐘看 IoT Blockchain (Part 5)：認識 Churn 現象</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-churn">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/mwX8cwYh.jpg">

Chord 能運用在 Peer-to-Peer 的 IoT 網路，但是有些技術細節必須從軟體架構的層面解決。第一個會面的技術問題，就是「Churn」現象。

## 進進出出

所謂的 Churn 現象就是：在 Peer-to-Peer 網路中，隨時都有節點（node）加入或離開（進進出出）。對於 Churn 的處理，要根據不同的 P2P 演算法來進行研究。Chord 如何處理 Churn 問題，以及 Handling Churn  的效能分析，過去已經有許多研究論文提出解決方法。

至於 [Flowchain 區塊鏈](https://flowchain.io) 當然也有針對 Churn 進行研究。在 Flowchain 裡面，處理 Churn 現象的方式，是以擴充 Chord Protocols 的方式來進行處理；這方面的研究，已經撰寫成學術論文，並且被 [AIoTAS 2017](https://sites.google.com/view/aiotas2017/program) 接受並發表。

## Churn Rates

當 P2P 網路的節點，很頻繁地加入或離開時，這個網路稱為 High Churn Rates。如果 IoT Blockchain 無法有效處理 High Churn Rates 的問題，Data Transactions 的能力就會降低。例如，有資料需要交易處理時，就要經由 DHT 裡找到負責節點，但這個節點可能已經離開了。

IoT 裝置會離開 P2P 網路的原因，可能是 Wi-Fi 訊號不良，也可能是電量問題等其它問題；這和以 PC 為主的典型 P2P 網路非常不同。例如：PC 並非電池式裝置，因此電池式裝置的 Churn Rates，理論上會比 PC 為主的 P2P 網路高。又如，Wi-Fi 訊號問題，造成 P2P 網路中，經常有裝置會離線，這又是另一個造成 High Churn Rates 的因素。

## 小結

原生 P2P 演算法要應用在 IoT Blockchain，需要處理演算法上的一些細節。

</script>]]></description>
         <link>https://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-churn.html</link>
         <guid>https://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-churn.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Thu, 15 Jun 2017 20:15:59 +0800</pubDate>
      </item>
            <item>
         <title>[Flowchain 專欄] 一分鐘看 IoT Blockchain (Part 4)：認識 Chord 通訊協定</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-chord">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/mwX8cwYh.jpg">

Flowchain 使用一個稱為 Chord 的 P2P 通訊協定，[flowchain-chord](https://github.com/jollen/flowchain-chord) 是一份 Node.js 的實作。

近來受到相當程度討論的去中心化（Decentralized）概念，則是基於 Peer-to-Peer 通訊網路的分散式系統。Peer-to-Peer 的研究在 2000 年左右，就已經有相關的研究論文發表。Decentralized 與 Block Store 的觀念，在這裡論文裡已經被提出討論。可見這二個目前廣為討論的觀念（Decentralized 與 Block Store）都不是新鮮技術了。

## 關於 DHT 與 Chord Protocol

Chord[1] 是一個 DHT（distributed hash table）通訊協定，在 peer-to-peer 通訊網路中，DHT 指的是節點（nodes）的 store，用來儲存「資料的負責節點」。因此，Chord 協定能讓 P2P 網路尋找 Data 的負責節點，Chord 協定同時也維護所謂的指向表（Finger Table）來提升尋找節點的效能。Chord 於 2001 年誕生於 MIT[2][3]。

除了 Chord 外，連同其它 3 個 P2P 演算法：CAN、Tapestry 與 Pastry，被稱為 P2P 的 4 大原始演算法。P2P 演算法會維護一份稱為 DHT 的表格，這個表格便是用來紀錄「資料的負責節點」；以 Chord 為例，如果有一筆資料 *D1* 被送進 P2P 網路裡，負責處理這筆資料的節點稱為 *successor*，以 *sucessor(D1)* 來表示，*successor()* 函數將傳遞進來的資料做 Hash 運算，以 *successor(D1)=key1* 來表示。

簡而言之，P2P 透過 DHT 來找到負責處理資料的人。Flowchain 區塊鏈就是使用 Chord 來維護 DHT；關於 Chord Protocol 的研究，會參考 [MIT Chord/DHash](https://github.com/sit/dht) 的原始實作。這是一份 C++ 的實作，Flowchain 則是使用 JavaScript 重新實作了一份輕量化的 Chord 協定。

## Chord Protocols

Chord protocols 分為 5 個部份：

* Basic query
* Finger table
* Node join
* Stabilization
* Failures and replication

## Stabilization 算法

這是 Chord protocol 最重要的一個環節，根據 Chord protocol 的規範：

* Stabilization protocol 是週期性地（periodically）在背景執行
* 更新 finger table 與 successor pointers
  
以 JavaScript 來實現 stabilization protocol 的方式：

* 使用 ```setInterval``` 實現週期性執行（理論上這可以有更好的 scheduling 實現）

Stabilization protocol 的算法分二個部份：

* Stabilize()
* Nofity()

先定義 ```Stabilized()``` 與 ```Notify()``` 的訊息：

```
// 定義訊息類型
var FIND_PREDECESSOR = 0;
var NOTIFY_PREDECESSOR = 1;
```

其中，```Stabilized()``` 負責向 successor 請求它的 predecessor，並決定 predecessor 是否要成為新的 sucessor：

```
setInterval(function Stabilize() {
// 請求 predecessor，並將 predecessor 做為新的 successor
    send(successor, {type: FIND_SUCCESSOR, next: next_finger});
}, 1000);
```

此外，```Notify()``` 負責告知 sucessor 我是它的 node，讓 successor 能更新 finger table：

```
setInterval(function check_predecessor_and_stabilize() {
// 向 successor 請求 predecessor
    send(successor, {type: NOTIFY_PREDECESSOR});
}, 1000);
```

## Node join

新的 node 建立時，要執行以下任務：

* 初始化 node 的 predecessor 與 finger table
* 通知其它 nodes 更新 predecessors 與 finger table
* 從 sucessor 取得 responsible keys

## Finger Table

Finger table 是 Chord 的搜尋演算法。Node 可以經由 finger table 來找到任意節點。Finger table 的演算法設計，搜尋節點的關鍵，它可以避免線性（linear）搜尋的做法。每一個 node 都會有 finger table，finger table 的第 i 個 entry 紀錄的是 sucessor( (n + 2^(i-1)) mod 2^m )。

* _m_ 是 ring 長度，也就是finger table 的長度。這是 Chord network 的最大（最多）節點數量。
* successor 與 predecessor 是重要的觀念。

Finger table 是一個 hash ring 的結構。Chord 經由 predecessor 的觀念，來簡化 join 與 leave 的機制[2]。

## 小結

要以 P2P Topology 方式建構 IoT Blockchain，對於 P2P 演算法的深入研究，絕對是一項基本功。

## References

[1]: Chord, [https://en.wikipedia.org/wiki/Chord_(peer-to-peer)](https://en.wikipedia.org/wiki/Chord_%28peer-to-peer%29)

[2]: I. Stoica, et al, [Chord: A scalable peer-to-peer lookup service for internet applications](https://pdos.csail.mit.edu/papers/chord:sigcomm01/chord_sigcomm.pdf)

[3]: LibraRing: An Architecture for Distributed Digital Libraries Based on DHTs, http://scholar.harvard.edu/files/ecdl05.pdf

[4]: CS138, http://cs.brown.edu/courses/cs138/s15/content/projects/chord.pdf

[5]: Distributed Hash Tables, http://merlot.usc.edu/cs551-m05/lectures/tentative/20a_chord.pdf

[6]: http://zoo.cs.yale.edu/classes/cs722/2011/Jason_chord.pdf

[7]: http://www.dcs.ed.ac.uk/teaching/cs3/ipcs/chord-desc.html

[8]: http://slideplayer.com/slide/4418035/

[9]: http://www.slideshare.net/imprataap/chord-node-join

</script>]]></description>
         <link>https://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-chord.html</link>
         <guid>https://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-chord.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Thu, 15 Jun 2017 20:12:29 +0800</pubDate>
      </item>
            <item>
         <title>[Flowchain 專欄] 一分鐘看 IoT Blockchain (Part 3)：認識 Servient</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-servient.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/mwX8cwYh.jpg">

MCS Lite 導入了我開發的 Devify 框架，所以也有 Servient 玩法喔。

# 認識 Servient

Servient 概念非常簡單：IoT Device 能同時扮演 Client 與 Server 的角色。這個 ```Client + Server = Servient``` 的觀念，是 Decentralized 與 P2P 非常重要的底層技術。

從 IoT Architecture 的觀念來說，並不是去辨別（identify）每一個物聯網裝置要扮演 Client 或 Server 哪一個角色，而是來開發一個能讓裝置扮演 ```Client + Server``` 角色的軟體。

為什麼需要 *Servient* 這樣的架構呢？這個問題，可以很容易用圖 1 來解答。

![flowchain-linux-foundation_r3 001](https://cloud.githubusercontent.com/assets/1126021/26793654/5b845500-4a51-11e7-8ea8-c64c2eeb35f8.jpg)
圖 1：IoT Servient

當 *Device A* 想要以 WebSocket 來傳送資料到 *Device B* 時：

* *Device A* 要扮演 WebSocket Client 的角色；
* *Device B* 則是必須成為 WebSocket Server

當 *Device B* 想要以 CoAP 來傳送資料到 *Device C* 時：

* *Device B* 要轉變為 CoAP Client 的角色，同時；
* *Device C* 則要扮演  CoAP Server 的角色

以上述的 Use Case 來說：

* *Device A* 是 WebSocket Client
* *Device B* 是 *WebSocket Server + CoAP Client*
* *Device C* 是 *CoAP Server*

Servient 並不是一個很普遍的架構觀念，過去在一些技術文件上也能偶然看到 Servient 的觀念；但是，Servient 在 IoT 領域被正式提及，則是在 ISWC 2016 國際研討會上。在 ISWC 2016 上，Soumya Kanti Datta 在 *Semantic Web meets Internet of Things and Web of Things* 的 Tutorial 上，正式提到 WoT **Servient** 的觀念。

近期，在 *Web of Things (WoT) Architecture: Unofficial Draft* 文件中，已經將 WoT Servient 編入 *Terminology*。隨著 WoT 在今年（2017）已經正式由 IG（Interest Group）轉為 WG（Working Group），未來 WoT 也會正式列入這個架構。

## 小結

當 IoT Device 能以 Servient 的形式運作，IoT Device 間的「互聯網」：互相聯結的 IoT 網路，就可以是 Client-Server 架構、Peer-to-Peer 架構或是 Distributed 架構。

## 關於 *MCS Lite*

關於 IoT Servient 的技術，在 [Devify](https://github.com/DevifyPlatform) 專案裡有完整的實作。Devify 是我過去在研究[物聯網區塊鏈](https://flowchain.co)時做開發的底層 WoT 系統。

由聯發科所開發的 LinkIt 7697 物聯網平台，提供一個稱為 *MCS Lite* 的私有雲方案；*MCS Lite* 裡就使用到我所開發的 Devify 框架。也就是說，[LinkIt 7697 + MCS Lite](https://dariachen1.gitbooks.io/mcs-lite-introduction/content/mcs_lite_example.html) 還有更多 IoT 的完法喔。

</script>]]></description>
         <link>https://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-servient.html</link>
         <guid>https://www.jollen.org/blog/2017/06/iot-blockchain-flowchain-servient.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Tue, 06 Jun 2017 01:54:59 +0800</pubDate>
      </item>
            <item>
         <title>IoT Blockchain paper accepted at LD-DL @ESWC&apos;17</title>
         <description><![CDATA[My paper titled “Flowchain: A Distributed Ledger Designed for Peer-to-Peer IoT Networks and Real-time Data Transactions” has been accepted by the <i>2nd International Workshop on Linked Data and Distributed Ledgers</i>, as co-located at ESWC 2017, taking in place in Portoroz, Slovenia in May 2017. The paper proposes <a href="https://flowchain.co">Flowchain</a>, an open source distributed ledger programming framework, for peer-to-peer IoT networks and real-time data transactions. 

Also, Flowchain proposes <b>Virtual Blocks</b> that provides a new blockchain data structure design to ensure the real-time data transactions. This paper also proposes a software architecture that provides peer-to-peer IoT networking and interoperable IoT application framework.

<ul>
<li> 2017.10.23 UPDATE: The paper can be download at the <a href="https://sites.google.com/site/lddleswc17/program">LD-DL</a> workshop website.</li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2017/05/iot-blockchain-paper.html</link>
         <guid>https://www.jollen.org/blog/2017/05/iot-blockchain-paper.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Sat, 20 May 2017 00:03:43 +0800</pubDate>
      </item>
            <item>
         <title>[Flowchain 專欄] 一分鐘看 IoT Blockchain (Part 2)：P2P 通訊架構</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/04/iot-blockchain-flowchain-p2p.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/mwX8cwYh.jpg">

Decentralized 物聯網架構，需要 P2P 的通訊架構。

# 邁向 Decentralized 的關鍵

在 IoT 架構裡，實作 Peer-to-Peer (P2P) 網路有技術上的挑戰嗎？實作 Peer-to-Peer IoT Networking 的目標，是為了讓 IoT Devices 間能建立 P2P 架構的通訊方式，這就是技術上的挑戰了。讓 IoT Devices 能形成一個 P2P 網路，技術上似乎不太困難；不過，如果更深入技術細節來討論，就會發現許多學問。

第一、應用層的考量。IoT 裝置間必須以 Application Layer Protocols 來通訊，例如：HTTP。所以，我們需要能在 IoT 裝置上運行一個「Application Server」，也就是說，必須有一個「Programming Framework」，然後才能在 IoT 裝置上開發這個 Application Server。這裡所提及的 Programming Framework，可以是 IoT 作業系統，或是 Middleware；但其實重點在於，為什麼 P2P 的 IoT Networking，要使用最上層的 Application Layer Protocols；這是一個值得探索的有趣議題。

第二、異質硬體的考量。[Flowchain](https://flowchain.io) 計畫的早期，是從打造一個 Web of Things Framework [1] 起步，這個軟體框架的目的，是以 JavaScript 實作一個 IoT Application Server 的開發框架，有了這個框架，就能達到二個目的：

* 能在不同的 IoT Device 上運行此 IoT Application Server 
* 將 IoT Device 抽象化為 **Virtual Thing**

如果異質硬體都能具備 JavaScript runtime，同樣的 IoT Application Server 就能佈署並運行在這些硬體上。因為 Node.js、JerryScript 等技術被帶入到這些硬體上，這個想法現在有了很高的可行性。

## 小結

IoT Blockchain 並非一個主題，而是一套 IoT Architecture，當中的 P2P Networking，有賴於一個 IoT Application Server 的框架來實現。

## References

[1]: Web of Things Implementations, https://www.w3.org/WoT/IG/wiki/Implementations

</script>]]></description>
         <link>https://www.jollen.org/blog/2017/04/iot-blockchain-flowchain-p2p.html</link>
         <guid>https://www.jollen.org/blog/2017/04/iot-blockchain-flowchain-p2p.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Sun, 30 Apr 2017 13:06:04 +0800</pubDate>
      </item>
            <item>
         <title>[Flowchain 專欄] 一分鐘看 IoT Blockchain (Part 1)：Decentralized 創造附加新價值</title>
         <description><![CDATA[<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/04/iot-blockchain-flowchain-decentralizedhtml.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/mwX8cwYh.jpg">

當你的 IoT 資料，將資料送至中央化的 IoT Platform 時，原本該屬於你的資料所有權、使用權與儲存地，將會默默超出自已的可控制範圍。

# 從 IoT Architecture 看物聯網區塊鏈

IEEE 在 2017 年 1 月發佈一篇 Newsletter 分析 IoT Blockchain 的技術挑戰 [1]，文中提到，從 IoT Architecture 的角度，可以看到 IoT Blockchain 的幾個主要技術挑戰。簡單來說，一個「Decentralized」的 IoT Architecture 將會有機會克服當中的一些技術挑戰。

將 Blockchain 技術應用在 IoT 架構中時，需要「Decentralized」的 IoT 架構，成為一個標準的討論議題。不過，這個去中心化的架構，能為 IoT 應用帶來什麼好處呢？其中最重要的議題，就是 Data Privacy。當你把 IoT 資料傳送到特定的 IoT Platform 時，對於珍貴的資料所有權、使用權與儲存地，就會開始失控。

資料是物聯網系統最重要的資產。因此，回歸區塊鏈的本質來看，IoT Blockchain 能為現有的物聯網網路架構提供 Data Privacy 的解決方案，同時，透過 Trust 機制的導入，讓 Data Security 更加提昇。Data Privacy 與 Data Security 二個議題，與 Semantic Web 的訴求有共同的交集。也就是說，從場景應用的角度來看，IoT 區塊鏈技術不在解決高深的技術問題，**而是為現有的 IoT 產業生態，提供 Data Privacy 與 Trust 的附加商業價值**。

解決技術問題不一定能創造出新的商業模式，但有了附加價值，新的商業價值，以及新的商業模式，就有機會自然而生。所以，Flowchain 之所以要有去中心化架構的原因，不在於新技術，而是希望創造這樣的附加商業價值。

## 後續

單純從技術來看，如何才能打造一個 Decentralized 的 IoT Architecture 呢？目前，最普遍的看法是，使用 Peer-to-Peer 的技術來實作 IoT Network。[Flowchain](https://flowchain.io) 計畫就是希望以 Peer-to-Peer Networking 的方式，來建立 IoT Blockchain 的系統。

> 本專欄透過「每天一分鐘」的短篇幅文章，整理「IoT Blockchain」的精華。內容主要來源為 Flowchain 計畫的研究分享，以及個人觀點匯整。

## References

[1]: IoT and Blockchain Convergence: Benefits and Challenges, http://iot.ieee.org/newsletter/january-2017/iot-and-blockchain-convergence-benefits-and-challenges.html

</script>]]></description>
         <link>https://www.jollen.org/blog/2017/04/iot-blockchain-flowchain-decentralizedhtml.html</link>
         <guid>https://www.jollen.org/blog/2017/04/iot-blockchain-flowchain-decentralizedhtml.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Fri, 28 Apr 2017 15:57:10 +0800</pubDate>
      </item>
            <item>
         <title>Blockchain Developer - 快速認識 Proof-of-Stake</title>
         <description><![CDATA[<p>除了 Proof-of-Work（PoW）外，還有其它「形成共識」的做法嗎？除了 Proof-of-Work 外，還有一種稱之為 Proof-of-Stake（PoS）的共識系統...</p>

<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2017/02/blockchain-developer-proof-of-stake.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/eoxcQmh.jpg">

# Blockchain Developer - 快速認識 Proof-of-Stake

前一篇文章提到的 Proof-of-Work 是利用「運算」的方式來取得「共識」。除了 Proof-of-Work（PoW）外，還有其它「形成共識」的做法嗎？除了 Proof-of-Work 外，還有一種稱之為 Proof-of-Stake（PoS）的共識系統。不像 PoW 是以運算做為基礎，PoS 以「權益」做為基礎，來決定挖礦的難度。

除了 PoW 與 PoS 外，還有其它不同的共識系統：

* PBFT[1] (Practical Byzantine Fault Tolerance - 拜占庭容錯算法)
* Paxos / The Part-Time Parliament [2] (希臘城邦算法)
* DPoS (Delegate Proof of Stake)

PoW 與 PoS 的共通點，就是他們都是以 hash function 為基礎，來「形成共識」；也就是 mining。

## PPCoin

Bitcoin 底層的區塊鏈，透過 mining 的方式來建立新的 block；mining 的 difficulty 透過 proof-of-work 系統來決定，接著，全世界各地的挖礦機，就要開始進行比賽，看誰能找出這個 difficulty 的 hash 值。

然而，Proof-of-Work 是一種消耗資源的工作；因此，開始有開發者，試圖使用其它的共識機制來建立新的加密貨幣（Cryptocurrency）系統。Peercoin（或稱為 PPCoin）就是第一個以 proof-of-stake 系統，所打造的加密貨幣 [3]。

## 快速認識 Proof-of-Stake

Proof-of-stake 的誕生，是為了取代 proof-of-work 系統，以減少大量運算所造成的資源消耗。此外，PoS 系統的加密貨幣，不一定要透過 mining 的過程來產生 cryptocurrency，可以採用「mint」的方式；即「鑄造」。這有點像「鑄幣廠」。在鑄造硬幣前，就要決定好「發行量」。

PoS 系統的 cryptocurrency 大多採用 mint 機制，而不是 mining 機制；所以說，貨幣是一開始就決定好數量並發行。

在 PoS 的區塊鏈系統裡，新的區塊如何產生呢？首先，要決定由哪個節點（node）來負責創造新區塊，再由該節點來建立下一個區塊。這就像是，大家一起討論，誰是下一個「鑄幣廠」，被選上的人就負責鑄造新硬幣。所有的節點都是鑄幣廠，也都有機會獲選鑄造新硬幣；所以，這不是「中央鑄幣廠」的機制，而是去中心化的機制。

至於如何決定負責創造新區塊的節點，就是以每個節點的權益（stake）來決定。這就有別於 Bitcoin 的 mining 機制了。Bitcoin mining 機制，是所有人在比賽創造新區塊，並且靠的是運算能力；誰創造了新的區塊？是無法預期的，所以是誰創造了下一個區塊，是非常隨機的（random）。

然而，PoS 機制下，創造了下一個區塊的人，是可預期的（deterministic）。PoW 系統的區塊產生是 random 方式；PoS 系統的區塊產生則是 deterministic 方式（或稱為 pseudo-random ）。

## 小結

做為 blockchain 的底層系統開發者，有許多議題是重要的研究功課，例如：物聯網區塊鏈，適合使用 PoW 或 PoS 系統來設計？

## References

[1] Practical Byzantine Fault Tolerance, http://pmg.csail.mit.edu/papers/osdi99.pdf

[2] The Part-Time Parliament, https://www.microsoft.com/en-us/research/wp-content/uploads/2016/12/The-Part-Time-Parliament.pdf

[3] PPCoin: Peer-to-Peer Crypto-Currency with Proof-of-Stake, https://peercoin.net/assets/paper/peercoin-paper.pdf

</script>]]></description>
         <link>https://www.jollen.org/blog/2017/02/blockchain-developer-proof-of-stake.html</link>
         <guid>https://www.jollen.org/blog/2017/02/blockchain-developer-proof-of-stake.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Thu, 16 Feb 2017 23:29:37 +0800</pubDate>
      </item>
            <item>
         <title>Blockchain Developer - 簡單易懂的 Memory-Hard Function</title>
         <description><![CDATA[<p>Bitcoin mining 演算法，就是使用傳統的 SHA-256 函數，而 SHA-256 的優點，也好就是它的一個缺點...</p>

<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/12/blockchain-developer-memory-hard.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/eoxcQmh.jpg">

# Blockchain Developer - 簡單易懂的 Memory-Hard Function

SHA-256 函數是傳統的 hash 演算法，但是應用在區塊鏈系統時，有一個缺點。Bitcoin mining 演算法，就是使用傳統的 SHA-256 函數，而 SHA-256 的優點，也好就是它的一個缺點。

## SHA-256 的問題

為了提升 SHA-256 的計算速度，工程師會利用行平行處理（parallelism）的技術。利用平行運算，大幅提升 SHA-256 的運算速度，這樣做不是很好嗎？

然而，這就是一個問題了。簡單來說，一個能平行化的演算法，就能使用硬體來做加速，例如：使用 GPU、FPGA 或是 ASIC。這裡就是「弊端」所在了。從 Proof-of-Work 的觀點來看，「大家必須公平地做計算」，意思是說，因為 hash 值的運算是「decentralization」的架構，所以「大家的硬體最好一樣」。

## Decentralization of Trust

Proof-of-work 的主要工作是 mining。此外，驗證交易的「可信度」，也是 proof-of-work 的一環。Proof-of-work 的工作不此於此，例如：miner 間的資料庫同步，也是包含在內。總之，proof-of-work 很忙。

這些 proof-of-work 的工作，是 miners 一起進行的，而不是由一個中央伺服器（centralized）來統一運算，這就是 decentralization of trust 的觀念。

更簡單來看，這就是所謂的 decentralization（去中心化）架構。以 mining 來說，每個人都可以參與 hash 值的運算（大家都可以挖礦），所以，想「快一點」的人，就會使用 ASIC 挖礦機。可是，有些人是用自已的電腦來挖礦。

到這裡，問題就很清楚了：大家的硬體如果等級不同，挖礦就會「不公平」。當大家的運算速度都差不多，沒有人可以「大幅加速」時，理論上就公平了。

## Memory-Hard Function

為了解決這個問題，科學家就想出了一個方法。這個方法非常簡單，首先，你不可能強制每個人都要買一樣的電腦才能挖礦，所以解決方式就要回歸演算法的本質：parallelism。

於是，科學家提出一種稱為 memory-hard function 的 hash 演算法觀念：一種不能或難以平行化的 hash 演算法。

這讓我想過幾年前的一個有趣故事。過去，多核心處理器開始後，開始有 Android 手機的製造廠，以「多核心手機」做為市場宣傳口號。這當然很好啊，「多核就是快」。但是，學軟體的人都知道一個道理，就是「軟體必須支援多核心」。如果你的軟體設計，本身就不是多核心架構，那就會像這支手機一樣：明明是 4 核心，但是開機後，其實只用了 1 個核心，另外 3 個核心被關閉（省電考量）了。

有了 memory-hard function 後，「挖礦」就理論上公平了，並且也能消除弊端。因為，就算有頂極的挖礦機，也會像這支多核心手機一樣：再強的硬體也很難加速軟體運算。

## Memory Intensive

Memory-hard function 是怎麼做到這點的呢？上述的「一種不能或難以平行化的 hash 演算法」，其實是利用這個原理：降低平行處理的優勢。讓平行處理難有發揮的空間，這樣就能降低 GFP、FPGA 或 ASIC 挖礦機的優勢了。

要消除平行處理的優勢，只要讓軟體是 memory intensive 即可。Memory intensive 是每天都會看到的現象：記憶體不足時、電腦變慢。

Memory-hard function 的原理就是這樣，在 hash 運算時，可以透過「亂塞一堆資料到記憶體」的方式，降低硬體的運算優勢。這樣的 hash 演算法，就稱為 memory-hard function。這堆要塞到記憶體的資料，稱為 data array（array of data）。

## Argon2

[Argon2](https://github.com/p-h-c/phc-winner-argon2) 是屬於 memory-hard function 的一種演算法，Blockchain 開發者會知道它，是因為 Argon2 在 2015 年，從 24 個參賽者中，拿下 [PHC](https://password-hashing.net/) 競賽的優勝。

## 小結

Argon2 可以取代傳統的 SHA-256 函數。如果想要知道原因的話，一般的說法就是：Argon2 沒辦法用 ASIC 挖礦機進行挖礦。

接下來，可以試著 fork 一份 [block0](https://block0.org) 專案，導入 Argon2 演算法，並試著比較 Argon2 與 SHA-256 的挖礦困難度。


## 其它

* [Node.js Fullstack《從零到一的進擊》：初學者寫給初學者的全端軟體教材](https://github.com/jollen/nodejs-fullstack-book)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/12/blockchain-developer-memory-hard.html</link>
         <guid>https://www.jollen.org/blog/2016/12/blockchain-developer-memory-hard.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Fri, 09 Dec 2016 22:50:27 +0800</pubDate>
      </item>
            <item>
         <title>Blockchain Developer - 簡單易懂的 Mining 演算法設計</title>
         <description><![CDATA[<p>假設表 1 是「最後一個 Block」內容，根據先前教學的介紹，要如何挖出新區塊呢...</p>

<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/12/blockchain-developer-how-mining.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/eoxcQmh.jpg">

# 簡單易懂的 Mining 演算法設計

## Mining 演算法初體驗

表 1 是截至目前為止，範例所設計的 Block 資料結構。假設表 1 是「最後一個 Block」內容，根據先前教學的介紹，要如何挖出新區塊呢？

|欄位       |範例      |用途說明 |
|--------|--------|--------|
|hash     |dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6      |Block Hash |
|previousHash       |0000000000000000000000000000000000000000000000000000000000000000 	   |前一個 Block 的 Hash 值 |
|timestamp     |Tue Dec 06 2016 15:14:58 GMT+0800 (CST)       |區塊建立的時間 |
|merkleRoot     |851AE7D7390A76384ACA2D7CC29BE820918CA900071FC22F41F5C399BE065558    |區塊的 Merkle Root |
|difficulty     |00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF    |挖礦的困難度 |

表 1 最後一個 Block 內容

表 1 的內容，將做為「挖礦」的依據：透過最後一個 Block 的資訊，計算出新區塊的 Hash 值。

一個簡單的挖礦演算法實作步驟如下。

### Step 1：建立新的 Merkle Tree

假設現在有一筆交易資訊，正等著被紀錄在區塊裡，這筆交易的狀態目前就是「待確認」。挖礦機就要先取得這筆「待確認」的交易資訊，再建立這筆交易的 Merkle tree。

多筆待確認交易的做法也相同：挖礦機先取得這些待確認的交易資訊，並建立它們的 Merkle tree。

以 Bitcoin 的網路來說，Bitcoin network 裡一個稱為「unverified pool」的地方，就是存放這些「待確認」的交易。因此，unverified pool 的設計與實作，是區塊鏈開發者的另一個課程，本教學暫不涉及 unverified pool 的介紹。

延續先前的教學，為一筆交易建立 Merkle tree 的程式碼實作如下：

```
// 一筆待確認的交易
var tx = [‘Created by Jollen’];

// Merkle root hash
var hashMerkleRoot;

merkleRoot.async(tx, function(err, tree){
    // 取得 Merkle Root 的 Hash
    hashMerkleRoot = tree.level(0)[0];
});
```

### Step 2：定義本文

這裡所講的「本文」，就是用來進行 SHA-256 計算的資料內容。一個簡單的本文定義，需要 3 個項資訊：

* *merkleRoot*：由前一個步驟產生
* *previousHash*：最後一個區塊的 block hash，未來產生的新區塊，要往前「鏈接」到這個區塊
* *nonce*：number once 的簡寫，在加密學裡，nonce 指的是只能使用一次的任意數

為簡化演算法的設計，可以將 *nonce* 定義為一個「流水號」。因為 *nonce* 只能使用一次，所以流水號只能「持續遞增」，不能歸零重算。

本文所需的資訊都收集齊全了，接著以 JavaScript 的物件語法，來定義本文如下：

```
var nonce = 0;

var header = {
	nonce: nonce,
	previousHash: ‘dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6’,
	merkleRoot: hashMerkleRoot
};
```

本文的定義由區塊鏈開發者自行決定，例如：把 timestamp 也加入到本文裡。

### Step 3：Double SHA-256 運算

將 ```header``` 物件 stringify（轉換為文件）後，使用這個「文件」做為本文，來進行 SHA-256 雜湊運算：

```
// Secret
var secret = ‘Dummy Blockchain’;

var hash1 = crypto.createHmac(‘sha256’, secret)
					.update( JSON.stringify(header) )
					.digest(‘hex’);
```

再將得到的 hash 值，做為新的 secret，進行第 2 次運算：

```
var hash2 = crypto.createHmac(‘sha256’, hash1)
					.update(‘powered by flowchain’)
					.digest(‘hex’);
```

現在，```hash2``` 存放的就是 Block Hash 的「候選人」。如果 ```hash2``` 的值，確認為「success」的話，表示「挖礦成功」了：一個新的區塊被計算出來了。

### Step 4：Difficulty 運算

候選人的意思是：它還不一定是成功的 hash 值。必須比對 difficulty 的條件設定，才能決定這個 hash 值是否能使用。

延續先前教學的介紹，假設困難度是「有足夠的零」時，就要進行困難度的確認：

```
if (hash2 < ‘00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF’) {
	console.log(‘success: ‘ + id);
}
```

當 ```hash2``` 不滿足目前的 difficulty 條件時，就要重新計算，直到成功為止。

以上述的範例來說，當 hash 值不滿足 difficulty 條件時，就變更 nonce 值後，再重新運算。本文範例，使用流水號的方式來產生 nonce 值。

### Step 5：完整範例

根據前個的步驟，實作一段簡單的 mining 演算法如下：

```
var crypto = require(‘crypto’);
var merkle = require(‘merkle’);
var merkleRoot = merkle(‘sha256’);

// Secret
var secret = ‘Dummy Blockchain’;

// Unverified pool
var tx = [‘Created by Jollen’];

merkleRoot.async(tx, function(err, tree){
    // Merkle Root 的 Hash
    var hashMerkleRoot = tree.level(0)[0];
    var nonce = 0;

    var hash = function(nonce) {
	    var header = {
			nonce: nonce,
			previousHash: ‘dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6’,
			merkleRoot: hashMerkleRoot
	    };

		var hash1 = crypto.createHmac(‘sha256’, secret)
							.update( JSON.stringify(header) )
							.digest(‘hex’);

		var hash2 = crypto.createHmac(‘sha256’, hash1)
                   			.update(‘powered by flowchain’)
							.digest(‘hex’);

		return hash2;
    };

    while (1) {
    	var id = hash(nonce++);
    	console.log(nonce + ‘: ‘ + id);
		if (id < ‘0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF’) {
			console.log(‘success: ‘ + id);
			break;
		}
    }
});
```

輸出結果：

```
…
7590: 9208c185a5d218dcd1a9ce63b4609a21c9ac90e0cad65d3355ce436522ded234
7591: 766ccefa06fd97cf8b1472809e03499321fde6ba1e7341e74bd7bbcdc0a7ce01
7592: f3cb6f4f6ae187556a3ec8218453d3073958eed430155cd73d9a8d2976d30e1f
7593: 74ff8bf0695100c6cce400fde5fcbfbb0574efb79664c229a8044df0525c39ca
7594: 0002db2b239b29f52711a2629e98face0151c2020f48c94a12459a43b24a3f85
success: 0002db2b239b29f52711a2629e98face0151c2020f48c94a12459a43b24a3f85
```

由這個結果發現，總計 mining 了 7594 次才得到成功的 hash 值。當 difficulty 提升時，mining 所花的時間也會更多。

例如，當困難度為「前面至少 4 個零」時，mining 的次數就增加到 118432  次。挖礦的困難度在於，產生的 hash 值有一定程度的「隨機」性，通常是不太可預期的。

### Step 6：難度調整

難度調整是 mining 的重要技術。本文暫不涉及這個部份，現階段，可以採用「前面有足夠的零」做為難度設定條件，並使用上述的範例進行練習。

調整後的 difficulty，以及 *nonce* 值，都必須儲存在新產生的區塊裡，以做為後續「挖礦」的依據。

## 更多 Mining 觀念

本節所實作的 mining 演算法，僅只是用來測試的粗淺程式（dirty code）。但透過這 30 行的程式碼，還能很快了解「如何開始設計 mining 的演算法」。

還有更多 mining 的觀念，正等待區塊鏈開發者學習：

1. 前面有足夠的零：這意味著 difficutly 會到一個極限，也就是當前面的零夠多時，表示這個數字可能是最小了，再也無法算出更小的數值了，這表示區塊的數量是有限的，總有一天會挖完所有的礦
2. 挖礦機：執行這段挖礦演算法的電腦（正式說法為節點：node），稱為挖礦機
3. Proof-of-Work：這是來自 Bitcoin 的觀念，大略的意思就是，「大家都同意你真的挖到礦了」，此外還有很多工作要做，像是挖礦機如何彼此間更新並同步資料庫等

Proof-of-work 是一個複雜的系統，除了上述提及的功能外，它還涉及 Peer-to-Peer 的網路技術，這個部份，是區塊鏈開發者的真正挑戰「之一」。

## 小結

下一個階段是加入資料庫功能，並且將目前為止的區塊鏈系統實作成伺服器。

## 其它

* [Node.js Fullstack《從零到一的進擊》：初學者寫給初學者的全端軟體教材](https://github.com/jollen/nodejs-fullstack-book)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/12/blockchain-developer-how-mining.html</link>
         <guid>https://www.jollen.org/blog/2016/12/blockchain-developer-how-mining.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Tue, 06 Dec 2016 17:05:25 +0800</pubDate>
      </item>
            <item>
         <title>Blockchain Developer - 為什麼要挖礦？</title>
         <description><![CDATA[<p>交易（transaction）確認後的資訊以 Merkle tree 來做紀錄，所以就要有 Block 來儲存這個 Merkle tree...</p>

<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/12/blockchain-developer-why-mining.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/eoxcQmh.jpg">

# 為什麼要 Mining？

交易（transaction）確認後的資訊以 Merkle tree 來做紀錄，所以就要有 Block 來儲存這個 Merkle tree。這個時候就需要有新的區塊。

在 Bitcoin 的生態中，mining（挖礦）的主要目的就是「產生新的區塊」，當區塊產生時，就會產生另一個「副作用」：新 Bitcoin 被產生出來。

簡單說，產生新的 Bitcoin 並不是挖礦的主要目的，這只是挖礦的副作用。挖礦的主要目的，是生產區塊來確認並紀錄新的交易資訊。本章的目標，在學習挖礦的基本知識，內容以簡單易懂為原則，並不是介紹如何重新實作 Bitcoin 的挖礦技術。但教學內容會以 Bitcoin 做為實例，輔助說明 mining 技術。

## Difficulty

眾所皆知，Bitcoin 的挖礦難度是非常高的。這個意思是：產生新的 Block 是一件非常困難的事情。Bitcoin 將挖礦設計的非常困難，其實是有一個很重要的原因：避免有人任意產生區塊。

要產生新的區塊，就會有所謂的 difficulty（難度），這個 difficulty 的作用是什麼呢？主要目的是：決定新的 hash 值產生條件。

要產生新的區塊前，必須先計算出這個區塊的 Block Hash，區塊的 hash 值如何決定呢？這點後續再談。因為，這裡有一個更重要的問題：如何決定這個 hash 值是否可用？

例如，根據新的交易與其它資訊，運算出一個 double SHA-256 的 hash 值如下：

```
18AC3E7343F016890C510E93F935261169D9E3F565436429830FAF0934F4F8E4
```

新的區塊是否就能直接使用這個 hash 值呢？如果可以，表示新的 hash 值已成功（success）建立，如果不行，表示新的 hash 值產生失敗。系統必須不斷進行運算，「直到成功得到新的 hash 值」。

不如用一個簡單的方法，來「定義什麼是 success」：當產生的 hash 值前面「有足夠的零」時，就是 success。例如，「前面至少要有 2 個零」，上述的 hash 值就是失敗的運算。以下的這個 hash 值，則是 success：

```
0018AC3E7343F016890C510E93F935261169D9E3F565436429830FAF0934F4F8
```

這個條件就是 difficulty 會儲存在最後一個區塊上，所以修改 22.1 節的範例，加入 difficulty 欄位，並且在 Genesis Block 裡，設定最原始的 difficulty 為「前面至少有 2 個零」。

```
function Block(block) {
	this.hash = block.hash || '';
	this.previousHash = block.previousHash || '';
	this.timestamp = block.timestamp || new Date();
	this.merkleRoot = block.merkleRoot || {};
	this.difficulty = block.difficulty || '00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
}
```

以 JavaScript 來實作時，只要以字串比對方式，就可以知道 hash 值是否為 success 了。例如：

```
if ('CD18AC3E7343F016890C510E93F935261169D9E3F565436429830FAF0934F4' <'00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF') {
	// success
} else {
	// failed
}
```

## 越來越難

新的區塊產生後，會「重新調整」這個 difficulty。例如，將 difficulty 調整為「前面至少有 3 個零」，這時，可以將新區塊的 difficulty 欄位設定為：

```
000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
```

Difficulty 的條件設定，由每一個區塊鏈系統的設計者所制定。但是有一個基本原則就是：難度必須越來越高，以上述例子來說，要產生「前面 3 個零」的 hash 值，其難度大於「前面 2 個零」的 hash 值。

## 小結

認識為什麼要 mining 以及什麼是 difficulty 後，就可以開始設計 mining 的演算法了。


## 其它

* [Node.js Fullstack《從零到一的進擊》：初學者寫給初學者的全端軟體教材](https://github.com/jollen/nodejs-fullstack-book)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/12/blockchain-developer-why-mining.html</link>
         <guid>https://www.jollen.org/blog/2016/12/blockchain-developer-why-mining.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Mon, 05 Dec 2016 11:32:56 +0800</pubDate>
      </item>
            <item>
         <title>Blockchain Developer - 建立 Merkle Tree</title>
         <description><![CDATA[<p>Merkle tree 用來存放交易資訊（transactions），為了要討論更詳細的 Merkle tree 生成過程，假設現在有 2 筆交易正在等候「處理」...</p>

<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/12/blockchain-developer-merkle-tree.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/eoxcQmh.jpg">

# Blockchain Developer - 建立 Merkle Tree

## Merkle Tree 的生成過程

Merkle tree 用來存放交易資訊（transactions），為了要討論更詳細的 Merkle tree 生成過程，假設現在有 2 筆交易正在等候「處理」。這 2 筆交易資訊，分別以 ```Tx0``` 與 ```Tx1``` 來表示。

![圖 1 生成 Merkle tree](https://raw.githubusercontent.com/jollen/nodejs-fullstack-book/master/images/figure-22_5.jpg)

圖 1 生成 Merkle tree

Merkle tree 節點存放的是 double SHA-256 運算結果。如何將這 2 筆交易資訊，以 Merkle tree 來表示呢？以下是這一顆 Merkle tree 的生成過程。

將 ```Tx0``` 的本文（content）以 double SHA-256 進行雜湊運算，並將結果儲存在 ```HA```，表示方法如下：

```
HA = SHA256( SHA256(Tx0) )
```

同理，再將 ```Tx1``` 進行 double SHA-256 運算，結果儲存於 ```HB```：

```
HB = SAH256( SHA256(Tx1) )
```

SHA-256 的運算結果，是一個 64 bytes 的 HEX（十六進位）字串。在得到 ```HA``` 與 ```HB``` 後，就將這二個字串連接（concat）在一起，成為一個 64*2=128 bytes 的字串，這裡以 ```HA + HB``` 來表示。

再將 ```HA + HB``` 進行 double SHA-256 運算，結果儲存於 ```HAB```：


```
HAB = SAH256( SHA256( HA + HB ) )
```

得到結果 ```HAB``` 就是 ```HA``` 與 ```HB``` 的父節點。這是一個 3 個節點的 binary Merkle tree。

## 更多交易

如果現在有 ```Tx0```、```Tx1```、```Tx2``` 與 ```Tx3``` 共 4 筆交易呢？完整的 double SHA-256 運算過程就是：

```
HA = SHA256( SHA256(Tx0) )
HB = SAH256( SHA256(Tx1) )
HC = SAH256( SHA256(Tx2) )
HD = SAH256( SHA256(Tx3) )

HAB = SAH256( SHA256( HA + HB ) )
HCD = SAH256( SHA256( HC + HD ) )

HABCD = SAH256( SHA256( HAB + HCD ) )
```

最後得到的 binary Merkle tree 就是圖 2。

## 使用 Node.js 打造 Merkle Tree

Node.js 開發者不一定要自行實作 Merkle tree 演算法，網路上能找到開放源碼的實作。在 GitHub 上可以找到 [Merkle](https://github.com/c-geek/merkle) 模組，這是 JavaScript 的 Merkle tree 實作，並且支援 SHA-256 在內的多種 hash algorithm。

### Step 1：安裝 Merkle 模組

先安裝 Merkle 模組：

```
$ npm install merkle --save
```

接著引入 ```merkle``` 模組：

```
var merkle = require('merkle');
```

建立 root node，並指定使用 SHA-256 演算法：

```
var merkleRoot = merkle('sha256');
```

### Step 2：準備交易資訊

宣告幾筆交易資訊，例如：

```
// 建立一筆新的交易紀錄
var tx = ['Created by Jollen'];
```

交易的內容，現階段可任意填寫。例如，如果有 4 筆交易資訊：

```
// 建立 4 筆新的交易紀錄
var tx = ['a', 'b', 'c', 'd'];
```

現在只是練習 Merkle tree 的生成，暫時還沒有定義交易的資料結構，所以填寫任意內容即可。

### Step 3：建立完整 Merkle Tree

呼叫 ```async``` 函數，傳入所有交易資訊來建立 Merkle tree：

```
merkleRoot.async(tx, function(err, tree){
});
```

透過 Callback 函數來取得 Merkle tree。根據 Merkle 官方文件的說明，可以呼叫 ```tree``` 物件的 ```root``` 函數，來取得 Merkle root 的 Hash 值。以下是完整的範例列表：

```
var merkle = require('merkle');
var merkleRoot = merkle('sha256');

// 建立一筆新的交易紀錄
var tx = ['a', 'b', 'c', 'd'];

merkleRoot.async(tx, function(err, tree){
    console.log( tree.root() );
});
```

結出結果：

```
AB4587D9F4AD6990E0BF4A1C5A836C78CCE881C2B7C4287C0A7DA15B47B8CF1F
```

## 更多 Merkle Tree 資訊

如圖 2 所示，Merkle tree 是 binary tree（二元樹），以 4 筆交易量來看，總計會有 6 個節點（nodes），並且「高度」為 3。這個高度稱為 level。

![圖 2 Merkle Tree 的 depth 為 2](https://raw.githubusercontent.com/jollen/nodejs-fullstack-book/master/images/figure-22_6.jpg)

圖 2 Merkle Tree 的 depth 為 2

這是一個 levels 為 3 的 Merkle tree，排除 leaf nodes 後的高度稱為 depth。所以：

* ```HA``` 與 ```HB``` 稱為 leaf nodes
* 這個 Merkle tree 的 depth  為 2
* 這個 Merkle tree 的 level 為 3

延續上述範例，取得該 Merkle tree 的 depth 與 levels：

```
merkleRoot.async(tx, function(err, tree){
    console.log( tree.root() );
    console.log( tree.depth() );
    console.log( tree.levels() );    
});

```

結出結果：

```
AB4587D9F4AD6990E0BF4A1C5A836C78CCE881C2B7C4287C0A7DA15B47B8CF1F
2
3
```

此外，呼叫 ```level``` 函數，可以取得指定 level 的所有節點，例如：


```
merkleRoot.async(tx, function(err, tree){
    console.log( tree.level(1) );
});
```

輸出結果：

```
[ '6A20F2EE7789E6BB7F404CC2DD729FF308B724D904F6A455B74D4851ADE5AECB',
  'A99E82F486656840A790C0EF6024D2C02359DE7674A587562FEB81C8970F24DD' ]
```

如圖 2 所示：

* level 0 是根節點（root）
* level 1 有 2 個節點

如果要顯示所有的節點，要怎麼修改程式碼呢？答案如下：

```
merkleRoot.async(tx, function(err, tree){
    // 印出所有節點
    for (i = 0; i < tree.levels(); i++) {
        console.log( tree.level(i) );
    }
});
```

## 視覺化 Merkle Tree

實際撰寫程式，來觀察 4 筆交易資訊的 Merkle tree：

```
var merkle = require('merkle');
var merkleRoot = merkle('sha256');

// 4 筆交易資訊
var tx = ['a', 'b', 'c', 'd'];

merkleRoot.async(tx, function(err, tree){
    // 印出所有節點
    for (i = 0; i < tree.levels(); i++) {
        console.log( tree.level(i) );
    }
});
```

輸出結果：

```
[ 'AB4587D9F4AD6990E0BF4A1C5A836C78CCE881C2B7C4287C0A7DA15B47B8CF1F' ]
[ '6A20F2EE7789E6BB7F404CC2DD729FF308B724D904F6A455B74D4851ADE5AECB',
  'A99E82F486656840A790C0EF6024D2C02359DE7674A587562FEB81C8970F24DD' ]
[ 'CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB',
  '3E23E8160039594A33894F6564E1B1348BBD7A0088D42C4ACB73EEAED59C009D',
  '2E7D2C03A9507AE265ECF5B5356885A53393A2029D241394997265A1A25AEFC6',
  '18AC3E7343F016890C510E93F935261169D9E3F565436429830FAF0934F4F8E4' ]
```

為了幫助學習，圖 3 以視覺化的方式，來呈現這個範例的結果。

![圖 3 視覺化 Merkle Tree](https://raw.githubusercontent.com/jollen/nodejs-fullstack-book/master/images/figure-22_7.png)

圖 3 視覺化 Merkle Tree

## 小結

現在，你學會了如何使用 Node.js 來生成 Merkle tree，並且也更進一步了解 Merkle tree 的結構。


## 其它

* [Node.js Fullstack《從零到一的進擊》：初學者寫給初學者的全端軟體教材](https://github.com/jollen/nodejs-fullstack-book)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/12/blockchain-developer-merkle-tree.html</link>
         <guid>https://www.jollen.org/blog/2016/12/blockchain-developer-merkle-tree.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Sun, 04 Dec 2016 15:42:31 +0800</pubDate>
      </item>
            <item>
         <title>Blockchain Developer - 開始建立 Genesis Block</title>
         <description><![CDATA[<p>使用 Node.js 發展區塊鏈的下一個動作，就是建立 Genesis Block...</p>

<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/12/blockchain-developer-genesis-block-2.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/eoxcQmh.jpg">

# Blockchain Developer - 開始建立 Genesis Block

使用 Node.js 發展區塊鏈的下一個動作，就是建立 Genesis Block。

## Step 1：定義區塊資料結構

根據 [[Blockchain Developer - 認識 Genesis Block](http://www.jollen.org/blog/2016/12/blockchain-developer-genesis-block.html)] 的說明，區塊的資料結構包含 4 個欄位如下：

* *hash*：區塊的 hash ID
* *previousHash*：紀錄前一個區塊的 hash ID
* *timestamp*：區塊建立的時間
* *merkleRoot*：區塊的 merkle tree

以 Node.js 來實作此資料結構，方式是以 *function* 關鍵字來定義 *Block* 類別（Class）：

```
function Block(block) {
	this.hash = block.hash || '';
	this.previousHash = block.previousHash || '';
	this.timestamp = block.timestamp || new Date();
	this.merkleRoot = block.merkleRoot || {};
}
```

## Step 2：生成 Hash ID

每個 Block 都有一個獨一無二（uniquely）的編號，這個編號是使用 SHA256 演算法產生，稱之為 Block Hash（即 Block Hash ID）。

Genesis block 的 hash ID 要如何生成呢？原則上是使用 SHA256 演算法來產生，當然開發者也能自行定義 Block Hash 的生成方式。在這篇教學裡，筆者打算根據 Merkle tree 的演算法來生成 Hash ID。

Merkle tree 同樣是使用 SHA256 演算法來產生 Hash ID，標準的 Merkle tree 會使用二次的 SHA256 來計算出 hash ID，這樣的做法也稱為 double SHA256。本文的 Block Hash 就以 double SHA256 來產生。

Node.js 內建的 ```crypto``` 模組，就提供了 SHA256 演算法函數。先引入 ```crypto``` 模組：

```
var crypto = require('crypto');
```

使用 ```createHmac``` 函數，計算出第 1 個 hash 值，用法如下：

* 第 1 個參數，填寫 ```sha256``` 
* 第 2 個參數，填寫 secret：任意一段句子即可

執行後，```createHmac``` 會建立 ```Hmac``` 的實例化（instance），再呼叫 ```Hmac``` 物件的 ```update``` 函數，並傳入一段本文來進行 sha256 編碼運算。完成後，呼叫 ```digest``` 將結果轉為 *hex* 格式。

完整範例：

```
var secret = 'blockchain developer';

var hash1 = crypto.createHmac('sha256', secret)
                   .update('created by jollen')
                   .digest('hex');
```

得到第 1 個的 hash 值。接著，使用這個 hash 值做為新的 secret，進行第 2 次的 hash 運算：

```
var hash2 = crypto.createHmac('sha256', hash1)
                   .update('powered by flowchain')
                   .digest('hex');

console.log(hash2);
```

輸出結果：

```
dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6
```

這就是 genesis block 的 hash ID 了。

## Step 3：定義 Genesis Block 

建立 genesis block 最簡單的方式，就是直接「定義」它。建立一個名為 *config.js* 的檔案，並且直接定義好 genesis block 的欄位資訊：

```
// Filename: config.js
'use strict';                                                                                              

exports.genesis = {
    hash: 'dd0e2b79d79be0dfca96b4ad9ac85600097506f06f52bb74f769e02fcc66dec6',

    prevHash: '0000000000000000000000000000000000000000000000000000000000000000',

    timestamp: new Date(),
    
    merkleRoot: {}
};
```

## Step 4：建立 Genesis Block

終於來到歷史性的一刻了。先引入事先準備好的 genesis block 定義：

```
var config = require('../config.js');
```

接著，再實例化 ```Block```，得到的物件，就是 Genesis Block 了。

```
// Filename: index.js
var genesis = new Block(config.genesis);
```

後續可以將 ```genesis``` 物件，儲存在 NoSQL 資料庫裡。

## 小結

創建出 Genesis Block 後，下一個步驟就是幫它加入一個空的 merkle tree。

## 其它

* [Node.js Fullstack《從零到一的進擊》：初學者寫給初學者的全端軟體教材](https://github.com/jollen/nodejs-fullstack-book)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/12/blockchain-developer-genesis-block-2.html</link>
         <guid>https://www.jollen.org/blog/2016/12/blockchain-developer-genesis-block-2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Sat, 03 Dec 2016 16:02:27 +0800</pubDate>
      </item>
            <item>
         <title>Blockchain Developer - 認識 Genesis Block</title>
         <description><![CDATA[<p>Merkle tree 是一種 hash tree，用來表示 hash 值的資料結構。Merkle tree 的發明人是 Ralph Merkle，當然這就是這個資料結構的名稱由來...</p>

<p>本文章採用 Markdown 語法撰寫，若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/12/blockchain-developer-genesis-block.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="http://i.imgur.com/eoxcQmh.jpg">


# Genesis Block

Merkle tree 是一種 hash tree，用來表示 hash 值的資料結構。Merkle tree 的發明人是 [Ralph Merkle](https://en.wikipedia.org/wiki/Ralph_Merkle)，當然這就是這個資料結構的名稱由來。

Merkle tree 的基本結構是 binary tree（二元樹），每一個 non-leaf 的節點（node），都被標示一個 hash 值。

圖 1：就是一個 binary Merkle Tree 的結構。其中，*Top hash* 的部份，就是 *Merkle Root*。

![圖 1 Merkle Tree（圖片來源：https://commons.wikimedia.org/wiki/File%3AHash_tree.png，By Davidgothberg at English Wikipedia，遵循 Public Domain 授權）](https://upload.wikimedia.org/wikipedia/commons/6/6d/Hash_tree.png)

圖 1：Merkle Tree（圖片來源：https://commons.wikimedia.org/wiki/File%3AHash_tree.png，By Davidgothberg at English Wikipedia，遵循 Public Domain 授權

學習 Merkle tree 資料結構，可以說是「Blockchain 系統開發者」的第 1 堂課。為什麼這麼說呢？

以圖 2 來看，分散（Distributed）在世界各地的所有 Block 之間，以一個鏈（Chain）的關係串連在一起，這就是 Blockchain（區塊鏈）的概念與名稱由來。

![圖 2 Block 與 Chain](https://raw.githubusercontent.com/jollen/nodejs-fullstack-book/master/images/figure-22_2.jpg)

圖 2：Block 與 Chain

## Block #0

這些分散在世界各地的 Block，都會有一個編號，如圖 3。這個編號就是區塊產生的「順序」。

![圖 3 Block #0](https://raw.githubusercontent.com/jollen/nodejs-fullstack-book/master/images/figure-22_3.jpg)

圖 3：Block #0

這其中，就一定會有編號為 0 的第一個區塊，這個區塊就稱之為 Genesis Block（創世區塊），學習如何建立 Genesis Block 就是 Blockchain 系統開發者的第 2 堂課。

而區塊的產生「方式」，則可以由 Blockchain 的系統開發者來設計。以 Nakamoto Blockchain 來說（Bitcoin 的 Blockchain 系統），區塊的產生過程，就稱為「挖礦」。

## Blockchain 與 Merkle Tree

那 Merkle Tree 與 Blockchain 的關係倒底是什麼呢？將 Blockchain、Genesis block 與 Merkle tree 放在一起討論時，它們的關係就是圖 4。

![圖 4 Blockchain、Genesis block 與 Merkle tree](https://raw.githubusercontent.com/jollen/nodejs-fullstack-book/master/images/figure-22_4.jpg)

圖 4：Blockchain、Genesis block 與 Merkle tree

如果我發展一個叫做 Jollen's Blockchain 系統時，一個粗略的起步應該就是：

* 建立 Genesis block，genesis block 會有自已的一個 hash 值，這個值是經由 hash 演算法產生，因此也叫做 hash ID

* 利用演算法，經過一段困難的演算法運算，產生 Block #1

* Block #1 也會有自已的 hash ID，同時， Block #1 要使用 *PreviousHash* 欄位，串接到前一個 Block，它的前一個 Block 就是 Genesis block（Block #0）

* 同理，產生 Block #2 與更多 Blocks，這些 block 之間都用 *PreviousHash* 欄位串接，這條鏈就是 Block *chain*

到這邊，還是很好奇 Merkle tree 的用途啊？回顧圖 4 發現，每個 Block 裡面都會有 *Merkle Root* 欄位，這個欄位就是一顆 Merkle tree。

區塊倒底能做什麼呢？每一個區塊，都能用來「記帳」，整個 Blockchain 串接起來就是一本完整的帳冊，所以說，Blockchain 也叫做 distributed ledger（分散式帳冊）。

更深入技術來看，區塊裡的 Merkle tree 就是負責記帳的欄位。

## 其它

* [Node.js Fullstack《從零到一的進擊》：初學者寫給初學者的全端軟體教材](https://github.com/jollen/nodejs-fullstack-book)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/12/blockchain-developer-genesis-block.html</link>
         <guid>https://www.jollen.org/blog/2016/12/blockchain-developer-genesis-block.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Blockchain</category>
        
        
         <pubDate>Fri, 02 Dec 2016 12:45:31 +0800</pubDate>
      </item>
            <item>
         <title>使用 Stripe Atlas 設立美國公司的心得與歷程分享</title>
         <description><![CDATA[<p>使用 Stripe Atlas 設立美國公司的心得與歷程分享</p>

<p>本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-1.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1453227588063-bb302b62f50b?ixlib=rb-0.3.5&q=20&fm=jpg&crop=entropy&s=cb544f8f6b9c25ac9f109fdecf760500">



# 前言

自從 Stripe 發佈 Stripe Atlas 計畫後，我就對這個服務非常有興趣，也因為在美國有發展業務的機會，就利用 Stripe Atlas 試著申請美國公司。以下是整個歷程的分享，希望對想透過 Stripe Atlas 在美國設立公司的朋友有幫助。



## 第一階段：2016 年 4 月 7 日

Stripe Atlas 今天開始 Private Beta。在收到 Private Beta 邀請函後，開始進行填表申請美國公司。申請還算簡單，一開始要詳讀 Stripe Atlas 提供的二份文件：

* https://stripe.com/atlas/resources/pwc-tax-guide.pdf
* https://stripe.com/atlas/resources/orrick-legal-guide.pdf

這二份文件相當值得一看，第一份是租稅法規的說明，第二份是公司架構的建議。二份的內容都非常詳細到位，例如：公司的 shares 數量不同，產生的稅率也會不同。

Stripe Atlas 還提到，申請公司本身並不是重點，而是創業者自已要思考清楚，為什麼需要美國公司？打算拿它來做什麼？以及打算建立什麼架構的美國公司：要建立全新的美國公司、或是國際公司的分支。

要透過 Strip Atlas 申請美國公司，只要準備 500 字以上的公司產品說明，以及 500 字以上的公司介紹，還有個人護照描掃檔即可。申請過程有什麼疑問，還可以預約專人的電話（Skype）咨詢。


## 第二階段：2016 年 4 月 11 日更新

上週送出申請後，今天進入開戶階段（好驚人的效率）。線上填寫申請表時，只需要 500 字的產品跟 500 字的公司介紹；今天收到 Stripe 的通知，他們說，銀行要求，希望開戶公司，能有 live product 以及 fully functional website。所以透過電子郵件，「補件」附上這二份資訊。

因此，未來想使用 Stripe Atlas 在美國開公司的話，可能要先把產品原型跟網站做好，才能加速審核進度。不過 Stripe 在郵件裡也提到，如果沒有產品或網站，他們也是可以試著協助的。


## 第三階段：2016 年 4 月 19 日更新

Stripe Atlas 透過 DocuSign 送來一份合約書，這份是公司設立與銀行開戶的合約，內容非常「詳細」。一般個人要看懂會有難度，最好找個熟悉美國稅法的律師協助。Stripe Atlas 在郵件再次提到：

> If you have your own legal counsel or tax advisors, we recommend asking them to review these documents before signing. We also suggest reading through the guides prepared by Orrick and PwC on common legal and tax considerations for Atlas users:

建議在簽名前，務必找法律與稅法專家，檢視這份合約內容。並且再次閱讀 Stripe 建議的二份文件。


## 第四階段：2016 年 5 月 24 日更新


花了一個月的時間「再次」閱讀相關文件，並且咨詢熟悉美國稅法的律師，終於完成合約內容的檢視。今天正式簽名回傳，幾個小時後就收到回覆：

> We are writing to let you know that your entity and bank account documents have now been signed by all company representatives, and we have submitted them on your behalf. 

Stripe Altas 的整體服務流程非常完善，而且效率也相當高。


## 第五階段：2016 年 5 月 26 日更新


Stripe Altas 再次通知，已經送出我的申請文件，一星期內會有結果。然後，非常意外地，收到一份禮物：AWS Activate Portfolio Package。

AWS Activate Portfolio Package 內含 $15,000 額度的 AWS credits、一堂價值 $600 的教育訓練課程、價值 $5,000 的 AWS Business Support，以及 1 對 1 的 virtual office hours。意外獲贈一送大禮，真是太高興了！


## 第六階段：2016 年 6 月 1 日更新


終於收到郵件：「Devify, Inc. - Your SVB Bank Account is OPENED」。順利完成公司設立與銀行開戶，新公司正式開張。同時，又獲得一份小禮物：前 2 年免收帳戶保管費，這真是好消息。



## 結案：2016 年 6 月 7 日


今天終於完成最後步驟了，將銀行帳戶與 Stripe 綁定。從填表，到美國公司設立與銀行開戶，總計花費 2 個月的期間，不過，大部份的時間，都花在檢視合約，以及研究美國稅法。整體 Stripe Atlas 的服務非常流暢，強力推薦給想在美國設立公司的創業者。

最後是二個重要心得：

1. 在美國設立公司的重點，並不是「申請流程」，所以你不需要幫你跑流程的代理人，而是能告訴你「如何建立國際營運架構」的專家。因為這樣，你才能知道「自已為何要成立美國或境外公司」，以及「打算建立什麼架構的美國公司」

2. 如果一覺醒來突然有創業的衝動，這天早上最重要的事情，就是去找到一位值得信任，又非常熟悉美國稅法的律師專家；這非常重要，好的律師能幫助你與 Stripe Atlas 團隊，加速整個流程。因為，你將會花費很長的時間，在閱讀相關文件，以及檢視合約；這些工作必須由你的律師協助


結論是，如果你是一位想在美國開公司的創業者，Stripe Atlas 是強力推薦的服務。






</script>]]></description>
         <link>https://www.jollen.org/blog/2016/06/stripe-atlas.html</link>
         <guid>https://www.jollen.org/blog/2016/06/stripe-atlas.html</guid>
        
        
         <pubDate>Wed, 08 Jun 2016 13:34:14 +0800</pubDate>
      </item>
            <item>
         <title>強化串連智付寶（Pay2go）交易的安全性</title>
         <description><![CDATA[<p>這幾天因為專案需要，開始將線上支付的金流平臺，從 Paypal 轉移到 Pay2go...</p>

<p>本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-1.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1453227588063-bb302b62f50b?ixlib=rb-0.3.5&q=20&fm=jpg&crop=entropy&s=cb544f8f6b9c25ac9f109fdecf760500">

## 前言

這幾天因為專案需要，開始將線上支付的金流平臺，從 Paypal 轉移到 Pay2go。要串接 Pay2go 金流平台是一個很簡單的工作，API 文件也很完整。在實作前端與後端的過程中，發現有一些應該要注意的事項，紀錄如下。

## 關於 MPG 參數

由於 Pay2go 是以 Form Post（HTML5 的 ```<form></form>```）的方式提交付款請求，所以必須使用 ```<input>``` 來紀錄交易參數，如圖一。

![2016-03-31 11 57 06](https://cloud.githubusercontent.com/assets/1126021/14165431/810d558e-f73b-11e5-8f03-3bb49591baba.png)

圖一：MPG API

一個稱為 ```CheckValue``` 的參數，編碼規則如下：

```js
var data = 'HashKey=' + hashKey +
          '&Amt=' + amt +
          '&MerchantID=' + merchantID +
          '&MerchantOrderNo=' + merchantOrderNo +
          '&TimeStamp=' + timeStamp +
          '&Version=1.2' +
          '&HashIV=' + hashIV;
// 使用 SHA256 壓碼後轉大寫
var checkValue = sha256(data).toLocaleUpperCase();
```

將幾個主要的交易參數、 ```hashKey``` 與 ```hashIV``` 連接成 Query String 形式後，再用 SHA256 編碼。```CheckCode``` 看起來是為了檢查交易參數的正確性。不過：

* 將 ```CheckCode``` 放在 Form 的欄位裡，有安全上的疑慮
* 最好能改用 REST API 的做法，由 Backend 直接向 Pay2go 的 API 送出一個自定的 HTTP headers，來傳送 ```CheckCode```，但 Pay2go 目前不支援這樣的做法

另外，有些前端網頁，直接將 ```hashKey``` 與 ```hashIV``` 也紀錄在 Form 裡面，也是非常危險的做法。如圖二（為避免不必要的問題，僅做截圖，恕不註明出處）。

![2016-03-31 12 04 17](https://cloud.githubusercontent.com/assets/1126021/14165535/95ff93a2-f73c-11e5-9a16-1c86046a814d.png)

圖二：曝露在外的 ```hashKey``` 與 ```hashIV```

類似的安全議題，可以在研發或工程階段，就進行嚴格檢視。不過，把交易機制、平台架構與 SDK 做到非常嚴謹，應該是金流平臺的基本責任。

一個安全且嚴謹的交易平臺，也要針對各種「使用不當」的案例，而造成的安全疑慮，進行完整測試。

## 關於 NotifyURL

NotifyURL 是 Pay2go 用來通知店家後台交易結果的 URL。Pay2go 在訂單完成交易後，會以 HTTP POST 方法，呼叫 NotifyURL。店家的後台則是要實作這個 API，並進行自已的訂單處理流程。

以下是一個 Pay2go 模擬交易，所回送的 POST 資料：

```
{ JSONData: '{"Status":"SUCCESS","Message":"\\u4ed8\\u6b3e\\u6210\\u529f","Result":"{\\"MerchantID\\":\\"31745140\\",\\"Amt\\":992,\\"TradeNo\\":\\"16033018180593725\\",\\"MerchantOrderNo\\":\\"14593335802368129\\",\\"RespondType\\":\\"JSON\\",\\"CheckCode\\":\\"4E2B2FB0A54CCFECE9616742FE9025EFBBE8FABBC6562268C11D39DFD6E03E9C\\",\\"IP\\":\\"36.231.213.27\\",\\"EscrowBank\\":\\"KGI\\",\\"PaymentType\\":\\"WEBATM\\",\\"PayTime\\":\\"2016-03-30 18:18:05\\",\\"PayerAccount5Code\\":\\"-\\",\\"PayBankCode\\":\\"-\\"}"}' }
```

這份資料會以 JSON 格式，經由 POST 方法，由 Pay2go 後台傳送到店家的 NotifyURL。將這筆資料的 ```JSONData``` 轉換為 JSON 物件：

```js
var jsonData = JSON.parse(postData.JSONData);  // postData 為上述回傳資料
```

詳細的交易狀態，則是紀錄在 ```jsonData``` 的 ```Result``` 裡，再將這個欄位轉換為 JSON 物件：

```js
var result = JSON.parse(jsonData.Result);
```

終於得到以下內容（疑問：為什麼不直接回傳一個 Stringify 過的 JSON 物件就好？）：

```
{ MerchantID: '31745140',
  Amt: 992,
  TradeNo: '16033018180593725',
  MerchantOrderNo: '14593335802368129',
  RespondType: 'JSON',
  CheckCode: '4E2B2FB0A54CCFECE9616742FE9025EFBBE8FABBC6562268C11D39DFD6E03E9C',
  IP: '36.231.213.27',
  EscrowBank: 'KGI',
  PaymentType: 'WEBATM',
  PayTime: '2016-03-30 18:18:05',
  PayerAccount5Code: '-',
  PayBankCode: '-' }
```

這個回傳結果也是頗耐人尋味：

* ```CheckCode``` 放在這裡面，不但有安全上的疑慮，必要性也值得探討
* ```CheckCode``` 的目的，如果是為了 Pay2go 與店家，自行驗證交易資料使用（檢查交易參數是否被竄改），或許可以在各自的後台運算即可，是否以明文傳送，有討論空間

回傳的結果，在交易使用的 Form 裡面幾乎都可以取得。因此向店家發出一個「模擬交易成功」的請求並不是太困難。

## 強化方案

消極作為：

1. 不要在 Form 裡面傳送 ```NotifyURL``` 參數，避免用心人士，向店家發出「模擬交易成功」的通知。可以在 Pay2go 的管理介面設定 ```NotifyURL```，減少 ```NotifyURL``` 曝露的機會。

2. 不要在 Form 裡面放入 ```hashKey``` 與 ```hashIV```。

3. 店家的 ```NotifyURL``` 實作，可以考慮移除「自動出貨」、「自動付款銷核」等流程，取消部份自動化，改採手工銷核。

4. 店家的 ```NotifyURL``` 實作，可以考慮加入更嚴謹的檢查，例如查看 HTTP 的 ```Origin:```，或是 ```Referer:``` 等檔頭，或是使用 DNS 反查詢等機制。不過因為 HTTP headers 也是很容易變造，所以根本之道還是 Pay2go 的後台能強化相關機制。

最後，展示如何用 ```curl``` 進行「模擬交易成功」。首先，在店家的 Form 裡面取得 ```MerchantID```、```CheckValue```、```MerchantOrderNo``` 與 ```Amt``` 四個參數。

接著，將以上參數帶入以下的內容模板：

```
{
"JSONData": "{\"Status\":\"SUCCESS\",\"Message\":\"\\u4ed8\\u6b3e\\u6210\\u529f\",\"Result\":\"{\\\"MerchantID\\\":\\\"31745140\\\",\\\"Amt\\\":992,\\\"TradeNo\\\":\\\"16033018180593725\\\",\\\"MerchantOrderNo\\\":\\\"14593258232469307\\\",\\\"RespondType\\\":\\\"JSON\\\",\\\"CheckCode\\\":\\\"4E2B2FB0A54CCFECE9616742FE9025EFBBE8FABBC6562268C11D39DFD6E03E9C\\\",\\\"IP\\\":\\\"36.231.213.27\\\",\\\"EscrowBank\\\":\\\"KGI\\\",\\\"PaymentType\\\":\\\"WEBATM\\\",\\\"PayTime\\\":\\\"2016-03-30 18:18:05\\\",\\\"PayerAccount5Code\\\":\\\"-\\\",\\\"PayBankCode\\\":\\\"-\\\"}\"}"
}
```

將上述內容儲存為 test.json。最後利用 ```curl``` 向店家的 ```NotifyURL``` 發出 POST 請求：

```
curl -X POST -d @test.json --header "Content-Type: application/json" http://localhost:80/pay2go/notify
```

從這個「模擬交易」的測試可以知道，把自已的 ```NotifyURL``` 藏好是一個很重要的工作。提供 REST API 來替代 Form Post 方式，可以消除主要的安全疑慮。

## Reports and Discussion

* Bug reports of this article: [https://github.com/jollen/blog/issues/18](https://github.com/jollen/blog/issues/18)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/03/security-pay2go.html</link>
         <guid>https://www.jollen.org/blog/2016/03/security-pay2go.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Thu, 31 Mar 2016 14:14:40 +0800</pubDate>
      </item>
            <item>
         <title>學習 Unikernel 與 Runtime.js (Part 2)－Build Runtime.js VM Image</title>
         <description><![CDATA[<p>Runtime.js 可以讓我們用 Node.js 與 JavaScript 來開發 Unikernel，熟悉 Node.js 是第一個基本功課，Runtime.js 的 OS kernel 採用 V8 JavaScript Engine，目前支援 KVM，研究 Runtime.js 的 kernel 實作，是第二個基本功課...</p>

<p>本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-1.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1447069387593-a5de0862481e?ixlib=rb-0.3.5&q=10&fm=jpg&crop=entropy&s=0dab06e522cf4cd96ee75e9801e73c8a">

## 前言

以下步驟參考自 [Runtime.js 官網](http://runtimejs.org/) 的說明，目標是初始化一個新的 Runtime.js 專案。請參考 [Getting Started](http://runtimejs.org/getting-started/) 上的環境安裝說明。

要使用 Runtime.js 必須安裝 [Node.js](https://nodejs.org) 執行環境，以下步驟以 Mac 環境為主，Node.js 的版本為 v4.2.6。

## 建立 Runtime.js 專案

Runtime.js 仍是一個發展中的開源計畫，目前的發展狀態如下：

* Hypervisor 部份，目前僅支援 [KVM](http://www.linux-kvm.org/page/Main_Page)
* API 部份，目前 Runtime.js 提供的 JavaScript library 非常有限，不過根據應用程式需求，可以自行安裝 Node.js 模組來使用
* 目前提供 *runtimeify* 工具來製作 Unikernel image，這是 *browserify* 的前端（wrapper）命令列工具

即便 Runtime.js 還是處於這麼早期的階段，但實作簡單的 IoT cloud VM image 問題並不大。以下是使用 runtime.js 製作 unikernel image 的準備工作說明。

### 1. 安裝 QEMU VM

安裝 QEMU VM 做為 Runtime.js 的執行環境。以 Mac 為例，使用 [homebrew](http://brew.sh/) 安裝 qemu：

```
$ brew install qemu   
```

Runtime.js 目前僅支援 KVM。

### 2. 建立新的 Node.js 專案

使用 *npm* 建立新的 Node.js 專案：

```
$ mkdir simple-iot-runtime
$ cd simple-iot-runtime
$ npm init
```

這次的學習計畫，將不使用 [Express](http://expressjs.com/)。如果要實作 REST API 的話，後續就要實作一個小型的 URL router。

### 3. 安裝 Runtime.js 模組

安裝 Runtime.js 的 core library:

```
$ npm install runtimejs --save

```

### 4. 安裝 Runtime.js CLI 與 Runtimeify

Runtime.js 提供二個命令列工具（CLI）：

* *runtime-cli* 是 *qemu* 的 wrapper，用來啟動 qemu，方便測試 runtime.js VM image
* *runtimeify* 是 *browserify* 的 wrapper，用來將 Node.js 的相依模組打包（bundle）成一個檔案 

```
$ sudo npm install runtime-cli -g
$ sudo npm install runtimeify -g
```

*Browserify* 原本是用給 frontend 開發使用的工具，讓 frontend 的 JavaScript 程式碼，也能以 *require()* 的風格引入相依模組。現在被應用於 Runtime.js 專案中。

### 5. 製作 Unikernel Images

撰寫應用程式 *index.js*，目前內容僅只是簡單的 *console.log()*，可參考 [jollen/simple-iot-runtime/index.js](https://github.com/jollen/simple-iot-runtime/blob/db8eafa74d9340840a62ba22ca4194f294d0a9a1/index.js)。

使用 *runtimeify* 將主程式 *index.js* 打包（bundle）成 *initrd* 檔案：

```
$ runtimeify index.js -o initrd
```

### 6. 啟動 Unikernel

使用 *runtime-cli* 啟動製作出來的 *initrd*：

```
$ runtime start 
```

![2016-02-15 6 58 35](https://cloud.githubusercontent.com/assets/1126021/13046853/2cf1d5da-d416-11e5-9cc9-18b0a0f53f1e.png)


## 小結

上述步驟得到的 *initrd* 就是一個 unikernel image。*runtime-cli* 是 *qemu* 的 wrapper，測試方式是使用 *runtime-cli* 啟動 qemu，並載入 *initrd* 檔案。*initrd* 是 runtime.js 預設的 unikernel 檔案。

雖然 unikernel 的檔名是 *initrd*，但這與 GNU/Linux 上的 initrd（init process）是完全不同的實作。Runtime.js 所製作的 *initrd*，其實就是 *browserify* 所編譯出來的 JavaScript 應用程式。

這個部份並沒有難度，只是操作層面的練習，熟悉指令就可以很容易上手。從這個練習也可以知道：

* Runtime.js 可以讓我們用 Node.js 與 JavaScript 來開發 Unikernel，熟悉 Node.js 是第一個基本功課
* Runtime.js 的 OS kernel 採用 V8 JavaScript Engine，目前支援 KVM，研究 Runtime.js 的 kernel 實作，是第二個基本功課
* Runtime.js 的 OS kernel 架構與 Exokernel 相似，所以研究 Exokernel 與 Runtime.js kernel 的架構，是第三個功課

## 延伸閱讀

* 2016-02-15: [學習 Unikernel 與 Runtime.js (Part 1)](http://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-1.html)
* 2016-02-16: [學習 Unikernel 與 Runtime.js (Part 2)－Build Runtime.js VM Image](http://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-2.html)

## Reports and Discussion

* Bug reports of this article: [https://github.com/jollen/blog/issues/13](https://github.com/jollen/blog/issues/13)
* 練習 Runtime.js 的專案, [https://github.com/jollen/simple-iot-runtime](https://github.com/jollen/simple-iot-runtime)
* 討論社團, [https://www.facebook.com/groups/wotcity/](https://www.facebook.com/groups/wotcity/)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-2.html</link>
         <guid>https://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">IoT &amp; WoT</category>
                  <category domain="http://www.sixapart.com/ns/types#category">unikernel</category>
        
        
         <pubDate>Tue, 16 Feb 2016 12:32:09 +0800</pubDate>
      </item>
            <item>
         <title>學習 Unikernel 與 Runtime.js (Part 1) </title>
         <description><![CDATA[<p>Unikernel 是一個很有趣的概念。不久前 Docker 收購 Unikernel Systems[1] 是很多人對它的第一印象，台灣的新聞媒體將 Unikernel 翻譯為「無核化」或「去核化」...</p>

<p>本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-1.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1447069387593-a5de0862481e?ixlib=rb-0.3.5&q=10&fm=jpg&crop=entropy&s=0dab06e522cf4cd96ee75e9801e73c8a">

## 前言

Unikernel 是一個很有趣的概念。不久前 Docker 收購 Unikernel Systems[1] 是很多人對它的第一印象，台灣的新聞媒體將 Unikernel 翻譯為「無核化」或「去核化」；不過，Unikernel 並「不是」要消滅作業系統核心，當然也不是要去除作業系統核心；相反地，作業系統核心技術，將更顯重要。

## Library OS

相較傳統的作業系統核心（conventional OS），Unikernel 的作業系統核心是以「Library」的形式實作 。技術上來說，Unikernel 可以說是一個「Library OS」的概念。

Unikernel 的做法（implementation）是將應用程式（applications）、相關模組（modules）與 library OS 打包（construct）成一個 image 檔。這樣做的目的，是希望將目標系統（target image）儘量簡化；簡化的目的，是縮小 images 的大小（image size）。

根據維基百科上的解釋[2]：

```
These libraries are then compiled with the application and configuration code to build sealed, fixed-purpose images (unikernels) which run directly on a hypervisor or hardware without an intervening OS such as Linux or Windows.
```

簡單來說，打包出來的 images 檔就稱為「Unikernel」。這個打包的過程，會將 library OS 編譯到應用程式裡，因此，製作 unikernel image 也需要配套工具。

Unikernel 並不是要去除作業系統。所謂的「無核化」的「核」，更精準的解釋應該是去掉 conventional OS 的「核（kernel）」，改採 library OS 的「核」。

專研作業系統與編譯器的工程師，不但沒有身價眨值的問題，還可能會倍數增值呢。為什麼學習作業系統又更重要了呢？因為 Unikernel image 是一種特定用途的 image file，這種 image file 也稱為 immutable VM image。

Immutable image 裡面的 library OS 讓 unikernel image 能直接（directly）在虛擬機上（hypervisor）運行，而不需要透過像是 Linux 或 Windows 的 conventional OS[2]。實作這個 library OS 需要對作業系統、微處理器、虛擬機、編譯器與 software stacks 有綜合的知識。

Unikernel 是一個 image file，也是一種 immutable image。它的用途單一不變（immutable），Unikernel 的「Uni」正能表達它的理念。Unikernel 自然有許多不同的實作；針對不同的應用程式（applications）甚致是不同的雲端佈署架構，也需要更多單一的 library OS。

受到 [jserv](https://github.com/jserv) 一直在自幹編譯器[3]的激勵，新的一年（2016 猴年）就幫自已定了一道作業：期許在新的一年，也可以自已做一個 Unikernel。不過，因為 Unikernel from scratch 的目標有點遠大，所以：

* 第一階段使用 runtime.js，學習 [Build JavaScript unikernel from scratch](https://github.com/jollen/simple-iot-runtime)
* 第二階段研究 runtime.js，可以的話也能幫忙改善 runtime.js 的 OS kernel 實作

因為，近幾年一直是 JavaScript 的愛好者，所以找了 runtime.js 做為研究目標。

## Runtime.js

Runtime.js 是 Unikernel 的一個實作。Runtime.js 提供一個稱為 *runtimeify* 的編譯器，可以將 JavaScript 應用程式，打包為 ramdisk image，並透過 *initrd* 啟動。這是第一階段的學習重點，關於這個部份的學習紀錄，都會發佈在 [jollen/simple-iot-runtime](https://github.com/jollen/simple-iot-runtime)

沒想到 Embedded Linux 也能有這樣的面貌，這是一種有點熟悉又有些陌生的奇妙感覺。*initrd* 與 JavaScript 放在一起使用，又是一個奇妙的感覺。

隨著 Node.js 的發展，以及 frontend 模組化（JavaScript modules）技術的進步，出現了 Browserify 技術，Browserify 今天更是運用在 Unikernel 概念裡，果真是 JavaScript 無極限。

Runtime.js 是一個開源的 library OS 實作，裡面包含二個 component [4]：

* Runtime.js 的 OS kernel
* Runtime.js 程式庫本身

Runtime.js 的 OS kernel 引入了  V8 JavaScript engine；整體來看，Runtime.js 提供了一個優雅的 JavaScript Unikernel 技術方案。

## 相關資源

* [1] Unikernel Systems Joins Docker, https://blog.docker.com/2016/01/unikernel/
* [2] Unikernel, https://en.wikipedia.org/wiki/Unikernel
* [3] 從無到有開發 C 編譯器, https://goo.gl/aqBw9R
* [4] Runtime.js, https://github.com/runtimejs/runtime
* [5] https://github.com/jollen/simple-iot-runtime

## 延伸閱讀

* 2016-02-15: [學習 Unikernel 與 Runtime.js (Part 1)](http://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-1.html)
* 2016-02-16: [學習 Unikernel 與 Runtime.js (Part 2)－Build Runtime.js VM Image](http://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-2.html)

## Reports and Discussion

* Bug reports of this article: [https://github.com/jollen/blog/issues/12](https://github.com/jollen/blog/issues/12)
* 練習 Runtime.js 的專案, [https://github.com/jollen/simple-iot-runtime](https://github.com/jollen/simple-iot-runtime)
* 討論社團, [https://www.facebook.com/groups/wotcity/](https://www.facebook.com/groups/wotcity/)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-1.html</link>
         <guid>https://www.jollen.org/blog/2016/02/build-javascript-unikernel-from-scratch-part-1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">IoT &amp; WoT</category>
                  <category domain="http://www.sixapart.com/ns/types#category">unikernel</category>
        
                  <category domain="http://www.sixapart.com/ns/types#tag">simple-iot-runtime</category>
        
         <pubDate>Mon, 15 Feb 2016 13:01:37 +0800</pubDate>
      </item>
            <item>
         <title>如何自行架設 Parse API Server</title>
         <description><![CDATA[<p>Facebook 在 2016 年 1 月 28 日（台北時間 1 月 28 日）宣佈，將關閉 Parse.com 平台服務，但也同時釋出 Prase Platform 的 API Server 原始碼。本文介紹如何自行架設 Parse Server。</p>

<p>本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/02/how-to-setup-parse-api-server.html">這裡</a>。</p>
<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1447069387593-a5de0862481e?ixlib=rb-0.3.5&q=10&fm=jpg&crop=entropy&s=0dab06e522cf4cd96ee75e9801e73c8a">

## 前言

WoT.City 將在 2 月 4 日晩間 08:00PM ，舉辦 Parse.com API 伺服器的架設教學與討論交流。在家裡上網，跟著大家在 IRC 上一起架設 Parse 伺服器，共同迎接 IoT 個人架構時代的開始。

[parse-server](parse-server) 是一個相容於 Express 的 URL Router 套件，這表示：

* 以 npm 模組方式引入使用，不需要直接 fork 此專案
* 可以建立新的 Express application 專案來使用
* 或是，將 parse-server 掛載（mount）到現有的 Express application 專案上使用

以下從建立新的 Express application 專案開始，介紹 parse-server URL router 套件的使用方法。

如果不想自行使用 Express Generator 產生專案的話，請參考 [nodejs-express](https://github.com/jollen/nodejs-express) 並直接由 **Step 4** 開始。

本文目標：

* 前置作業
* 建立 Node.js 與 Express 專案
* Create the first **ParseServer** instance and **Cloud Code**
* Use **restAPIKey**
* 測試 REST API

## 前置作業

環境安裝：

* 安裝 Node.js 4.1 以上版本（建議 v4.2.x TLS）
* 安裝 MongoDB Server，或直接到 [MongoLab](https://mongolab.com/) 申請免費的 MongoDB 資料庫服務即可

MongoDB 的新手建議可先申請 MongoLab 服務。MongoLab 目錄提供 500MB 的免費資料庫服務（如圖一）。

![2016-02-04 12 27 02](https://cloud.githubusercontent.com/assets/1126021/12805879/318eb416-cb3b-11e5-9947-252815dcbb50.png)
圖一：申請 MongoLab 服務

## Step 1: 安裝 Express Generator

```
$ sudo npm install express-generator -g
```

## Step 2: 建立新的 Express 專案

```
$ express my-parse-app

   create : my-parse-app
   create : my-parse-app/package.json
   create : my-parse-app/app.js
   create : my-parse-app/public
   create : my-parse-app/public/javascripts
   create : my-parse-app/public/images
   create : my-parse-app/public/stylesheets
   create : my-parse-app/public/stylesheets/style.css
   create : my-parse-app/routes
   create : my-parse-app/routes/index.js
   create : my-parse-app/routes/users.js
   create : my-parse-app/views
   create : my-parse-app/views/index.jade
   create : my-parse-app/views/layout.jade
   create : my-parse-app/views/error.jade
   create : my-parse-app/bin
   create : my-parse-app/bin/www

   install dependencies:
     $ cd my-parse-app && npm install

   run the app:
     $ DEBUG=my-parse-app:* npm start
```

進入專案目錄，並安裝 npm 模組：

```
$ cd my-parse-app/
$ npm install
```

目前的專案內容：

```
$ ls -l
total 16
-rw-r--r--  1 apple  staff  1442  2  3 14:22 app.js
drwxr-xr-x  3 apple  staff   102  2  3 14:22 bin
drwxr-xr-x  3 apple  staff   102  2  3 14:23 node_modules
-rw-r--r--  1 apple  staff   362  2  3 14:23 package.json
drwxr-xr-x  5 apple  staff   170  2  3 14:22 public
drwxr-xr-x  4 apple  staff   136  2  3 14:22 routes
drwxr-xr-x  5 apple  staff   170  2  3 14:22 views
```

## Step 3: 安裝 parse-server  模組

```
$ npm i parse-server --save
```

## Step 4: 修改 app.js 主程式

開啟 **app.js** 主程式，分別加入以下幾段程式碼。

### 4.1. 引入 *parse-server* 模組：

```
var ParseServer = require('parse-server').ParseServer;
``` 

### 4.2. 建立 *ParseServer* 的 instance

修改 ```app.js```，加入：

```
// Specify the connection string for your mongodb database
// and the location to your Parse cloud code
var api = new ParseServer({
  databaseURI: 'mongodb://localhost:27017/dev',
  cloud: '/home/myApp/cloud/main.js', // Provide an absolute path
  appId: 'myAppId',
  masterKey: 'mySecretMasterKey',
  fileKey: 'optionalFileKey'
});
```

以上有幾個選項要修改：

* ```databaseURI```：MongoDB 的 URI
* ```cloud```：Cloud Code 主程式
* ```appId```：Application ID

### 4.3 修改 ```databaseURI```

如果是申請 MongoLab 的服務，請填入 MongoLab 提供的 URI 填入（如圖二）。```<dbuser>``` 與 ```<dbpassword>``` 填入自行建立的 database user。

![2016-02-04 12 35 16](https://cloud.githubusercontent.com/assets/1126021/12805923/c583ccd8-cb3b-11e5-9972-3873b96ea1c8.png)
圖二：申請的 MongoDB URI

### 4.4 加入 Cloud Code

這個是 Parse Cloud Code 的程式碼路徑，請建立想存放 Parse Cloud Code 的路徑，接著建立 **main.js** 主程式，內容如下：

```
Parse.Cloud.define("hello", function(request, response) {
  response.success('hello');
});
```

### 4.5 設定 ```appId```

Application ID 用來管理 Parse API 的使用權限，原則上可以自行指定一個任意字串，例如：```5de49f1cd4bd09be95bf35ecbf1117b0```。Mac 或 Linux 的使用者，可以用 ```md5``` 做一個 md5sum 當 appId 用：

```
$ md5 /etc/hosts
MD5 (/etc/hosts) = 91df01d82a846dbddc163a65c0f7b047
```

### 4.6 掛載 parse-server 的 URL router

修改 ```app.js``` 加入：

```
// Serve the Parse API on the /parse URL prefix
app.use('/parse', api);
```

### 4.6 練習加入 ```restAPIKey ```

同樣的觀念，為 Parse Server 加入 ```restAPIKey```。修改示範：

* [jollen/nodejs-express/commit/59880ac](https://github.com/jollen/nodejs-express/commit/59880ac63b29d97130ebb69eb2f43c716cc2ab68)

### 完整示範

提供 **app.js** 程式碼修改示範：

* [jollen/nodejs-express/commit/08f58a4](https://github.com/jollen/nodejs-express/commit/08f58a450ca1cee772a6a12e16c73acdf2a0fdd2)

## Step 5: 啟動 MongoDB

申請 MongoLab 服務請略過本步驟。

## Step 6: 啟動 Node.js

```
$ npm start
```

## API 測試

使用 ```curl``` 來呼叫 Parse 的 REST API 進行初步測試。閱讀 [REST API Guide](https://parse.com/docs/rest/guide#objects-creating-objects) 了解 Parse Server API 細節。

### Creating Objects

測試指令：

```
curl -X POST \
-H "X-Parse-Application-Id: 123456789" \
-H "X-Parse-REST-API-Key: 9d676c364a11d96b8c67e69bf7bbfb82" \
-H "Content-Type: application/json" \
-d '{"score":1337,"playerName":"Sean Plott","cheatMode":false}' \
http://localhost:3000/parse/classes/GameScore
```

返回結果：

```
{"objectId":"B2BjW12yXo","createdAt":"2016-02-04T05:29:35.187Z"}
```
Parse API 使用 ```X-Parse-Application-Id``` 檔頭送出 ```appId```，請填入自行設定的 ```appId```。

## 參考資源

* Parse Server Guide, https://parse.com/docs/server/guide

## Reports and Discussion

* Bug reports of this article: [https://github.com/jollen/blog/issues/10](https://github.com/jollen/blog/issues/10)
* [Parse API Server 開發者聊天室](https://gitter.im/wotcity/parse-server)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/02/how-to-setup-parse-api-server.html</link>
         <guid>https://www.jollen.org/blog/2016/02/how-to-setup-parse-api-server.html</guid>
        
        
         <pubDate>Thu, 04 Feb 2016 22:14:05 +0800</pubDate>
      </item>
            <item>
         <title>How to build a CoAP message and send it to Internet using FreeRTOS on ESP8266.</title>
         <description><![CDATA[<p>

How to build a CoAP message and send it to Internet using FreeRTOS on ESP8266.

</p>

<p>Please follow <a href="http://www.jollen.org/blog/2016/01/build-coap-message-freertos-esp8266.html">this link</a> if you are unable to read this article.

<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1431887773042-803ed52bed26?ixlib=rb-0.3.5&q=10&fm=jpg&crop=entropy&s=65b80c72d88022965f20b535b5411918">

## Abstraction

This article describes how to build a CoAP message by er-coap-13 APIs. *er-coap-13* is a high-level CoAP library of Contiki operating system. This article will introduce rtos-wot project which is based on  esp-open-rtos from SuperHouse.

With rtos-wot, you are able to build CoAP messages with FreeRTOS and send them over ESP8266 WiFi module.

## Introducing rtos-wot

[rtos-wot](https://github.com/wot-sdk/rtos-wot) is an open source FreeRTOS distribution for ESP8266 WiFi module. It aims to conform the W3C web of things framework and is heavily based on [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos).  

The development is still in progress. 

## CoAP Library

*libcoap* of Contiki has already been ported to rtos-wot project. The following steps explains how to build a CoAP message and send it to Internet over ESP8266 wifi module.

### 1. Include necessary header files.

```
#include "er-coap-13.h"
#include "er-coap-13-transactions.h"
```

### 2. Prepare and initialize a CoAP packet.

```
coap_packet_t request[1];
coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0);
```

In the example, the packet is initialized to *COAP_TYPE_CON* type and *POST* method.

### 3. Fill CoAP headers.

```
coap_set_header_uri_path(request, '/object/123456/send');
coap_set_header_uri_host(request, 'wot.city');
```

In the example, the packet headers was filled with both server URI path and server host.

### 4. Set the payload.

```
const char *payload = "{}";
coap_set_payload(request, (uint8_t *)payload, strlen(payload));
```

The payload is the message context. In the example, the payload is an empty JSON object.

### 5. Get message ID

```
request->mid = coap_get_mid();
```

### 6. Serialize CoAP message

```
coap_transaction_t *transaction = coap_new_transaction(request->mid, &ipaddr, uri->port);
transaction->packet_len = coap_serialize_message(request, transaction->packet);
```

### 7. Final step

After serializing the CoAP message, the final CoAP packet is stored at *transaction->packet* field. The final step is to call lwip APIs to create a UDP socket and send out the serialized message. For example, assume that the server was connected via local socket _s_.

```
write(s, transaction->packet, transaction->packet_len);
```

CoAP is basis of web of things framework. For push pattern of WoT, the TD (thing description) can be serialized in CoAP binary format.

### Put together

Please refer to [coap_send](https://github.com/wot-sdk/rtos-wot/tree/master/examples/coap_send) for how to put all things together.

## Reports and Discussion

* Bug reports of this article: [https://github.com/jollen/blog/issues/2](https://github.com/jollen/blog/issues/2)
* Discussion group: [https://www.facebook.com/groups/wotcity/](https://www.facebook.com/groups/wotcity/)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/01/build-coap-message-freertos-esp8266.html</link>
         <guid>https://www.jollen.org/blog/2016/01/build-coap-message-freertos-esp8266.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">IoT &amp; WoT</category>
        
        
         <pubDate>Wed, 27 Jan 2016 23:58:03 +0800</pubDate>
      </item>
            <item>
         <title>使用 ESP8266 做為 FreeRTOS 的學習與開發環境</title>
         <description><![CDATA[<p>

根據 2016 CES 的 IoT 產業新聞分析：WiFi Module 正以出乎意料外的速度，佔領 IoT 市場，並且 RTOS 與 HTML5 扮演重要的主流 IoT 技術。本文介紹如何使用 ESP8266 做為 FreeRTOS 的學習環境。

</p>

<p>本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/01/study-freertos-using-esp8266.html">這裡</a>。</p>

<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1443453489887-98f56bc5bb38?ixlib=rb-0.3.5&q=20&fm=jpg&crop=entropy&s=ebbdd1c51d315bf2bdcba3f56e6c2ba6">

## 前言

ESP8266 是 Espressif（樂鑫信息科技有限公司） 所開發的一款 WiFi 模組，ESP8266 WiFi Module 受到開發者與自造社群的喜愛。目前可見的 ESP8266 開發模式有：

1. Microcontrollers 搭配 ESP8266 WiFi Module，使用 microcontrollers 來控制 ESP8266，例如：Arduino + ESP8266

2. 使用官方的 ESP8266-RTOS-SDK，使用 FreeRTOS 來開發 ESP8266 的應用程式，ESP8266 WiFi Module 可以做為主動方（即不需要 microcontroller 即可單獨執行）

3. 使用 NodeMCU 的 ESP8266 firmware，使用 Lua script 的方式來開發 ESP8266 應用程式；ESP8266 WiFi Module 可以做為主動方（即不需要 microcontroller 即可單獨執行）

如果要把 ESP8266 做為 FreeRTOS 的學習平台，倒底要選擇那一個 SDK 呢？

## 開始學習 FreeRTOS

要將 ESP8266 做為 FreeRTOS 的學習平台，如果要練習修改或閱讀 FreeRTOS kernel 程式碼，就要使用開源的 FreeRTOS 版本。以下是使用 ESP8266 做為 FreeRTOS 學習或實驗平台的要點：

* 要使用 Open Source 的 [FreeRTOS](http://www.freertos.org) 原始碼
* 要有 ESP8266 的 Portable Layer 實作，例如 *pvPortMalloc()*

由 SuperHouse 社群所維護的 ESP8266 SDK 正好能滿足上述要點。此外，使用 FreeRTOS 在 ESP8266 平台上，打造 IoT 應用程式時，則是要滿足以下基本條件：

* 使用 lwip 做為 TCP/IP Stacks
* 具備 CoAP 的支援
* 硬體控制部份，最好有一個 Component-based 的程式設計模式

為了方便起見，WoT.City 發起 [rtos-wot](https://github.com/wot-sdk/rtos-wot) 計畫，將上述開源計畫整合成一個 FreeRTOS distribution，目標是維護一個用於學習 FreeRTOS + IoT 的 FreeRTOS 特別版本（distribution）。

## 安裝 FreeRTOS 開發環境

以下是開發環境的架設教學，教學內容以 Mac 環境為主。

### 安裝 ESP Open SDK

在 MacOS 系統建置 ESP8266 的編譯環境。首先，確認是否已安裝 Xcode command line tools：

```bash
$ xcode-select --install
```
再使用 [homebrew](http://brew.sh) 安裝所需的套件：

```bash
$ brew tap homebrew/dupes
$ brew install binutils coreutils autoconf automake wget gawk libtool gperf gnu-sed --with-default-names grep bison libvorbis
$ export PATH=/usr/local/opt/gnu-sed/libexec/gnubin:/usr/local/opt/gperf/bin:$PATH
```
製作一個 "case-sensitive"（可區分大小寫檔名）的虛擬磁碟，大小是 8GB：

```bash
$ hdiutil create ./eos.dmg -volname "esp-open-sdk" -size 8g -fs "Case-sensitive HFS+"
```
將 ESP8266 Open SDK 下載至此虛擬磁碟：

```bash
$ hdiutil mount ./eos.dmg
$ cd /Volumes/esp-open-sdk
$ git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
$ cd eos-open-sdk
```

編譯 "separated SDK"：
```bash
$ make STANDALONE=n
```
編譯完成後的畫面：

```
Xtensa toolchain is built, to use it:

export PATH=/Volumes/esp-open-sdk/esp-open-sdk/xtensa-lx106-elf/bin:$PATH

Espressif ESP8266 SDK is installed. Toolchain contains only Open Source components
To link external proprietary libraries add:

xtensa-lx106-elf-gcc -I/Volumes/esp-open-sdk/esp-open-sdk/sdk/include -L/Volumes/esp-open-sdk/esp-open-sdk/sdk/lib
```

根據畫面提示，修改 *PATH* 環境變數：

```
$ export PATH=/Volumes/esp-open-sdk/esp-open-sdk/xtensa-lx106-elf/bin:$PATH
```
完成 ESP8266 Toolchain 的安裝，接下來就可以開始編譯 rtos-wot 了。

### 編譯 FreeRTOS Application Firmware

安裝 [pyserial](https://github.com/pyserial/pyserial) 套件：

```bash
$ git clone https://github.com/pyserial/pyserial.git
$ cd pyserial/
$ sudo python setup.py install
```

下載 [rtos-wot](https://github.com/jollen/rtos-wot) 原始碼：

```bash
$ git clone https://github.com/wot-sdk/rtos-wot
```
編輯 ```include/ssid_config.h``` 檔案，設定 WiFi 的 SSID 與密碼：

```c
#define WIFI_SSID "<the-SSID>"
#define WIFI_PASS "<your-passworld>"
```
完成後，挑選一個 RTOS 應用程式，並進行 firmware 編譯：

```bash
$ cd examples/blink
$ make
```
編譯完成後，在目前的應用程式路徑下，得到以下二個檔案：

* firmware/0x00000.bin
* firmware/0x20000.bin

將以上二個檔案燒錄至 ESP8266 更新即可，指令如下：

```bash
$ make flash ESPPORT=/dev/cu.SLAB_USBtoUART 
```

使用 Linux 環境的讀者，請另行參考[這篇文章](http://www.allaboutcircuits.com/projects/guts-of-the-iot-part-1-building-nodemcu-from-source-for-the-esp8266/)的說明。

### 使用 esptool 更新 ESP8266 Firmware

如果要使用 *esptool.py* 工具，手動更新 firmware 檔案的話，指令如下：

```
$ esptool.py -p /dev/cu.SLAB_USBtoUART --baud 115200 write_flash -fs 32m -fm dio -ff 40m 0x20000 ./firmware/0x20000.bin 0x00000 ./firmware/0x00000.bin
```

在 Mac 下使用 *esptool.py* 更新 NodeMCU firmware 時，需額外加上 *-fm* 與 *-fs* 二個參數。

## 關於 RTOS WoT 計畫

本文使用的 RTOS WoT 版本，是專為 ESP8266 製作的 FreeRTOS 學習、教學與實驗平台。RTOS WoT 的目標是打造一個 Web of Things 的 RTOS SDK。

RTOS WoT 也在 RTOS Application Layer 做了一些加強，請參考以下文章：

* [RTOS WoT (v0.1.0) 使用 FreeRTOS、 lwIP 與 C++ 元件重用](http://www.jollen.org/blog/2016/01/freertos-lwip-esp8266-mbed-styles.html)

## Reference

* http://www.allaboutcircuits.com/projects/guts-of-the-iot-part-1-building-nodemcu-from-source-for-the-esp8266/

## 留言與討論

* 回報本文的錯誤：[https://github.com/jollen/blog/issues/8](https://github.com/jollen/blog/issues/8)
* 技術討論社團：[https://www.facebook.com/groups/wotcity/](https://www.facebook.com/groups/wotcity/)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/01/study-freertos-using-esp8266.html</link>
         <guid>https://www.jollen.org/blog/2016/01/study-freertos-using-esp8266.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">IoT &amp; WoT</category>
        
        
         <pubDate>Wed, 27 Jan 2016 19:02:30 +0800</pubDate>
      </item>
            <item>
         <title>RTOS WoT (v0.1.0) 使用 FreeRTOS、 lwIP 與 C++ 元件重用</title>
         <description><![CDATA[<p>

沿續前一個 Web of Things 的實驗計畫<a href="https://github.com/jollen/blog/issues/1">在 NodeMCU 上使用 er-coap-13</a>，RTOS WoT 使用由 SuperHouse 開發的 esp-open-rtos 版本，進行一些實驗性質的修改。esp-open-rtos 同樣是基於 Espressif 官方的 <a href="https://github.com/espressif/esp_iot_rtos_sdk">Espressif IOT RTOS SDK</a>，但改採 open source 版本的 FreeRTOS 與 lwIP 程式碼。

</p>

<p>本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2016/01/freertos-lwip-esp8266-mbed-styles.html">這裡</a>。</p>

<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1446776858070-70c3d5ed6758?ixlib=rb-0.3.5&q=10&fm=jpg&crop=entropy&s=9bbb8a4b7e9b0107fdb0fe52d0dbcaff">

## 關於 libc

標準 C 程式庫（libc）部份，esp-open-rtos 改用 newlib 來取代 Espressif 官方的標準 C 程式庫（libmain.a），並加入了 thread-safe 的支援。相關說明可參考 SuperHouse 官方的 [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos) 計畫。

## RTOS WoT 目標

[RTOS WoT](https://github.com/wot-sdk/rtos-wot) 是 *esp-open-rtos* 的一份 fork，目標是基於 FreeRTOS 設計並實作符合 W3C Web of Things 標準的 WoT server。RTOS WoT 目標是支援 constrained devices（Micro-controllers），這與目前 W3C 正在發展的 Web of Things Framework（採用 Node.js）目標不同，但未來都會經由 Web of Things 標準，讓不同的平台互通（interoperability）。

## RTOS WoT v0.1.0

RTOS WoT 共有 10 個版本計畫，第一個版本（v0.1.0）的目標非常簡單：

* 使用 C++ programming model
* 加入 WebSocket（版本 13 以上）的支援

WebSocket 的部份還在測試中，C++ programming model 的概念說明如下。

## Reusable Components

加入 C++ programming model 的想法，「不是」為了使用 C++ 來撰寫 FreeRTOS 應用程式，而是能將 device drivers 的程式碼，封裝為 component，以達到重用（reuse）的目的。

設計考量部份，只需要將 ESP8266 的 GPIO、Analog、I2C、UART 腳位，封裝為 class library 即可，但不封裝 FreeRTOS APIs。這項工作的目標，是讓 FreeRTOS 可以具備一層能重用的 device driver 架構。

實作部份，則是直接引用 ARM mbed 的程式碼，範例可參考 [AnalogIn.h](https://github.com/wot-sdk/rtos-wot/blob/master/examples/mbed_air_quality/AnalogIn.h)。有了  mbed components 的移植，未來還可以使用 mbed programming style 來撰寫 FreeRTOS 的驅動程式。

例如，要讀取 ESP8266 的 A0 數據，使用 mbed programming style 的寫法如下：

```
AnalogIn    AIR(17);
int a = AIR;
```

以下是一個 Air Quality 的完整程式碼範例：

```
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "esp8266.h"
#include "math.h"

// C++ programming model
#include "AnalogIn.h"

struct userdata {
    xQueueHandle xQueue;
    xTaskHandle xHandle;
};

/* user context */
static struct userdata user;

/* ADC0 (A0)
 *
 * MP503 Air Quality Sensor -
 * http://www.seeedstudio.com/wiki/File:Air_quality_sensor_MP503.pdf
 */
AnalogIn    AIR(17);

/* This task uses the high level GPIO API (esp_gpio.h) to blink an LED.
 *
 */
void readTask(void *pvParameters)
{
    struct userdata *user = (struct userdata *)pvParameters;
    int a;

    while(1) {
        // read from sensor output voltage
        a = AIR;

        if (a > 798 || a <= 10) {
            printf("Sensor is initializing. Waiting for 5 seconds...\n");
            wait(5);
            continue;
        }

        // send to queue
        xQueueSendToBack( user->xQueue, (void *) &a, portMAX_DELAY );

        // Resume the suspended task ourselves.
        if( user->xHandle != NULL ) {
            vTaskResume( user->xHandle );
        }

        wait(5);
    }
}

void transmitTask(void *pvParameters)
{
    struct userdata *user = (struct userdata *)pvParameters;

    int a;

    while(1) {
        // Suspend ourselves.
        vTaskSuspend( NULL );

        xQueueReceive(user->xQueue, &a, portMAX_DELAY);

        printf("{ \"quality\": %d }\n", a);
    }
}

extern "C" void user_init(void)
{
    uart_set_baud(0, 115200);

    user.xQueue = xQueueCreate(2, sizeof(uint32_t));
    xTaskCreate(readTask, (signed char *)"readTask", 256, &user, tskIDLE_PRIORITY+1, NULL);
    xTaskCreate(transmitTask, (signed char*)"transmitTask", 256, &user, tskIDLE_PRIORITY, &user.xHandle);
}
```

同樣的方式，移植 [DigitalOut.h](https://github.com/wot-sdk/rtos-wot/blob/master/examples/mbed/DigitalOut.h)。以下是一個 GPIO 控制的完整範例：

```
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "esp8266.h"

// mbed API layer
#include "DigitalOut.h"
#include "AnalogIn.h"

// GPIO2 (D4) 
DigitalOut D4(2);

/* This task uses the mbed programming style to blink a LED.
 *
 */
void blinkenTask(void *pvParameters)
{
    while(1) {
        D4 = 1;
        wait(1)
        D4 = 0;
        wait(3);
    }
}

extern "C" void user_init(void)
{
    uart_set_baud(0, 115200);
    xTaskCreate(blinkenTask, (signed char *)"blinkenTask", 256, NULL, 2, NULL);
}
```


## 留言與討論

* [https://github.com/jollen/blog/issues/7](https://github.com/jollen/blog/issues/7)

</script>]]></description>
         <link>https://www.jollen.org/blog/2016/01/freertos-lwip-esp8266-mbed-styles.html</link>
         <guid>https://www.jollen.org/blog/2016/01/freertos-lwip-esp8266-mbed-styles.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">IoT &amp; WoT</category>
        
        
         <pubDate>Thu, 07 Jan 2016 10:57:42 +0800</pubDate>
      </item>
            <item>
         <title>[IoT] 如何用 Wio Link 快速自製 GoPro Remote</title>
         <description><![CDATA[<p>上週（12/23 與 12/24）分別在「<a href="https://www.facebook.com/mokoversity/posts/500405160140707">WoT.City x 台灣大學電機系：Wio Link 工作坊</a>」課程，以及「<a href="https://www.mokoversity.com/conference/party">LinkIt Smart 7688 與 Wio Link（ESP8266）技術沙龍</a>」活動上，閃電展示了如何使用 Wio Link 自製 GoPro 的 Remote 控制器，整個過程只需要大約 30 分鐘，以下分享製作方法。</p>

<p>本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法完整閱讀全文，請點擊<a href="http://www.jollen.org/blog/2015/12/wio-link-gopro-remote.html">這裡</a>。</p>

<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1441644599508-24ae08965c5c?ixlib=rb-0.3.5&q=10&fm=jpg&crop=entropy&s=86ac0e8a9fa4f935beafaf83148abd62">

## 實作原理

本專案的技術原理非常簡單：

* GoPro 內建 Cherokee Web Server
* 使用 HTTP GET 方法呼叫 GoPro 的拍照 API

本文使用 Wio Link 或 NodeMCU 開發板，並以 Lua 來撰寫 HTTP request 程式碼。改用 Node.js 來實作 HTTP request 的話，可以改用近期火紅的 MediaTek LinkIt Smart 7688 (Duo) 開發板來製作 GoPro 控制器。

## Step 1: 材料準備

* GoPro 一台，筆者使用的是 GoPro Hero 4 Silver
* Wio Link 或 NodeMCU 開發板（皆使用 ESP8266 模組）

## Step 2: 更新 GoPro Firmware

下載 [GoPro Studio](http://zh.shop.gopro.com/softwareandapp/gopro-studio/GoPro-Studio.html) 安裝後，將 GoPro 連接到電腦。GoPro Studio 會自動更新 GoPro firmware。

## Step 3: 更新 Wio Link Firmware

使用 NodeMCU 的開發者可忽略這個步驟。

Wio Link 預載 Seeed Stduio 專門打造的 firmware，可搭配 Wio Link App[1] 快速打造 IoT application。在這個小專題裡，我們會使用 Lua 來撰寫簡單的 GoPro 控制器，因此要更換為 NodeMCU firmware。

更新 NodeMCU firmware 的詳細步驟，請參考[ESP8266 & NodeMCU 開發入門 (Part 5) - 編譯並更新 NodeMCU Firmware](https://wotcity.com/blog/2015/11/13/esp8266-nodemcu-iot-starter-part-5/)。

## Step 4: 連接 GPIO Button

如圖 1，將 Grove Kit 的 Button 接到 Wio Link 的 Digital 插糟。以圖 1 為例，Button 將經由 GPIO14 來控制，後續撰寫程式時，須將 GPIO14 設定為中斷觸發。

![wio-link-gopro-1](https://cloud.githubusercontent.com/assets/1126021/12014473/64353fe8-ad68-11e5-9eb7-c1391fc05205.jpg)
圖 1：連接 GPIO Button

## Step 5: 認識 Wio Link GPIOs

Wio Link 設計了 3 個 Digital 插糟，每個插糟上都有 2 根 GPIO 腳位。以上圖為例，觀察黃色接線，可以看出，Grove Kit 的 Button 是經由 GPIO14 來控制。Wio Link 的 GPIO 腳位，相容於 NodeMCU，因此應用上可視為 NodeMCU 的替代品。

| Wio Link 腳位名稱 | NodeMCU 腳位名稱 | Note | 
|-------|---------|------------|-------|
| GPIO-13 | GPIO-13 |  D7  | 
| GPIO-12 | GPIO-12  |  D6 | 
| GPIO-14 | GPIO-14  |  D5 | 

以圖 1 為例，撰寫 Lua 程式將 GPIO-14（D5）設定為中斷觸發模式：

```
pin = 5
gpio.mode(pin, gpio.INT)
```

再定義中斷觸發的 callback function：

```
--set the interrupt callback function
gpio.trig(pin, "both", button)
```

對 Wio Link 與 NodeMCU 的 Lua 開發環境不熟悉的話，可以參考以下文章：

* [ESP8266 & NodeMCU 開發入門 (Part 1) - Hello World](https://wotcity.com/blog/2015/08/31/esp8266-nodemcu-iot-starter-part-1/)

WoT.City 提供的[ESP8266 & NodeMCU 開發入門](https://wotcity.com/blog/tag/esp8266/)系列文章，可做為初學者的自學教材。

## Step 6: 撰寫 Lua 程式

將以下程式碼上傳至 Wio Link：

```
--
-- GoPro Remote Simple
-- See: https://github.com/jollen/blog/issues/6
--

-- Configs
host = "10.5.5.9"
port = 80
ssid = "<SSID>"
pass = "<PASSWORD>"
pin = 5
api = "/gp/gpControl/command/shutter?p=1"

-- Configure the ESP as a station (client)
wifi.setmode(wifi.STATION)
wifi.sta.config(ssid, pass)
wifi.sta.autoconnect(1)

-- Create a TCP socket
sk = net.createConnection(net.TCP, 0)
sk:on("receive", function(sck, c) 
  print(c)
end)

-- Set GPIO
gpio.mode(pin, gpio.INT)

-- Poll GPIO
now = 0
duration = 0
    
function button(level)
  duration = tmr.now()-now
  print(duration)
  now = tmr.now()

  if level == 1 then 
    print("down")
    sk:send(headers)
  else 
    print("up")
  end
end

--set the interrupt callback function
gpio.trig(pin, "both", button)
    
-- Print IP address
local ip = wifi.sta.getip()

-- Make HTTP request headers
if (ip == nil) then
  ip = "localhost"
end
headers = "GET " .. api .. " HTTP/1.1\r\nHost: " .. ip .. "\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n"
print(headers)

-- Connect to GoPro host
sk:connect(port, host)
```

可將程式碼儲存為 *init.lua* 後上傳至 Wio Link。Wio Link 開機時，會自動執行 *init.lua*。

## Step 7: 開啟 GoPro Wifi 並進行測試

1. 開啟 GoPro Wifi 
2. 將 GoPro 設定為拍照模式
2. 上傳上述程式碼至 Wio Link
3. 按下 Button 拍照

實境測試影片如下。

<iframe width="560" height="315" src="https://www.youtube.com/embed/vsLIuOPg83c" frameborder="0" allowfullscreen></iframe>

## 網路資源

[1]: Wio Link App, https://itunes.apple.com/tw/app/wio-link/id1054893491?mt=8


## 留言與討論

* [https://github.com/jollen/blog/issues/6](https://github.com/jollen/blog/issues/6)

</script>]]></description>
         <link>https://www.jollen.org/blog/2015/12/wio-link-gopro-remote.html</link>
         <guid>https://www.jollen.org/blog/2015/12/wio-link-gopro-remote.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">IoT &amp; WoT</category>
        
        
         <pubDate>Mon, 28 Dec 2015 15:34:50 +0800</pubDate>
      </item>
            <item>
         <title>[IoT] 在 NodeMCU 上使用 er-coap-13</title>
         <description><![CDATA[<h1>源由</h1>

<p>為了在 NodeMCU 上進行 WoT 實驗，日前開啟了一個小型的專案 <a href="https://github.com/node-wot/node-wot">node-wot</a>，node-wot 將會持續小幅修改 nodemcu-firmware，以做為 WoT 的實驗用 firmware。</p>

<p>本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2015/12/nodemcu-firmware-er-coap-13.html">這裡</a>。</p>

<script id="markdown" type="text/plain" data-header-image="https://images.unsplash.com/photo-1436985487860-712a3b558087?ixlib=rb-0.3.5&q=10&fm=jpg&crop=entropy&s=7a36e714238ddd2b2e54633e9eeaf18d">


## node-wot 專案：ESP8266 CoAP SDK

目前的主要修改內容，是將 NodeMCU firmware 裡的 libcoap 更換為 Contiki OS 的 er-coap-13 實作，並且只保留了 CoAP client。主要的原因如下：

* er-coap-13 有比較嚴謹的 API 設計，可做為 NodeMCU 的 CoAP SDK，未來可以更方便開發 CoAP 應用程式
* 「有朝一日」，或許可加入基於 er-coap-13 的 LWM2M 實作 (wakaama)
* 未來計畫引入 protothreads 機制，取代 FreeRTOS multitasking
* Heap management

現階段，僅將 ESP8266 做為 data push object，並不做為 CoAP server，因此只保留 CoAP client 的實作。主要考量是節省 RAM + ROM 的空間（ESP8266 的 iram1_0_seg + irom0_0_seg）。

### 使用 CoAP API

er-coap-13 的完整 API 設計，是 node-wot 專案的重要考量。例如，要建立一個新的 CoAP 封包，只要呼叫 *coap_init_message()* 函數即可：

```
// 引入 er-coap-13 APIs
#include "er-coap-13.h"
#include "er-coap-13-transactions.h"

// 宣告 CoAP packet
coap_packet_t request[1];

// 將 CoAP packet 初始化為 COAP_TYPE_CON 類型，並使用 HTTP POST
coap_init_message(request, COAP_TYPE_CON, COAP_POST, 0);
```
要初始化 CoAP headers，例如：填寫 CoAP header 的 Uri Path 與 Uri Host，只需要這樣寫：

```
coap_set_header_uri_path(request, 'object/123456/send');
coap_set_header_uri_host(request, 'wot.city');
```
要加入 payload（本文），也只要這樣寫：

```
const char *payload = "{}";
coap_set_payload(request, (uint8_t *)payload, strlen(payload));
```

最後也只要呼叫不到 5 個 APIs，就可以將 CoAP 封包送出。

er-coap-13 有更好的 abstraction level，可以隱藏所有 CoAP 標準的技術細節。

### Heap Management

node-wot 上的 er-coap-13 做過微幅修改，以及完整的測試，可保證 CoAP requests 不會消耗 Heap 空間。Heap 空間若持續減少，會發生記憶體不足的現象，Lua 程式會 crash。

## Lua 範例程式

node-wot 的修改並不影響 Lua programming model，因此 Lua 程式可同時在 nodemcu-firmware 與 node-wot 上執行。

以下是 2015 年 12 月 8 日在 [ESP8266 IoT Workshop](https://www.mokoversity.com/events/esp8266) 上使用的 Lua 範例（搭配 node-wot firmware 使用）。

### Hello, Lua

以 HTTPD 做為學習 Lua 的 "Hello, World"。

```
-- Select IO - GPIO4
outpin=4
gpio.mode(outpin,gpio.OUTPUT)
gpio.write(outpin,gpio.HIGH)    

function power(stat)
  if stat=="ON"  then gpio.write(outpin,gpio.HIGH) 
    return 
   end
  if stat=="OFF" then gpio.write(outpin,gpio.LOW) 
    return 
   end
end

-- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Configure the ESP as a station (client)
wifi.setmode(wifi.STATION)  
wifi.sta.config("JY", "1234567654321")  
wifi.sta.autoconnect(1)

-- Create a server
-- and set 30s time out for a inactive client
sv = net.createServer(net.TCP, 30)

-- Server listen on 80
-- Print HTTP headers to console
sv:listen(80,function(c)  
    c:on("receive", function(conn, payload)
        print(payload)

        if (string.find(payload, "GET /power/on") ~= nil) then
            power("ON")
        elseif (string.find(payload, "GET /power/off") ~= nil) then
            power("OFF")
        end
        
        conn:send("HTTP/1.1 200 OK\n\n")
        conn:close()
    end)
end)
```

### Hello, CoAP

以 CoAP request 做為學習 IoT 的 "Hello, World"。

```
-- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Configure the ESP as a station (client)
wifi.setmode(wifi.STATION)  
wifi.sta.config("JY", "1234567654321")  
wifi.sta.autoconnect(1)

-- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Create a CoAP client
cc = coap.Client()

-- Make a POST request
uri="coap://127.0.0.1:8000/object/12345678/send"

tmr.alarm(0, 1000, 1, function() 
    cc:post(uri, "{\"temp\":20}\r\n")
end)
```

### Hello, LWM2M

最後是一個 CoAP-LWM2M Broker 的範例。需搭配 WoT.City 專案使用。

```
- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Configure the ESP as a station (client)
wifi.setmode(wifi.STATION)  
wifi.sta.config("JY", "1234567654321")  
wifi.sta.autoconnect(1)

-- Print IP address
ip = wifi.sta.getip()  
print(ip)

-- Create a CoAP client
cc = coap.Client()

-- Make a POST request
uri="coap://172.20.10.4:8000/object/12345/send"
cc:post(uri, "{\"temp\":30}\r\n")

-- Register LWM2M object
registry="coap://172.20.10.4:8000/75000/1/create"
cc:post(registry, "{}\r\n")
```

這個範例需要搭配一個 *LWM2M broker server * 來使用：

* NodeMCU 以 CoAP 發送一個 LWM2M 的註冊請求
* LWM2M broker 協助 NodeMCU 準備完整的 LWM2M object，並向 LWM2M server 註冊
* LWM2M broker 作為一個代理（proxy）的角色

這個範例，假設 NodeMCU 僅做為 CoAP client 使用，因此 node-wot 目前並沒有移植 er-coap-13 的 CoAP server 實作。

此外，考量硬體限制（ROM 與 RAM 大小），也沒有移植 LWM2M 程式庫至 node-wot。

## 關於 LWM2M

OMA LWM2M 建構在 CoAP 協定層之上，在這次的工作坊裡，展示了如圖 1 的佈署（deploy）策略。

以下是這個佈署策略的簡要說明：

* LWM2M 只佈署在 connected device 上（具對外網路能力的裝置），現階段可使用 Notebook、LinktIt Smart 7688 或 Intel Edison 做為 connected device

* ESP8266 做為 physical data object（IoT data device），並且將 CoAP blocking request 修改為 protothread 機制

* Physical data object 不具備 control 能力

## 留言與討論

* [https://github.com/jollen/blog/issues/1](https://github.com/jollen/blog/issues/1)

</script>]]></description>
         <link>https://www.jollen.org/blog/2015/12/nodemcu-firmware-er-coap-13.html</link>
         <guid>https://www.jollen.org/blog/2015/12/nodemcu-firmware-er-coap-13.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">IoT &amp; WoT</category>
        
        
         <pubDate>Mon, 28 Dec 2015 12:00:39 +0800</pubDate>
      </item>
            <item>
         <title>物聯網架構師： 談 IoT Diagram</title>
         <description>物聯網架構師： 談 IoT Diagram
文/Jollen Chen（原文刊載於 CTIMES 雜誌 2015 年 8 月號）

上一期專欄提到的通訊協定革命，是微觀的 IoT 經濟體；這一期來討論宏觀的 IoT 經濟體：IoT Diagrams。IoT Diagram 簡單說，就是物聯網的構成，這包含很多面向，例如：Product、Service、Experiences、Business Models 等。以下從中摘錄幾個重點主題（Agenda）與大家分享。

第一、Sensor。這是 IoT 最基本的構成，Sensor 的成本與技術，直接影響 IoT 的佈署與互連方式。此外，整合無線通訊技術（WiFi、BLE、GPS、Satellite 與 GPRS ）的 Sensor 會帶出各式的物聯網應用。目前有一些新創公司（例如：WEFT），使用無線通訊技術的 Sensor 來提供物流追蹤（Logistics）服務。

第二、Actor。最主要的 Actor 是使用者（人類），但 M2M 的情境運用越來越廣泛，因此 Device 本身也是 Actor 的角色。在 IoT Diagram 上，Actor-Sensor 是很常見的關聯。例如，Actor 走路時，帶動腳上的計步器。

第三、Device。泛指連網裝置（Connected Device），例如：手機。Sensor-Device 是大家最熟悉的關係，例如：計步器 Sensor 將數據傳送到手機上。報導指出，在 2020 年時，會有超過 50 billion 的連網裝置，當中又以 Sensor-type 的成長最多（可能佔比也最多）。Device-Device 的關聯（M2M）是最重要的使用情境。

第四：Cloud。Cloud-Actor、Cloud-Device 甚致是 Cloud-Sensor 都是重要的關鍵。目前許多通訊協定標準與技術，都在解決 Cloud-Sensor 的技術問題。所以數據收集與傳輸並不會關鍵議題，真正的關鍵是 IoT Cloud 的供應商。IoT Cloud 可以是企業級的服務供應商，也可以是個人自行架設的服務；前者是中央化的生態，後者則是去中央化（decentralized）的生態。

第五：Data。IoT 的重點並不是「Things」，而是「Data」。Data 又涉及隱私（Privacy）與安全性（Security）等問題，這未來將是 IoT 產品化的過程中，需要優先克服的二個問題。其中，隱私問題層面又更廣，例如：涉及法律問題。另外，中央化的 IoT Cloud 生態，因為儲存了大量的個人資料，除了隱私問題外，也可能衍生資料的所有權問題。

第六：Interface。Interface-Device 是最典型的關聯，例如：觸控。Interface 的設計直接影響到 User Experience，因此這個議題有很大的發揮空間。Interface-Sensor 的關鍵中，最常見的設計就是 LED Lights，但隨著 IoT 逐漸被佈署在 Web 上，Interface-Actor 的設計會成為關鍵，目前常見的機制是 Notification 與 Alert 。

第七：Energy。能源，是最近突然被重視的 IoT 議題，Smart Energy 會成為 IoT 領域的創新熱點。更多的裝置，代表更多的能源消耗，所以 Energy Delivery、Smart Energy System 與 Power Sustainability 或許是很有潛力的創業與投資項目。Energy-Device 的關聯，目前仍以電池為主，但 Energy-Sensor 則開始在研究無線供電的做法。

最後，補充說明二點。Smart Grid 是一種 Energy Delivery 的方式，被視為是未來明星產業，目前認為需要產生一套新的電力標準。美國現行的電力網（Power Grid）系，從 1900 年代開始施行至今，已經超過 100 年沒有進行更新。藉著 IoT 的發展，順勢重構現有的 Power Grid，已被提出來討論。

Smart Grid 的技術很可能先應用在 Smart Home 領域。目前的 Smart Home 系統缺乏 Energy 的考量，例如：在屋頂裝設溫度感測器時的永續供電問題。Smart Home 不考慮 Smart Grid 時，就像是買好了一套電視與音響，但裝潢時佈線沒有到位。TI 目前已經開始研究 Smart Grid 技術議題。
</description>
         <link>https://www.jollen.org/blog/2015/09/iot-diagram.html</link>
         <guid>https://www.jollen.org/blog/2015/09/iot-diagram.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Fri, 11 Sep 2015 23:34:57 +0800</pubDate>
      </item>
            <item>
         <title>Change of IoT Apps：mBaaS 遇上 IoT</title>
         <description>文／Jollen Chen（原文刊載於 CTImes 雜誌：2015 年 5 月號）

mBaas 雖然不是新的概念，但它改變了應用程式開發的思惟。比如說：Real-time WebHooks。WebHooks 是一個 Backend-to-backend 的設計模式，mBaas 對 Mobile app 開發模式，產生有一些重要的改變。

「Change of Mobile Apps」是對 mBaaS 最佳的註解。這個 mBaaS 模式，也正開始「Change of IoT Apps」。根據筆者的觀察，mBaaS 商業模式，最有機會在 IoT 領域，取得大規模的成功。現在的 mBaaS 供應商，往 IoT 服務平台發展，已經成為一個標準策略。一個 mBaaS + IoT 的平台，應該具備哪些基本功能呢？以下是筆者的一些匯整，供大家參考。

第一、Smart Push Notification。智能手機將 Push Notification 機制運用到極緻，手機用戶也已經很習慣這樣的推送通知機制。從技術的角度來看，Push Notification 在 IoT 架構中，是一種 Physical to Mobile 的使用案例。將 Physical（裝置實體）的數據，推送到手機上，中間需要一個「IoT 通道」。除了現有的 mBaaS 供應商外，未來應該會有大量的新創公司，提供這類型的的服務。

第二、Social Integration。讓 IoT 裝置與社交網路整合也是一個趨勢，例如：Facebook、WeChat、Twitter 等。從技術的角度來看，與社交網路整合是為了加入 OAuth 認證機制，以及將訊息推送至個人社交平台上。另外，筆者認為，IoT 與 Social Networks 的結合，可能會是另外一個 IoT Apps 的呈現形式。

第三、Small Data Analytics。小量資料分析技術，大多實作於裝置端（In-place analysis）。進行小量資料分析的目的，大致可分為：Filtering、Real-Time Notfitication 與 Time-Series Data Push。將一些無效或無用的資料捨棄，技術上並不太困難，可以安裝一個固定的演算模式或模型（Pattern and Models）在裝置上。小量分析與即時推送的結合，比較偏向於警示性質的訊息（Alert），但未來也可能應用在 LWM2M（Lightweight M2M）的情境中。至於 Time-Series Data 則是應用在連續資料的推送，在推送的過程式，可以為資料加註訊息（例如：Timestamp）或記號等。

第四、REST API Broker。IoT 裝置本身會提供一些簡單的 REST API，或是經由「通道」來「代理提供」，因此筆者也認為，REST API Broker（Proxy）服務，未來也將扮演重要的角色。REST API Broker 的另一個重責大任，就是進行 Backend-to-Backend 的整合。例如，知名的 Firebase 服務，就是一個 Backend-to-Backend 整合的平台。未來這樣的平台，也將延伸到物聯網裝置。

第五、Code Generation。對一個以 MCU 為主的 IoT 裝置，自動代碼產生相對應的代碼，可能是一個重要的機制。例如，Temboo 就提供這樣的服務。從「物聯網裝置」的角度來看，Code Generation 的重要性，應該略大於「視覺化編程」；因為物聯網裝置，不只有 GPIO 控制的功能，也會有網路與演算法的能力。因此，Code Generation 其實是一種 Code Template 的服務，等於是開發者的「懶人包」。另一個需要 Code Generation 的原因是，IoT 裝置會與 mBaaS 或 REST API 做整合，這些程式碼直接由系統產生即可。

第六、IoT Apps Hosting。將 Physical 包裝為 App 將是一個潮流，目前提供相關服務的供應商還不多，但許多新創公司正在往這個方向前進。像是，built.io 就提供 IoT Apps 的代管服務。

以上都是由技術的角度出發，以及過去的收集與觀察，所進行的整理。其中，物聯網應用程式代管服務，是筆者認為最重要，也是最具商業潛力的領域。從上述的分析，可以歸納出一個結論：未來的物聯網新創團隊，勢必要具備 Full Stack 的技術能力，以及 End-to-End 商業模式的策略思考能力。</description>
         <link>https://www.jollen.org/blog/2015/05/change-of-iot-apps.html</link>
         <guid>https://www.jollen.org/blog/2015/05/change-of-iot-apps.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Tue, 05 May 2015 13:18:42 +0800</pubDate>
      </item>
            <item>
         <title>ARM mbed 學習紀錄, #2：IoT Objects 與 Websocket</title>
         <description><![CDATA[<p>Websocket 是 HTML5 標準的一項技術，Websocket 讓 client 與 server 能建立永續性的 TCP 連線。簡單來說，有了 Websocket，就能實作出 real-time data streaming 機制。</p>

<p>以下將說明 IoT 第 4 階段，也就是 WoT 最重要的一個觀念：使用 Websocket channel server 來封裝 IoT objects，讓 IoT devices 成為抽象化的 Websocket server。</p>

<h2>關於 IoT 與 Websocket</h2>

<p>一般來說，Websocket 的使用案例（use case）是 server push（data push）機制，也就是說，ARM mbed 物件本身，應該是扮演 Websocket server 的角色。但現實層面，讓 IoT 分演 Websocket server 的話，會有幾個技術問題：</p>

<p>1. ARM mbed 要管理 client 端的 connections<br />
2. 需要更多的內存來維護 client connections<br />
3. 需要實作 Data push 的演算法，例如：round-roubin<br />
4. 要考量 error handling 與 exception handling</p>

<p>因此，最簡單的 scenario 設計如下：</p>

<p>1. 佈署專用的 Websocket channel server<br />
2. ARM mbed 將 data 即時推送（push）到 Websocket channel server<br />
3. 用戶（user）與 Websocket channel server 建立 Websocket connection<br />
4. 用戶接收 Websocket channel server 的即時資料（經由 server push）</p>

<p>抽像上來看，ARM mbed 仍然是 server 端，而不是 client 端；真正的 client 端是用戶。</p>

<h2>Websocket Channel Server</h2>

<p>Websocket channel server 扮演封裝 IoT 物件的角色，對 Websocket server 來說，只要能定義好「channel」的結構，就能封裝數以萬計、千萬計的 IoT 物件。「抽像上來看，ARM mbed 仍然是 server 端」，就是這樣的觀念。</p>

<p>ARM mbed 官方就提供了 Websocket channel server 的服務：</p>

http://sockets.mbed.org

<h2>ARM mbed 與 Websocket Client</h2>

<p>了解上述觀念後，技術的實作就能區隔為二個部份：</p>

<p>1. Websocket channel server 服務，可使用 sockets.mbed.org<br />
2. ARM mbed 的 Websocket client 實作</p>
]]></description>
         <link>https://www.jollen.org/blog/2015/01/arm-mbed-iot-objects-websocket.html</link>
         <guid>https://www.jollen.org/blog/2015/01/arm-mbed-iot-objects-websocket.html</guid>
        
        
         <pubDate>Mon, 26 Jan 2015 20:49:19 +0800</pubDate>
      </item>
            <item>
         <title>ARM mbed 學習紀錄, #1：IoT、WoT 與 Physical</title>
         <description>在 IoT 的技發發展藍圖裡，描述了 IoT 的 4 個發展階段，其中第 4 個階段就是 WoT。而目前正好處於第 4 個 IoT 發展階段。去年 Google 發起的 Physical Web 計畫，是一個非常先期的研究計畫，就是為了 IoT 的新階段預做準備。

IoT 的第 4 個階段，將聚焦在 Advanced Sensor Fusion 與 Physical-World Web 層面，這二個層面簡單來說，就是 WoT。根據維期百科上的定義，WoT 是 IoT 的 Application Layer，並且是使用 Web 技術來打造 application。也就是說，IoT + Web-enabled technologies 就是 WoT。

對 WoT 來說，最重要的觀念，就是以 URL 來表示 IoT 裝置；為 IoT 加入 URL 的觀念，就是 Google 提出的 Physical Web 計畫。所以說，WoT 與 Physical Web 是一體兩面的觀念，都是 IoT 正進入的新發展階段。

雖然 WoT 都是使用目前已經存在的軟體技術，但許多觀念都要重新思考，例如：

* Architecture 與 Framework
* Composition Layer 的重新設計

一個重新定義的 application 框架，或是 frontend 的 Composition layer 設計，可能會是 WoT 的關鍵技術。因此，利用這次帶領 Mokoversity 農場計畫團隊，到深圳與 Seeed Studio 交流的機會，開始了相關的研究工作。目前已經完成的實驗性質開發，就是利用 virtual DOM 技術，來進行 UI 的 boundary composition。

目前的實驗計畫，選用的 IoT 平臺是 ARM mbed 系統，主要原因有：

* ARM mbed 是 full stack OS
* 更易於實作 REST API（mbed device driver）
* Apache 2 license 更易於商業發展

目前有許多 ARM mbed 的開發板，這些開發板並不是「另一個 Arduino」硬體，而是更能符合 WoT 理念的 RESTful device。同樣的硬體，不同的觀念、技術框架與商業思維，能帶來不同的產品思維與商業模式。所以，ARM mbed 與 WoT 帶來的，將是一場新的革命與機會。</description>
         <link>https://www.jollen.org/blog/2015/01/arm-mbed-1-physical-web.html</link>
         <guid>https://www.jollen.org/blog/2015/01/arm-mbed-1-physical-web.html</guid>
        
        
         <pubDate>Sun, 25 Jan 2015 11:45:44 +0800</pubDate>
      </item>
            <item>
         <title>Life Hacking Startups</title>
         <description>文／Jollen Chen（原文刊載於：CTimes 2015 年 1 月號）

Life hacking 是 1980 年代 Hacker 文化下的副產物。Hacker 的一個重要精神，就是製作能提高生產力（productivity）的工具，例如：命令列工具（command line tools）、快速鍵（shortcuts）或是一些程式設計的小技巧（programming skills）。

程式設計師有很多可以解決常見問題的巧妙方式（ingenious），比如說：利用 editorconfig 解決跨編輯器的問題。使用 editorconfig 也解決了不同人寫程式時的格式問題。Life hacking 文化同樣如此，利用一些方式或技巧，解決生活上的問題，或是提高自已的生產效率。其中一個知名的 life hacking 就是「2mm 法則」。

2mm 的差異，決定了足球射門得不得分，這就是 2mm 法則的一個例子。又如，知名演說家與名嘴，也是 2mm 的差異。2mm 法則是知名演說家 Tony Robbins 提出的 life hacking 觀念。Tony Robbins 在 2007 年被富比士（Forbes）選為「Celebrity 100」。2mm 法則可以延伸到商業、健康與財務等各方面，當然也能延伸到創業活動上。

Life hacking 與技術創業家又有何關係？ 記得不久前，在 Mokoversity 舉辦的一場對談活動中，我提到了「成功，每個人都有不同的定義」。在 Medium 上的一位作者 Ali Mese 也提到：「How do you define success?」這或許應該是創業者的第一堂課。

當我們從上班族切換到創業者身份時，許多 Life 都要去 Hacking。如果你不知道如何幫自己或團隊定義「成功」，那創業又要如何成功呢？每個人對自己事業的成功，都有不同定義，所以必須先建立遵重與信任。例如，有人認為賺到錢，成為億萬富翁叫做成功。有人認為，能滿足自已的生活需要，然後創造出新奇有趣的產品，並從中獲得成就感，就叫成功。

如何定義成功，當然是很重要的 life hacking。Ali Mese 的文章提到，成功的創業者，不一定是輪了幾輪投資，然後拿到幾百萬資金的人。在台灣，有一個現象，就是拿到投資人的資金，或是有了 A 輪融資，或是被併購，就叫創業成功。在媒體的推波助瀾下，這也變成常規了。這是一個有 life hacking 文化的創業環境嗎？

在矽谷，擁有很成功的失敗經驗者，也被視為成功的創業者。所以，life hacking 看重的是創業的過程，一個 journey。在這個 journey 中，是否交到新朋友，找到可以互許終生的對象，是否得到家人的全面支持等等；以及，自己是否很享受這樣的過程。Life hacking 的另外一個層面就是，「從生活中思考」。

Life Hacking Startups 被認為是 2015 年的創業趨之一。比如說，你是創造一個讓生活更便利的產品，或是創造一個能解決大問題的服務；還是，只是 Coding 出一個功能強大的 App 呢？Life hacking 讓創業者思考解決生活問題的妙招，life hacking 也讓創業者更享受創業的 journey。

另一個 life hacking 的例子，就是筆者的 Mokoversity 農場計畫。這個計畫要利用大家的「黃金四小時」來創業。從 life hacking 的哲學告訴我們，「上班當然可以創業」。上班當然也能為創業預做準備，前提是，要有能力 hack 自己的生活。Life hacking startups 另一個層面的哲學，就是如此。

想創業，可是還不想放下全職工作，而且也有老婆跟小孩，還能創業嗎？這當然可以。最有名的例子就是 Ted Roden。Ted Roden 是 FancyHands  的創辦人，他在 New York Times 工作時負責 News.me 項目，就是利用這段時間，他建立了 FancyHands。Ted Roden 在 New York Times 期間，有老婆跟小孩要照顧，但還是成功發佈了 FancyHands，而且還募得資金。

Life hacking 是重要的 hacker 文化，當然也發展出像是 Hack College 這個的組織。成立於 2006 年的 Hack College 就是知名的 life hacking 學校；Hack College 提供大學生 life hacking 方面的知識。Hacker 擅於製作巧妙的工具來解決問題，創業旅途中所遇到的各種問題，例如：老闆、家人或時間等問題，都可以發揮 Hacker 精神，找到 shortcuts 來巧妙解決。Hacker 精神不只用在程式設計上，今年，Hack Your Life，創業去。</description>
         <link>https://www.jollen.org/blog/2015/01/life-hacking-startups.html</link>
         <guid>https://www.jollen.org/blog/2015/01/life-hacking-startups.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 05 Jan 2015 16:30:20 +0800</pubDate>
      </item>
            <item>
         <title>Mokoversity 農場計畫 Hackathon</title>
         <description><![CDATA[<img src="http://i.imgur.com/HtyMT08.jpg" width="800" />
圖：Joker 跟大家分享 The Execution Premier 的讀書心得

Mokoversity 農場計畫進入到第六週的關鍵時刻，也是 ABC of XYZ 的 Stage B。

這個階段是一個 close door 的 hackathon，農場計畫的碼農們（coders）利用週末的二天時間，討論 strategy change agenda，並且 coding 自已的 prototype，為將來的 startup 做好準備工作。大家在一起 Coding Dreams，是一個很愉快的經驗。

這次的 hackathon 還有一個重要目的，就是展示 Web trends 2015 的重要技術。所以，我就以 mokoversity.com 當例子，從 strategy change agenda 的角度，說明 mokoversity.com 重構的目標，包含中期策略與想法。

明年的 Web 技術發展，會進入到非常關鍵的時期，無論是新產品開發、創業、App 設計等，將會有很大的改變。在農場計畫第六週的「Web Trends 2015」課程中，為大家整理並分析了一些 Web trends；在 hackahton 期間，經由實際重構 mokoversity.com 的過程，讓大家看見新技術的特性、設計與架構。

<img src="http://i.imgur.com/6mfQz16.png" width="800" border="1" />

Mokoversity 重構後的新網站已經上線了，新首頁的背後，是這些技術：

1. 使用 Virtual Dom
2. 檢視了 Backbone Model 的 immutable data structure
3. 討論 SPA 與 immutable data structure 的設計與觀念
4. Mutable 與 Immutable 的 MVC 框架效能
5. 以 Underscore template 建立 virtual tree (vtree)
6. UI composition 與 composition boundary 實作
7. Subtree 的 diff 與 patch
8. 以 MVVM 設計軟體架構
9. Multiple data model 的設計與實作
10. 使用 Browserify 與 CommonJS
11. 提到了 ES6
12. Finite Stata Machine 與 UI interactivity
13. 使用了 Constructor pattern、prototype pattern 與 facade pattern

大家在 2014 的歲末，都儲備好 2015 的技術能量了。雖然我的 hackahton 任務，只是重構首頁的小題目，但對自已來說，卻是很充實的學習過程。

另外，雖然經過一些分析後，暫時不打算採用 React，但這是一個重要的 Web trends。Web trends 2015 年的設計主軸仍是 SPA，但技術上有許多不同。特別的是，這些技術已經開始進入到 sensor hardware 領域。

明年開始，Mokoversity 農場計畫，將開始供應 IoT 的內容。從 SPA + IoT 的角度，讓 Maker 打造更有趣的作品。歡迎到 www.mokoversity.com 了解農場計畫。]]></description>
         <link>https://www.jollen.org/blog/2014/12/mokoversity-hackthon.html</link>
         <guid>https://www.jollen.org/blog/2014/12/mokoversity-hackthon.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Mon, 22 Dec 2014 12:36:32 +0800</pubDate>
      </item>
            <item>
         <title>農場計畫 Week #1：Behavior-Driven Development</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/11/mokoversity-farm-1.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/11/mokoversity-farm-1.html">這裡</a>。

<script id='markdown', type='text/plain'>

## Abstract

Behavior-Driven Development（BDD）是以故事情節做為基礎，因此 BDD 的核心在 user story。從 user story 來發展 software prototype 的過程可以單純是 coding，並且使用開發者界的手法「code is document」，來讓代碼與文件合而為一。Code is document 的關鍵，首先取決於二個層面：

* 代碼品質（code quality）
* 專案管理（Git）

現在是一個軟體開發的先進時代，只要代碼品質夠好，它就是一份文件，所以你不必要另外花時間，就只是為了寫 Documentation。好的開發者，閱讀 code is document 的效率，是讀傳統文字文件的數十倍。

有了 code is document 的觀念，就可以明白為什麼在完成一個 user story 後，就可以直接撰寫程式碼、製作第一個 Prototype。這是又直接、又有效率的做法：user story -> prototype。

要測試這個 prototype，可以想辦法將 user story 直接轉化（code generation）為測試步驟（step definitions）。以上介紹的 user story 與 step definitions，可以透過一些 BDD framework 工具來實現。

cucumber 是目前很受歡迎的 BDD framework，它採用 Gherkin 語法來撰寫 user story，並且將 user story 編譯為 step definitions 的 code stub（程式碼骨架）。

## User Story

用「故事情節」的方式，來表達、描述與表現你的功能（features），而不是用規格（spec）的方式。如果你想知道什麼叫「規格描述」，台灣 ODM / OEM 廠裡，應該隨地都可以撿到這種文件。

這個部份，BDD 用到很多方式跟軟體工具，不過從 Startup 的角度來看，其實一開始採用 story board  的方式，再搭配一段 story template 就夠了。一份典型的 story template 如下：

```
In Order To <biz value is derived>
As a <role>
I want <some feature> 
```

從這個 template 的句型來看，story 的敍述應該是「value first」，而不是 function first。一份簡單的 user story 可以極簡到使用「記事本」來撰寫，不過，為了易於管理，選用一個適合的 BDD 寫作架構還是有必要的。

## Cucumber and Gherkin

Cucumber[1] 是目前頗受歡迎的寫作框架，它原本是為 Ruby 語言所打造，不過現在也有 JavaScript 的版本：cucumber-js[2] 。Cucumber 採用 Gherkin 語法來描述 user story。Gherkin 語法所描敍出來的文件結構稱為 'Give-When-Then[3]'，這裡有一個簡單的例子：

```
 1 #
 2 # features/unsplash.feature
 3 #
 4 
 5 Feature: Unsplash feature
 6   As a traveler of photographer
 7   I want to post photos on website
 8   So that I can share with my excitings
 9 
10   Scenario: Post photo
11     Given User login
12     Given Use Facebook account
13     When Interact with upload element
14     Then You see the photo
```

以 user story 來表示 spec 是 BDD 的核心精神。同樣地，你可以用記事本、用自已的語意來撰寫 user story，然後用瀏覽器，以及「工人瀏覽」方式來測試。但有一個 BDD 框架，還是最省時省力的。

Cucumber-js 就提供了這樣的測試框架。要測試你的 features，要再加入二個東西：

* Support files
* Step definitions

Support files 要定義一個 *World* 類別，裡面要實作 *visit* method。Step definitions 就是測試 user story 的步驟，這個部份的程式碼骨架，可以透過 cucumber-js 來產生。

以下是 support files 與 step definitions 的例子，下一個階段再說明程式碼實作細節。

## Support Files

```
 1 // test/features/support/world.js
 2 
 3 module.exports = function() {
 4   var zombie = require('zombie')
 5     , HTML5  = require('html5');
 6 
 7   this.World = function World(callback) {
 8     this.browser = new zombie.Browser(/* {runScripts:true, debug:false, htmlParser:  HTML5} 
 9 */);
10 
11     this.page = function(path) {
12      return "http://localhost:3000" + path;
13     };
14 
15     this.visit = function(path, callback){
16       this.browser.visit( this.page(path), function(err, browser, status) {
17         callback(err, browser, status);
18       });
19     };
20 
21     callback(); // tell Cucumber we're finished and to use 'this' as the world instance
22   };
23 };
```

## Step Definitions

```
 1 // test/features/support/world.js
 2 'use strict';
 3 
 4 module.exports = function() {
 5   var zombie = require('zombie')
 6     , HTML5  = require('html5');
 7 
 8   this.World = function World(callback) {
 9     this.browser = new zombie.Browser(/* {runScripts:true, debug:false, htmlParser: HTML5} 
10 */);
11 
12     this.page = function(path) {
13      return "http://localhost:3000" + path;
14     };
15 
16     this.visit = function(path, callback){
17       this.browser.visit( this.page(path), function(err, browser, status) {
18         callback(err, browser, status);
19       });
20     };
21 
22     callback(); // tell Cucumber we're finished and to use 'this' as the world instance
23   };
24 };
```

## 實作

實作的方式當然是使用 HTML5 技術，也就是 HTML5/CSS/JS。從能力上來看，Full Stack Web Development 的技能是必備的。雖然 Full Stack Web Developer 的工作看似包山包海，要學習的主題也很廣泛，但有架構思維，也有主軸的話，其實學習可以很有效率；否則，學習再多，都是「碎片化」的學習方法，感覺學到或看過的東西很多，但每一種都只有「Hello World」等級。

這裡提到的架構思維就是 Single-Page Application - SPA。

## Feature: Unsplash Story

一份 .feature 檔描敍一個 user story。底下是一個例子，故事的主題叫做「Unsplash」。

```
 1 #
 2 # test/features/unsplash.feature
 3 #
 4 
 5 Feature: Unsplash feature
 6   In order to share my enjoys
 7   As a traveler of photographer
 8   I want to post photos on website
 9 
10   Scenario: Post photo
11     Given User login
12     When Interact with upload element
13     Then I should see "my photo"
14 
15   Scenario: Slideshow
16     Given The latest photo gallery
17     When Tap to next photo
18     Then I see one photo
19       And Full page
```

第 5 行的「Feature:」是 Gherkin 的語法，說明故事標題。標題底下的縮排區段，是故事敍述，請使用先前介紹的 user template 句型，來寫故事：In order to ... As a ... I want ...。

緊接著用「Scenario:」語法，來定義「功能」。Scenario 的寫法是使用「Given-When-Then」句型：

* Given：系統的狀態，例如：User login，表示系列要處在登入狀態
* When：使用者的動作，例如：Interact with upload element，表示使用者與上傳介面互動
* Then：outcomes，也就是輸出。例如：I should see "my photo"，表示畫面顯示使用者上傳的照片

一個 user story 可以包含數個 scenario，也就是多個功能。除此，Gherkin 也有「And, But」語法，例如第 18-19 行。Unsplash story 的 prototype：

http://innoboard.cc/unsplash

## Feature: Slidenow Story

設計一個使用 Markdown 語法的簡報服務，力求極簡易用。

```
 1 #
 2 # test/features/slidenow.feature
 3 #
 4 
 5 Feature: Slidenow feature
 6   In order to lively introduce something
 7   As a coder, developer and instructor
 8   I want to make slides in Markdown
 9 
10   Scenario: Read slides
11     Given Landing page showing slides items
12     When Click the slide item
13     Then I should see full screen "slide"
14 
15   Scenario: Submit a slide
16     Given The submit page
17       And User login
18     When Commit markdown document
19     Then I send a new slide
20       And Use CC4.0 license
```

Prototype 實作：

http://booklog.io

## 特別說明：Solution Storyboard vs User Story

Solution Storyboard（或簡稱 Story Board）是 101 Design Methods 的一個流程，一般是採用漫畫的方式呈現。這是非常有幫助的設計思考方法，細節請看 Leon 老師的摘要文件；以及 101 Design Methods 的 Model 6.7。

Story board 的範例說明裡，將 Mokoversity Farm 的思考，製作成簡報後，再實作為 landing page。但是請注意，這個 landing page 呈現的是一份「簡報」；但 user story 要實作的是 Prototype（App）。

Leon 老師在 hackpad 有一份 story board 說明，這個例子先將想法做成簡報（Presentation），再將簡報內容實作為  landing page。請注意，這份 landing page 是 idea 的「簡報」，而不是 idea 的實作。

同時也要注意，BDD 的 user story 屬於軟體工程的一環，它的目標是書寫 Scenario（也就是軟體功能），然後實作 prototype；BDD 不是用來製作 business plan 簡報的方法論，所以不能把 BDD 的 user story 跟 Design Thinking 或 Lean Startup 畫上等號。實際上，BDD 的 user story 是屬於數學上的 finite state machine（FSM）。

BDD 的 User story 要達到的目的是實作，也就是想法的實作；即 Prototype。

## 關於 Mokoversity 農場計畫

Mokoversity 農場計畫（Farm Team）是一個 Pre-Startup 的訓練場（Play Ground）。在農場裡可以學習 Coding、打造 Prototype 與建構 Entrepreneur Mindset，這裡更是一個 Entrepreneur 黃金圏（Golden Circle）。更多資訊，請訪問：[https://www.mokoversity.com/farm/zh-tw](https://www.mokoversity.com/farm/zh-tw)


## References

[1]: http://cukes.info

[2]: https://github.com/cucumber/cucumber-js

[3]: Given When Then, https://github.com/cucumber/cucumber/wiki/Given-When-Then

[4]: BDD in JavaScript: CucumberJS, http://custardbelly.com/blog/blog-posts/2014/01/08/bdd-in-js-cucumberjs/

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/11/mokoversity-farm-1.html</link>
         <guid>https://www.jollen.org/blog/2014/11/mokoversity-farm-1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 19 Nov 2014 16:13:17 +0800</pubDate>
      </item>
            <item>
         <title>Frontend Engineering－認識 Single Page Application（SPA）</title>
         <description><![CDATA[想開始學網站製作嗎？先看看這篇文章，認識「網站製作」：不只是寫寫 HTML 文件。

關於 Single Page Application（SPA）的討論，較早的學術文章，可以追溯到由 Delft University of Technology 的 Software Engineering Research Group，所發表的一份技術報告：Migrating Multi-page Web Applications to Single-page Ajax Interfaces[1]。Ali Mesbah 與 Arie van Deursen 在這份 2006 年的技術報告裡，提出一個很重要的問題：

"web applications have suffered from poor interactivity and responsiveness towards end users"

這個問題來自傳統的 Web Site 設計模式：「Multi-page」。為解決「poor interactivity and responsiveness」的問題，Single Page Application（以下簡稱 SPA）的設計模式（model）就被提出。從軟體工程（Software Engineering）的角度來看，SPA 必須有一個軟體框架，讓開發者以這個框架為基礎，來進行 Web Site 的開發；從這裡，可以很容易歸納出 2 個結論：

1. 傳統的 Web Site 採取多頁式（Multi-page）做法

2. 現在的 Web Site 開始轉移到 SPA 的觀念，採單頁式（Single-page）做法

從 Multi-page 到 Single-page，不只是將多個頁面，拼成一個頁面而已，而是提供 User 更偏向 Desktop application 的使用經驗[2]。實際上，將 Single-page 看做「把多個頁面組成一頁就好」是錯誤的觀念。怎麼讓 Single-page 的使用經驗，更像是 Desktop application 呢？第一個步驟，就是要把 UI 從 Server-side 移到 Client-side；第二個步驟，在 Client-side 實作 Application Logic。

這二個步驟就是學習 SPA 的第一課。學習 SPA 最好的方式，就是選擇一套適合自已的 SPA 框架，經由學習這個框架的過程，來理解上述這二個步驟的意義。SPA 觀念的提出，源自 AJAX 技術的流行；這麼多年來，有非常多能幫助我們實作 Single-page 的技術，甚致只用 jQuery AJAX 也可以做出 SPA。使用 jQuery AJAX 是一個做法，而且不需要學習像是 Backbone.js 或 AngularJS 的框架，但前提是，你要能接受不斷長高的 Callback Hell。當然，也有人把 Callback Hell 當做一個藝術，而樂此不彼。

無論如何，選擇一套 SPA 框架是比較建議的做法。好消息是，現在有許多優雅好用的方案，上述提及的 Backbone.js 與 AngularJS 是當中最知名的框架。

如果你不想殺雞用牛刀，也喜歡 Callback Hell 的自然美，其實可以在 Backbone.js（或其它框架）或 AJAX 之間做選擇。但差別在哪裡呢？以 Backbone.js 為例，它提供一些重要的機制：

1. Client-side URL Routing

2. Data Model

3. Model State

4. 整合 REST API

5. etc.

現在 Frontend 都要整合 REST API，所以使用 Backbone.js 會讓開發工作更便利、也更簡省時間。

O'Reilly 不久前出版了一本書，書名為：Developing Backbone.js Applications－Building Better JavaScript Applications[3]，更是直接了當地介紹，如何使用 Backbone.js 來製作 Single-page Application (SPA) Model 的 Web Frontend。

另外，只有平面設計的技能，現在並不足以幫助我們製作 SPA；換個角度來說，Frontend 製作，並不是用 Photoshop 等平面設計的工具，將設計好的 UI 製作成 HTML 文件。Frontend 與 Art Design 工作內容不同。比如說，Frontend 需要以 JavaScript 來撰寫 Application Logic，或是呼叫 REST API；正因為 Frontend 開發需要整合 REST API，所以最好能懂一點 Backend 的知識。這就是為什麼近年來，Full Stack 開發受到重視的原因。Art design 的工作可能只涉及美術設計。

最後，Frontend 的製作，並不是規定一定要使用 Single-page 的模式，但是從 User Experience 的角度來看，讓 Frontend 的操作就像是 Desktop Application，似乎是一個很棒的選擇。如果 Frontend 的 User Experience 就像是 Desktop Application，更可以使用打包工具，將 Web Frontend 打包成手機 App，還可以上架到商店。網站製作，已經不是以前的網站製作觀念了。所以，想要學習網站製作嗎？不如直接以 SPA 模式，開始你的第一個 Web Site 吧。

<h2>參考資源</h2>

[1]: Migrating Multi-page Web Applications to Single-page Ajax Interfaces, http://arxiv.org/pdf/cs/0610094v2.pdf

[2]: Single-page application, http://en.wikipedia.org/wiki/Single-page_application

[3]: Developing Backbone.js Applications, http://shop.oreilly.com/product/0636920025344.do
]]></description>
         <link>https://www.jollen.org/blog/2014/09/single-page-application.html</link>
         <guid>https://www.jollen.org/blog/2014/09/single-page-application.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
        
        
         <pubDate>Fri, 19 Sep 2014 01:06:21 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #10：認識 JSON Stringify</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

## JSON Stringify

請注意，上述的 JSON 是一個型別（Type），是一個 Array Type。我們不能儲存或傳送「Type」，所以要將 Type 轉成字串（String）後，才能儲存或傳送。例如，對電腦來說，這是一個物件（Object）：

```
{ "name": "James" }
```

我們把這個物件轉成字串：

```
"{ \"name\": \"James\" }""
```

對電腦來說，這才是字串。所以，將 JSON 物件（Object）轉成字串後，才能儲存或傳送。這個動作就叫 JSON Stringify（字串化）。當然，字串化過的 JSON 字串，要使用時，也要解析（Parse）回物件。

在 Node.js 裡如何做 JSON Stringify 呢？只要呼叫 JSON.stringify() 函數即可。例如：

```
var obj = { "name": "James" };    // 一個物件
var str = JSON.stringify(obj);    // 字串化
```

下面是簡約的寫法：

```
var str = JSON.stringify({ "name": "James" });    // 字串化
```

沿續先前的範例，將 requestHandler.js 加入 JSON Stringify 的處理。完整程式碼如下：

{title="07-websocket-data-push/requestHandler.js"}
```
// 07-websocket-data-push/requestHandler.js
 1 var querystring = require('querystring'); 
 2 
 3 /**
 4  * Global variables
 5  */
 6 var history = [ ];
 7 
 8 function start(response, query, clients) {
 9     console.log("Handler 'start' is started.");
10     console.log("Query string is: " + query);
11 }
12 
13 function send(response, query, clients) {
14     console.log("Handler 'send' is started.");
15     console.log("Query string is: " + query);
16 
17     var parsedstring = querystring.parse(query); 
18 
19     var obj = {
20         message: parsedstring.m,
21         username: parsedstring.u,
22         timestamp: (new Date()).getTime()
23     };
24 
25     history.push(obj);
26 
27     //////// DEBUG ////////
28     for (var i = 0; i < history.length; i++) {
29         console.log("["+i+"]: " + history[i].message);
30     }
31 
32     var json = JSON.stringify({ type: 'message', data: obj });
33 
34     // Data push to all clients
35     for (var i = 0; i < clients.length; i++) {
36         clients[i].sendUTF(json);
37     }
38 }
39 
40 exports.start = start;
41 exports.send = send;
```

第 19 行到第 23 行，將訊息封裝到物件裡，同時也加入使用者名稱，以及時間戳記（Timestamp）。第 32 行將物件字串化，這就是一個標準的 JSON 資枓了。接著，第 35 行到第 37 行，將這筆 JSON 資料，傳送給所有的 WebSocket Client 端。

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/07/nodejs-json-stringify.html</link>
         <guid>https://www.jollen.org/blog/2014/07/nodejs-json-stringify.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Wed, 23 Jul 2014 21:37:01 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #9：學習 JSON 格式</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

## 學習 JSON 格式

思考將要製作的 NoChat 聊天室範例，Server 要把收到的訊息，Push 給所有的 Client 端。Server 與 Client 所使用的標準資料交換格式，就是 JSON。如何把訊息打包成 JSON 格式？方式非常簡單。以表 2 為例，要將這個資料表，撰寫為 JSON 格式，只需要二個步驟：

## Step 1：以 JavaScript 物件表示一筆資料

例如，第一筆個人資料，以 JavaScript 物件來表示的話，只要用 *var* 來宣告此物件即可：

~~~~~~~~
var obj = {
  "name": "Jollen",
  "score":  80
};
~~~~~~~~

大括號是 JavaScript 表示物件的語法。上述範例，我們宣告了 *obj* 物件。

## Step 2：轉換成標準 JSON 語法

去掉等號，以及等號左邊的物件宣告，結果如下：

~~~~~~~~
{
  "name": "Jollen",
  "score":  80
}
~~~~~~~~

請注意，結尾的分號也要一併去除。上述的表示方法，就是標準的 JSON 語法。這個例子用 JSON 來表示一筆個人資料。JSON 表示方法非常地簡單，只要會 JavaScript 保證 1 分鐘即可上手，不需要特意學習。

## Step 3：用陣列表示多個物件

|"name" 欄位    |"score" 欄位   |說明     | 
|==============|==============|==============|
|"Jollen"      |80             |第 1 筆使用者資料|
|"Paul"        |170            |第 2 筆使用者資料|
|"Peter"       |250            |第 3 筆使用者資料|
|"Ellaine"     |580            |第 4 筆使用者資料|

表 2 資料表

表 2 共有 4 筆個人資料，因此需要撰寫 4 個物件，每個物件之間用逗號隔開。試想，過去撰寫程式的經驗裡，我們用哪一個資料結構（Data Structure）來表示多筆型別（Data Type）相同的資料呢？答案是：陣列（Array）。

JavaScript 的陣列用中括號來宣告，例如：

~~~~~~~~
var string = ['Jollen', 'Paul', 'Peter'];
~~~~~~~~

這個例子宣告 *string* 陣列，裡頭有 3 個字串。用 JavaScript 怎麼表示 4 個物件的陣列呢？答案如下：

~~~~~~~~
var persons = [
  {
    "name": "Jollen",
    "score": 80
  },
  {
    "name": "Paul",
    "score": 170
  },
  {
    "name": "Peter",
    "score": 250
  },
  {
    "name": "Ellaine",
    "score": 580
  }
];
~~~~~~~~

我們只要把上述的 4 個物件，用陣列「群組」起來即可。和 Step 2 相同，保留以下的寫法即可：

~~~~~~~~
[
  {
    "name": "Jollen",
    "score": 80
  },
  {
    "name": "Paul",
    "score": 170
  },
  {
    "name": "Peter",
    "score": 250
  },
  {
    "name": "Ellaine",
    "score": 580
  }
]
~~~~~~~~

這就是表 2 的 JSON 表示方式了。將上述的 JSON 儲存為純文字，這個純文字檔就叫做 JSON Document，這就是 JSON Document 資料庫的概念。

同理，NoChat Server 收到訊息後，只要把訊息表示成 JSON 後，傳送給 Client 端即可。JSON 相當簡單易學，更是優秀的輕量級資料交換格式。

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/07/nodejs-json.html</link>
         <guid>https://www.jollen.org/blog/2014/07/nodejs-json.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Wed, 23 Jul 2014 21:35:28 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #8：認識 JSON 與 Web App 的概念</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

## Web-Oriented Architect

Web 導向架構（WOA, Web-Oriented Architect）著重幾個觀念：

- Device-Server 設計模式
- Device 端使用 Browser，以 Browser 做為執行環境（Runtime）
- Server 端提供 APIs，即 PaaS 概念
- Device-Server 採用非同步通訊（Asynchronous communication）

事實上，非同步通訊大家都使用過，就是 AJAX；AJAX  的第一個 A 就是 Asynchronous。但是考量 Server 端的負載（Loading），以及百萬連線（Millions requests）等級的處理能力需求，應該儘量少用 AJAX 機制。這就是 Device-Server 與 Client-Server 的差別，大家可能還不太明白。所以，將二者的差別簡單整理如下：

- Client-Server 做法：在瀏覽器裡（Client）主動向 Server 請求內容，Client 定時（如：每隔5秒鐘）發出請求，持續更新內容。
- Device-Server 做法：在裝置端（Device）和 Server 建立連線，Server 主動將更新內容推送（Push）給裝置端，更明確地說，裝置裡的瀏覽器，瀏覽器再將新內容刷新。

這樣就很清楚了，傳統的 Client-Server 做法是「Data Pull」，即主動去拉資料；Device-Server 的做法是「Data Push」，即推送資料，由 Server 在必要時才將資料推送給 Device。Data Push 的經典代表作就是 BlackBerry（黑莓機）的郵件服務。

為什麼 AJAX 不好用？因為 Server 要冒著「不知道有多少 Client、不知道同時有多少 Requests」的風險，會增加 Load Balancer 佈署的成本；另外，當然就是即時性的問題（Live Streamming）。要達成 Data Push 的目的，有解決二個技術問題：

- Device 端要與 Server 建立永續性（Persistent）連線，也就是 Socket Connection
- Server 推送出去的資料，格式要有統一標準，且輕量化

要解決這二個問題，要使用到二項技術：WebSocket 與 JSON。從以上的說明，可以大略了解 HTML5 的威力在於「Web App」的應用領域，而不只是 Web Page 的製作：

- 從 Web Page 角度來看，以 Client-Server 為主，這像是傳統 PC 時代的使用案例
從 Web App 角度來看，將 Device-Server 為主

- Web App 的開發思惟，與 Web Page 有很大的不同。目前 Web App 的 UI 製作，採是強調跨螢幕與裝置的特性，這種設計稱為 Responsive Design。並且，Responsive Design 進向以行動裝置為預設值的做法，也就是「Mobie First」


## Web App 技術

從 Web App 的角度看 HTML5，以下是初學者可考慮優先切入學習的技術：

- PhoneGap：Device API 的標準，使用 JavaScript 呼叫 Device API 的好技術，Nitobi 公司是 PhoneGap 的開發商，這家公司現已被 Adobe Systems 收購

- WebSocket：HTML5 標準裡的一個技術

- Node.js：開發專用 Web Service 的技術，採用 JavaScript 語言

Apache Web Server 是通用型的 Web Server，主要提供頁面（Pages）服務。Node.js 則是 Web Service 的技術，用來開發專用的 Web Service APIs。

現在開發專用的 Web Service 非常重要，這是 PaaS 的靈魂。例如，開發股票報價專用 Web Server。過去常聽到的 Web Server，例如：Apache，都是一般用途的 Web Server，用來「host Web pages」。

現在 Client 端的網頁是用 JavaScript，Server 端的開發也可以用 JavaScript，Client/Server 通通都用 JavaScript，這是一個「All in JavaScript」的時代。


## 重要的資訊交換格式：JSON

傳統 Backend 的做法，會提供 Client 端一份 HTML5 的片斷文件，而不是格式化後的資料（Formatted），這是一個缺點。

如果 Server 回傳的是格式化後的資料，Client 端就可以更有效率地利用這些資料。試想，如果 Google 傳回的搜尋結果是一堆 HTML5，那我們不就還要再去 Parse 這份文件，才能取出真正的結果，然後才能再次利用這些資料（例如：再儲存為 CVS 格式）。

為了解決這個問題，必須要有一個標準，不能大家都用自已的 HTML5 文件，或是自定的格式。軟體工程師設計了一些標準。一開始提出的做法，是制定標準的XML標籤，這樣大家就可以統一文件格式了。但是還有一個問題，就是「資料量太大」。

試想，Server 要回傳二筆資料，這二筆資料都是電話號碼：

- 0911-111-111
- 0900-000-000

然後用XML來表示，就變成：

```
<Telephone>
   <Item>0911-111-111</Item>
   <Item>0900-000-000</Item>
</Telephone>
```

這種把資料打腫了才回傳的做法，大大浪費網路頻寬。上面只是一個簡單的例子，現實環境，要回傳的資料可能是一個 10MB 的 XML 文件，結果原始資料可能只有 1MB。

要解決這個問題，就要有一個輕量級（Light-weight）的資料交換格式，這項技術就是 JSON。所以，JSON 是 Client/Server 交換資料的一種格式，一種 Light-weight data exchange 技術。


</script>]]></description>
         <link>https://www.jollen.org/blog/2014/07/nodejs-json-web-app.html</link>
         <guid>https://www.jollen.org/blog/2014/07/nodejs-json-web-app.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Wed, 23 Jul 2014 21:26:53 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #7：儲存用戶端 WebSocket 連線</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

## 儲存用戶端 WebSocket 連線

要儲存所有的用戶端 WebSocket 連線，最簡便的方式是使用 Global Array：

```
// Connected WebSocket clients
var clients = [];
```

當用戶端與 Node.js 建立連線時，將會回呼上述提及的 onWsRequest() 函數。所以，儲存連線的程式碼，應該添加至這個地方。繼續加入程式碼如下：

```
function onWsRequest(request) {
  var connection = request.accept('echo-protocol', request.origin);
  console.log("WebSocket connection accepted.");

  // Save clients (unlimited clients)
  clients.push(connection);

  connection.on('message', onWsConnMessage);
  connection.on('close', onWsConnClose);
}
```

以下目前為止的最新程式碼。

```
// 07-websocket-data-push/server.js
 1 var http = require("http");
 2 var url = require("url");
 3 var WebSocketServer = require('websocket').server;
 4 
 5 // Connected WebSocket clients
 6 var clients = [];
 7 
 8 function start(route, handlers) {
 9   function onRequest(request, response) {
10     var pathname = url.parse(request.url).pathname;
11     var query = url.parse(request.url).query;
12 
13     console.log("Request for " + pathname + " received.");
14 
15     route(pathname, handlers, response, query, clients);
16 
17     response.writeHead(200, {"Content-Type": "text/plain"});
18     response.write("Hello World");
19     response.end();
20   }
21 
22   var server = http.createServer(onRequest).listen(8080, function() {
23      console.log("Server has started and is listening on port 8080.");
24   });
25 
26   wsServer = new WebSocketServer({
27     httpServer: server,
28     autoAcceptConnections: false
29   });
30 
31   function onWsConnMessage(message) {
32     if (message.type == 'utf8') {
33       console.log('Received message: ' + message.utf8Data);
34     } else if (message.type == 'binary') {
35       console.log('Received binary data.');
36     }
37   }
38 
39   function onWsConnClose(reasonCode, description) {
40     console.log('Peer disconnected with reason: ' + reasonCode);
41   }
42 
43   function onWsRequest(request) {
44     var connection = request.accept('echo-protocol', request.origin);
45     console.log("WebSocket connection accepted.");
46 
47     // Save clients (unlimited clients)
48     clients.push(connection);
49 
50     connection.on('message', onWsConnMessage);
51     connection.on('close', onWsConnClose);
52   }
53 
54   wsServer.on('request', onWsRequest);
55 }
56 
57 // Export functions
58 exports.start = start;
```

接下來的任務是製作 Frontend（Client 端）；不過，現在是一個認識與學習 JSON 的絕佳時機。

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/07/nodejs-websocket-connections.html</link>
         <guid>https://www.jollen.org/blog/2014/07/nodejs-websocket-connections.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Wed, 23 Jul 2014 21:18:38 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #6：撰寫 WebSocket Server</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

## 認識 WebSocket

WebSocket 是 HTML5 裡的一個標準，它是一種 TCP/IP 的連線技術。在協定部份，則是基於 HTTP（Over HTTP）協定。因此，WebSocket 標準定義了一些 HTTP Headers 來進行 Client/Server 的通訊。

WebSocket 分為 Client 端與 Server 端二個部份，本章要介紹的是利用 Node.js 技術，來開發 WebSocket Server。
目前有許多現成的 Node.js WebSocket 模組可使用，實作時，我們就不必自行處理複雜的 WebSocket 協定問題。

## 安裝 WebSocket 模組

本章所使用的 WebSocket 模組，要使用 npm 工具另外安裝。利用 npm 安裝 WebSocket-Node：

```
 $ npm install websocket
```

WebSocket-Node 原始碼可由 Github 上取得：

https://github.com/Worlize/WebSocket-Node

安裝，依據需要，可引入不同的模組。WebSocket-Node 提供 4 個模組如下：

- var WebSocketServer = require('websocket').server;
- var WebSocketClient = require('websocket').client;
- var WebSocketFrame  = require('websocket').frame;
- var WebSocketRouter = require('websocket').router;

NoChat 範例，將會使用 'server' 模組。

## 建立 WebSocket Server

基於先前的範例，繼續修改 server.js 的程式碼如下：

```
// 06-websocket-with-protocol/server.js
 1 var http = require("http");
 2 var url = require("url");
 3 var WebSocketServer = require('websocket').server;
 4 
 5 function start(route, handlers) {
 6   function onRequest(request, response) {
 7     var pathname = url.parse(request.url).pathname;
 8     var query = url.parse(request.url).query;
 9 
10     console.log("Request for " + pathname + " received.");
11 
12     route(pathname, handlers, response, query);
13 
14     response.writeHead(200, {"Content-Type": "text/plain"});
15     response.write("Hello World");
16     response.end();
17   }
18 
19   var server = http.createServer(onRequest).listen(8080, function() {
20      console.log("Server has started and is listening on port 8080.");
21   });
22 
23   wsServer = new WebSocketServer({
24     httpServer: server,
25     autoAcceptConnections: false
26   });
27 
28   function onWsConnMessage(message) {
29     if (message.type == 'utf8') {
30       console.log('Received message: ' + message.utf8Data);
31     } else if (message.type == 'binary') {
32       console.log('Received binary data.');
33     }
34   }
35 
36   function onWsConnClose(reasonCode, description) {
37     console.log(' Peer disconnected with reason: ' + reasonCode);
38   }
39 
40   function onWsRequest(request) {
41     var connection = request.accept('echo-protocol', request.origin);
42     console.log("WebSocket connection accepted.");
43 
44     connection.on('message', onWsConnMessage);
45     connection.on('close', onWsConnClose);
46   }
47 
48   wsServer.on('request', onWsRequest);
49 }
50 
51 // Export functions
52 exports.start = start;
```

先將 WebSocket-Node 的 'server' 匯入，如程式碼第3行。其它的修改細節條列如下：

- 第 19~26 行：將原本的 HTTP Server 物件，聚合至（傳遞）WebSocket Server。WebSocker Server 的物件名稱為 wsServer
- 第 48 行：在 wsServer 物件裡註冊一個 Request Handler，即 onWsRequest() 函數
- 第 40 行：當 WebSocket 的連線請求發生時，便回呼此函數
- 第 41 行：接受該 WebSocket 連線，第一個參數是 WebSocket Protocol，這是一個自定的協定名稱，用途由開發者定義
- 第 44~45 行：為此連線註冊 Message Handler 與 Close Handler 函數
- 第 28 行：收到用戶端傳送過來的訊息時，回呼此函數，後續將繼續擴充此函數，將收到的訊息儲存，並將訊息即時（Real-time）推送（Push）到所有的用戶端
- 第 36 行：該 WebSocket 連線關閉後，回呼此函數

學會建立 WebSocket Server 後，就可以開始建立與 Client 端的連線了。

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/07/nodejs-websocket-server.html</link>
         <guid>https://www.jollen.org/blog/2014/07/nodejs-websocket-server.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Wed, 23 Jul 2014 21:13:18 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #5：解析 Query String</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

## 解析 Query String

Client 端呼叫 Server 所提供的 Web Service API。所以，現在的關鍵是如何解析 Query String。如圖 2.2，Node.js 使用 querystring 模組來解析 Query String。先將 querystring 模組匯入，接著呼叫 parse() 函數：

```
var querystring = require('querystring'); 
var parsedstring = querystring.parse(“m=helll&u=jollen”); 
```

解析後的結果存放於 parsedstring 物件，回傳結果：

```
{ m: 'hello', u: 'jollen' } 
```

parse() 函數有三個參數：

```
querystring.parse(str, [sep], [eq])
```

- str 是 Query String
- sep 是「Separator」，也就是字串的分隔字元，預設是 '$'，通常不做變數
- eq 則是字串與值的對應字元，預設是 '='，通常不做變數

了解如何解析 Query String 後，就可以開始進行後續的工程了。再次修改 requestHandlers.js，如下：

```
// 05-query-string/requestHandlers.js
 1 var querystring = require('querystring'); 
 2 
 3 /**
 4  * Global variables
 5  */
 6 var history = [ ];
 7 
 8 function start(response, query) {
 9     console.log("Handler 'start' is started.");
10     console.log("Query string is: " + query);
11 }
12 
13 function send(response, query) {
14     console.log("Handler 'send' is started.");
15     console.log("Query string is: " + query);
16 
17     var parsedstring = querystring.parse(query); 
18 
19     var obj = {
20         message: parsedstring.m,
21         username: parsedstring.u,
22         timestamp: (new Date()).getTime()
23     };
24 
25     history.push(obj);
26 
27     //////// DEBUG ////////
28     for (var i = 0; i < history.length; i++) {
29         console.log("["+i+"]: " + history[i].message);
30     }
31 }
32 
33 exports.start = start;
34 exports.send = send;
```

這裡利用一個全域陣列 *history* 來儲存訊息。將收到的訊息封裝成物件後， 再使用標準的陣列操作將物件放到陣列裡。另外，我們也將一個時間記號（Timestamp）一併封裝至該物件，用來紀錄接收到訊息的時間。

## 結論

到這裡完成了 Node.js 入門的學習：

- 學會撰寫第一個 Node.js 程式
- 學會啟動 HTTP Server
- 了解並實作 URL Routing
- 學會解析 Pathname 與 Query String



</script>]]></description>
         <link>https://www.jollen.org/blog/2014/07/nodejs-parse-query-string.html</link>
         <guid>https://www.jollen.org/blog/2014/07/nodejs-parse-query-string.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Wed, 23 Jul 2014 21:11:04 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #4：認識 HTTP API</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

## 設計 HTTP API

完整的 NoChat 分為二個部份：

- Backend（Server-side）將基於目前的 Node.js 程式碼繼續完善
- Frontend（Client-side）手機端的 App 將以 HTML5 + PhoneGap 來製作

NoChat 提供二個API，現在將 API 詳細定義如下。

- /start，建立與 Client 的 WebSocket 連線
- /send，送出訊息。

'/send' API 的 Query String 參數定義如表 1。這部份在前面已做過說明。

|參數    |值       |用途說明      |
|========|========|=============|
|m      |'hello'  | 指定要傳送的訊息 (message) |
|u      |'jollen' | 指定 Username |

表 1 API 的參數

## 測試案例

以下設計一個簡單的測試案例，在完成第一個 NoChat 的 Prototype 後，將以下列步驟進行測試：

1. 在 localhost 啟動 Node.js

2. 打開 client.html 聊天網頁

3. client.html 呼叫 API：http://localhost:8080/start，Server 回傳 "OK" 訊息

4. client.html 與 Server 建立 WebSocket 連線

5. clieht.html 開始接收 Node.js 推送（Data Push）的即時訊息

傳送訊息給 Node.js 的測試步驟：

1. 開啟一個新的瀏覽器視窗

2. 使用瀏覽器呼叫 API：http://localhost:8080/send?m=hello

3. Node.js 收到訊息，並透過 WebSocket 將訊息 Push 給所有的用戶端

這還不算是一個真正的使用案例(Use Case)，但至少可以幫助我們實作出第一個Prototype。

## 關於 Web Service

前一篇文章中的圖 2.2，是大家所熟悉的 HTTP API 形式。許多網站，像是：Google、Facebook 等，都有開放 HTTP API 供開發者存取它們的服務。以 NoChat 來說，透過上述二個 API 可以向 Server 請求服務。因此，Node.js 的重心，就是在發展 Web Service。

Web Service 的 API 定義，未來將重構為 REST 標準。基於 HTTP 的 Web Service API，是目前為止，我們所學到的重要觀念。

此外，呼叫 HTTP API 的方式，可使用 GET 與 POST 二種 HTTP 方式（HTTP Method），這二種方式都是定義在 HTTP 裡的標準。REST 標準，也引用了其它的 HTTP Method。

目前，NoChat 仍暫時以 Query String 的方式來傳遞參數。

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/07/nodejs-http-api.html</link>
         <guid>https://www.jollen.org/blog/2014/07/nodejs-http-api.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Wed, 23 Jul 2014 20:46:53 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #3：URL Routing 觀念與實作</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

延續前二篇文章的範例，接著介紹 Node.js 最基本也最重要的觀念－URL Routing。範例網址：https://github.com/jollen/html5-websocket-nodejs

## URL Routing

這是處理 URL（HTTP Request）與 Query String 的核心觀念，這是利用 Node.js 開發 Web Service 的重要步驟。讓我們先來了解 Routing 的寫法，再來探討它的觀念。

首先，先改寫 server.js 模組如下：

```
// 03-route/server.js
 1 var http = require("http");
 2 var url = require("url");
 3 
 4 function start(route) {
 5   function onRequest(request, response) {
 6     var pathname = url.parse(request.url).pathname;
 7     console.log("Request for " + pathname + " received.");
 8     console.log("Request url: " + request.url);
 9 
10     route(pathname);
11 
12     response.writeHead(200, {"Content-Type": "text/plain"});
13     response.write("Hello World");
14     response.end();
15   }
16 
17   http.createServer(onRequest).listen(8888);
18   console.log("Server has started.");
19 }
20 
21 // Export functions
22 exports.start = start;
```

Routing 觀念的主要用途是處理 URL，所以我們利用 url 模組來取出 URL 裡的 pathname，並將 pathname 交給 route() 函數來處理。這裡很特別的地方是，start() 函數裡所呼叫的 route()函數，是透過參數列傳遞進來的，這和模組的 Closure 特性有關係，觀念說明如下：

- route() 函數實作在 router 模組，而不是 server.js 模組
- 目的是將 Routing 的功能，拆成單獨的模組來維護
- route() 函數由 router.js 模組提供，必須引用 router 模組

在這裡範例裡，我們由 index.js 引入 router.js 模組，並且將裡頭的 router() 函數，透過參數列交給 start()函數。如此一來，start() 也可以呼叫到 route() 函數了。

這個部份，可以選擇另外一個實作方法：在 server.js 裡引用 router.js 模組。不過，就概念上來說，範例的實作方式好一些。原因如下。

- Decompostion：將 router.js 與 server.js 模組的相依性解除
- Component-based software engineering：將 router.js 與 server.js 做成獨立的模組，他們之間如果沒有相依性，就可以做為二個不同的模組來使用。例如，將 router.js 模組抽換成其它專案的 Routing 模組，並且 server.js 可以重用。Node.js 的軟體架構，主軸是模組化，即 Component-based 軟體工程的觀念

目前透過 npm 指令，不但可以安裝到各式不同的 Node.js 模組，甚致可以將自已的模組出版（Publish）給其他開發者使用。

Node.js 的事件處理機制，採用典型的 Callback Functions 做法。

接著，要開始處理 Pathname 與 Query String 的解析，請先參考圖 2.2。

![圖 2.2：API 與 Query String](23/figure-2_2.png)
圖 2.2：API

改寫 index.js 主程式如下：

```
// 03-route/index.js
1 var server = require("./server");
2 var router = require("./router");
3 
4 server.start(router.route);   // 傳遞route物件
```

將 Routing 的演算法製作成獨立的模組，並將 router() 函數傳遞給 start()，函數的參數，可以傳遞一個函數，這個觀念就是 Lambda。router.js 完整程式碼如下：

```
// 03-route/router.js
1 function route(pathname) {
2     console.log("Route this request: " + pathname);
3 }
4 
5 exports.route = route;
```

請注意，這個範例雖然陽春，但是展示了一個非常重要的觀念：

- 函數就是物件，所以我們把 *route* 物件交給 start() 函數，讓 start() 函數去使用物件
- 直接在 start() 裡呼叫 route() 函數也可以，為什麼不這樣做？因為這不是 JavaScript 的觀念，倒是有點像是標準 C 語言呼叫函數的觀念，同時也會降低程式碼的可維護性

接下來，要讓 route() 解析 pathname。例如，我們定義了二個 API：

- http://localhost:8080/start，用來連接伺服器並接收即時訊息
- http://localhost:8080/send，送出文字訊息

分別要處理二個 pathname 如下：

- /start，呼叫專屬的 Handler 'start()' 來處理
- /send，呼叫專屬的 Handler 'send()' 來處理

實作的關鍵來了，我們要利用 Request Handler 的觀念來實作，首先，修改 index.js 如下：

```
// 04-request-handlers/index.js
 1 var server = require("./server");
 2 var router = require("./router");
 3 var handlers = require("./requestHandlers");
 4 
 5 // 使用 Object 來對應 pathname 與 request handlers
 6 var req = {
 7    "/": handlers.start,
 8    "/start": handlers.start,
 9    "/send": handlers.send
10 };
11 
12 // 傳遞 request handler 
13 server.start(router.route, req);
```

上述的二個 Handler 函數：start() 與 send() 將另行實作於 requestHandlers 模組。requestHandlers 模組匯出 start() 與 send() 函數，分別處理相對應的 pathname。

因此，主程式在第 6 行到第 10 行的地方，利用 *req* 物件來對應這個關係。在呼叫 start() 時，將 req 物件傳入。

另外，JavaScript 雖然不是物件導向式語言，但仍要以物件的觀念來撰寫。所以，我們將 *req* 以 var 語法定義成 object。很多時候，或許也能以 associative array 來實作，但並不是很建議。

以下就是一個以 associative array 的實作範例，原則上不推薦：

```
 1 var server = require("./server");
 2 var router = require("./router");
 3 var handlers = require("./requestHandlers");
 4 
 5 // 使用 associative array 來對應 pathname 與 request handlers
 6 var req = {};
 7
 8 req["/"] = handlers.start;
 9 req["/start"] = handlers.start;
10 req["/send"] = handlers.upload;
11 
12 // 傳遞 request handler 
13 server.start(router.route, req);
```

修改後的 router.js 如下：

```
// 04-request-handlers/router.js
 1 function route(pathname, handlers, response) {
 2     console.log("Route this request: '" + pathname + "'");
 3 
 4     // 檢查 pathname 是否有對應的 request handlers
 5     if (typeof handlers[pathname] == "function") {
 6         handlers[pathname](response);
 7     } else {
 8         console.log("No request handler for this pathname: '" + pathname + "'");
 9     }
10 }
11 
12 exports.route = route;
```

再次修改 server.js 如下：

```
// 04-request-handlers/server.js
 1 var http = require("http");
 2 var url = require("url");
 3 
 4 function start(route, handlers) {
 5   function onRequest(request, response) {
 6     var pathname = url.parse(request.url).pathname;
 7     console.log("Request for " + pathname + " received.");
 8 
 9     route(pathname, handlers, response);
10 
11     response.writeHead(200, {"Content-Type": "text/plain"});
12     response.write("Hello World");
13     response.end();
14   }
15 
16   http.createServer(onRequest).listen(8080);
17   console.log("Server has started.");
18 }
19 
20 // Export functions
21 exports.start = start;
```

最重要的模組：requestHandlers.js，完整程式碼如下：

```
// 04-request-handlers/requestHandlers.js
 1 function start(response) {
 2     console.log("Handler 'start' is started.");
 3 }
 4 
 5 function send(response) {
 6     console.log("Handler 'send' is started.");
 7 }
 8 
 9 exports.start = start;
10 exports.send = send;
```

到這裡，已經完成了一份很基本的 Web Service 實作。接下來，我們要將這個成果發展成一個即時聊天軟體，就命名為 NoChat。NoChat 將會是一個完全使用 HTML5 技術開發的即時聊天軟體。

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/07/nodejs-url-routing-practice.html</link>
         <guid>https://www.jollen.org/blog/2014/07/nodejs-url-routing-practice.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Wed, 23 Jul 2014 20:39:01 +0800</pubDate>
      </item>
            <item>
         <title>Web Starter Kit: 環境與開始動手</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/06/web-starter-kit-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

本文介紹 Web Starter Kit 的入門第一步：簡單認識開發環境，並開始修改第一個 Web App。首先，必須根據 [Web Starter Kit](https://developers.google.com/web/fundamentals/tools/setup/setup_kit#install-tooling) 官方網站上的說明，安裝 Web Starter Kit 所需的開發環境。

從架構面來看，Web Starter Kit 提供二個環境：

* Node.js 的 Runtime
* 一套 Web App 的 HTML5 文件（ Mobile-optimized HTML 模板與 CSS 定義）

Web Starter Kit 並非像是 Android SDK 提供完整的 IDE 環境，所以使用 Web Starter Kit 前，仍有一些背景知識與工具要事前準備好。第一個必備工具，當然就是 Editor 了。撰寫程式碼，包含 HTML5 與 CSS，最佳首選當然就是 Sublime Text 3。Web Starter Kit 官方也推薦這個編輯器；這個編輯器是工程師的基本裝備。

![圖 1: 編譯](http://i.imgur.com/9fiUTeb.png)

Web Starter Kit 內容比較單純：就只有 Node.js 的 Runtime，以及一套 HTML5 文件模板。不過，它確實簡化許多繁鎖的技術細節。例如，現在的 Node.js 開發者，每個人都有不同的環境，有些人用 Grunt，有些人用 Gulp；同樣是使用 Grunt 環境，每個人撰寫的 Tasks 又不一樣。

使用 HTML5 來開發 Web App，大家的 Page Structure 又不同，每個人還會有自已的 CSS 定義；如果是 RWD 的話，Media Query 的寫法又不一致（對螢幕寬度的定義標準不一）。如果現在大家都直接引用 Web Starter Kit，這一切都可以成為標準。所以，Web Starter Kit 或許有機會把 Multi-Device HTML5 App 的開發與 User Experience 標準化。


## Step 1: 編譯

下載並完成 Web Starter Kit 的環境安裝後，直接在 *web-starter-kit-0.2.0-beta/* 目錄下執行 *gulp* 指令，編譯 *app/* 下的內容。編譯後的結果，會存放至 *dist/* 目錄下。

![圖 2: 編譯](http://i.imgur.com/XKyfmzq.png)

## Step 2: 執行

編譯後，再執行 *gulp serve* 指令，啟動 Node.js 的 Web Server。Web Starter Kit 的 gulp 環境支援 Live Reload；這時，gulp 會自動開啟瀏覽器，並且自動瀏覽我們的 Web App。

![圖 3: 啟動 Node.js Web Server](http://i.imgur.com/n4KzH0F.png)

![圖 4: 第一個 Web App](http://i.imgur.com/PTEJ8YW.png)

## Step 3: 體驗 Live Reload

開啟 *app/* 目錄下的 *index.html* 文件，這就是在上個步驟所看到的網頁。你會發現，*index.html* 已經將 ViewPort、Page Structure 等各種技術細節，都事先定義完成了。我們的第一個 Web App 就從這裡開始。

首先，可以找到 &lt;header&gt; 區塊，接著將裡頭的 &lt;h1&gt; 文字修改為「InnoBoard」：

```
        <header class="app-bar promote-layer">
            <div class="app-bar-container">
                <button class="menu"><img src="images/hamburger.svg"></button>
                <h1 class="logo">InnoBoard</h1>
                <section class="app-bar-actions">
                <!-- Put App Bar Buttons Here -->
                </section>
            </div>
        </header>
```

存檔的同時，會發現 gulp 幫我們重新「整理網頁」了，而且重速相當快。這個功能就叫做 Live Reload。

</script>
]]></description>
         <link>https://www.jollen.org/blog/2014/06/web-starter-kit-2.html</link>
         <guid>https://www.jollen.org/blog/2014/06/web-starter-kit-2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Fri, 27 Jun 2014 15:57:40 +0800</pubDate>
      </item>
            <item>
         <title>認識 Web Starter Kit</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/06/web-starter-kit-1.html">這裡</a>。

<script id='markdown', type='text/plain'>

Bootstrap 是一個廣受歡迎的 CSS Framework，它有現成的 Grid System 可以支援 Responsive Web Design（RWD）網頁製作。Responsive Web Design是製作多屏（Multi-Device）網頁的技術，RWD 能讓網頁同時在 PC、平板與手機等不同的 Screen 上顯示。

一些 App 開發者，會以 HTML5 的技術來製作 App。除了直接撰寫 Media Query 外，利用像是 Bootstrap 這樣的 CSS Framework 也大有人在。現在，HTML5 App 開發者又多了另外一個選擇了：[Web Starter Kit](https://developers.google.com/web/starter-kit/)。Web Starter Kit 是 Google 官方出品的工具，它的主要目的就是幫助開發者，製作 Responsive Web Design 的 App。

## 主要特色

根據 Web Starter Kit 的官方說明，這套工具的主要特色如下：

* 提供一個 Mobile-optimized 的 HTML5 文件模板
* 提供 RWD 的能力
* 提供一些視覺化的組件（Component）
* 提供一套基於 GulpJS Build System 的環境

在 2013 年以前，Node.js 開發者會使用 Grunt 來搭建工作環境；Grunt 是一個 Task Runner 環境，用來撰寫一些「任務」，例如：啟動 Node.js 主程式的任務、或是最小化 CSS 的任務。這有點像是 Makefile 環境。

## 使用 Gulp.js

Gulp.js 在 2014 年橫空出世，有一種完全取代 Grunt 的感覺。 Gulp.js 的特點是使用了 Node.js 的 Streams  系統，Streams 是一個非常重要的 Node.js 特色。Streams 提供了抽象的 Read/Write 接口，並且基於 EventEmitter。

EventEmitter 又是什麼東西呢？這是一個 Node.js 的事件處理系統，Backend 開發者會用它來實作 Workflow Automation。總而言之，基於 Streams 的 Gulp 擁有更好的處理效能。

此外，寫過 Gruntfile.js （Grunt 的設定檔）的開發者都知道，Gruntfile.js 雖然也是使用 JavaScript 語法，但程式碼內容並不好看；意思是，感覺不像是在寫 Node.js 程式。Gulp.js 就很有寫程式的感覺。Web Starter Kit 會採用 Gulp.js 一點都不另人意外。

## Responsive Web Design Patterns

Web Starter Kit 的另一個特點是，它把 Responsive Web Design 歸納成一套 Patterns，稱為 Responsive Web Design Patterns。個人認為，這是 Web Starter Kit 非常重要的貢獻。雖然類似這種 Mobile-optimized UI Patterns 不在少數（例如 Android App 就有自已的 UI Patterns），但 Web Starter Kit 為繁為簡、汲取各家之優點，整理出 5 個基礎的模式，對於使用 HTML5 來開發 Multi-Deivce Apps 是非常重要的 Milestone。這項貢獻有助於達到二個重要目的：

* Unified user experience。未來可以統一操作方式：提供使用者一致性的操作經驗。
* Device experience。Web Starter Kit 的目標是 Multi-Device，所以提供好的、一致性的裝置使用經驗，就是一個重要的技術工作。

Web Starter Kit 的 Responsive Web Design Patterns 看起來，直接引用了大家已經習慣多時的操作介面，而不是重新發明，例如：Off canvas；Off canvas 是在設計 Web 時，很常用的一種選單設計方式。所以，依循 Web Starter Kit 的這 5 個 Patterns 來設計 UI，理論上可以讓使用者「無痛上手」你的 App。

## Mobile-optimized HTML5 模板

在 Web Starter Kit 的 *app/* 目錄下，可以找到一個 *index.html* 檔案，這就是 Web Starter Kit 提供的 "Hello, World" 模板。在這個模板裡，已經定義好 ViewPort、iOS web apps meta data、page structure 等基本資訊，這些重覆性的工作，都不需要再自已實作了；所以，開啟這個檔案，開始擴充你的第一個 Web app 吧。

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/06/web-starter-kit-1.html</link>
         <guid>https://www.jollen.org/blog/2014/06/web-starter-kit-1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Fri, 27 Jun 2014 15:55:04 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #2：Node.js 模組</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html">這裡</a>。

<script id='markdown', type='text/plain'>

## 製作 Node.js 模組

學習 Node.js 的第一件事情，就是了解如何將程式碼模組化，簡單來說，就是製作一個程式庫 Node.js 的模組隱含著 Closure 的特性。

JavaScript 比較講求模組化，所以我們重構 hello.js。先將 Web Server 的部份獨立成一個模組，程式碼規劃如下：

- index.js：主程式
- server.js：啟動Web server的模組

index.js的完整程式碼如下：

```
// 02-modules/hello.js
var server = require("./server");

server.start();
```

主程式的部份，以 require() 函數將 server 模組（即 server.js 檔案）引入，接著呼叫模組裡的 start() 函數。server.js 完整程式碼如下：

```
// 02-modules/server.js
 1 var http = require("http");
 2 
 3 function start() {
 4   function onRequest(request, response) {
 5     console.log("Request for " + pathname + " received.");
 6 
 7     response.writeHead(200, {"Content-Type": "text/plain"});
 8     response.write("Hello World");
 9     response.end();
10   }
11 
12   http.createServer(onRequest).listen(8080);
13   console.log("Server has started.");
14 }
15 
16 // Export functions
17 exports.start = start;
```

程式碼第 3 行到第 16 行的地方，我們實作了一個函數，並且將它匯出。請特別留意，沒有匯出的函數，將不是 Public，它不能被外部的人呼叫。exports 是 Node.js 的一個 Global object，用來讓我們匯出模組裡的函數，成為 Public Function。

目前為止，我們發現了一些觀念：

- Frontend 與 Backend 都使用 JavaScript 做為主要的程式語言
- Frontend 與 Backend 都要模組化，並引入 Closure 觀念
- Frontend 與 Backend 的 Module / Closure，相念相通，實作方式不同

### Chaining Pattern

另外，server.js 裡也做了一些改寫。程式碼第 4 行的地方，以具名函數的方式重新實作，目的是讓程式碼更具可維護性。此外，程式碼第 14 行的地方：

```
http.createServer(onRequest).listen(8080);
```

物件接著下一個物件來連續呼叫多個方法的寫法，就叫 Chaining Pattern（鏈接模式）。這個設計模式的目的，同樣是為了提升程式碼的可維護性：不但能簡化程式碼，更能讓程式碼能構成一個句子。

在接下來的範例裡，我們將善用具名函數以及 Chaining Pattern 來提昇程式碼的可維護性。

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html</link>
         <guid>https://www.jollen.org/blog/2014/04/nodejs-getting-started-2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Tue, 15 Apr 2014 23:58:19 +0800</pubDate>
      </item>
            <item>
         <title>Node.js 入門, #1：Hello World</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2014/04/nodejs-getting-started-1.html">這裡</a>。

<script id='markdown', type='text/plain'>

## Node.js 入門, #1：Hello World

Node.js 並不是使用於 Client-side，它使用於 Server-side。有關 Node.js 的說明，可參考 Node.js 官方網站。在繼續進行範例說明前，請先備妥這份文件：

http://nodejs.org/api/

另外，也請參考 Node.js 官方網站的說明，來安裝 Node.js 環境。關於環境安裝，以及 Node.js 的入門觀念，可參考由 MokoVersity 所提供的免費線上課程：

http://www.mokoversity.com/course/html5/nodejs-overview

## 取得範例

本文所撰寫的 Node.js 程式碼，皆可在 Github 上取得：

https://github.com/jollen/html5-websocket-nodejs

接下來，讓我們用一個連貫性的實例：即時通訊軟體，來為大家介紹 Node.js 技術。

## 第一個 Node.js 程式

Node.js 能提供 Web Server 的能力。所以，不免俗地先了解 "Hello, World" 的寫法：

```
// 01-create-server/hello.js
 1 var http = require('http');
 2 
 3 var httpServer = http.createServer(function (req, res) {
 4   res.writeHead(200, {'Content-Type': 'text/html'});
 5   res.end('<h1>Hello World</h1>\n');
 6 });
 7 
 8 httpServer.listen(8080);
 9 
10 console.log('Server running at http://127.0.0.1:8080/');
```

程式碼第 3 行的地方，呼叫 http 模組的 createServer() 函數來建立一個 Web Server 物件。建立 Web Server 物件，並且啟動一個 Web Server，是  Node.js  技術的第一個步驟。

createServer()函數的說明如下：

```
http.createServer([requestListener])
```

createServer() 執行成功後傳回 Web Server 物件，參數 requestListener 是一個 Request Handler Function，用來處理 request 事件。關於 Node.js 的事件處理技術，後續再做說明。當 request 事件發生時，Request Handler Function 將被 Callback，並帶有二個參數：

- req：http.ServerRequest的實例化(instance)
- res：http.ServerResponse的實例化

將上述的範例，儲存為 hello.js，並且利用 node 指令執行：

$ node hello.js 

安裝 Node.js 後，就可以取得 node 命令。

這是執行 Node.js 程式碼的陽春版做法，後續將導入 forever 工具，以進階的方式來執行 Node.js 程式。

 Node.js 採用 Google 所開發的 V8 JavaScript 引擎，原本 V8 引擎是設計給瀏覽器使用的 JavaScript 引擎，現在有開發者把它抽離出來，變成一個獨立的直譯器，讓 JavaScript 程式碼升格為 Server-Side Script。

我們利用瀏覽器連到 http://127.0.0.1:1234/。

## V8 JavaScript引擎介紹

JavaScript 引擎將成為手持裝置的重要技術。早期的 Android 系統，使用的 JavaScript 引擎稱為 JavaScriptCore (JSC)，這是由 Apple 所開發的 JavaScript 引擎，並且包含在 Webkit 中。因為一些原因，Google 也決定開發自已的 JavaScript 引擎，稱之為 V8。

技術上，JSC 與 V8 的設計理念不同，一般相信，新一代的 V8 引擎效能比 JSC 引擎更好。Android 2.3 加入了 V8 引擎，若想使用最近的 V8 引擎，就要使用 Android 2.3 以上的版本。

## 為什麼要使用 Node.js？

到這裡，大家可能會有一個疑問。為什麼不使用現有的 Web Server 來開發 Web Service 就好，例如使用 Apache。非要使用 Node.js 技術不可嗎？這個問題的答案，要從 Thread Model 說起。

典型的 Web Server 以 Multi-thread 架構來實作「Concurrency」，也就是以建立 Thread 的方式，來處理處理事件（Events）。但是過多的 Thread 會造成伺服器的負擔：

- 假設一個 Thread 可以處理 10 個事件
- 同時處理 1000 個事件，就必須建立 100 個 Thread
- 大量的 Thread 在分時作業系統裡，會造成 Context-Switch Overhead，讓每一個 Thread 處理事件的時間拉長，形成效能低落的現象

由此可知:

- 若是將 Multi-thread 架構，應用在「處理巨量的同時連線請求」上，伺服器的負擔就會很大
- Multi-thread 架構的軟體，可能在系統產生過多的 Thread，過多的 Thread 除造成伺服器的負載增加外，也需要大量的記憶體

是否有替代方案呢？將上述的 Multi-thread 架構，改為 Event Loop 架構即可。Node.js 的訴求之一就是：採用 Event Loop 架構。此外，Node.js 採用 JavaScript 程式語言，JavaScript 本身也有一些很好的語言特性：

- 具備 Lambda 運算子，大量使用暱名函數（anonymous function）與 Closure（封閉性）觀念
- 使用 Callback Object 做為函數的參數（Lambda），易於處理 Non-blocking Operation 與 Event Handling

Node.js 本身的 I/O 操作，也大多是（幾乎）Non-blocking 的機制。這點與 PHP 有很大的不同。這樣的機制，對於消化巨量的連線請求，有非常大的幫助。

</script>]]></description>
         <link>https://www.jollen.org/blog/2014/04/nodejs-getting-started-1.html</link>
         <guid>https://www.jollen.org/blog/2014/04/nodejs-getting-started-1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Tue, 15 Apr 2014 23:51:31 +0800</pubDate>
      </item>
            <item>
         <title>我用 Markdown 語法</title>
         <description><![CDATA[過去，我的文章大多以純文字方式撰寫，技術筆記也是。大部份的文章與筆記，都會整理到部落格和大家分享。我的部落格後台是採用 MovableType 這個古老的系統，因為一些原因（個人的一些特殊喜好），所以至今仍使用這套軟體。不管是 WordPress 或 MovableType，都需要登入後台，並且還要以 HTML 標籤語法來加工文章。

這一年多因為工作習慣的改變，以及使用網路習慣的改變，登入後台更新文章並不方便，加上還要處理 HTML 的加工，所以就不常更新部落格了。文章就靜靜地躺在我的硬碟裡。

直到去年，我開始使用 Markdown 語法來整理這些文字，原因是，希望將整理過的文字，批次出版成 E-book。Markdown 語法自然成為最佳方案之一。自助出版平臺，例如：Leanpub，都能支援 Markdown 格式。利用 Pandoc 也能將 Markdown 製作成簡報，非常方便。

將 Markdown 再轉為 HTML 雖然很簡單，但又要多做一個工。不如把 Markdown 內文，直接貼到部落格就好：加上一段「<a href="http://www.jollen.org/blog/2013/08/client-side-markdown-parsing.html">Client-side Markdown Parsing</a>」的程式碼即可一勞永逸。

Markdown 語法真是打遍天下，不管是編修 Wiki，或是撰寫 Github 專案的說明，還是在 Github issues 裡貼文，都難不到。

]]></description>
         <link>https://www.jollen.org/blog/2014/04/use-markdown-syntax.html</link>
         <guid>https://www.jollen.org/blog/2014/04/use-markdown-syntax.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Tue, 15 Apr 2014 23:14:11 +0800</pubDate>
      </item>
            <item>
         <title>Software 與 NoHardware － 不只是硬體的時代</title>
         <description><![CDATA[因為 Mokoversity 計畫，筆者近期有機會和團隊成員，以及各界專家，交換許多對產業的看法。因此，有了 NoHardware 的想法。在這裡提出與大家分享。

近來聊到，如何媒合（Bridge）軟體人與硬體人時，筆者提到，現在的時代，不應該太過於強調軟體人與硬體人。意思是，不去鼓勵軟體人就只做軟體，也不鼓勵硬體人只做硬體。所以，軟體人與硬體人的媒合機制，有點被筆者打了回票。因為，「媒合」是一種消極的做法，更積極的做法是從「自已」出發。

硬體人也可以學 Coding，這不就是硬體人做軟體的起步嗎。因此，培養全端能力（Full Stack）就是實現想法，將想法實現為產品的關鍵。這並不是強調「單打獨鬥」的做事方式，而是將內心想法 Prototyping 出來的重要能力。有了 Prototyping 後，接下來就是尋找專業伙伴，進入專業分工、團隊運作的階段。這是一個「跨領域學習、再交流結合」的時代。

又如，Designer 也可以是 Coder，Coder 也可以是 Designer，這就是 Mokoversity 的精神：站在全民寫程式的角度。當 Designer 也是 Coder 時，就可以 Reinvent 很多事、物。

為什麼硬體廠做出來的硬體就是硬體？因為他們過度依賴「專業分工」，所以沒有火種：公司內部沒有能在硬體上點火的軟體人，也沒有能在軟體上點火的硬體人。

<strong>NoHardware－Not Only Hardware</strong>

從事 Backend 開發的工程師都聽過 NoSQL[1]，它的意思是 Not Only SQL。傳統的關聯式資料（RDMBS）採用 SQL 查詢語法，來新增、刪除、查詢與修改資料庫。NoSQL 是一種不採用 SQL 做為查詢語法的資料庫系統；從 2009 年開始，因為大量數據與分散式儲存的需求提昇，使得 NoSQL 受到相當程度的矚目。MongoDB 與 CouchDB 是非常知名的 NoSQL 資料庫。

智能手機在 2007 年開始逐步改變人類的生活習慣。PC 至今仍然存在，並沒有完全被智能手機取代，但是相當多的資訊應用都已經被智能手機取代了。有了 iOS 與 Android 後，大家發現了 App 的便利性，所以不斷從 App Store 與 Play Store 上下載各種 App；接著，像是 WhatsApp、LINE 這類的即時通訊軟體，取代了 PC 時代的 MSN。2013 年 4 月，有 14 年歷史的 MSN 吹熄燈號。

2010 年開始，手機通訊軟體改變了傳統網站的通訊方式，人們的習慣，過去是從 PC 上使用即時通訊軟體，現在已經轉變成在手機上使用通訊軟體。然而，並不是把 MSN 移植到手機上就叫「手機通訊」。筆者認為，「手機通訊」帶來的改變，和「開放源碼運動」本質相同：他們都改變了「社會文化」，而不是只有「使用習慣而已」。

手機通訊時代，帶來的社會文化改變，也帶來產業與商業規則的改變。手機製造商，不再把手機當做硬體來製造與銷售。手機製造商，不再用 PC 思惟去思考手機。智能手機時代，讓手機硬體，不再只是硬體。所以，手機，並不只是硬體－Hadware is not only hardware，就像 NoSQL 一樣，現在是 NoHardware 的時代。

The Economist 不久前的專題「The new GE: Google, everywhere[2]」，更清楚地指出，Google 把自已重新定位為「Hardware Re-inventor」。當硬體不再只是硬體時，就要加入更多的創意與想法，這需要大量的軟體工程去實現。

NoHardware 不但只是 Not Only Hardware，背後更有「只有硬體還不夠」以及「重新發明硬體」的涵義。

<strong>矽谷的硬體創業潮</strong>

大約在 2012 年開始，矽谷出現一波硬體創業風朝[3]，到了 2013 年底，這波風潮因為穿戴裝置與自造者（Maker）的關係，形成一個強力的創業生態圈。在這個硬體創業生態圈裡，「創業加速器」當然成為重要的角色。

筆者在 2013 年 10 月份，受邀至深圳參加第二屆的 Android World 開發者大會，這屆大會談的創業，除了開放平臺外，也提到了硬體創業的現象。然而，以硬體製造優勢自居的台灣產業，卻沒有站在這波「硬體創業」的浪頭；這就算了，台灣已經完全被硬體創業圏排除在外，關於這點，可以從二個現象來觀察：

有矽谷越來越多的硬體創業加速器，前往深圳獲取資源，而不是來台灣
非常多創新硬體都是在深圳生產，透過深圳的硬體產業試量或產量
發表過著名的「免費」一書作者 Chris Anderson 在 2013 年 11 月時來台演講，他在今年所發表的新書「自造者」，談的就是「每個人都能製造硬體」的時代，這不正與「每個人都在寫程式」的時代，相互呼映嗎？不管是軟體或硬體，台灣跟不上前一波的軟體復興運動，似乎也無法跟進這波的硬體復興運動。

台灣的硬體廠，仍然可以繼續做硬體，因為這是台灣產業的好價值。要尋求突破，就要心中沒有硬體，做到 NoHardware 的精神。

<strong>參考資源
</strong>

[1]: http://zh.wikipedia.org/zh-tw/NoSQL "NoSQL"
[2]: http://www.economist.com/news/business/21594259-string-deals-internet-giant-has-positioned-itself-become-big-inventor-and "The new GE: Google, everywhere"
[3]: http://www.pingwest.com/highway1/ "Highway1"]]></description>
         <link>https://www.jollen.org/blog/2014/02/nohardware.html</link>
         <guid>https://www.jollen.org/blog/2014/02/nohardware.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Thu, 06 Feb 2014 11:18:47 +0800</pubDate>
      </item>
            <item>
         <title>Coding 就是 Writing：寫程式是一種作文能力</title>
         <description><![CDATA[（文／Jollen，原文刊載於 <a href="https://www.mokoversity.com/post/Coding%20就是%20Writing：寫程式是一種作文能力">Mokoversity</a>）

「每個人都要學 Coding」、「全民 Coding」，這是近一年來最特別的現象。大家都來學 Coding，也是 MokoVersity 的理念。

<h2>寫作文</h2>

為什麼要學習程式設計？並不是為了將自己訓練成軟體開發專家，而讓自己能夠具備「寫作」的能力。 從寫作（Writing）的角度來討論程式設計，就好像小時候大家都在學校，學習寫字與作文一樣。

如果，你是個設計師，內心深處充滿著許多想法，而這些想法可能必須以程式設計的方式來呈現： 這就是為什麼設計師要學習程式設計的原因。會寫作，你就可能用作文的方式，完整表達自已的想法。為什麼要自已寫作文？因為很難透過他人來「轉述」心裡最深處的思想，或是感覺。

設計師的許多想法，特別是一些細節，除了透過自己親自動作來實作原型（Prototype）外，並不容易經由工程人員，幫你完整實現想法。透過他人的筆，很難完整表現自已的想法：工程師並不能幫助設計師，完整表現出想法。

所以，我們可以換個角度思考。當你有許多想法時，可以自已寫作，透過文章的方式來表達，並將它發佈在網路上，和許多人討論分享。

同樣的，也能把這個概念，套用在「每個人都要學習寫設計」的角度。如果我有一些想法，而且也可以很快的用程式碼來「寫作」的話，就能了解為什麼 Coding 就是另一種 Writing 的能力。這已經成為人們在社會上生活的基本技能了。

現在在國外，特別是美國，正在推行小朋友學習程式設計的運動。一些學校，從小學開始教導小學生，如何將自己的想像，以程式碼的方式表現在電腦螢幕上。 教導這些小學生寫程式，並不是希望他可以成為最厲害的工程師，而是希望他們能夠將自己的思維，具體表達出來而已。

美國總統歐巴馬，在 Computer Science Education Week 上提到：「Learning these skills isn’t just important for your future, it’s important for our country’s future[1]。」學習程式設計技能，並不只是為了自已的未來，也是未了整個國家的未來。Computer Science Education Week 正在推廣 “Hour of Code” 活動。

所以，每個人都要學習程式設計的原因，並不是為了能夠成為 Super coder。就像， 小時候在學校，我們都要學習寫字，但不是要每個人長大後，都成為作家。美國是科技創新的中心，美國體認到 Coding 技能對國家發展的重要性，對於「人人都要學 Coding」的觀念，接受度很高，而且還有很多名人自發性的協助推廣。

美國紐約市市長彭博的二○一二年新年希望，就是學會程式設計。彭博報名參加的程式語言課程，是由 CodeCademy 推出的 Code Year 活動。類似的免費程式語言課程，也越來越多。NBA 球星 Chris Bosh 也說「Here’s Why You Should Learn to Code[2]」，他參加許多 Code.org 的程式設計課程。

學 Coding 就像寫作文，寫作文不是要人人成為作家，寫程式也不是要人人成為工程師。從小朋友的角度，或許他們不知道，在大人的世界裡：許多人認為 Coding 是工程師的事情。

比如說，學習基本的 Frontend 開發並不難，基本的 JavaScript 搭配 Twitter Bootstrap，就能做出很棒的作品。所以，認為 Coding 是一個深奧難懂的學問，或是對學習 Coding 沒自信，又或是認為這是工程師的專有技能，大概只是大人們給自已太多束縛而已。

<h2>參考資源</h2>

[1]: http://www.wired.com/wiredenterprise/2013/12/obama-code/ "Obama Says Everyone Should Learn How to Hack"

[2]: http://www.wired.com/opinion/2013/10/chris-bosh-why-everyone-should-learn-to-code/ "NBA Superstar Chris Bosh: Here’s Why You Should Learn to Code"]]></description>
         <link>https://www.jollen.org/blog/2014/01/coding-likes-writing.html</link>
         <guid>https://www.jollen.org/blog/2014/01/coding-likes-writing.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Tue, 14 Jan 2014 14:59:21 +0800</pubDate>
      </item>
            <item>
         <title>開放創新（Open Innovation）是治理公司的思想</title>
         <description>開放創新是治理公司的思想

文／Jollen Chen（原文刊載於 CTimes 雜誌 2014 年 1 月號）

三星不久前成立了 Open Innovation Center，正好台灣的宏碁也在努力進行轉型，並且提出「自建雲」做為轉型的方向。在這裡，筆者希望從三星的 Open Innovation Center 來討論「開放創新」與公司治理的關係，並且提出一些對宏碁轉型的個人看法。

三星的 Open Innovation Center 本質上是一個「公司治理」的方法，這是一種人才政策，也是三星在「開放」的潮流下，十年來的第三次人才政策轉變。Open Innovation Center 並不是在做「創新研發」，而是吸引有想法、具創新思維的人才，進入到這個中心，藉由三星的資源，扶植這些人才創業，讓這些人才進行「研發」工作。

由此來看，三星的政策應該是採取精英政策，而不是為了吸引大量的創新人才，主要的原因之一是，三星的 Open Innovation Center 將天使投資與併購看成是一項業務，而不是為了招攬有能力的開發人員。創新研發的重點在人才，人才的重點在「正確的人才政策」，有了正確的人才政策，這些人才就會流進來你的生態體系，幫你做「創新研發」。台灣廠商只是從「創新研發」的字面去解讀的話，並無法深入了解三星 Open Innovation Center 的目標是什麼，以及它的戰略是什麼。

不久前，筆者在解讀三星的 Open Innovation Center 文章裡，提出二個給傳統硬體製造商的建議。這二個建議都是以開放創新做為主要戰略。開放創新談的是公司治理政策，再加上創業活動是全球的熱門活動，因此從投資與收購的角度切入，看來是一個最佳化的策略。比如說，Wearable Devices（比如：手錶），這些裝置是「創新的硬體」，而不是「製造的硬體」或「Cost-down 的硬體」，後二者是台灣的強項。然而，傳統硬體商要進入「創新的硬體」，沒有人才，就沒有轉型機會。

傳統硬體廠如何解決人才的問題？扮演天使投資人的角色，一定是一個好方法。從這個角度也可以了解，為什麼三星要在矽谷成立 Open Innovation Center。因為，人才政策涉及到一個根本的問題：具創新思維的人才，不管是軟體人，或硬體人，因為他們熱愛自由，不喜歡受拘束，所以讓他們成為員工，並且上班打卡，很明顯就是錯誤的公司治理方法。又如，這些人才，也不可能在人力銀行找工作，所以傳統的招募政策，也是錯誤的政策。

Open Innovation Center 以號召、吸引、投資、扶植與併購的戰略，建立正確的人才政策，取得「創新研發」的能量，再透過資源協助，或參與的方式共同開發。所以，開放創新的機制，並不是由廠商直接投入研發工作，也不是在公司內部舉辦「腦力激盪」會議。從這個角度來看，就可以了解宏碁再次轉型之路，仍有很大的思維改變空間。

不只是軟體，開放創新的思維，包含硬體，最明顯的例子就是「自造者」。發表過著名的「免費」一書作者 Chris Anderson 在 2013 年 11 月時來台演講，他在今年所發表的新書「自造者」，談的就是「每個人都能製造硬體」的時代，這不正與「每個人都能寫程式」的 App 時代，相互呼映嗎？

從自造者的角度來看，每個人都可以做硬體，也可以把產品放在網路上集資，成立公司。台灣的硬體廠商，應該如何去看「自造者時代」呢？開放創新的思維，仍然是最好的角度。硬體廠可以透過天使投資人的角度，參與自造者的創業活動，再投入自身的硬體製造資源等，形成一個健康的生態體系。

不管是軟體或硬體，台灣跟不上前一波的軟體復興運動，似乎也無法跟進這波的硬體復興運動。一個在台灣的國際品牌，近期正在努力轉型的公司，從新聞媒體上所做的觀察發現，「思維」仍然是舊式思維。筆者所謂的思維，是「如何把事情做對」的思考，而不是單純的「要做什麼」的問題。比如說，我們都知道雲端是一個好題目，所以筆者認同「雲端」的方向，但如果沒有完整配套規劃，這場腦力激盪會議，就沒有太大的意義了：只要坐在家裡上網，每個人都知道雲端是個好題目。

所謂的配套，就是執行面的問題，比如說：人才很重要，沒有人才，再好的題目，都很難產出結果。為了不讓一個好的轉型機會，淪為會議室裡的談話，所以這裡需要的是人才，有了人才，就會有執行力。需要人才時，一個對的人才政策是關鍵。人才政策談的是公司治理方法，而不是挖角或招幕。

所以，公司治理政策就要做相對的調整：以開放創新思維，建立人才政策。例如：成立「自建雲天使投資基金」，用以投資年輕人的雲端公司，取代傳統的招募員工做法。這儘只是思維的一個小改變，但卻可以達到三贏的效果：公司扮演天使投資人，創業家能有很大的發展舞台，開發者可以很快樂地跟隨創業家打併。
</description>
         <link>https://www.jollen.org/blog/2013/12/open-innovation-thinking.html</link>
         <guid>https://www.jollen.org/blog/2013/12/open-innovation-thinking.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Fri, 20 Dec 2013 16:03:03 +0800</pubDate>
      </item>
            <item>
         <title>Node.js + Express.js 應用 - Middleware 觀念解說</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫，若無法閱讀內文（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），請點擊<a href="http://www.jollen.org/blog/2013/11/expressjs-middleware.html">這裡</a>。

<script id='markdown', type='text/plain'>

Express.js 的 Middlware 分為二個部份：所有 URL 與特定 URL。要了解 Middlware 的觀念，最快的方法就是實作「頁面保護」的功能。現在，讓我們為 '/hello' URL 加上密碼 '123456' 的保護。

## 使用 *app.get()* 撰寫 Middlware

針對特定 URL 加入 Middleware，必須透過 *app.get()* 函數的第二個參數。實作步驟如下。

### Step 1：加入 Middleware

延續上一章的範例，為 '/hello' 加入一個 Middleware：

```
app.get('/hello', function (req, res, next) { }, hello.index);
```

說明如下：

- 第 2 個參數，是一個暱名函數，這個暱名函數就是 '/hello' 的 Middleware
- 第 3 個參數，是原本處理 URL Routing 的函數

Middleware 會收到 3 個參數：

- *req* 是 Request 物件，存放這此請求的所有資訊
- *res* 是 Response 物件，用來回應該請求
- *next* 用來控制流程，後續說明

當使用者輸入密碼時，就要撰寫一段控制流程來處理，典型的控制流程邏輯如下：

```
if (password == '123456') {
	send_page();
} else {
	end_request();
}
```

但 Express.js 並不是用這種方式，來實作流程方式。如何為 '/hello' 加入控制邏輯呢？請看步驟 2。

### Step 2：找出密碼

使用 Query String 來傳入密碼，這是最簡便的方式（也是最糟的做法）。Express.js 可以幫助我們解析 Query String，這是 Express.js 框架的另一個優點。我們就不必像第 3 章介紹的內容一樣，自已去撰寫解析 Query String 的程式碼。

Express.js 將解析好的 Query String 放在 *req.query* 物件裡，現在先直接將它印出。修改程式碼如下：

```
app.get('/hello', function (req, res, next) {
	console.log(req.query);
}, hello.index);
```

啟動 *app.js* 後，利用瀏覽器開啟 http://localhost:3000/hello?passwd=123456 網址。接著，可以在 Console 畫面看到這段訊息：

```
Express server listening on port 3000
{ passwd: '123456' }
GET /hello?passwd=123456 200 306ms - 135b
```

還有一個可能讓你嚇一跳的現象：瀏覽器的畫面是空白的，而且一直打轉。感覺好像是卡住了，這是怎麼會事兒呢？修改程式碼如下：

```
app.get('/hello', function (req, res, next) {
	console.log(req.query);
	next();
}, hello.index);
```

Express.js 傳入的 *next* 參數，實際上是一個 Lambda。呼叫 *next()* 表示「進行下一個流程」的意思，所謂的下一個流程，當然就是執行 *hello.index* 函數。

加上 *next()* 後，就能在瀏覽器上看到原本的畫面了。所以，我們只要判斷 *req.query.passwd* 就知道密碼是否正確。不過，還有更好的做法，Express.js 的功能可不是只有這樣。

### Step 3：使用內建 Middleware

Express.js 內建幾個好用的 Middleware：

- basicAuth()
- bodyParser()
- compress()
- cookieParser()
- cookieSession()
- csrf()
- directory()

像「使用者驗證」這麼常見的「流程」，Express.js 就有提供 *basicAuth()* Middleware。再次修改程式碼如下：

```
app.get('/hello', express.basicAuth('jollen', '12345678'), hello.index);
```

*basicAuth()* 使用 HTTP 的方式做認證，並不是 Query String 的做法。只要再次瀏覽網頁，就可以看到一個非常熟悉的畫面，如下圖。

<img src="http://www.jollen.org/blog/2013/11/14/figure-9_1.png" width="640" />

圖 9-1 使用 *basicAuth()* Middleware

同時，在 Node.js 的 Console 也可以看到以下訊息：

```
GET /hello 401 10ms
```

這表示 *basicAuth()* 使用的是 HTTP 401 認證方式。

### Step 4：控制流程

將上述的寫法，重構為更清楚的「流程」觀念：

```
app.get('/hello', express.basicAuth('jollen', 'abcdef'));
app.get('/hello', hello.index);
```

這其實是較為常見，而且更好的寫法。觀念整理如下：

- 請注意，過去將第 2 個參數解釋為 URL Routing 的 Handler，在這裡則是解釋為 Middleware
- 所以，'/hello' 現在有 2 個 Middleware
- 瀏覽 URL 時，依「順序」來呼叫 Middleware
- 順序指的是程式碼的寫法順序
- *basicAuth()* 先被呼叫
- *basicAuth()* 會進行使用者驗證
- 如果驗證成功，就會呼叫 *next()* 進到下一個「流程」
- 下一個「流程」就是 *hello.index* 函數

這是 Express.js Middleware 的基本觀念，也是初學者必學的主題。

## Middleware 與流程控制

Express.js 使用 Middleware 來實作流程控制，我們可以發揮一些巧思，讓流程控制的程式碼邏輯更優雅。比如說，'/hello' 現在的流程是：

1. 使用者驗證
2. 進行一些環境設定的調整
3. 送出頁面

如果使用典型的 if...else... 來實作，肯定會寫出很醜的 Node.js 程式碼。反之，利用 Middleware 的觀念來實作，不但觀念簡單，程式碼也更優雅：

```
app.get('/hello', express.basicAuth('jollen', 'abcdef'));
app.get('/hello', hello.config);
app.get('/hello', hello.index);
```

完整的 *hello.js* 如下：

{title="hello.js"}
```
1 exports.index = function(req, res, next) {
2   res.render('hello');
3 };
4 
5 exports.config = function(req, res, next) {
6   console.log("Do some configs here...");
7   next();
8 };
```

使用者通過驗證後，會進到 *hello.config* 流程。重要的觀念補充如下：

- 在 *hello.config* 裡要記得呼叫 *next()*
- 請注意，以上 2 個暱名函數都加上了第 3 個參數 *next*，成為 Middleware

Express.js Middleware 很像是 URL 的 Plugin，例如上述的範例，可以想像成是在 '/hello' 裡，加入 *hello.config* 的插件。

以下是截至目前為止，最新版本的 *app.js*。

{title="app.js"}
```
 1 var express = require('express');
 2 var routes = require('./routes');
 3 var user = require('./routes/user');
 4 var http = require('http');
 5 var path = require('path');
 6 var hello = require('./routes/hello');
 7 
 8 var app = express();
 9 
10 // all environments
11 app.set('port', process.env.PORT || 3000);
12 app.set('views', path.join(__dirname, 'views'));
13 app.set('view engine', 'jade');
14 app.use(express.favicon());
15 app.use(express.logger('dev'));
16 app.use(express.json());
17 app.use(express.urlencoded());
18 app.use(express.methodOverride());
19 app.use(app.router);
20 app.use(express.static(path.join(__dirname, 'public')));
21 
22 // development only
23 if ('development' == app.get('env')) {
24   app.use(express.errorHandler());
25 }
26 
27 app.get('/', routes.index);
28 app.get('/users', user.list);
29 
30 app.get('/hello', express.basicAuth('jollen', 'abcdef'));
31 app.get('/hello', hello.config);
32 app.get('/hello', hello.index);
33 
34 http.createServer(app).listen(app.get('port'), function(){
35   console.log('Express server listening on port ' + app.get('port'));
36 });
```

目前為止的範例，都是為特定的 URL 來撰寫 Middleware。

## 使用 *app.use()* 撰寫 Middlware

Express.js 的 Middleware，也能針對所有的 URL，方式是使用 *app.use()* 函數。例如，我想為「所有的 URL」加上使用者認證的「流程」，做法非常簡單。以下是修改後的 *app.js*：

{title="app.js"}
```
 1 var express = require('express');
 2 var routes = require('./routes');
 3 var user = require('./routes/user');
 4 var http = require('http');
 5 var path = require('path');
 6 var hello = require('./routes/hello');
 7 
 8 var app = express();
 9 
10 // all environments
11 app.set('port', process.env.PORT || 3000);
12 app.set('views', path.join(__dirname, 'views'));
13 app.set('view engine', 'jade');
14 
15 app.use(express.favicon());
16 app.use(express.logger('dev'));
17 app.use(express.json());
18 app.use(express.urlencoded());
19 app.use(express.methodOverride());
20 app.use(express.basicAuth('jollen', '654321'));
21 app.use(app.router);
22 app.use(express.static(path.join(__dirname, 'public')));
23 
24 // development only
25 if ('development' == app.get('env')) {
26   app.use(express.errorHandler());
27 }
28 
29 app.get('/', routes.index);
30 app.get('/users', user.list);
31 
32 app.get('/hello', hello.config);
33 app.get('/hello', hello.index);
34 
35 http.createServer(app).listen(app.get('port'), function(){
36   console.log('Express server listening on port ' + app.get('port'));
37 });
```

各位是否能看出當中的細節？說明如下：

- 第 20 行，使用 *app.use()* 來加入 *basicAuth()* Middleware，表示針對所有的 URL
- 第 21 行，為所有 URL 加入了 URL Rounter，*app.router* 是 Express.js 內建的 URL Router
- 第 20 行與第 21 行，依照「流程」的邏輯，應該是先進入 *basicAuth()* 流程，再到 URL Routing 的流程
- 如上，也就是說，這 2 行的順序是不能修改的，否則就會變成「先做 URL Routing、再做使用者驗證」的錯誤

此外，我們也發現，Express.js 預設加入了這些 Middleware：

```
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
```

*express.static()* 這個 Middleware 是用來指定 "Static Files" 的路徑。典型的 Use Case 是將 Static Files 放在 public/ 目錄下，比如，瀏覽器送出這個請求：

```
GET /style.css
```

當 *app.router* 無法處理這個檔案的 Routing 時，就會進到 *express.static()* 流程，這時 Express.js 會到 *public/* 子目錄下搜尋這個檔案，最後將 *public/style.css* 送出。Static files 一般指的是 CSS、JavaScript、圖片、影片、靜態 HTML 文件等。

Middleware 的「順序」，非常的重要。所有的 Middleware 都是依照寫作順序逐一呼叫。通常，較早先被使用的 Middleware 為 *express.logger()*，這是 Middleware 的 Log System，用來紀錄 HTTP 的請求。

由上述程式碼的順序來看，*express.logger()* 會紀錄下幾乎所有的資訊，包含 HTTP 請求、送出的 Static Files 等。如果不想把 Static Files 放到紀錄訊息裡呢？只要調整其順序即可：

```
app.use(express.favicon());
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.logger('dev'));
```

這個順序，可以讓 *express.logger()* 紀錄最少量的資訊。

## 常用的 Express.js Middleware

以下介紹幾個經常使用的 Middleware。

### 使用 *compress()*

Express.js 內建 *express.compress()* Middleware，這個 Middleware 可以把 Response Data 壓縮，節省網路頻寬，當然也就縮短 Response Data 所需的時間。修改後的程式碼如下：

```
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.compress());
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.basicAuth('jollen', '654321'));
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
```

請注意，*express.compress()* 的順序要放在比較前面。

### 使用 *cookieParser()*

這是一個用來處理 HTTP Cookies 的 Middleware。它可以協助我們解析 Cookies，並將所有的 Cookies 放在 *req.cookies* 物件（Key-Value Pairs 格式）。寫法如下：

```
app.use(express.cookieParser());
```

比如說，有一個 Cookies 叫做 *purchase_id*，使用這個 Middleware 就可以透過 *req.cookies.purchase_id* 讀取該 Cookies 的值。

### 使用 *cookieSession()*

提供 Sessions 機制的 Middleware。為 *app.js* 加入 Sessions 功能：

```
app.use(express.cookieSession());
```

因為 Express.js 的 Sessions 是建構在 Cookies 的機制之上，所以為了防止 Sessions 被不當修改，可以傳入 *secret* 參數：

```
app.use(express.session({
    secret: 'N1j2o3l4l5e6n7'
}));
```

此外，也可以設定 Cookies 的屬性：

```
app.use(express.session({
    secret: 'N1j2o3l4l5e6n7',
    cookie: { path: '/', httpOnly: true, maxAge: null }
}));
```

上述設定是原本的預設值。

## 結論

Node.js + Express.js 初學者，務必了解 Middleware 的觀念，並且學會使用 Middleware 做流程控制。關於 Middleware 的實作，可分為特定 URL 與所有 URL，這是 Express.js 開發的基本功。網路上有一些支援 Express.js 的 Workflow 模組，也都是基於 Middleware 的觀念來實作。
</script>]]></description>
         <link>https://www.jollen.org/blog/2013/11/expressjs-middleware.html</link>
         <guid>https://www.jollen.org/blog/2013/11/expressjs-middleware.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Thu, 14 Nov 2013 21:24:53 +0800</pubDate>
      </item>
            <item>
         <title>Publish Early 早期出版新文化</title>
         <description><![CDATA[本文章採用 Markdown 語法撰寫（<a href="http://www.jollen.org/blog/2014/04/use-markdown-syntax.html" target="_blank">why?</a>），若無法閱讀內文，請點擊<a href="http://www.jollen.org/blog/2013/10/publish-early.html">這裡</a>。

<script id='markdown', type='text/plain'>
資訊圖書的出版要進入一個新時代了。因為資訊科技進步神速，以及開放內容（Open Content）的推波助瀾，過去資訊相關圖書的出版模式將再次改變。

過去最明顯的現象就是專業電腦書的輕薄化。輕薄化有二個意涵：第一、內容簡明扼要，甚致沒有太多的鋪陳，讀者的背景知識必須充份；第二、頁數變少，過去幾年，新技術的電腦書，或是主題限定在一定範圍的電腦書，頁數大多維持在 200 頁附近。

<h2>輕薄短小、簡潔有力</h2>

對筆者來說，這種輕薄化的電腦書，可以帶來無比舒適，以及有效率的閱讀。雖然這類型電腦書的頁數少，但內容可不簡單，不但意簡言賅，直達技術的核心，對於重要觀念的講解，也相當精確。當然，閱讀這種電腦書，就要具備一定的背景知識。

現在，由於技術進步更快，使得寫作到實體出版的速度遠不及技術的變化。實體書已經無法扮演傳播知識的良好角色了。所以，現在我們可以看到資訊出版再度進入新的轉變期。一個是電子書與自助出版的盛行。例如：Amazon Single 可以提供作者一個自助出版的管道。另一個則是 Micro Book 概念的出現。Amazon Single 的出現，其實也是想幫助作者出版 Micro Book 作品。

以電子書、電子出版與微型書的模式，讓知識能快速傳遞到讀者手上，這種新的出版模式，才能跟上技術的快速變化。

## 微型書

近二年來，電腦出版業似乎出現一波微型書（Micro Book）的出版風潮。所謂的微型書，就是頁數介於 50-80 頁的電腦書。這類型的電腦書，一般都是先採取電子書或電子出版形式，最後出版商再印製實體書上架販售。

Micro Book 本身的文字內容，大多在網路上能免費取得，但網路上的文章形式過於鬆散，所以 Micro Book 在這個時代，有另外一種價值。台積長董事長張忠謀先生，在 2000 年一場講座中說：「網路無益於知識的累積。」如果我們要讓網路上的內容 （Content）變成自已的知識（Knowledge），系統化地整理並編篆這些免費內容是很重要的工作。

就是因為免費內容太多，我們更需要專家為我們去整理並編篆這些內容，Micro Book 就是一個很好的解決方案，因為它的出版相當快速（一般是三個月內），不必等到完整的書寫完後再出版（可能費時一年），甚致可以做到隨時改版（Publish Often）的服務。花一年去編寫並出版的實體電腦書，等到上市後，不但內容也老了（這一點都不誇張），而且技術人員根本等不及它的出版。

微型書是技術工程人員的寶物，它能幫助我們將免費內容形成個人的知識。因為免費內容太多，微型書能讓技術學習者接軌這些免費內容，這是一個免費內容的新時代，微型書有著它的時代價值。

## Publish Early, Publish Often

軟體技術的更新速度很快，微型書不但能快速出版，傳遞新知識到每個人手上，也能節省學習者的時間。目前網路上有許多 E-Book 的出版平臺，上面出版的電腦書，很多都是非常新的主題，而且篇幅都不長。這對新知識的吸收很有幫助。

在 Leanpub 上面可以找到很多「寫作中的書」，讀者可以先購買寫作中的書，提早學習新知識。這種讀書與寫書的新文化，頗值得推廣。筆者不只開始養成閱讀這些「早期出版」圖書的習慣，也開始對創作早期出版品產生興趣。

筆者開始在 Leanpub 的平臺上，[<a href="https://leanpub.com/u/jollen" target="_blank" rel="nofollow">嚐試進行早期寫作</a>]，將原本部落格式的寫作，轉移成書藉的寫作習慣。這樣的轉變目的，是希望讓寫作的結構更好，成為優質寫作者。
</script>]]></description>
         <link>https://www.jollen.org/blog/2013/10/publish-early.html</link>
         <guid>https://www.jollen.org/blog/2013/10/publish-early.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Fri, 25 Oct 2013 13:02:16 +0800</pubDate>
      </item>
            <item>
         <title>深入淺出 JavaScript Lambda</title>
         <description><![CDATA[<div class="wiki-page"> 

<p>註：本文節錄自「<a href="https://leanpub.com/html5-javascript-thinking" target="_blank">Node.js & HTML5 開發思惟與入門學習</a>」一書，並原文重現。</p>

<p>本章的目標，是提昇初學者的開發功力。經過前面 4 個章節的介紹，學習到許多基本觀念與技術。接下來，將從三個不同的層面，深化目前所學到的觀念與技術：</p>
<ul>
<li>JavaScript 語言</li>
<li>Web Service 架構</li>
<li>Node.js 觀念</li>
</ul>
<p>本章先從 JavaScript 語言開始。JavaScript 最重要的觀念是 Closure，這可以說是 JavaScript 初學者的第 1 堂。Closure 與暱名函數有很緊密的關係，這要由 Lambda 的觀念開始講起。</p>
<h2>Lambda</h2>
<p>Lambda（λ）是一個希臘字母（Λ 是它的大寫字母），用來表示許多觀念：</p>
<ul>
<li>物理學家用來表示波長的符號</li>
<li>數學家用來表示空字串的符號</li>
<li>電腦科學家用來表示暱名函數（Anonymous Function）的符號</li>
</ul>
<p>Lambda 在電腦科學領域，用來表示暱名函數，目的是進行運算。為了尋找一個語法簡易的運算表示方式，電腦科學家會這麼做。</p>
<h3>Step 1：取得一個具名的函數</h3>
<p>例如一個加法函數：</p>
<pre><code>sum(x) = x + 2
</code></pre>

<p>函數 sum() 是具名函數，利用 JavaScript 來實作的話，寫法如下：</p>
<pre><code>function sum(x) {
     return x + 2;
}
</code></pre>

<h3>Step 2：改寫為暱名函數</h3>
<p>Lambda 希望可以找到一個能運算的表示方法，而且要簡單，去除掉函數名稱，就是一個方式。沒有具體名稱的函數，就稱為暱名函數（Anonymous Function）。如果表示暱名函數呢？上述範例，以 Lambda 來表示的話，只要改寫成：</p>
<pre><code>λx.x + 2
</code></pre>

<p>用 JavaScript 來實作的話，要如何撰寫呢？方式如下：</p>
<pre><code>(function(x) {
     return x + 2;
})();
</code></pre>

<p>這就是第 1 章介紹的 Closure 觀念，最後加上的一對括號，稱為立即函數，意思是立即執行此暱名函數的意思。立即函數，以物件導向的角度來看，也可以解釋為立即實例化。</p>
<h3>Step 3：使用 <em>var</em> 來宣告暱名函數</h3>
<p>JavaScript 的 <em>var</em> 關鍵字用來宣告變數，所以也可以把暱名函數做為運算子（Operator）來宣告變數。例如：</p>
<pre><code>var lambda = (function(x) {
     return x + 2;
})();
</code></pre>

<p>變數 <em>lambda</em> 被指定（Assign）為一個暱名函數。從 JavaScript 語言的角度來看，Closure 用來封裝出 Module，所以 <em>lambda</em> 變數也可以解釋成「一個模組」。宣告一個暱名函數的變數時，可以不需要 Closure。也可以採用以下的寫法：</p>
<pre><code>var lambda = function(x) {
     return x + 2;
};
</code></pre>

<p>事實上，這個寫法更為普遍。一些文章也把這種寫法，稱做 Function Expressions。</p>
<p>有一個重要的觀念要釐清，上述的寫法，正規的解釋方式是「宣告暱名函數的變數」，所以 <em>lambda</em> 是一個變數，不能解釋為函數。如果說 <em>lambda</em> 是一個函數宣告，觀念上就不對了。不能單就 JavaScript 語法的角度來做解釋。</p>
<p>以 JavaScript 來說，函數宣告的寫法為：</p>
<pre><code>function lambda(x) {
     return x + 2;
};
</code></pre>

<p>這個時候，<em>lambda</em> 就是一個函數。那一種寫法比較好呢？理論上，採用暱名函數宣告的方式較佳，因為 JavaScript 本身是一種 Lambda 的程式語言。</p>
<h2>Callback Function</h2>
<p>Lambda 本質上是一種表示方法，用來表示 Input 與 Output。所以，要表示一個平方的運算的話，寫法如下：</p>
<pre><code>λx.x*x
</code></pre>

<p>這個運算式，也可以做為另一個 Lambda 式子的 Input，請看以下的說明。</p>
<h3>Step 1：撰寫第一個計算平方的 Lambda 表示式</h3>
<p>做法如下：</p>
<pre><code>λx.x*x
</code></pre>

<h3>Step 2：把上述的式子做為另一個 Lambda 的輸入</h3>
<p>有一個 f(x) = x + 2 的函數，用 Lambda 來表示的話，寫法如下：</p>
<pre><code>(λx.x+2)
</code></pre>

<p>如果要把 Step 1 的結果，做當上面式子的 <em>x</em>（輸入），合併後的寫法為：</p>
<pre><code>(λx.x*x)(λx.x+2)
</code></pre>

<p>讓我們來筆算看看：</p>
<ul>
<li>x = 3 時，λx.x*x 的 Output 為 9</li>
<li>9 做為 λx.x+2 的 Input，成為 9 + 2，Output 為 11</li>
<li>答案就是 11</li>
</ul>
<p>(λx.x*x)(λx.x+2) 等價於 9 + 2。</p>
<h3>Step 3：使用 JavaScript 來實作</h3>
<p>怎麼把 (λx.x*x)(λx.x+2) 寫成程式碼呢？非常簡單，由於 Output 就是返回值，所以等於「讓暱名函數的返回值，當做另一個暱名函數的參數」。程式碼如下：</p>
<pre><code>var lambda = function(x) { return x + 2 };
var result = lambda(function(x) { return x * x} (3) );

console.log(&quot;Result: &quot; + result);
</code></pre>

<p>這個輸出的輸出結果為 11。這可不是在賣弄程式碼，而是實現出 (λx.x*x)(λx.x+2) 這個 Lambda 演算。以暱名函數來表示 Lambda 是很常見的做法，但其實反應出 JavaScript 語法上的不足。如果能有一個更簡易的語法，讓我們表示 Lambda，這段程式碼就會比較精簡。</p>
<p>所以，這很可能要從修改 JavaScript 語法的角度，來做強化。未來，新的 ECMAScript（JavaScript 的語法標準）或許可以讓我們這樣寫程式：</p>
<pre><code>function(x) { return x + 2 }  // 複雜的寫法
x =&gt; x + 2                            // 希望可以有這種精簡的語法
</code></pre>

<p>再舉一個例子：</p>
<pre><code>var result = (x =&gt; x * x) 3;  // 左結合寫法，輸入值 3 放在最右邊，最後 result 為 9
</code></pre>

<p>如果將 Lambda 做為函數的參數：</p>
<pre><code>[1, 2, 3, 4, 5]
     .map(function(x) { return x * x });
</code></pre>

<p>未來或許能簡化為：</p>
<pre><code>[1, 2, 3, 4, 5]
     .map(x =&gt; x * x);             // 希望可以有這種精簡的語法
</code></pre>
 
<p>在 ECMAScript 還沒有正式加入相關語法前，我們目前還是只能使用暱名函數的寫法。</p>
<h2>使用 TypeScript</h2>
<p>在深入了解 Lambda 的觀念後，就能知道根本之道在於「新的 JavaScript 語法」。目前有一些 Open Source 程式庫，就試著在解決這個問題。唯有透徹底了解 Lambda 的觀念，才能知道這些程式庫目地何在。</p>
<p>筆者推薦的解決方案是：TypeScript。這是一個由 Microsoft 所開發的工具，實際上是一個 Compiler。TypeScript 提供了擴充的 JavaScript 語法，可藉由 TypeScript 編譯為標準的 JavaScript 語法。</p>
<p>首先，必須先使用 npm 安裝 TypeScript 工具：</p>
<pre><code>$ npm install -g typescript
</code></pre>

<p>接著，以 TypeScript 的語法撰寫 JavaScript 程式碼。TypeScript 的語法就是 JavaScript 語法，只是提供了許多好用的擴充語言，因此學習上並沒有障礙。以下是一個 TypeScript 範例：</p>
<pre><code>var square = (x) =&gt; x * x
console.log(square(3));
</code></pre>

<p>將程式碼儲存為 l.ts 後，再利用 TypeScript 編譯：</p>
<pre><code>$ tsc l.ts
</code></pre>

<p>編譯後可以得到 l.js 檔案，以下是 l.js 的內容：</p>
<pre><code>var square = function (x) {
    return x * x;
};
console.log(square(3));
</code></pre>

<p>看到編譯後的程式碼後，馬上可以反應出這個觀念：TypeScript 提供一個簡單好用的 Lambda 語法。當然，TypeScript 的功能很豐富，這只是牛刀小試。</p>

</div>]]></description>
         <link>https://www.jollen.org/blog/2013/10/javascript-lambda.html</link>
         <guid>https://www.jollen.org/blog/2013/10/javascript-lambda.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Node.js &amp; RESTful</category>
        
        
         <pubDate>Fri, 18 Oct 2013 00:57:32 +0800</pubDate>
      </item>
            <item>
         <title>解讀三星的開放創新中心</title>
         <description>文/Jollen Chen（原文刊載於 CTimes 雜誌 2013 年 10 月號）

自從三星將開放源碼做為軟體經營的主要戰略後，現在又更進一步，在矽谷設立開放創新中心（Samsung Open Innovation Center），藉此打造更大的人才平臺。三星的開放源碼戰略，仍至於開放創新中心，都圍繞在「人才」的層面。

三星在 2013 年 9 月 14 日於矽谷舉辦的「創業公司如何全球化」會議中提到，三星開放創新中心的工作，主要為創業加速器（accelerator）和投資。創業加速器與投資，很明顯就是從天使投資人的角度，成為創業者早期的投資人。

三星，一家已經是世界上最大的硬體製造公司，用微觀的角度，去做宏觀的事情。開放創新，將會是開放軟體後，另一個改變世界的力量。從 2013 年開始，第一波的網路創業潮開始，這一年可說是 Startup 元年；Startup 創業風潮，是開放創新的代表性文化。三星在成為世界的硬體巨獸後，還能保有如此膽大心細的公司文化，預見不久的未來，大約在 2013 下半年左右，第四代的新三星雛型將浮現；我們將能推斷，三星的下一波競爭力將建構在硬體結合創新的網路服務上。

軟體與硬體的整合，其戰場將更進一，延伸到軟體、硬體與服務的整合。2013 年出現大量的 Startup、天使投資機構與創業加速器，這個現象幾乎能斷定未來三年內，也就是 2014~2016 年間，將是網路產業的另一波革命。傳統的硬體製造商，面對新一波來勢兇兇的變革，要怎麼因應？筆者提出幾個建議：

1. 開放創新做為公司的經營戰略：這點與開放源碼的戰略地位相同，都在於「人才」的部份。傳統的招募制度，已經無法吸引到優秀的軟體人才；傳統的招募制度，可能完全失靈。

2. 經營開放創新業務：三星的開放創新中心，將開放創新中心做為一項業務。筆者認為這是相當可行的觀念，將「天使投資」與「收購」做為主要業務，不但能取得大量的外部創新資源，也能達到直接吸納（收編）人才的目的。

創業如果是開放創新的主要文化，我們就要把員工的觀念，提昇到創業者的層次；把專案的觀念，提昇到育成與加速器輔導的層次。最後，把收購做為是一項業務。在開放創新的生態體系中，創業家、天使投資人與收購者，是不可或許的三個元素。如今，能做為收購者的超大型企業，或是手上握有現金，具收購實力的中大型企業，將面臨市場開發飽和的問題；收購新創公司，將可成為成長的一個動力。

三星開放創新中心的 Marc Shedroff 說：「他認為做加速器、投資和收購已經不是三星的一個選擇，而已經是一項關鍵業務。」開放創業是一項業務（Business），所以三星與創業者之間，應該是一種生意關係。筆者認為，這是很合理的，這是一個很公平的 Win-Win 關係。

在開放創新的生態系統中，創業家、天使投資機構與收購者，是最主要的三個角色。預計從 2013 年底開始，這會是開放創新模式的主要商業模式；並且會持續至少五年的時間。現階段看來，三星不但掌握住了未來五年的產業主流價值，而且還在這個開放創新的主流價值浪潮中，扮演天使投資機構與收購者的雙重角色。</description>
         <link>https://www.jollen.org/blog/2013/09/samsung-open-innovation-center.html</link>
         <guid>https://www.jollen.org/blog/2013/09/samsung-open-innovation-center.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Fri, 27 Sep 2013 11:57:02 +0800</pubDate>
      </item>
            <item>
         <title>改用 Client-Side Markdown Parsing</title>
         <description><![CDATA[Markdown 的解析可以從 Server-side 來做，也可以由 Client-side 進行。如果是從 Server-side 來解析，我在目前專案裡採用的是 [<a href="https://github.com/chjj/marked" target="_blank">marked</a>]。這是很不錯的 Markdown parser，並且支援 [<a href="https://help.github.com/articles/github-flavored-markdown" target="_blank">GitHub flavored markdown</a>]。

Marked 也有一份 Client-side 的 port，稱為 [<a href="http://strapdownjs.com" target="_blank">Strapdown.js</a>]，Strapdown.js 也支援 GitHub flavored markdown。有於以下 3 個原因，後來我將 Server-side parsing 的做法，修改為 Client-side parsing：

1. UX - 原先採用 Server-side (RESTful) + Backbone way 的做法，使用者體驗比較不好

2. Better SEO - RESTful 與 Backbone way 的做法，使得 HTML 文件本身並不夾帶靜態文字，較不利於 SEO。雖然可以採用 Google 建議的方式，但會讓 URL 長的不好看

3. Editable - 將 markdown 以靜態方式放置在 HTML 文件裡，除了有利 SEO 外，未來也可以實用成 Client editable，讓 User 可以線上編輯 markdown 內容

目前，Server-side 的做法是利用 &lt;script> 標籤來放置 markdown 內文。

* Jollen's Blog 將透過 Booklog 平臺，將日誌以 Ebook 形式集結為更系統化的電子書，歡迎<a href="http://booklog.io" target="_blank">關注</a>。]]></description>
         <link>https://www.jollen.org/blog/2013/08/client-side-markdown-parsing.html</link>
         <guid>https://www.jollen.org/blog/2013/08/client-side-markdown-parsing.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
        
        
         <pubDate>Sat, 31 Aug 2013 16:44:11 +0800</pubDate>
      </item>
            <item>
         <title>Startup Engineering 演講</title>
         <description><![CDATA[我在 Android Day 2013 上的 [<a href="http://www.android-day.com/schedule.html" target="_blank">Keynote 演講</a>]。 創業熱潮在全球發燒，並從網路、Apps延燒到開放硬體，如今，開發者不僅可以在應用上創新，成為創業家也不過是一線之隔。不過，從開發者到創業家，除了熱情，還需要具備更多條件。 《Startup Engineering》是Stanford大學所開的一門知名課程，目的是有系列的教導軟體創業的技術、設計、行銷與運籌。在這場演講中將剖析軟體創業不可不知的必要條件。

<iframe class="imgur-album" width="100%" height="550" frameborder="0" src="http://imgur.com/a/s75C7/embed"></iframe>]]></description>
         <link>https://www.jollen.org/blog/2013/08/startup-engineering.html</link>
         <guid>https://www.jollen.org/blog/2013/08/startup-engineering.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 26 Aug 2013 23:22:15 +0800</pubDate>
      </item>
            <item>
         <title>談開放創新與管理：精實軟體開發</title>
         <description><![CDATA[文／Jollen Chen（原文刊載於 CTImes 雜誌，2013 年 8 月號）

Lean Software Development，精實軟體開發。另一個開放創新與管理的支柱就是 Lean Software Development。Lean Sfotware Development 的概念源自日本 Toyota 的生產系統，後由 Agile 社群將之導入軟體工程領域，成為敏捷開發模式的重要思想基礎。在敏捷開發模式的發展過程中，Lean Software Development 的觀念不斷被討論；不久後，便由 Mary Poppendieck 與 Tom Poppendieck 將其發展成一套系統化的模式，Mary 與 Tom 同時也提出了 22 套工具，以落實 Lean Development。這是 Lean Software Development 的起源。

直至今日，App 產業的成形，以及大量的創新網路服務被發展出來，又再強化了 Lean Software Development 的重要的。現在軟體產業，已由技術導向的行業，轉變為文化與創意的產業。因此，Lean Software Development 方法論，結合 Lean Startup 創業模式，成為重要的管理思想。

今日的軟體開發，講究精實模式（Lean Software Development），敏捷開發方法的 Kanban 方法論，部份相當符合精實模式的精神。Kanban 方法論追求打造一個自我組織型（Self-Organized）的研發團隊，且主要以外部開發者為主要資源。這一點與Chesbrough的理念不謀而合（Chesbrough 2006）。這個部份的管理經驗，是台灣各大硬體廠所久缺的重要元素。

Lean Software Development 的其中一個法則（Lean Principles）就是：滅少不必要的浪費，這點與精實創業（Lean Startup） 的觀念一致，也和原始 Toyota 的精實生產系統一致。這個觀念在許多討論 Lean Startup 的文章都有提到。對於新創團隊來說，所謂減少不必要的浪費，可以先以下二個角度開始。
creativeLabs Office

第一、避免不必要的內部溝通成本。Lean Startup 要表達的深層精神應該是：「先推出最有用的功能」，並專注服務固定的幾位使用者，讓第一批使用者滿足你所推出的產品。敏捷開發與 Lean Software Development 都提出實際的工具（有些工具指的是一套系統化方法），來幫助團隊解決這個問題。

第二、善用外部資源。以筆者近期的一個 Startup 計畫為例，將這個網站上線的硬體成本，大約只要美金300元左右；這與12年前的環境相差百倍以上。當時，我可能需要一個小型機房，或是 Co-Location 服務，加上頻寬費用，初期資金可不止要3萬塊美元。善用各種免費資源，或是付費服務（例如：Amazon EC2），都能減少不必要的浪費。另一個浪少良費與提昇效率的方式，就是使用開放源碼元件，這也是 Open Innovation 的核心觀念之一。

我看到有些現象是，新創公司盲目追求組織架構，許多傳統科技公司的新創過程，也太過於強調組織策略，這些經常埋下了日後的敗因。一個精實模式下的軟體開發，經常不需要依賴傳統的組織策略。對於經營 Startups 的團隊來說，在日後取得創投的資金浥注後，需要好好地思考這個議題。

例如，現在的組織策略，很難說明如何使用 Github 這樣的工具，創造成功的 Startup 計畫。更不用談，有些 Startup 團隊，更是以虛擬團隊的形式運作。在這裡提到的組織策略議題，並非要表達組織策略不具重要性，而是要強調如何採用新的管理方式來執行它：不能一味地 COPY 別人的組織結構與管理方法。

Lean Software Development 與 Lean Startup 同樣講求效率與消除浪費，如何善用現有的各項工具、技術與資源，以及導入新的管理方法與開發觀念，都是 Startup 團隊必須要不斷學習的新知識。到這裡就不難看出，Lean Software Developemnt 與 Open Innovation 的思考，是相輔相成的關係。 

* Jollen's Blog 將透過 Booklog 平臺，將日誌以 Ebook 形式集結為更系統化的電子書，歡迎<a href="http://booklog.io" target="_blank">關注</a>。]]></description>
         <link>https://www.jollen.org/blog/2013/07/lean-software-development.html</link>
         <guid>https://www.jollen.org/blog/2013/07/lean-software-development.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sun, 28 Jul 2013 21:40:41 +0800</pubDate>
      </item>
            <item>
         <title>談開放創新與管理：外部的力量</title>
         <description><![CDATA[文／Jollen Chen（原文刊載於 CTImes 雜誌，2013 年 7 月號）

Open Innovation，開放創新。University of California, Berkeley 的 Henry Chesbrough 教授在他的著作 Open Innovation: The new imperative for creating and profiting from technology 裡提出了開放創新 (Open Innovation) 一詞。 Henry Chesbrough 教授目前是 University of California, Berkeley 的開放創新中心 (Center for Open Innovation) 總監。開放創新從多個面象來探討研究與發展 (RD) 的新現象，當中最為人津津樂道的就是開放源碼 (Open Source) 文化。開放創新探討「創新」的新模式，其中最基本的課題就是，分析產業如何取得創新的泉源。

在 Chesbrough 教授的開放創新著作裡，特別討論到典型的內部 RD 團隊，不再是公司重要的策略資產，為什麼呢？主要的原因是網路與社群的興起，讓很大比例的創新是來自於公司外部，而不是公司內部。Hacker 文化則是主要的一個原因。八零年代開始，網際網路急速地發展。被稱為 Hacker 的軟體高手，透過網路集結並分享自已的軟體；其中有一個相當有代表性的人物，叫做Richard M. Stallman。他特別喜歡讓大家「自由地」使用或修改他的軟體，例如：GCC (Linux 與 Android 使用的編譯器)、GDB (Linux 與 Android 使用的除錯器) 等。

Richard Stallman的理念得到Hacker們的熱烈迴響。於是為了保障「使用與修改軟體」的「自由」，他成立了一個基金會，稱為「自由軟體基金會 (FSF, Free Software Foundation)」。FSF組成了一個律師團，擬定了一份保障「軟體使用與修改自由」的授權合約，供取得這些軟體的個人或廠商共同遵守，這份合約就是名聞遐邇的公眾授權條款 GPL (General Public License)。

在開放創新的討論裡，GPLv2 成為重要的議題，包括隨後而至的 Apache 授權條款，都是開放創新的源動力。Hacker、社群、GPLv2 與 Apache、自由軟體與開放源碼，都是開放創新的元素。以 GPL 授權釋出的開放源碼軟體，最知名的就是 Linux 作業系統核心。

這是 Hacker 文化下的代表性產物。透過網路的連結，Linux kernel由超過五十萬個開發者共同「集體創作」而成，這也是現今我們所熟知的「社群文化」。許多新技術與新概念，就在這個社群裡產生了，所以，許多大公司便開始參與 Linux 作業系統核心的開發社群，這些公司也 Donate 社群。

Chesbrough 教授在他的著作裡也提到，在開放創新的模式下，創新想法與 IP 的取得方式更為多樣化，其中一個方式便是透過 donation 的模式來取得。另一個知名的開放創新模式，稱為 Hackathon。1999 年開始了第一次的 Hackathon 活動，這是開發者的聚會，一些開發者聚集在一起，在很短的時間內，把想法撰寫成實際可執行的程式碼。

知名的 PhoneGap 專案，就是在某一次的 Hackathon 活動中誕生。PhoneGap 是開放式創新的代表性專案之一，IBM 曾贊助 PhoneGap 開發一段時間。PhoneGap 的開發團隊後來也成立公司，這家專門開發 PhoneGap 的開發商，後來被 Adobe 併購。

從 Dreamwaver 5.5 版開始，設計師可以利用網頁的模式來開發手機 App，這完全是 PhoneGap 專案的功勞。拜開放創新模式下產生的 PhoneGap 之賜，Adobe 的 Dreamwaver 產品線，取得了一些創新。從 Linux kernel 開始，到現在的 PhoneGap，這都不是任何一家公司的成果，這些都來自於外部的力量。難怪 Chesbrough 教授會這麼強調「外部的力量」。

近期筆者與幾家科技廠的高階主管，討論到開放創新的管理模式時，他們會認為這是一個有待證明的管理模式，並且因為這種模式過於鬆散，這種無法集中管理的專案，是不會成功的。可見這些傳統核板的管理思惟，已經成為台灣硬體產業轉型的障礙了。

* Jollen's Blog 將透過 Booklog 平臺，將日誌以 Ebook 形式集結為更系統化的電子書，歡迎<a href="http://booklog.io" target="_blank">關注</a>。]]></description>
         <link>https://www.jollen.org/blog/2013/06/open-innovation.html</link>
         <guid>https://www.jollen.org/blog/2013/06/open-innovation.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Thu, 27 Jun 2013 19:28:12 +0800</pubDate>
      </item>
            <item>
         <title>創業者也能扮演天使投資人</title>
         <description><![CDATA[去年開始，我想在 Moko365 的基礎之上，以天使投資模式，從事一些專案開發工作，所以有一些朋友，以為我轉行做創投了。 但是天使投資對我來說，並不是投資行業，而是一種全新的創業形式。所以，我自已喜歡用 Startup 來對比天使投資（Angel <s>investors</s>）。

一個顛覆傳統的時代，是創業家的時代，現在就是一個傳統不斷被顛覆的時代，所以我相信，未來十年，甚至二十年的時間，創業家將不斷製造有用的創新產品或服務。而更彰顯這個時代特點的是軟體創新事業。如果你有一個想法，並且也有一筆很小的資金，你就可以很快推出軟體到市場上去測試顧客的反應。

於是，提供小額冒險資金的天使投資人，以及精實模式（Lean Startup）的概念，變成現在當紅顯學。所以，我的公司只要準備一筆小額的預算，就可以讓一個概念（Idea）付諸實行，或許是睹一把的心態，也或許是真正看好一個專案的潛力，但是本質上就是一個創業的行動。所以，天使投資對我來說，是一種創業的新模式，而不是創投行業。

許多人注意到這些不斷出現的新創公司，有它的獨特價值，所以願意透過小額投資的方式，去支持這些新創團隊。所以這有點像是掏金文化。小額出資者在創新軟體的世界，用力挖金礦，哪怕亂槍打鳥，壓對一個團隊，就像挖到黃金一樣，所有投資都能獲得巨大的回報。

近一年來，我也和許多創投或天使投資人接觸，個人非常認同天使投資人對新創企業的價值，在西方國家，有非常多成功的天使投資案；我發現，成功的天使投資人，他們和新創團隊站在一起，他們認為是和團隊共同在創業。這點非常的重要，把天使投資當做一個職業，把自已變成創業者之一，是天使投資者的精神。

天使投資者此刻是和創業團隊站在同一陣線，並且提供自已的人脈與經驗，這是天使投資的另一個精神，「扮演導師（Mentor）的角色」。我們會發現，有許多投資案，特別是在台灣，投資者（出資人、金主）與創業團隊，一開始就是一種對立的關係，並且在雙方利益談判上，互不相讓，這對軟體創業者來說，是一個不利的現象。所以雙方如果不能互讓一步，尋求合作、創造利益，金主就是在跟自已的錢過不去，創業家是和到手的機會過不去：變成二邊都是睹客，睹一把看看會不會中獎，公司會不會成功。

很令人興奮的事情在台灣發生了，過去台灣產業不具備天使投資環境，所以產業欠缺創造力，失去轉型能力；而現在，不斷有產業經驗豐富的天使投資者出現，並且積極尋找台灣與國外的投資對象。台灣政府對技術創業與新創事業，也提出了非常友善的政策。這或許能讓台灣從硬體製造的經濟，進入到軟體創新的經濟。

<strong>和天使合作</strong>

筆者在台灣接觸了許多天使投資人，或是天使投資團隊。我也在轉換自已的想法，以天使投資的角度，去看待自已未來的創業活動。同時，更希望未來能和更多的天使合作，並且也希望他們都能思考：天使投資也是自已在創業，是一種創業活動，而不是出資者。

軟體創業者在 Zero stage 時，找對天使投資者，會有很大的加分效果，因為等於你邀請了有影響力或豐富資源的伙伴，加入到創業團隊，成為創業者之一。浮士德的交易是將自已的靈魂交給魔鬼，以換取驚人的才華與能力，所以又叫魔鬼交易。

天使投資人與創業家並不是魔鬼交易的關係，所以創業家不是在把自已的靈魂出賣，換取資金與豐富的資源。所以在未來和天使們的合作過程中，我不會因為天使的個人喜好，去改變自已的信念，也不會動搖原有的核心價值，如果我是天使的身份，我也不希望創業團隊，因我的個人喜好，失去自已的靈魂。如果不是這樣的話，天使就是魔鬼，我就是和魔鬼在做交易了；並且，原本想當天使的我，也成為魔鬼了。

* Jollen's Blog 將透過 Booklog 平臺，將日誌以 Ebook 形式集結為更系統化的電子書，歡迎<a href="http://booklog.io" target="_blank">關注</a>。]]></description>
         <link>https://www.jollen.org/blog/2013/05/working-with-angel-funders.html</link>
         <guid>https://www.jollen.org/blog/2013/05/working-with-angel-funders.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Thu, 30 May 2013 20:38:41 +0800</pubDate>
      </item>
            <item>
         <title>瀏覽器引擎的黃金時代開始：Blink 出現、三星來了 </title>
         <description>文／Jollen Chen（原文刊載於 CTimes 雜誌）

Google 在 2013 年的 4 月份發佈了一則消息：將開發新的瀏覽器引擎。這是瀏覽器界的大事，這代表著瀏覽器競賽已經進入了一個新的里程碑。這個新的瀏覽器引擎稱為 Blink，不過它並不是全新的開發專案，Blink 是 WebKit 的一個分支。目前，最新版本的 Chrome 瀏覽器已經改採 Blink 引擎。

Blink 直接採用 WebKit 的原始程式碼，並不是從新做起。這叫人好奇 Google 為何不繼續在 WebKit 引擎上開發，卻硬是將 WebKit 給「fork」了出來？這是瀏覽器引擎的競爭下，一個積怨已久的問題，簡單來說，這是一個商業策略，並不是單純的技術考量。今年二月份，Opera 宣佈每個月有超過 3 億使用者，在使用它們的瀏覽器，同時，Opera 也將開始貢獻 WebKit 專案。這個事件應該是 Google 決定開啟 Blink 計畫的主因之一。

Apple 在 2005 年公佈了 WebKit 原始程式碼，接著受到多家瀏覽器開發商採用，其中一個便是 Chrome。經過多年發展，WebKit 變成一個好用現成的瀏覽器引擎。於是，Opera 也開始採用，並貢獻程式碼。這豈不是 WebKit 引擎的一大勝利。眼看第一階段的瀏覽器引擎大戰，將由 WebKit 引擎勝出，Google 大概不會坐視不管，特別是 WebKit 引擎已經有超過 40% 的市場份額了。

Blink 似乎是為了與 WebKit 有所區隔，或說是為了與 Apple 切割的一個策略產品。但是，制衡 WebKit 並不是 Google 官方的解釋。根據 Google 的說法，Chrome 與 WebKit 的 multi-process 架構不同，讓二者結合只會增加軟體的複雜度，於是 Blink 專案誕生。不過，最後 Google 又提到，多個瀏覽器引擎的存在，可以改善整個 open web ecosystem 的體質，讓它更健康。無論是明示或暗示，這意謂著 Blink 專案同時有著技術上，以及商業上的考量。

回到行動瀏覽器，Opera 擁有這個市場的最多份額。隨著 Google 發佈 Blink 計畫，Opera 很快的也宣佈轉進 Blink 引擎。不管是巧合，還是特意安排，這都是 Blink 計畫的一大勝利：Google 毫不費力，便取得了在行動瀏覽器引擎的重要位子。瀏覽器引擎大戰最精采的一刻還沒結束，幾乎在同一時間，Mozilla 與三星也發佈消息，宣佈在 ARM 平臺上，合作開發新一代的瀏覽器引擎，稱為 Servo。Servo 引擎的重點是具備支援多核心 (Multi-Core) 架構的能力，並且已釋出原始程式碼。

三星的行動裝置多核心處理器，具有相當高的工藝水準，它的多核心軟體研發方面，也是兵強馬壯，實力堅強。因此，Servo 計畫的未來性更加吸引人。以技術能量來論，三星與 Mozilla 的 Servo 引擎，實力不輸 Blink 引擎。筆者認為，Servo 引擎的關鍵在於，三星能不能利用它在行動裝置市場的領先地位，將 Servo 推上一把，使它與 Opera 抗衡。

至於 Apple，此時也不必要有什麼動作，因為 Safari 瀏覽器與 iPhone 裝置是密不可分的關係，Safari 使用的是 WebKit 引擎，而行動版 Safari 瀏覽器的成敗，完全取決於 iPhone 與 iPad 裝置的銷售量。總結來看，瀏覽器引擎大戰，是要繼續三分天下了， 競爭者變成 WebKit、Blink 與 Servo。此外，還有一個影響因素是 HTML5 標準，這又是另一個層面的問題了。最後，此刻最傷腦筋的，應該是微軟。</description>
         <link>https://www.jollen.org/blog/2013/04/blink-servo-war-of-rendering-engine.html</link>
         <guid>https://www.jollen.org/blog/2013/04/blink-servo-war-of-rendering-engine.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 29 Apr 2013 02:28:25 +0800</pubDate>
      </item>
            <item>
         <title>簡報：讓 HTML5 走進 IPTV Framework</title>
         <description><![CDATA[Moko365 與 OESF 於 4/16 日舉辦一場有關 IPTV 的<a href="http://www.oesf-global.com/event/seminar-ip-tv-20130416-en/" target="_blank">研討會</a>，目的是探討 ITU-T 所制定的 H.762 標準。

H.762 技術採用部份 HTML5 標準，成為一個 IPTV 的互動多媒體環境。根據筆者近期的開發心得，H.762 對於 TV Apps 的開發仍有所不足，因此在這個演講裡，筆者分享了一些想法，並提出 H.762 的修正建議。這個修正建議，主要是希望能把 HTML5 技術帶入 IPTV Framework 裡。

對 IPTV 標準有興趣的朋友或廠商，歡迎多加交流。簡報網址如下：

<a href="http://www.slideshare.net/jollen/html5-iptvframework-r2">HTML5 in IPTV Framework</a>]]></description>
         <link>https://www.jollen.org/blog/2013/04/html5-iptv-framework.html</link>
         <guid>https://www.jollen.org/blog/2013/04/html5-iptv-framework.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
        
        
         <pubDate>Tue, 23 Apr 2013 13:21:01 +0800</pubDate>
      </item>
            <item>
         <title> SmartTV 與客廳：更大的 App 戰場、更多的商業模式</title>
         <description><![CDATA[文／Jollen Chen（原文刊載於<a href="http://wired.tw/author/jollen-chen" target="_blank"> WIRED.tw</a>）

今年（2013）是電視大戰的一年，不過身為軟體人與創業者，更關心的是另一個角度的問題：電視大戰，將會演變為客廳大戰，軟體與網路創業家們，你們是催手。

電視將不再只是電視機，從只是看電視頻道，到能連接網路；這樣的電視機就叫做 IPTV 或 SmartTV。 它正在走手機過去的發展道路。過去手機，從單純的通話功能，演變成現在的 SmartPhone。

IPTV 與 SmartTV 以模糊概念來說，都是「可以連到網路」的電視。不過就定義來看，這分屬於二種不同的產品概念。傳統的電視採用類比訊號方式傳送節目，而 IPTV 則是以數位方式傳送節目。所以，IPTV 就是我們經常聽到的數位電視。IPTV 透過數位機上盒（Set-top-box）來接收節目。Set-top-box 將接受到的數位內容，以 HDMI 輸出方式顯示在電視機上。

SmartTV 則是另一個概念。SmartTV 也稱做 Connected TV，即連網電視。SmartTV 本身內建一個作業系統（OS），這讓電視機本身很像是一台電腦。就像 SmartPhone 也內建作業系統，本身就是一台小型電腦一樣。根據 Wikpedia 上的定義，SmartTV 等於 SmartPhone。從這個角度來看，SmartTV 無疑是 SmartPhone 的延伸。

透過 IPTV 無法觀看網路電視（例如：PPS.TV），因為機上盒可能不支持。但透過 SmartTV，只要安裝 PPS.TV 的 App，就可以連上網路看影片。這個情境又表達了 IPTV 與 SmartTV 的另外一個差異：SmartTV 有 SDK，可以撰寫 App，也可以安裝 App。Set-top-box 可不一定能辦到這件事。這意謂著「TV App programming」的時代悄然而至。

TV App 會是引燃客廳大戰的火苗。從產業觀點，電視已經不再只是單純的硬體製造業，也不單單只是供應鍊的問題。新的電視產業，會很像是這五年的 SmartPhone 產業，也有 App、也有社群、也有服務等等。台灣的硬體產業，勢必要儘快從這個新的角度去看 SmartTV，而不是在過去的製造與成本問題來看這個新機會。

另一方面，TV App 的開發，也多了另一個選擇，它就是 HTML5。簡單說，電視內建一個瀏覽器（TV Browser）來執行 HTML5 App，並存取網路服務。以 HTML5 來開發 TV App 或 TV Service，雖然說這不一定是強制的標準，但可能是最能創造商業價值的標準。

科技業的遊戲規則，有一個硬道理：標準本身好不好，技術含量高不高，都不重要。重要的是，標準能不能產出商業價值。這點在軟體的遊戲圏裡，更是攻不可破的道理。因此，ITU Telecommunication 標準組織，便制定了 H.760 系列的標準供電視行業使用。

以 H.762 來說，這是一個多媒體互動 App 的標準，而 H.762 便是採用了 HTML5 技術。 ITU Telecommunication 過去所制定的 H.264 標準，現在也成為重要的影音編解碼標準。簡單易懂的說法是，H.762 是 TV App 的國際標準。TV 也開始重視 App 開發了。

過去在手機 App 與網路奮戰的創業家們，SmartTV 來臨了，這不是一個新的戰場，而是我們所熟悉的 App 與網路的老戰場。SmartTV 讓軟體創業家的戰場更大了些。過去做好的一些準備，或許在未來的 SmartTV 行業就可以開始收割了。

SmartPhone 有了 SDK 後，手機大戰開始。SmartTV 也有 SDK，可以安裝 App。一家人坐在客廳沙發上，將不只是對著 SmartTV 看節目。人類的客廳與居家生活形態也將產生很大的變化。SDK 與開發者，又將成為客廳大戰的催手。創業家也將在客廳裡，創造出更多的商業模式。]]></description>
         <link>https://www.jollen.org/blog/2013/03/smart-tv-apps.html</link>
         <guid>https://www.jollen.org/blog/2013/03/smart-tv-apps.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Fri, 01 Mar 2013 17:06:16 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] 關於 SurfaceFlinger::createSurface() 的 DisplayID</title>
         <description><![CDATA[近期為幾家科技廠進行 Android 的企業內訓，課程主題著重於 Framework 層。最近的內訓主題是 Android Graphics 系統。今天 (2/4) 在講解 SurfaceFlinger Server 建立 Surface 時，提到這個方法：

<p><pre>1287 sp&lt;ISurface&gt; SurfaceFlinger::createSurface(
1288         ISurfaceComposerClient::surface_data_t* params,
1289         const String8& name,
1290         const sp&lt;Client&gt;& client,
1291         DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
1292         uint32_t flags)
</pre></p>

其中 DisplayID 參數的部份，代表 Screen。這部份可以參照 /system/lib/egl/egl.cfg 設定檔，例如，以下的設定：

<p><pre>0 1 qcom</pre></p>

第一個數字代表 Display，第二個數字代表 Implementation，最後的字串為 TAG，用來與 HGL 程式庫匹配。上述設定指定 Display 設定為 0，代表 Default (LCD)，有些 Android 系統可為 1，代表 HDMI。

此外，Display 參數在實例化 Layer 時也會用到：

<p><pre>1303     //LOGD("createSurface for pid %d (%d x %d)", pid, w, h);
1304     sp&lt;Layer&gt; normalLayer;
1305     switch (flags & eFXSurfaceMask) {
1306         case eFXSurfaceNormal:
1307             normalLayer = createNormalSurface(client, d, w, h, flags, format);
1308             layer = normalLayer;
1309             break;
1310         case eFXSurfaceBlur:
1311             // for now we treat Blur as Dim, until we can implement it
1312             // efficiently.
1313         case eFXSurfaceDim:                                                
1314             layer = createDimSurface(client, d, w, h, flags);
1315             break;
1316         case eFXSurfaceScreenshot:
1317             layer = createScreenshotSurface(client, d, w, h, flags);
1318             break;
1319     }
</pre></p>

在實例化 Layer 時，可指定 Layer 的 Display。附帶一提，Android 的 Surface 分為四大類，一般 App 使用的是 Normal Surface，其它還有 Blur Surface、Dim Surface 與 Screenshot Surface。Dim Surface 是支援 Android Power Management (Wakelock) 的特殊 Surface。]]></description>
         <link>https://www.jollen.org/blog/2013/02/surfaceflinger-display-id.html</link>
         <guid>https://www.jollen.org/blog/2013/02/surfaceflinger-display-id.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Mon, 04 Feb 2013 20:53:49 +0800</pubDate>
      </item>
            <item>
         <title>鴻海「八屏一雲」計畫：導入 HTML5 與技術關鍵</title>
         <description>文／Jollen Chen（原文刊載於 CTimes 雜誌 2013 年 2 月份）

八屏一雲是鴻海科技集團擎畫的技術藍圖。鴻海科技集團協助 Moko365 舉辦 HTML5 相關課程，筆者有幸擔任系列課程的主要講師，在整理相關技術的過程中，得到了一些心得。使用 HTML5 做為八屏裝置的核心技術時，有什麼關鍵的地方要特別注意？筆者整理了幾個議題，在此與大家分享。

第一、跨裝置。這是第一個「八屏一雲」的重要技術工作。從技術的角度來說，「八屏一雲」，要能跨裝置（cross-device），而好的技術莫過於 HTML5。HTML5 是一個標準，透過此標準來開發 Web App；Web App 必須透過瀏覽器來執行。我們說，Web App 的執行環境（runtime）是瀏覽器。

第二、跨瀏覽器。 第二次瀏覽器大戰開始於2010年左右，大概是 HTML5 標準即將發佈，以及智慧型手機發展進入最高峰，二個時間的交匯點。現今，在 HTML5 世界裡，較具代表性的瀏覽器是 Firefox、Safari 以及 Chrome。

由於每一個瀏覽器實作 HTML5 標準的程度不一，並且實作上還會有一些小差異，因此，讓 Web App 能做到跨瀏覽器（cross-browser）成為另一個重要任務。還好，這個工作是階段性的，不過倒是能讓開發者忙碌很長一段時間。

第三、善用 Media Query。這是 CSS3 裡的標準，簡單說，它可以幫助開發者自動偵測目前的裝置屬性，並且依不同的屬性來套用不同的 CSS 設計。為不同裝置撰寫不同的 CSS 樣式，並依裝置的不同來選取套用，這就是 Media Query 的功能。

很麻煩的是，現今的瀏覽器仍有 Media Query 支援不足的問題。例如：rem 單位在 Media Query 的區域裡可能失效。這是瀏覽器的不足，理論上未來的實作將會解決這些問題。目前，可以使用 JavaScript media query 的暫時替代方案。

第四、用對 UI Framework。在「八屏一雲時代來臨 教你HTML5六小時打通」課程裡，筆者整理了不只 20 種 UI Framework。以「cross device」做為題目，較廣為流行的是 Less Framework 4，它極為簡單易用。Less Framework 4 能幫助開發者生成支援不同裝置的 CSS Media Query。

但這些 UI Framework 在支援跨裝置方面仍有不足之處。以鴻海的八屏一雲理念來說，仍沒有一個 UI Framework 真正具備「跨八屏」的能力。這裡仍有許多技術工作，若鴻海能在這個部份投入資源，補足這個基礎建設，將會是很有價值的事情。

第五、瀏覽器作業系統化。從 Chrome 到 Firefox OS（前身為 Boot to Gecko），瀏覽器作業系統的發展，從沒有停滯過。Gecko 是 Firefox 的 HTML5/CSS 引擎，因此「Boot to Gecko」就是「一開始就是瀏覽器環境」的概念。

從工程的角度來看，Webkit 是一個很值得投資的瀏覽器引擎，它的發展始於 KDE 計畫，並吸引像是 Apple 這類型的知名企業加入。現在，Webkit 是一個很活躍的 Open source 計畫。許多知名的瀏覽器，例如：Safari，都採用 Webkit 做為其核心。

或許「Boot to Webkit」會是一個很好的創新研發題目，不管如何，具備維護 Webkit 引擎的能力，將可能是八屏一雲計畫的技術核心。

Moko365 與鴻海科技集團，從贊助免費 HTML5 課程開始，希望能逐步推廣 HTML5 技術與八屏一雲觀念。最後，八屏指的是哪八種不同的屏？手機屏、平板電腦、NB、AIO、Portable TV、TV、電子白板、LED顯示屏。</description>
         <link>https://www.jollen.org/blog/2013/01/foxconn-go-8panel.html</link>
         <guid>https://www.jollen.org/blog/2013/01/foxconn-go-8panel.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Fri, 25 Jan 2013 16:12:36 +0800</pubDate>
      </item>
            <item>
         <title>第二個手機時代開始：2013 年觀察重點 </title>
         <description><![CDATA[文／Jollen Chen（原文刊載於 CTimes 雜誌 2013 年 1 月號）

幾個月前在這裡提到「<a href="http://www.jollen.org/blog/2012/07/goodbye-smartphone-gold-years.html">手機業黃金五年結束</a>」，意思是智慧手機產業初生的高速成長蜜月期，已經正式結束。這段時間是2008到2012年。手機業的營業利益率將開始下滑。

2012年底是很重要的一個時間點，三星以智慧型手機龍頭的頭銜，完美地在第一個時間的戰場，繳出漂亮的成績單。三星的Galaxy SII成功奪下第一個灘頭堡，宏達電也因此挨了不輕的一拳；延續Galaxy SII的氣勢，SIII更是打下iPhone，贏得智慧型手機的王座。

下一個智慧型手機的時期，筆者認為可能會是在2013~2015年。第二個智慧型手機時代開始了。2013年度到來，未來一年手機業的觀察重點為何，在此提出筆者的看法與大家分享。

第一、差距不至於再擴大。就如同大家所知的，智慧型手機的第一與第二戰線已經抵定，由Apple與Samsung形成的第一戰線，正與第二戰線的手機品牌擴大差距。從市場份額的角度來看，華為、中興、Sony、LG、HTC等第二戰場品牌的總合，還略小於50%。這種大者恆大的現象，應不至於再持續，原因Apple與Samsung的品牌成本效益將更顯著，從贏家的角度來看，爭奪市份份額可能不是第一要務，反倒是思考如何善用品牌成本效益。並且第二戰線的產品，具備多樣化的特性，在品質與性能方面也有顯著提昇，將更能滿足一般消費者的需求。

第二、品牌成本效益決定利潤。筆者認為，第二個智慧型手機的戰場，不能由硬體成本或是出貨量來觀察；在這個新戰場，利潤更有意義，裝置出貨數量只是配菜。台灣的硬體廠應該由品牌的角度來看手機市場，Apple將持續發揮它的品牌成本效益，而Samsung的品牌成本效益明年才正要開始爆發。在品牌效益的加持下，對手賣一支手機的成本是0.2台手機，但自已卻是賣一支手機要一支手機的成本，沒有品牌效益，何來高獲利。

關於品牌成本效益可由幾個消費者數字來觀察。根據近期調查，頂極手機的消費者詢問，Apple大約是33%、RIM大約是3%、Nokia大約是8%，HTC則是7%，至於Samsung，高達45%。以品牌的消費者滿意度來看， 根據近期調查，Apple大約是40%、Samsung大約是50%，而HTC則是大約5%。品牌效益創造獲利的第二個手機時代來臨。

第三、新手機生態系統出現。就如大家所知道的，除了Android與iOS外，微軟的Win8將可能成為第三個生態系統。

第四、人才搶奪戰。簡單說，就是App開發者的資源搶奪大賽。關於這一點，大家都有很深的感受，就不必多提了。但是有一個現象則是值得觀察，就是App開發者或開發商，同時為Android與iOS製作App的比例越來越高，這代表者只為單一作業系統製作App的問題並不存在。但是關鍵會在App開發者的獲利問題上，Google與Apple如何在廣告與下載二個模式外，為開發者創造新的獲利模型，可能成為第二個手機時代的關鍵。

歐洲接下來將可能釋出大量的優秀人才，以及更大量的開發者資源。台灣的產業應把這個議題列入第一要務。合宜的政策與合約架構，才是能吸引這些人才的關鍵。錯過了這次好機會，優秀軟體開發者大量出現的情況可能難以再見。

第五、大量的手機App新創公司。根據筆者的觀察與實地了解，如果你認為現在的手機App新創公司已經夠多了，那麼未來二年的Startups（新創公司）數量與實力可能另你瞠目結舌。這些新創公司的共同特色是走向精實模式，即小團隊、零固定成本、高毛利、更新速度極快、應變能力強、焦點式社群行銷等，另外團隊更年輕化也是一個趨勢。

第六、HTML5與瀏覽器時代。雖然這是在2012年，談了一整年的議題，不過還是有一點值得再次提醒。從開發的角度來看，社交網路（Social Networks）的加據發展，將使HTML5的技術更為重要。而HTML5的運行環境「瀏覽器」，也會有快速的進步與技術創新。

第七、多核App。顯然多核心已經成為手機應用處理器主流，不過，從技術的角度來看，多核心技術的重點在於App本身。雖然多核心技術涉及處理器、作業系統、Android 虛擬機技術、Android 框架等，但是App本身的設計是否能支持並發揮多核心特色，才是讓使用者最有直接感受的環節。簡單說，App開發「多核化」會是一個技術工作的重點。

鑑古知來，筆者整理過去一年的心得，希望能更了解第二個智慧型手機時代的發展格局；此外，還有一點，如同上一期評論提到的「精實消費時代」，一味地Cost Down，消費者可能未蒙其利，先受其害。

軟體團隊的更年輕化，在接下來幾年會更加明顯，以現況來看，大部份的新創軟體團隊，年齡大約在 23~30 歲之間，28 歲後的軟體開發者，更具備經營公司的能力；因此，如同筆者在 [<a href="http://www.jollen.org/blog/2012/11/android-world-2012-china.html">我在 Android World 2012 深圳：與會心得分享</a>] 一文所提，打造一個好的投融資環境，是留住人才的最佳方法。目前台灣的投融資環境來說，和大陸與東南亞相較，實屬不足，這點值得大家省考。]]></description>
         <link>https://www.jollen.org/blog/2012/12/smartphone-next-age-2013.html</link>
         <guid>https://www.jollen.org/blog/2012/12/smartphone-next-age-2013.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sun, 30 Dec 2012 12:07:03 +0800</pubDate>
      </item>
            <item>
         <title>iPad Mini 或將終結這場瘋狂遊戲：精實化時代來臨</title>
         <description>文／Jollen Chen（原文刊載於 CTimes 雜誌 2012 年 12 月號）


2012 年的一個現象：全球手機廠都陷入了一個瘋狂競賽，更是一個瘋狂遊戲。Apple、Samsung 構成這個遊戲的雙軸心，在這二個軸心外環繞著許多品牌。這些品牌的瘋狂競賽就是瘋狂地製造 Android 手機與平板電腦，然而，Apple 與 Samsung 卻已悄悄改變行銷商品的方法，他們由大量消費的銷售方式，逐漸轉變為「精實」的做法。

我認為，iPad Mini 可能會開啟新的消費史。過去工業生產模式下的大量消費模式，如今可能因為 iPad Mini 的發表，讓人類逐漸進入硬體的精實消費時代。過去精實消費只存在內容、軟體與服務產業，現在可能更擴大到硬體消費上。這意味著硬體產業將發生以下二種情況：

第一、「以低價搶攻市場，進而促進銷售的增長」這個幾乎被認為是鐵律的法則將失效。市場上為數眾多的低價 Android 平板電腦將面臨滯銷。

iPad Mini 的定價出乎市場意料之外，並沒有採取低價策略。可是我們要知道，如果這樣的價格，並不能如 Android 陣營所願地「嚇跑消費者」，對非蘋陣營造成的後果將更加嚴重。非蘋陣營將面臨「產品懸崖」的困境：完全找不出能與 iPad Mini 對抗的策略，因為低價策略也無法提升銷售數字了。

第二、Android 應用程式獨立開發商，以及內容供應商，將被迫做出決擇：壓注 iOS 平臺，或是分散資源同時發展 iOS 與 Android 平臺。

第二種現象對非蘋陣營更為不利，App 開發商枇杷別抱將對 Android 平板陣營造成莫大的傷害。不過，倒也不用過於悲觀，因為 Android 陣營在智能手機領域還是能擁有很大的優勢。iPad 開啟平板電腦之戰後，iPad Mini 是否能終結 Android 陣營加入平板戰場後所形成的混沌格局，讓我們繼續看下去。

一般看來，Android 平板電腦，已經有點「PC 化」，這多少和一開始 Android 陣營就以 PC 的觀點來開發平板電腦有關係。從這個角度來看，iPad Mini 更沒有必要直接和低價 Android 平板電腦競爭，只需要繼續走原本 iPad 的道路即可。

手機與平板已經不再是 PC 時代的硬體產品，所以相關產品的設計與開發都必須精緻且實用，售價固然是重要的因子，但只要合理，不需要低價，也能吸引特定的消費者。總結來說，大量消費時代下的銷售模式，低價必定能創造新的需求；這就像「老闆倒店了」的破盤價銷售模式。

筆者認為，這些製造與銷售低價 Android 平板的廠商，都應該清楚區分「需求」與「需要」的差別。 精實消費 (Lean Consumption) 的模式說明了一件事情：為什麼消費者寧可在誠品書店消費 300 元購買實體書，也不願意花費 49 元下載電子書。

蘋果已故執行長賈伯斯說：「消費者往往不知道自已真正的需求」，Android 低價平板陣營誤以為這是消費者的需求，然而，真正滿足他們需求的產品出現後，消費者才會突然了解自已的真正需求。這就是蘋果一向引人入勝的產品策略：告訴消費者這才是你的需求。

對於實體書來說，這是「需求」，因應考試、閱讀或上課等行為而生。電子書則是「需要」，在公車或捷運上為把握時間，有行動閱讀的需要而生。所以，如果只需要便宜、能用、易攜的平板電腦，Android 平板是好的選擇。然而，真正能滿足各方面需求的平板電腦，很可能只有 iPad Mini。

走向精實化的手機與平板電腦，老闆倒店的做法是否能行得通，就看 iPad Mini 幫我們解答了。筆者認為，低價 Android 平板因 iPad Mini 的出現，將正式面臨上述所提及的產品懸崖問題。iPad Mini 就像誠品的實體書，比較貴，但賣得比較多。</description>
         <link>https://www.jollen.org/blog/2012/12/ipad-mini-leads-lean-consumption.html</link>
         <guid>https://www.jollen.org/blog/2012/12/ipad-mini-leads-lean-consumption.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sat, 29 Dec 2012 17:02:29 +0800</pubDate>
      </item>
            <item>
         <title>我在 Android World 2012 深圳：與會心得分享</title>
         <description><![CDATA[文／Jollen Chen（原文刊載於 CTimes 雜誌 2012 年 11 月號）

筆者在 10/26 參加了由 IDG 美國國際數據集團主辦的 2012 Android World Global Developers Conference，原本是受邀發表演講，後來很認真聽了多場演講，相當有收獲，特別整理一些心得與觀察和大家分享。

<img src="http://www.moko365.com/enterprise/wp-content/uploads/misc/2012-10-26-09.52.03.jpg" />
圖：大會現場

IDG 過去在全球主辦的 Linux World 是非常知名的開發者大會，今年開始也啟動了 Android World 系列活動。Android World 第一次開辦就選擇在中國深圳，可見 IDG 對 Android 在中國發展的重視。根據現場工作人員表示，在網站上註冊的參加者有 1800 人。今天的會場是在深圳福田的香格里拉大酒店，主會場能容納 500 人；現場座無虛席，熱情的開發者填滿了整個會場。
 
一早來報到時，很認真研究了二天的議程表，我發現這次的活動有一個特色：完全沒有人談硬體；還有另外一個特色：現場設有投融資專區！有志創業的軟體高手，在現場就可以洽談投資了。

會場設置的投融資專區，有紅杉資本、同創偉業、IDG 資本、勝訊產業共贏基本、鼎暉創投、高原資本、松禾資本、凱鵬華盈等，不管是風險、投資或融資，來了很多專業的投資人。投融資專區設有「密談專區」，有創業需求的軟體開發人，都能直接找這些投融資人洽談。

早上的主題演講還有共同點：多位嘉賓不約而同都談到了開發者與投資這塊議題。最特別的是奇虎 360 的副總裁，他在演講裡提到：「能力越大、責任越大」，他的意思是奇虎創造了今天這樣的公司規模後，他們便開始扶植有能力的軟體開發者。至今他們已投資超過 50 個開發者，總金額在 2 億 (人民幣) 以上。相信這些種子將會成為未來中國軟體產業的重要生力軍。

奇虎 360 的李總裁又說，只要有能力的開發者，都可以找奇虎投資。我認為，「能力越大、責任越大」，是一種企業展現社會責任的表現，個人相當欣賞。台灣成功的企業，也應該學習這種格局，共創後才能共榮。李總裁現場也公開留下他的手機號碼，要有需要的開發者直接找他！果然是年輕創業家的風格，這是一種完全沒有身段的風範啊。

這次的 Android World 談的都是開發者、軟體或投資，完全嗅不到硬體的味道。現場軟體、網路與創業的氛圍濃厚，與台灣的產業味道全然不同。因此個人認為，要提昇台灣軟體產業的競爭力，應該要從投融資環境的角度著手，學習中國模式，由投融資環境，打造一個創業平臺，活化年輕軟體工程師的創業動力，藉由年輕人的創業活動來建立有競爭力的軟體產業。意思是，政府不能光靠蓋蓋軟體園區就能造出「軟體產業」，只有這批有能力的年輕人才能創造一個軟體產業。

下午則是分會場演講，我只能留在自已的演講會場。在我後面的幾位講者，談的正好是我最感興趣的主題：手機遊戲。觀察了今天的所有分會場後，發現主要的議題都環繞在以下三個主題：遊戲、移動廣告與移動閱讀。移動閱讀的部份讓我較感意外，這也是中國火熱發展的一個行業。移動閱讀最基本的形式就是電子書。

大會還規劃了一個專門討論遊戲的分會場。遊戲產業，可以說是今日大會的大亮點。以下是中國遊戲業者的經驗分享，在此列出重要的幾個結論。

第一、關於遊戲收費部份，Freemium 已經成為主流的收費模式。第二、手機遊戲的生命週期大約三個月左右（用戶把遊戲整個玩一遍的時間）。第三、透過任務系統、成就系統與道具系統，可以延長遊戲的生命週期。第四、傳統 RPG 遊戲的付費比例比較高。第五、遊戲運營，大約要投入 20% 的人力來評估用戶行為。第六、遊戲的運營如何運作，要根據收集到的數據來分析得知。第七、遊戲產業非常需要細緻化的運營。第八、便捷的支付管道是關鍵。

感覺中國的手機遊戲業發展非常快速，這些遊戲業者都有 1~2 年的經營經驗。對於有志初入手機遊戲的創業者，這些前輩的經驗是很寶貴的課程。]]></description>
         <link>https://www.jollen.org/blog/2012/11/android-world-2012-china.html</link>
         <guid>https://www.jollen.org/blog/2012/11/android-world-2012-china.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Fri, 30 Nov 2012 22:15:48 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] 讓 Android WebView 支援 WebSocket Client</title>
         <description><![CDATA[Android 內建瀏覽器不支援 WebSocket Client 端，導致使用 HTML5 開發的 Apps 無法使用 WebSocket 與 Server 建立連線。主要的問題在於 WebView 元件沒有實作 WebSocket 協定。Android SDK + PhoneGap 所製作 HTML5 Apps 是將 WebView 封裝至 APK 裡，所以 WebSocket 無法正常工作是正常的。

不過這個問題也沒有那麼難解決，在等待 WebView 加入 WebSocket 以及更多 HTML5 功能前，我們只能暫時自行實作。還好，現在有很多 Open source 的 WebSocket 程式庫可供使用。在這裡推薦的是 [<a href="http://autobahn.ws" target="_blank">Autobahn WebSocket</a>]。

現在，只需要自行擴充 WebView，並使用 Autobahn WebSocket 來實作 WebSocket Client 即可。Android WebView 不支援 WebSocket 的問題解決了。在此提供一份簡單的程式碼實作：<a href="https://github.com/moko365/android-browser-websocket">android-browser-websocket</a>。


上述範例，亦使用於筆者的訓練課程「<a href="http://www.moko365.com/enterprise/html5-cloud-1-messenger-app">HTML5 與雲端技術教學: 六小時完成手機即時通APP</a>」。]]></description>
         <link>https://www.jollen.org/blog/2012/10/android-webview-websocket-howto.html</link>
         <guid>https://www.jollen.org/blog/2012/10/android-webview-websocket-howto.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Fri, 19 Oct 2012 11:48:19 +0800</pubDate>
      </item>
            <item>
         <title>宏碁與阿里雲事件附錄：到底，哪裡出錯了？從根本的 CTS 談起。</title>
         <description><![CDATA[宏碁與阿里雲事件的核心是 Google 要求 Android 的硬體裝置都要通過 CTS 測試要求。CTS (Compatibility Test Suite) 的目的是維護建全的 Android 生態系統。簡單來說，CTS 相容的硬體，理論上能運行所有 Play 商店上的軟體；Play 商店上的應用軟體，當然是由遍佈全球的開發者或開發商所製作。Google 的理念是希望讓所有開發者的軟體，都能在所有 Android 的裝置上運行無礙。

<strong>CTS 相容性測試</strong>

因此，Google 會對 Android 生態系統裡的製造商做出一些要求。最基本的要求就是上述的 CTS。所有裝載 Android 作業系統的裝置，都必須通過 CTS 測試。CTS 完全是技術問題，這裡面包含了近 17,000 條測試案例 (Test case)。這些案例的目的，是為了確保手機的實作品質、實作完成度、用戶體驗的一致性等等。

通過 CTS 測試後，Google 就會把你的硬體加入到「CTS 相容硬體列表」。理論上，必須成為 CTS 相容硬體，才能讓產品上市銷售。問題是，Android 不是一個號稱人人皆可自由使用的開放系統嗎？市面上不是也銷售許多沒有通過 CTS 測試的 Android 裝置嗎？

這個問題又是另一個層次了 (商標授權)，目前先暫不做討論。CTS 是單純的技術問題，我認為一些媒體報導把 CTS 相容性與 Google 的商業戰略牽扯在一起，略有不妥。CTS 是為了幫助硬體廠，它是對大家都有益的必要過程。

Play 商店眾多軟體，你不知道使用者今天會下載哪個應用軟體，如果 Android 裝置在研發時，出了一丁點差錯，可能有些軟體在這個硬體上，會發生運行失敗的問題。CTS 是為了幫助硬體廠，提升產品質量，避開這些技術問題。

我們必須把 CTS 做到 100% Pass，也就是上述 17,000 個測試案例都能通過，再將報告提交給 cts@android.com。然後，你的硬體就成為 CTS 相容設備了。這是 Google 對 Android 裝置做授權的第一個等級。

<strong>GMS 套件與授權</strong>

成為 CTS 相容設備後，上面是沒有 GMS 套件的。GMS 套件包含許多 Google 官方的應用軟體，例如：Play 商店、Gmail、Google Map、Youtube、Google Calendar、Google Talk 等等。要取得 GMS，我們就要向 Google 申請授權；有難度的地方就是在這裡。

因為一些考量，Google 的授權合約裡，不一定會授權 GMS 裡的所有軟體。Google 會針對申請者的「基本條件」來客製化授權合約。這些條件並沒有很特定的項目，像是品牌知名度、工業設計、產品相互競爭關係、銷售地區等等，都會被列入考慮。但不管如何，GMS 裡一個天字第一號的軟體「Play 商店」通常都會授權給申請者。所以，申請者可能只能拿到 GMS 的部份授權，而且也不能使用 Google 商標；這是 Google 對 Android 裝置做授權的第二個等級。

第三個等級就是取得全套的 GMS 授權。根據我過去所參與過的專案來看，這個等級的難度比想像中更高，目前能取得全套授權的廠商並不多。這個等級的授權，能使用 Google 商標，簡單說，就是手機上能打上 Google 的字樣。所以，要知道有哪些廠商取得這個等級的授權，是很容易的。

<strong>授權等級</strong>

Google 針對 Android 裝置的授權：

1. 通過 CTS，授與 Android 商標使用權，但沒有 GMS 授權。
2. 通過 CTS，授與 Android 商標使用權，取得部份的 GMS 授權，但沒有 Google 商標使用權。
3. 通過 CTS，授與 Android 商標使用權，取得完整的 GMS 授權，有 Google 商標使用權。

<strong>結語</strong>

另外，還有幾點要注意的是：

1. Android 確實是開放平臺，這和上述的說明沒有衝突。將宏碁與阿里雲事件與「Android 邁向封閉」做關聯，是有失專業的報導。

2. Android 的開放有二個層面。第一、開放框架與虛擬機的原始碼，稱為 Android Open Source Project (AOSP)。第二、開放 API，即 Android SDK，人人都可以為 Android 開發應用軟體。

3. Android 的開放性是一個層面，Android 的 Ecosystem 又是另一個層面。Google 以最基本的 CTS 來維持 Android 生態系統的健全。

最後，阿里雲事件來說，不是上述的 (1)，也不是 (2) 或 (3)，這又是另外一個層次的問題。阿里雲，或是其它客製化的 Android ROM，可能都不考慮 CTS，也沒有通過 CTS 測試。如果把這些 ROM 放到宏碁的硬體上，可能真的不行，原因是宏碁或許和 Google 簽訂了 GMS 方面的合約，當中可能包含業界所稱的「反 Android 分裂條款」；不過詳情我們當然無從得知。

不過，會有這種失誤，除了可能這個合作關係太高調外，硬體廠的專業經理人專業度可能也要受到挑戰；當初在規劃時，就應該要做考量與溝通。花轎都到門口了，結果新娘還娶不回家，不免讓人把芧頭指向當初介紹雙方認識的媒婆 (經理人) 身上。

至於，如果把阿里雲放到白牌硬體上，是否就可行？理論上，是。但沒有通過 CTS 測試的話，因為沒有 Android 商標使用權，所以使用上要注意 trademark 的法律問題。此外，也不會有 GMS 授權，如果手機上內置了 GMS 套件的軟體，例如：「Play 商店」，那就可能會被視為盜版。此外，這個情況，也要注意 Apache License 條款裡的 Copyright 與 Patent 等法律問題。

]]></description>
         <link>https://www.jollen.org/blog/2012/09/asus-aliyun-cts-issue.html</link>
         <guid>https://www.jollen.org/blog/2012/09/asus-aliyun-cts-issue.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sat, 22 Sep 2012 12:12:57 +0800</pubDate>
      </item>
            <item>
         <title>宏碁與阿里雲事件附錄：除了阿里雲，中國還有哪些 Android ROM？</title>
         <description><![CDATA[近幾天大家都在新聞媒體上看到了宏碁與阿里雲事件，這反應了定製 Android ROM 在中國已經是一個熱門的研發項目。定製 ROM 的目的無疑是為了搶佔互聯網的商機。在不違反 Apache License 與 Android 商標權的前提下，廠商可以自由地移除 Google 服務，並且加入自已的訂製服務。

這是網路版的圈地活動。這也難怪 Google 要在發表會前，要求宏碁臨時抽腿。

訂製 Android ROM 是 AOSP (Android Open Source Project) 的分支 (Branch)，同時也以刷機方式，提供用戶自由替換硬體上的操作系統。例如，不喜歡官方 Sense UI 的用戶，可以將手上的 HTC 手機刷成 MIUI。

用戶刷機，到底刷上誰的 ROM，這件事情真的很重要。因為這是一種渠道 (通路)。能否掌握刷機渠道，影響網路圈地競爭成敗。例如：我們可以想辦法讓所有 HTC 手機的用戶，都刷上我的 ROM。當然，我的 ROM 裡面，都可以把別人 (Google) 的服務拿掉，換成自已的服務。

除了新聞上曝光的阿里雲外，在中國還有哪些訂製的 Android ROM 呢？讓我們來總覽一下。

一、CyanogenMod (CM)：這是由 Cyanogen 社群所製作的 Android ROM，普遍受到中國社群與開發者的喜愛。Cyanogen 可以說是 Android ROM 之父。目前筆者所開發的多核心 Android ROM 也是建構在 CM 的基礎之上。所以，CM 是巨人，大家都站在他的肩膀上。

二、阿里雲：由阿里巴巴所推出的版本，主打雲計算，提供像是雲儲存等服務。阿里雲當然也把阿里巴巴的消費服務整合進去了，並且也替換掉通訊錄、日曆、搜尋引擎、郵件服等服務。這也難怪 Google 有話要說。

三、百度雲：最新加入戰局的 Android ROM。百度雲於 2012 年 6 月 4 日推出，不用介紹也知道百度雲整合了自家的搜索服務。Dell 百度雲手機就採用了這款 Android ROM。

四、樂OS (LeOS)：聯想推出的 Android ROM。

五、TITA。這也是最近加入戰局的 Android ROM，發佈於 2012 年 4 月份，開發者是騰訊。

六、樂蛙OS：針對千元以下雙卡雙待智能手機開發的 Android ROM。這樣一說就很明顯了，這是專門針對聯發科平臺所訂製的 Android ROM。

介紹到這裡，大家應該可以知道，為什麼低價手機的 Design house (過去被大家稱為山寨商) 只要專心做好「<a href="http://www.jollen.org/blog/2009/02/android_china_business_opportunities.html" target="_blank">品牌硬體</a>」即可。中國的低價智能手機市場需求，都還沒走到山腰，接下來的需求量會呈現很陡峭的向上趨勢。但是很可惜，過去二年缺乏佈局的台灣供應鍊，將完全沾不到好處，原因是中國供應鍊已經能 100% 自製自足，並且競爭規則也改變了。

七、點心OS：這是在創新工場裡誔生的 Android ROM，可以說是中國訂製 Android ROM 的第一家，出現時間較早。開發商北京風靈創景科技是創新工場投資的第一家公司。點心OS團隊過去也拜訪過台灣的硬體廠，不過台灣的硬體廠對這個項目反應似乎較冷淡。目前合作廠商名單裡也有宏碁。

八、MIUI：把 MIUI 放在最後當壓軸，這就是小米公司所推出的 ROM，目前小米手機就是搭配 MIUI 出貨。MIUI 也是基於 CyanogenMod ROM 所訂製的版本。MIUI 支援的機型較多，你可以把三星、HTC 或 Moto 等眾多手機硬體，刷成 MIUI。

<strong>結語</strong>

小米手機的高知名度真的不必多說了，它的 MIUI 確實得到許多用戶的支持與肯定。刷機文化將讓傳統的手機市佔率計算公式失真。原因很簡單，例如：每賣出的 10 支 HTC 手機裡，就有 3 個用戶把機器刷成 MIUI，這 3 支手機到底要算 HTC 品牌還是算 MIUI 品牌。

所以說，市佔率以後要分為硬體市佔率，以及互聯網裝置市佔率二個數字。以上述的例子來說，被刷掉的 3 支 HTC 手機還是要算在 HTC 的硬體市佔率裡，但是卻要加到 MIUI 的互聯網裝置市佔率上。結論是，Android ROM 以及刷機文化，會「刷」掉傳統品牌的互聯網價值。當然，這些廠商都意識到這個現象了，也正設法擁抱中國的互聯網。]]></description>
         <link>https://www.jollen.org/blog/2012/09/most-popular-android-rom-china.html</link>
         <guid>https://www.jollen.org/blog/2012/09/most-popular-android-rom-china.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sun, 16 Sep 2012 15:06:21 +0800</pubDate>
      </item>
            <item>
         <title>小三大戰簡史</title>
         <description><![CDATA[昨天一個朋友問到什麼是「小三大戰」，驚訝之餘，其實也反應出台灣中階主管不太關注網路事件的問題。所以我就簡單介紹了有名的小三大戰，細節，網路上有豐富的資訊。

<strong>小三大戰</strong>

小米手機創造了新的銷售模式，簡單說就是和社群緊密地走在一起。高配置的硬體規格，加上親民的價格，小米手機一推出果真轟動。小米之後，360也發表特攻手機 (稱為 AK47)，同樣講求高階硬體規格，以及漂亮價格。不想讓小米專美於前，就在360手機開始受到各方觀注的同時，360董事長周鴻禕發表微博，質疑小米機水軍炒作 (網路炒作)。小米手機副總裁很快給了回應，他說360手機雙核1G還賣這種價錢，價格不如同等級的小米機。就當雙方嘴巴還沒說熱時，小米董事長雷軍迅速加入戰團，史稱小三大戰 (小米與360大戰)。

小三大戰到後來幾乎演變為人身攻擊，周鴻禕說雷軍是機霸，別人碰不得他的奶酪。雷軍叫周鴻禕放下AK47，不要沉醉於東方不敗的幻覺中。周鴻禕後來在微博上說「到朝陽公園見面」，疑似要和雷軍定孤支 (決鬥)，讓小三大戰進入高潮。雙方戰火正酣時，QQ董事長馬化騰又加進來幫腔，演變成「小三Q」大戰。

其實二年多前，馬化騰和周鴻禕也打過口水戰，史稱「3Q大戰」。]]></description>
         <link>https://www.jollen.org/blog/2012/09/mi-360-war.html</link>
         <guid>https://www.jollen.org/blog/2012/09/mi-360-war.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 12 Sep 2012 12:57:05 +0800</pubDate>
      </item>
            <item>
         <title>即將開始提供的內訓課程：Android 電源管理、硬體加速與 Multi-Core HAL</title>
         <description><![CDATA[這二個月的時間，有一半都投資在 Multi-Core 的技術工作上，開發之餘，順勢整理了 8 門課程給一些客戶。日前也開始進入教材編輯階段了，本週完成 3 門課程，在此簡單做個紀錄。

<strong>Android 電源管理: PowerManagerService, Power Hint 與 Power HAL</strong>

Jelly Bean 強化了 Power HAL 的功能，並且透過 Power Hint 的方式增強 Power Saving。這個主題會著重在 Power HAL 的實作，以及 Wakelock 的原理。如何將 Control Group、CPU Governors 與 PowerManagerService 放在一起使用 (Put all together)：能達到什麼目標，解決哪些過去的問題？都是這門課程的重點。

<strong>Android Hardware Rendering and Composer</strong>

針對 SurfaceFlinger的Hardware Rendering 架構做介紹，此外也將介紹 hwcompower 的原理以及實作概念。這門課程是 Android Graphics System 的延伸，特別將硬體加速的部份抽離出來，獨立講解。這門課程的重點戲之一，就是 hwcomposer HAL 的實作。

<strong>HAL Stub: Implementation in Multi-core Way</strong>

這是筆者近期比較特別的研究主題，實作成果也陸續整合至 MagicLEGO 平臺，或許未來能提供一個絕佳的效能與多核心 Turnkey solution。本課程跨越 Android Framework、Kernel scheduling、Thread building block 等多個領域，將是未來很重要的多核心技術。本課程利用簡單的實例，說明 HAL 的實作如何針對多核心進行重構，以支援多核心環境。

<strong>後記</strong>

上述課程，都會搭配 Simple code 進行講解，當然也會有實作展示。所有的成果，都會整合至 <a href="http://magiclego.org" target="_blank">MagicLEGO</a> 平臺，課程也以 MagicLEGO 做為講解標地。]]></description>
         <link>https://www.jollen.org/blog/2012/09/android-power-manager-gal-training.html</link>
         <guid>https://www.jollen.org/blog/2012/09/android-power-manager-gal-training.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Tue, 11 Sep 2012 23:28:40 +0800</pubDate>
      </item>
            <item>
         <title>如果把 HTC One X 刷上 CM 的 ROM，就會發現其實...</title>
         <description><![CDATA[從宏觀的角度來看，全球的手機市場「<a href="http://www.jollen.org/blog/2012/07/goodbye-smartphone-gold-years.html" target="_blank">繁華再見了。手機業黃金五年結束。</a>」。另一個要注意是中國的手機市場。從 Android 作業系統的角度來觀察，中國手機市場，正進入第二個發展時期。

在 2009 年初，Android 掀起的波瀾，傳遞到了中國。從 2009 年開始，到 2012 年初，正好滿滿的三年時間裡，是中國智能手機市場的第一個發展時期。在這三個整年裡，山寨機、中國品牌與小米機，是最精采的三個故事。

山寨行業最早也最快吃到甜頭。山寨行業具有高度的商業嗅覺，自然不會放過這個機會。這三年裡，山寨商無不大顯身手，並依賴其特殊的「商業模式」，提供快速且高度客製化的手機，在競爭激烈的手機行業裡，找到一個利基市場。

除了山寨市場的改變與進步外，中國的品牌手機也有很大的進展。例如，中興與華為的崛起正是代表。根據IDC 的研究，中興 (ZTE) 目前是全球第四大手機製造商。HTC 市佔率目前滑落至 2% 左右，位居全球第八。

小米機的故事就不必多說了，其「社群導向」的經營模式，將成為經典。幾天前發表的「小米機二代」，也被外資券商形容為「HTC 在中國的可怕敵人」。小米手機，一家軟體公司的作品，在分析師眼裡，已經和 HTC 平起平坐了。

上述提及的「山寨特殊的商業模式」代表案例之一，就是深度 Android 愛好者的「刷機文化」。這和小米手機有著相當高度的交集。簡單來說，就是經由社群來開發並提供 ROM，讓愛好者刷機，並經營「品牌硬體」。關於這個部份的觀點，可以閱讀「<a href="http://www.jollen.org/blog/2009/02/android_china_business_opportunities.html" target="_blank">CTimes 矽導論壇：Android元年 vs 山寨機氣象變化元年</a>」一文的說明。

刷機文化，從產品的角度來看，就是一種「客製化手機」的服務。客製化，並不只是訂製硬體，更重要的是訂製軟體。刷機文化正是一種軟體訂製機制，它的運作模式是，由社群開發者扮演 ROM 製造商的角色，整合並提供很多好用的 Android ROM。

小米手機成功的關鍵之一，就是它扮演很好的 ROM 製造商角色。

刷機文化發源自重度使用者與開發者社群，所以這種文化應該是全球性的。事實上正是如此。舉一個例子，CyanogenMod (CM)，是國外相當知名的 Android ROM 開發者社群。在這裡，你可以找到很多「非官方」的 Android ROM，幫助你讓手機改頭換面。

不要小看這些「非官方」的 ROM，許多 ROM 擁有很高的技術水平，並且經常能提供比「原廠」更快的更新速度。例如，你可以找到針對 HTC One X 所製作的 Jelly Bean ROM。而且，根據該開發者表示，這個 ROM 如果放到 HTC One X 上，其效能表現，遠勝於官方的 ICS 版本。

該開發者又表示，「HTC 實在應該儘速升級至 Jelly Bean」，「Jelly Bean 解決了 HTC 過去的技術問題」。截至目前還止，我們還沒有辦法得到官方版的 Jelly Bean 更新。

「<a href="http://www.youtube.com/watch?v=hL5nTprjOZQ" target="_blank">如果把 HTC One X 刷上 CM 的 ROM，就會發現其實 HTC One X 真的是…很棒的硬體！</a>」

所以，「社群」是一個重要的發展方向。硬體製商如果認為這不能創造成功的企業，那麼應該看看小米手機的故事，再重下定論。山寨機、中國品牌與小米機，都要邁向新的發展格局了。

對於台灣的供應鍊產業，我想在這裡也有一個警訊。邁向另一個發展格局的中國手機市場，已經能 100% 自製了，意思是中國的供應鍊已經能 100% 自足，很多台灣的零件組將被取代。台灣供應鍊優勢，何時完全消失，正持續觀察中。]]></description>
         <link>https://www.jollen.org/blog/2012/08/community-based-android-rom.html</link>
         <guid>https://www.jollen.org/blog/2012/08/community-based-android-rom.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 20 Aug 2012 12:52:35 +0800</pubDate>
      </item>
            <item>
         <title>Phonesmpd 升級了：支援 Jelly Bean</title>
         <description><![CDATA[[<a href="http://www.moko365.com/enterprise/phonesmpd" target="_blank">Phonesmpd</a>] 目前正和 Jelly Bean 整合當中，並且也開始加入 Linux 3.2 的一些特性。Jelly Bean 新增許多與多核心有關的特色，Phonesmpd 考慮了其中幾個新特色：

1. 整合 Jelly Bean 新的 Thread Group 功能
2. Jelly Bean 修改了 Cgroup 的設定，除了 fg_boost 外，還有一個 app group
3. 針對 Vsync 整合一些多核心功能，目前測試結果顯示能提昇 UI 的使用效能
4. 整合 Cpuset ，並支援 Linux 3.2 以及 /proc/&lt;pid&gt;/cpuset 功能
5. 開始測試全面移除 Automatic Cgroup 功能，這是 Phonesmpd 的重要里程碑

由於這次新的功能較多，實作與測試需要比較多時間，希望能在八月底前與 MagicLEGO 整合好。不久後，MagicLEGO 的使用者，都能享受到 Phonesmpd 帶來的多核心威力。]]></description>
         <link>https://www.jollen.org/blog/2012/08/phonesmpd-jelly-beab.html</link>
         <guid>https://www.jollen.org/blog/2012/08/phonesmpd-jelly-beab.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
                  <category domain="http://www.sixapart.com/ns/types#category">Multi-core Software</category>
                  <category domain="http://www.sixapart.com/ns/types#category">產品開發</category>
        
        
         <pubDate>Sat, 04 Aug 2012 15:09:39 +0800</pubDate>
      </item>
            <item>
         <title>開放平臺為戰略的軟體研發結構？企業管理技術的一環。</title>
         <description>大約在二零零九年左右，三星在軟體發展方面，有了更重要的戰略，簡單說，就是更開放、更創新的做法；開放與創新的軟體戰略，就是我們所熟知的開放平臺與開放源始碼。

據維基百科上的定義，開放平臺(Open platform)訴求是提供應用程式開發界面，稱為 API (Application Programming Interface)，廠商可以將 API 打包成應用程式開發套件，稱為 SDK (Software Development Kit)；SDK 的目的是提供撰寫應用程式(App)的工具箱(Kit)。最成功的 SDK 就是大家所熟知的 iPhone SDK 與 Android SDK。

意思是說，SDK 本身不一定要公開原始碼(Open source)，只要有整合良好的 SDK 即可。開放源碼是軟體研發結構的一環，這可以從三星積極參與Android與Linux kernel的開放源碼計畫看出。提到Open source，必須要來說明一件事。

李健熙說過：「三星集團必須繼續開放與創新...公司文化變得更為開放、富有彈性並勇於創新...。」他所提到的「開放」，就包含了Open platform。不管是Open platform或Open source，「都改變了企業製造軟體的方法」。

然而，台灣的企業還沒有建立起這種研發結構。台灣的企業並不是沒有能力，而是沒有觀念。沒有Open platform的思想，就沒有重新建構研發文化的可能性。那就享受不到開放平臺帶來的價值。

三星在向大家介紹Bada作業系統時，新聞說：「Samsung presents Bada, open platform for app developers.」Bada在2009年時發佈，可見三星很早就開始在思考開放平臺了。

所以，以開放平臺為戰略的軟體研發結構是什麼？例如，有一種結構是 All in-house developers。開放平臺下的研發結構，應該是 in-house developers + free developers(自由開發者)。和具有實力的 3rd party 合作，就是 free developer 的一個例子，這些公司經常是幾個人的小軟體公司。Free developers 在開放源碼上也適用，例如，三星加入了Linaro，以維持它的研發結構裡，有足夠的自由開發者能量。

從結果來看，三星不只是將Android開源計畫或Linux kernel，當做是可免費取得的原始碼（台灣廠商普遍有這樣的誤解），而是把Open platform，乃至於Open source都融入了企業經營技術裡。Google的Android雖然是開放源碼，但商業上則是偏向開放平臺的概念。</description>
         <link>https://www.jollen.org/blog/2012/07/open-platform-software-development-structure.html</link>
         <guid>https://www.jollen.org/blog/2012/07/open-platform-software-development-structure.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 23 Jul 2012 22:31:00 +0800</pubDate>
      </item>
            <item>
         <title>繁華再見後，手機業經營者挑戰巨大。至少別再衝浪了。</title>
         <description><![CDATA[在黃金五年的尾聲，毫無疑問，三星與蘋果是贏家。以Android作業系統手機來說，三星後來居上，登上王座。

三星在這黃金五年裡，以「開放平臺」為戰略，建立了一個相當有競爭力的技術研發結構。

反觀許多智能手機商，似乎並沒有利用過去這寶貴的黃金五年，建立公司的軟體研發結構。台灣某手機大廠，似乎也是如此。

黃金五年很寶貴，而且機會一去不復返。一般來看，行業的黃金時期都是甜蜜時間，大部份的人都能吃到糖；行業裡的所有人都是享受激情式的成長。

投資大師巴非特說：「當浪潮退去之後，便知道誰在裸泳。」這個比喻很傳神，我們也把它拿來這裡用。

手機黃金五年是一股巨浪，直接把你往前推。有時，公司不需要建立嚴密的研發結構，衝浪就是了。最典型的代表就是山寨機行業。行業內用白話文說：「躺著賺錢。」

總結來說，你可能並不是完全靠自已，而是佔「天時」的便宜。有些手機品牌廠只知衝浪、太過於得意忘形，所以沒有利用寶貴時間，好好建立公司的技術研發結構。當浪潮退去，一切都要回歸本質。 現在是誰在祼泳？ 

三星的智能手機，嚴格來說，從2010年才開始。這段時間，三星建立了一個以開放平臺(Open platform)為中心的軟體研發結構。三星不是祼泳者，因為它在開放平臺戰略方面，做得很成功。

以開放平臺為戰略的研發結構，將幫助三星進入另一個境界。開放平臺是一個概念，在這個概念下所產出的軟體並不一定是開放源碼(Open source)，關於這點筆者後續再做說明。比起其它玩「當衝」的手機業者，三星花更多時間做長期佈局的工作。

做為執行者，現在應該去思考如何以開放平臺建立一個精緻的研發結構，為未來做好準備。然而，這是一個需要文化革命的經營運動，是否能落實執行，就是個未知數了。

<strong>本文有續集</strong>

<ul>
<li><a href="http://www.jollen.org/blog/2012/07/open-platform-software-development-structure.html">開放平臺為戰略的軟體研發結構？企業管理技術的一環。</a></li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2012/07/smartphone-odms-thinking-the-next-wave.html</link>
         <guid>https://www.jollen.org/blog/2012/07/smartphone-odms-thinking-the-next-wave.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 23 Jul 2012 00:02:09 +0800</pubDate>
      </item>
            <item>
         <title>繁華再見了。手機業黃金五年結束。</title>
         <description><![CDATA[手機業黃金五年結束。

存貨反應一個行業的經營體質，而手機行業目前面臨較沈重的庫存問題。當存貨天數過高，營業利益率會有很大的影響。而一些品牌手機商正面臨這個困境。

2008到2012年是智能手機的黃金五年，因為今年可能劃下休止符。我們都全程參與了這個行業，見證到智能手機(觸控手機)的大崛起。2008年9月15日雷曼兄弟宣佈破產，引發金融海嘯。當年智能手機業的營業利益率大約在18%~23%之間，金融海嘯襲捲後還能維持在16%以上。

這個數字在2012年Q1下滑到約9%左右，已經很接近PCB行業了。以銷售硬體為主要商業模式的手機業者，未來將面臨巨大挑戰，Apple早已跳出硬體銷售的模式，因此iPhone提早免役。

市場變化太快，智能手機席捲全球，以功能型手機起家並稱霸手機業多年的Nokia最早落難。繁華過後，高速的成長，反讓智能手機自已面臨成長困境。

張忠謀董事長提到的庫存問題，在手機業情況危急。2008年開始，手機業的存貨天數不超過40天，最高大約是37~38天。但是今年(2012)第一季的存貨數字來到51~52天，創下新高。台灣某品牌的營業利益率則是在今年跌破10%。

這些數字得到一個總結，智能手機不再是新興科技，它是傳統產業。營業利益率方面，賣手機的和作PCB的人差不了多少。高速成長的榮景不復再見，回不去了。

大陸的煤碳行業黃金十年也結束了，在今年六月份，煤碳庫存量來到28天，同樣是繁華老去。所以這樣的故事一直不斷在進行著，也不需要感到可惜。當下重要的事情是去尋找下一個黃金行業。

<strong>本文有續集</strong>

<ul>
<li><a href="http://www.jollen.org/blog/2012/07/smartphone-odms-thinking-the-next-wave.html">繁華再見後，手機業經營者挑戰巨大。至少別再衝浪了。</a></li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2012/07/goodbye-smartphone-gold-years.html</link>
         <guid>https://www.jollen.org/blog/2012/07/goodbye-smartphone-gold-years.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Fri, 20 Jul 2012 22:05:09 +0800</pubDate>
      </item>
            <item>
         <title>多核心手機應併用 CPU On-demand 與 CPU Boost 技術</title>
         <description><![CDATA[CPU Ondemand 並非萬能。最重要的例子就是 Android 4.1 的 CPU input boost (Touch Event)，在接收 Touch Event 時，提高 CPU 的運算效能。

延伸 Android 4.1 的 CPU input boost。我們也可以讓應用程式享用 CPU Boost 功能。根據使用者目前的操作，將 CPU Boost，讓使用中的應用程式，衝到最高的效能。Boost 有點像是「猛衝」的感覺，可以在這個時刻讓使用者享受高效能的應用程式。

筆者目前參與開發中的 Phonesmpd 軟體，符合了這樣的設計想法。

<strong>CPU On-Demand 到處都適用嗎？</strong>

由於 Android Process Model 與典型的 GNU/Linux 有些不同，CPU Ondemand 的方式並不一定能使用在所有的 Use Case。有鑑於此，筆者過去進行了一些研究，並將成果整合進 Phonesmpd 軟體，讓多核心技術，除了 CPU Ondemand 外，還有另一個更符合手機裝置的選擇。Phonesmpd 現階段的成果，可參考 Moko365 網站：

<a href="http://www.moko365.com/enterprise/phonesmpd" target="_blank">http://www.moko365.com/enterprise/phonesmpd
</a>

至於 CPU Ondemand 的使用時機為何？筆者認為，應該是從應用程式的角度來考慮。當一個應用程式，本身是平行化的設計時，或許它就不太適合 CPU Ondemand 的做法。

通常平行化的設計，是以 Data Partitioning 的方式，將資料分散至不同處理器上計算後，再經由 Shared memory 合併回來。這就是平行處理 (Parallel Computing) 在討論的技術。所以平行處理是多核心軟體的根本。

在進行 Data Partitioning 時，有時也會將任務 (Task) 與 CPU 事先指派好，這時就不太需要 CPU Ondemand 了。多核心手機目前還是一個需要細部研究的領域。]]></description>
         <link>https://www.jollen.org/blog/2012/07/multi-core-phone-cpu-ondemand-boost.html</link>
         <guid>https://www.jollen.org/blog/2012/07/multi-core-phone-cpu-ondemand-boost.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 12 Jul 2012 01:33:45 +0800</pubDate>
      </item>
            <item>
         <title>台灣沒有軟體人才的問題！年輕人請奮起 </title>
         <description><![CDATA[文/Jollen Chen（Moko365創辦人），原文刊載於 CTimes 雜誌2012年七月號

這一期，我想談談台灣人才外流的問題，也分享在業界的實務心得。今年以來，媒體不斷報導台灣人才外流的現象，根據筆者最近與大陸業界人士的交流經驗，這個問題確實存在。<u>並且，這個人才移動的速度非常驚人</u>。台灣人才的流失正處於「主升段」。筆者在大陸從事教育訓練多年，課程中遇到台灣朋友的比例，確實逐年升高。今年以來，這個比例更高。現在，在大陸進行企業內訓時，遇見台灣同鄉朋友，已經是家常便飯了。

諷刺的是，台廠與政府不約而同大喊「台灣人才不足」。試問既然人才不足，又怎麼外流？讓我們來還原真相：問題核心是台廠與政府人才政策失當。

<u>宏觀來看，台灣不是面臨人才不足的問題，而是人才政策的問題</u>。台灣談的人才問題，指的是本土人才數量的不足，這個不足是反應在「員工招募」的結果之上。<u>但是，台灣本土優秀的人才很多，特別是軟體人才</u>，不但沒有不足的問題，素質之高也超乎老闆們的想像。

所以，廠商反應的人才不足，<u>說白了是吸引不到優秀人才，而不是優秀人才不足</u>。應該要思考為什麼人才或年輕人不願進台廠就業。同時，政府往人才培養方向去做，也解決不了根本問題。台廠與政府的人才政策需要檢討。

筆者所談的人才政策，是指如何網羅頂級人才，借助他們打造企業的新競爭力。這和人力資源普遍在談的員工關係、教育訓練、招募、挖角等，是不同的思想。什麼樣的人才政策，才能吸引優秀人才？讓我們向三星學習正確的軟體人才政策。

<u>三星的人才政策一向做得很好，他是硬體與半導體業，擁抱開放軟體政策的學習典範</u>。中國人講過幾句話：「舜發於畎畝之中，傅說舉於版築之間，膠鬲舉於魚鹽之中，管夷吾舉於士，孫叔敖舉於海，百里奚舉於市。」出自《孟子‧告子下》。這就是三星軟體人才政策的最佳寫照。

<u>開放源碼社群上的高手很多沒有接受過正規的職業訓練，然而，他們是公認的天才</u>。三星深知「生於憂患、死於安樂」的道理，所以經常保持高度的警覺心，對人才也特別重視。三星以海納百川的心胸，去挖掘、去吸納這群天才，因為三星相信，「天才可以拯救一家公司」。這就舜發於畎畝之中的道理。三星從1999年開始，就全面實行這種人才政策，無怪乎，三星的面板、DRAM、手機與軟體等，都有驚人的技術成果。

政府的人才政策不當，也反應在產業換血的問題上。五月初，Moko365 和 CTimes 合作舉辦了 Android Day 大會。大會邀請了多位大陸的專家來分享演講，與其說是專家，用「熱血年輕人」來形容更為貼切。<u>幾位年輕朋友，都是網路和軟體方面的八零後創業家</u>。和他們聊天，可以很明顯地感受到創業的熱情，以及對技術的激情。

Android的出現，大陸產業是大贏家。我並不是從產品的角度下結論，而是從社會現象的角度來觀察。<u>Android，它激起了全大陸年輕人的創業激情</u>。從社會的角度來看，這是一股強撼的力量，這股力量將會不斷促進大陸的軟體與行動網路行業的進步。GDP 反應了這個結果。

去年（2011）中國的 GDP 成長大約是 9.2%，<u>根據麥肯鍚的數據，網路相關的產業，就貢獻了其中約 3% 左右</u>。中國的經濟成長是有新血的。反觀台灣，只能設法提升過去硬體產業的產值，透過出口來提升 GDP。

<u>軟體、社群與行動網路產業，在台灣都是小範圍的成功</u>，例如：單一公司的成功、個人開發者的成功，並沒有大範圍，也就是整體產業的成功。台灣的產業看不到新血換舊血，這是人才的政策問題。 台灣的GDP成長沒有新生力軍，十年後，產業無以為繼。

結論是，台廠與政府需要思考對的人才政策，為什麼孫叔敖都去了三星此類的地方。台灣的軟體人才也請奮起，因為相較於大陸，台灣的年輕人，普遍缺乏衝鋒陷陣精神，也少了創業激情。]]></description>
         <link>https://www.jollen.org/blog/2012/07/between-taiwan-talent-and-industry.html</link>
         <guid>https://www.jollen.org/blog/2012/07/between-taiwan-talent-and-industry.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Thu, 05 Jul 2012 12:46:59 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] jQuery 模式的優點：提升 JavaScript 程式碼效率</title>
         <description><![CDATA[在教育訓練課程「<a href="http://www.moko365.com/enterprise/html5-軟件開發的-10-堂課-mobile-web-cloud-設計模式" target="_blank">HTML5 軟件開發: Mobile, Web & Cloud 設計模式</a>」裡提到「使用 JavaScript 選擇器」可提升程式碼效能的原則。這個觀念，可延續前一篇日記的內容繼續探討。

在實作Web Socket連線生成時，筆者選擇使用了jQuery pattern的觀念來實作。jQuery pattern 本質上是一種選擇器模式。

為什麼要使用選擇器模式，除了程式碼的組織較好外，另一個原因就是效能：使用選擇器方式可以讓JavaScript程式碼效能更好。

根據不同瀏覽器的實作，選擇器模式可以達到超過十倍以上的效能。典型的選擇器模式，是直接呼叫DOM的API：

<pre>document.querySelector(“#header”);</pre>

使用jQuery的選擇器「$」是目前的主流做法。再回顧上一篇日記的寫去：

<pre>&lt;div id="message">&lt;/div>
&lt;script type="text/javascript">  
$("#message").createWebSocket();
&lt;/script>
</pre>

總計利用了三個模式：

<ul><li>以Closure模式將類別封閉，這與Static class有關係，在這裡先不做討論</li>
<li>使用選擇器模式，範例採用目前最流行的jQuery selector ”$”</li>
<li>Read/Write Div Pattern</li></ul>

選擇器模式的效率取決於瀏覽器本身的實作，不過，以選擇器模式來代替直接存取DOM，一般相信是最好的做法。
]]></description>
         <link>https://www.jollen.org/blog/2012/06/javascript-selector-api-performance.html</link>
         <guid>https://www.jollen.org/blog/2012/06/javascript-selector-api-performance.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Wed, 27 Jun 2012 17:28:40 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] HTML5+JavaScript 設計模式：jQuery pattern</title>
         <description><![CDATA[上週在北京舉辦「<a href="http://www.moko365.com/enterprise/html5-雲端整合-深入mobile-cloud-設計模式" target="_blank">課程名稱HTML5 & 雲端整合: 深入MOBILE & CLOUD 設計模式</a>」課程。這門課是從 HTML5 觀念到實作的第一門課（The 1st lesson from concepts to practice），這門課的重點在於「設計模式」(Design pattern)。

當天介紹了「jQuery pattern」。jQuery pattern就是開發jQuery插件(Plugin)的方式，所以技術上倒也沒有什麼學問。不過，jQuery pattern有很高深的哲學道理，意思是說，在軟體工程領域裡，它創造了一個獨特的觀念。這個觀念就是jQuery知名的”$”(Dollar sign)，也就是「Selector」。

以下的例子，就是jQuery pattern：

<blockquote><pre>$(“div#news”).html(“&lt;h2>News Today&lt;/h2>”);</pre></blockquote>

從jQuery設計模式的角度思考，如果今天我們想要透過WebSocket與伺服器溝通，並且在一個”div”裡來顯示結果，應該怎麼設計呢？想法如下：

1. 將WebSocket的功能寫成一個function

2. 將JavaScript function封裝成module

3. 在jQuery裡擴充新的函數，簡單說，就是製作一個jQuery插件(Plugin)

以下是一段程式碼樣板：

<blockquote><pre>(function($) {
$.fn.createWebSocket = function () {
  if ("WebSocket" in window)
  {
     alert("WebSocket is supported by your Browser!");
     var ws = new WebSocket("ws://&lt;you-ip-adderess>:8888/start");
     ws.onopen = function()
     {
     };
     ws.onmessage = function (evt) 
     { 
     };
     ws.onclose = function()
     { 
     };
     ws.onerror = function()
     { 
     };
  }
  else
  {
     alert("WebSocket NOT supported by your Browser!");
  }
};

})($);</pre></blockquote>

上述的寫法，採用暱名模組來實作。接者，再將程式碼儲存為jquery.websocket.js。使用方法如下：

<blockquote><pre>&lt;!DOCTYPE html>
&lt;head>
&lt;script type='text/javascript' src="./jquery.min.js"></script>
&lt;script type='text/javascript' src="./jquery.websocket.js"></script>
&lt;/head>
&lt;body>
&lt;div id="message">&lt;/div>
　
&lt;script type="text/javascript">  
$("#message").createWebSocket();
&lt;/script>
&lt;/body>
&lt;/html>
</pre></blockquote>

這種做法也可以良好地組織HTML5與JavaScript程式碼。此外，JavaScript的module具備「Closure」的特性，即封閉性，可以避免一些衍生問題。

由於HTML5+JavaScript的設計思想，和Natvie App的作法有很大的不同，所以了解HTML5+Javascript的應用程式「如何設計」，會是重要的一門課。了解設計模式，除了能有效組織HTML5+JavaScript程式碼外，也能做出正確的設計。]]></description>
         <link>https://www.jollen.org/blog/2012/06/html5-javascript-jquery-pattern.html</link>
         <guid>https://www.jollen.org/blog/2012/06/html5-javascript-jquery-pattern.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Mon, 18 Jun 2012 12:23:20 +0800</pubDate>
      </item>
            <item>
         <title>Tizen 奮起、將打敗 Opera 在行動瀏覽器技術稱王</title>
         <description><![CDATA[「<a href="http://www.jollen.org/blog/2012/05/browser-html5test.html">什麼最重要、Browser 最重要</a>」提到說，瀏覽器方面，Maxthon 與 Opera 目前在桌面與行動裝置，呈現暫時領先的狀態。還有其它爆點嗎？

Tizen 的前身 MeeGo，身世相當坎苛，它是 Moblin 與 Maemo 的合體。Moblin 原本是 Intel 主導的一項開源計畫，Maemo 則是 Nokia 主導的一項開源計畫，這二項計畫的身世又更為坎苛，已經不忍心再提了。 

<strong>Tizen 即將打敗 Opera</strong>

<img src="http://www.jollen.org/blog/2012/05/16/score-mobile.jpg" />

眼尖一點的網友，可以發現，在行動瀏覽器方面，Tizen 居然已經突破 400 分大關。待 Tizen 正式上市後，將擁有最強的行動瀏覽器。Tizen 在行動瀏覽器方面，即將稱王，並且還大幅領先了老字號的 Opera，真是令人佩服。 

不得不承認，在行動裝置市場，歷經風風雨雨的 Intel，這回找三星一起做 Tizen，還真是找對人了。正巧，就在一個多星期前，Tizen 1.0 也正式釋出 SDK 以及原始程式碼了。從 Tizen 官方的消息看出，Tizen 1.0 主打的亮點，正是 HTML5 標準。

<strong>又是三星</strong>

瀏覽器目前有二匹大黑馬，第一匹是 Maxthon，它在桌面瀏覽器部份，超越 Chrome，成為 HTML5 相容性最佳的瀏覽器。另一個大黑馬，目前還沒浮出檯面，不過威力應該也是爆點的，就是 Tizen。目前幾乎是三星在主導 Tizen，果不其然，全力搶進行動市場。這次或許能成功。

從技術的角度來看，這個新的 HTML5 行動裝置作業系統 (HTML5 & Mobile OS)，有機會以黑馬之姿和 Android 分庭抗禮。所以，筆者稱為，Tizen 與 Android 的關鍵差異，將會在 HTML5 的支援性。

單就 Android 4.0 的 HTML5 支援性來看，Android 4.0 追不上了。因為 Android 4.0 內建的瀏覽器，對 HTML5 的支援性並不佳，Google 改派 Chrome 或許有機會。這或許可以解釋，為什麼 Google 急著讓 Chrome 跳進 Android。

]]></description>
         <link>https://www.jollen.org/blog/2012/05/tizen-is-going-to-beat-opera.html</link>
         <guid>https://www.jollen.org/blog/2012/05/tizen-is-going-to-beat-opera.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
        
        
         <pubDate>Wed, 16 May 2012 13:04:19 +0800</pubDate>
      </item>
            <item>
         <title>什麼最重要、Browser 最重要</title>
         <description><![CDATA[HTML5 來了。什麼最重要，Browser 最重要。因為所有 HTML5 App 都在 Browser 環境裡執行，所以，HTML5 App 的 Runtime 就是瀏覽器。

正因為這個原因，各家軟體大廠無不加碼研發人才，努力打造一個能完全相容 HTML5 的瀏覽器，連今年10月份要登場的 <a href="http://www.windowsmobile8.com/" target="_blank">Windows 8 Mobile Phone</a> 也在 HTML5 做了很大的改進。

可以這樣想，第一代的 App 使用 OS 做為 Runtime。第二代的 App 使用 Java Virtual Machine 做為 Runtime，例如：Android。第三代的 App 將使用 Browser 做為 Runtime。所以，Runtime 就是個關鍵技術。No Runtime No Running。以前沒有掌握好 OS 沒關係，過去沒掌握好 VM 沒關係，現在還是沒掌握好 Browser 技術，就很有關係了。

所以，各大瀏覽器與 HTML5 的相容性，成為相當重要的指標。因運而生的網站 html5test.com 可以幫助我們了解瀏覽器的 HTML5 相容性。根據最新的 html5test 測試報告，桌面瀏覽器 (Desktop) 平均分數仍然領先行動 (Mobile) 瀏覽器，可見 HTML5 在桌面環境的部份，會優先成熟。

而目前在桌面瀏覽器的部份，來自北京的 Maxthon 瀏覽器以 437 分取得第一，領先第二名的 Chrome 18。Chrome 原本是被寄於厚望的 HTML5 先鋒者，沒想到被大黑馬 Maxthon 超越。由此可見，大陸的軟實力不容小覻；並且，在 HTML5 方面，大陸目前也扮演了非常重要的角色。


<img alt="my-score.jpg" src="http://www.jollen.org/blog/2012/05/16/my-score.jpg" width="717" height="410" />
我目前使用的瀏覽器

<img alt="score-desktop.jpg" src="http://www.jollen.org/blog/2012/05/16/score-desktop.jpg" width="674" height="459" />
桌面瀏覽器：Maxthon 成為領頭羊，Chrome 緊追在後

<img alt="score-mobile.jpg" src="http://www.jollen.org/blog/2012/05/16/score-mobile.jpg" width="655" height="586" />
行動瀏覽器：沒有意外地，Opera 拿到第一

]]></description>
         <link>https://www.jollen.org/blog/2012/05/browser-html5test.html</link>
         <guid>https://www.jollen.org/blog/2012/05/browser-html5test.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
        
        
         <pubDate>Wed, 16 May 2012 11:53:29 +0800</pubDate>
      </item>
            <item>
         <title>[Jollen&apos;s AFC] 1.3 C &amp; Object-oriented</title>
         <description><![CDATA[最經常使用的物件導向觀念就是繼承(Inherit)，使用C語言如何實作繼承？最實用的例子就是 Android HAL。以下圖為例，這是一個標準的 HAL Stub 設計。這個例子試圖在原有的 Android 作業系統裡加入一個「LED Stub」，透過 LED Stub 來控制底層的 LED 硬體。

圖1.3是一個標準的繼承設計，也就是說，在設計 HAL Stub 時，需要重用 hw_module_t 設計。從架構設計的角度來看，我們進行設計重用的工作，以擴充出 LED Stub；錯誤做法是，直接修改原有的 hw_module_t 設計，以達到原本的要求。上述觀念，就為設計重用(Design reuse)，這是軟體工程領域相當重要的知識。

￼<img alt="figure-1-3.png" src="http://www.jollen.org/blog/2012/04/15/figure-1-3.png" />
圖1.3 HAL Stub設計重用

在開發 Android 系統時，會不斷地 reuse 原有的設計，以擴充出想要的功能。

以標準 C 語言實作圖1.3的方式如下：

1. 以資料結構(Data structure)來描述類別(Class)
2. 以資料結構的第一個欄位(First field)，來表示繼承

所以圖1.3的意思是 led_module_t 繼承 hw_module_t，用 C 語言來實作，結果如下：

<pre>struct led_module_t {
   struct hw_module_t parent;
};</pre>

C語言沒有明顯的物件導向語法，因此以C語言實作物件導向的繼承，又是另一個Implicit of object-oriented的例子。]]></description>
         <link>https://www.jollen.org/blog/2012/04/jollens_afc_1-3-c-inherit.html</link>
         <guid>https://www.jollen.org/blog/2012/04/jollens_afc_1-3-c-inherit.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 15 Apr 2012 13:18:44 +0800</pubDate>
      </item>
            <item>
         <title>Jollen&apos;s AFC 電子書開工了，順便提暢 Single 的概念</title>
         <description><![CDATA[先前在「<a href="http://www.jollen.org/blog/2012/03/jollen-android-framework-complete-launch.html">Jollen 的 Android Framework Complete (框架大全) 課程發表、外加感想</a>」提到了想把 Jollen's AFC 寫成一本書的構想，現在開始付諸行動了。學生時代寫書，其實也不是為了寫書而寫，大多是平常做技術的零碎紀錄，再匯集而成。在看到自已的成果出版成實體書時，內心可是無比的感動與激動，要特別感謝旗標出版社當時給了我這麼好的機會。

這次，也打算開始整理手邊的資料，慢慢匯整成一本書，並且一邊整理，一邊上線。這就是數位內容的好處，不用一次全寫完再出版。新書的書名也未定，所以暫且叫做「Jollen's AFC」吧。不過，這次不打算出版實體書，原因有三：

1. 這是冷門書。內容不在迎合業界需求，而是希望寫出真正自已想像中的作品。

2. Light-weight content，頁數鎖定在 200 頁上下。簡單說，出版社考量實體書售價（根據頁數），以及管銷成本等，應該也不願意出版。

3. 不能 Print on demand，也就是不能「隨選列印」。有時讀者可能只想看，並且列印部份內容而已。

近二年，原文書掀起一股輕量級風，越是有深度、越是複雜的技術，內容就更輕量級。這種書的優點是，讀起來全不費工夫，而且一氣呵成，暢快無比。缺點是，底子不深，就很難啃，自已也踼過幾次鐵板。寫出這種書的作者，必定底子深厚，而且把知識融會貫通得很好。我決定多磨練一下功力，挑戰這種輕量級的寫作。

輕量級寫作的重點，在於頁數少，或是字數少。不過，最不重要的就是它的頁數或字數，而是內容的價值。輕量級的書，就是「底子不深不好啃」的概念，看起來小小一本，質量卻很大，讀起有重量。中文電腦書流行的磚頭書概念，也是經常有不錯的作品，但是請避免看起來很大，讀起來卻很輕的現象。例如，連續20頁都是程式碼（Source code listing）。

其實網路上有許多優質的短篇內容，但並不利於做實體出版，就算要出版，出版商也會請作者想辦法補到一定頁數。於是，為了補頁數、趕截稿，作者只好草草做工，所以最後的章節，也就草草了事。非常可惜。

為了不讓短篇內容，被出版商裝上義肢，所以 Amazon 就提出了一個解決方案。線上自助出版平臺。

Amazon 把這類有價值，字數介於一萬至三萬字間的短篇（小品），稱為 Single，並且搭配 Kindle Fire 推出了一個獨立的 Single 線上自助出版產品線。

所以，「Jollen's AFC」將考慮數位內容與數位出版。曾幾何時，實體出版居然變成作者的 Last choice。
]]></description>
         <link>https://www.jollen.org/blog/2012/04/jollen-afc-book-single-publish.html</link>
         <guid>https://www.jollen.org/blog/2012/04/jollen-afc-book-single-publish.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Thu, 12 Apr 2012 16:39:50 +0800</pubDate>
      </item>
            <item>
         <title>JavaScript 王者再臨</title>
         <description><![CDATA[JavaScript 是「王者再臨」的最佳代言人，由於 jQuery 被大量應用在網頁設計上的原因，讓 JavaScript 再度被重視了起來；再加上 HTML5 的推波助瀾，JavaScript 儼然成為今年最受矚目的程式語言。

現在，JavaScript 的主要用途，已經由過去的動態網頁（Dynamic Webpages），轉為開發 HTML5 App 角色；也就是 HTML5 的應用。我們不僅僅使用 JavaScript 製作有動態效果的網頁，還藉助它來開發大量的 UI interactive、使用者體驗的設計，以及，最重要的雲端服務的整合。

還有一個很重要的應用，就是「JavaScript in Browser」，也就是利用 JavaScript 來增強瀏覽器的功能，最為大家所熟悉的例子，就是 Google Chrome。Google Chrome 為了增強對 JavaScript 的支援與效能，開發了新的 JavaScript 引擎；在日記「<a href="http://www.jollen.org/blog/2012/01/html-5-boost-up-mobile-devices.html">HTML5在手持裝置將開始爆發式成長</a>」就提到了，「JavaScript引擎的成熟度是關鍵」。

所以，測試 JavaScript 的使用案例（Use Cases）在各大瀏覽器的效能，更為一項重要的工程工作。目前被軟體工程師廣為使用的 <a href="http://jsperf.com/" target="_blank">jsPerf</a> 就是為此而生。更進一步地，由於 JavaScript 現在搭配 HTML5 來開發「軟體」，而不只是用來製作動態網頁，所以研究 JavaScript 的軟體設計模式，當然也就變成一門顯學；目前被廣為推薦的就是「<a href="http://addyosmani.com/resources/essentialjsdesignpatterns/book/" target="_blank">Essential JavaScript Design Patterns</a>」一書。

JavaScript 過去曾經在動態網頁製作上紅極一時，後來又迅速沈寂，2003到2007年這段時間，應該是 JavaScript 最谷底的時候。而後在 2007 到 2009 年，因為 Web 2.0 風格網頁，以及 jQuery 的盛行，再度得到開發者的重視。2010 到 2011 年因為 Mobile Native App 的大量流行，使得眾多開發者不再以 JavaScript 做為首選，再度走入低潮。

時間到了 2012 年，在 HTML5 時代正式啟動的今天，JavaScript 成為軟體工程師的必修語言，也是程式設計初學者的最佳選擇。從去年大約 1.5% 的使用率，飆升到這個月的 3.3% 左右的使用率。雖然它不是最受歡迎的程式語言，但是在「Browser & Webpages」的領域，頗有王者再臨的感覺。]]></description>
         <link>https://www.jollen.org/blog/2012/04/javascript-the-king-is-back.html</link>
         <guid>https://www.jollen.org/blog/2012/04/javascript-the-king-is-back.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">HTML5 &amp; JavaScript</category>
        
        
         <pubDate>Wed, 11 Apr 2012 19:10:46 +0800</pubDate>
      </item>
            <item>
         <title>[Jollen&apos;s AFC] 1.2 C &amp; Object-oriented</title>
         <description><![CDATA[Linux 核心是 implicit of object-oriented 的典型實例。例如，在開發驅動程式的過程中，雖然不需要特別了解驅動程式的物件導向架構，但其架構則是依循嚴謹的物件導向觀念。Linux 核心與驅動程式，都是以 C 語言實作。

另外一個例子，就是筆者在 Android Framework & HAL 課程中所提到的 HAL Stub 觀念。HAL Stub 可採用 C 語言實作，編譯後以 *.so 形式佈署，但它不是程式庫(Library)，而是一個物件(Object)。

<img src="http://www.jollen.org/blog/2009/10/08/android-hal-libhardware.png" />
圖1.2 HAL Stub 架構

HAL 的模組稱為 Stub，Stub 是一種物件。Stub 在軟體工程領域代表「樁」的角色，也就是整體架構中的一小段基礎程式碼，這段程式碼有著以下二個特色：

1. 符合架構的程式碼（打地基）
2. 一段程式碼範本，最終的實作 (final implementation) 是代理人 (proxy) 的角色

但是，Android 將上述的物件導向封裝得非常好，不需要懂這些架構面的觀念，也能依循「步驟」實作出基本的 HAL Stub。

在軟體裡加上自已的實作，最重要的第一門課就是練習寫「樁」，也是打地基的程式碼。所以，我們要知道 Android 的整體架構，並依循其架構，設計並實作自已的 HAL Stub。由此可知，從事 Android 軟體整合開發，重要的背景知識是物件導向、軟體工程與 HAL 架構，而不是硬體的知識。另外，還有很重要的背景知識，就是如何以 C 語言實作物件導向設計。

步驟性的知識可以解決部份的工程(實作)問題，但因為缺乏系統性的觀念，將會影響解決整體性問題的能力。這就是「隱性物件導向」的概念，大部份我們接觸過的軟體，都是嚴謹的物件導向設計，只是因為它被封裝得很好，所以常常會忽略它，或是認為它不存在。

有一種誤解，可以說明這個現象。有些工程師，會誤以為使用 C 語法實作的軟體，並不是物件導向的架構，實則不然。Linux 核心便是一個很好的例子。Axel-Tobias Schreiner 在 1993 年發表了著作「Object-oriented programming with ANSI-C」，解釋了如何使用標準 C 語言 (ANSI C) 撰寫物件導向程式碼；國外有些大學，甚致將這個科目列為必修課。

因此，C 語言是隱性的物件導向程式碼，因為它不像 C++ 或 Java，有著明顯的物件導向語法特徵。這點對於研究工作其實是個麻煩，因為 C 語言沒有鮮明的物件導向語法，所以經常需要花費時間，來探索隱藏其中的物件導向設計。]]></description>
         <link>https://www.jollen.org/blog/2012/04/jollens_afc_1-2-c-object-oriented.html</link>
         <guid>https://www.jollen.org/blog/2012/04/jollens_afc_1-2-c-object-oriented.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 08 Apr 2012 12:11:58 +0800</pubDate>
      </item>
            <item>
         <title>[Jollen&apos;s AFC] 1.1 切割 View &amp; Control</title>
         <description><![CDATA[將View與Control切割是軟體設計的根本。這個觀念非常的好懂，也是學習軟體設計的基本功。View被解釋成UI等可見物，Control則是控制功能，例如：關機。一般來說，控制功能由控制者提供，所以經常被稱為View/Controller模式。

這個觀念真的簡單不過，把畫面與控制功能分開。這個觀念的發展始於 1980 年代，當時為了開發圖形介面的電腦，電腦科學家於是想到「 Separated Presentation」，也就是從事視覺(Presenetation)的程式碼就只做視覺(例如：Drawing)，把其餘的切割(separated)出來。後來形成今天家喻戶曉的軟體設計模式 - MVC(Model-View-Controller)。

把View與Controller切割的觀念被一直沿用到今天，所以先想清楚View/Controller為什麼切割，原因何在？才是重點。「Model」是擴充出來的觀念(Concept extending)，所以，可以先將「Model」是什麼放著不談。

將View與Controller切割是為了不讓控制的動作影嚮到畫面(View)，根本原因是希望有更好的使用者體驗。做法很簡單，從現今的分時多工作業系統(Time-sharing & multi-task operating system)角度來看，只要切割成獨立的process即可。如圖1.1。


<img alt="view-controller.png" src="http://www.jollen.org/blog/2012/04/03/view-controller.png" width="581" height="413" />

二個process就使用訊息(message)傳遞的方式溝通，也就是我們所熟悉的IPC(Inter-process communication)。

<strong>切割 View & Control</strong>

Android 系統將 View & Control 切割，因為這樣可以設計出 UI 使用性更好的軟體。同時還有二個好處。第一、讓程式碼更容易維護，第二、軟體架構更清楚。為什麼將 View 與 Control 切割成獨立的 process，可以讓 Android 系統有更好的 UI 使用性呢？
]]></description>
         <link>https://www.jollen.org/blog/2012/04/jollens_afc_1-1-separate-view-control.html</link>
         <guid>https://www.jollen.org/blog/2012/04/jollens_afc_1-1-separate-view-control.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 03 Apr 2012 13:23:58 +0800</pubDate>
      </item>
            <item>
         <title>[Jollen&apos;s AFC] 1. Implicit of Object-oriented</title>
         <description><![CDATA[自從完成「<a href="http://www.jollen.org/blog/2012/03/jollen-android-framework-complete-launch.html">Jollen 的 Android Framework Complete (框架大全) 課程</a>」後，對於軟體設計領域有了更深一層的認識，並且發現許多軟體設計的美麗之處。接下來的工作，除了過去近十年的系統程式領域，也打算在軟體設計領域繼續提升。系統程式(systems software)的技術工作，除了需要大量的經驗累積外，還需要「手感」；這點相信有經驗的系統程式高手，都有同感，所以必須保持不斷的接觸程式碼。系統程式領域廣大，包含：kernel、device drvier等，這些都是筆者過去相當有興趣的主題；所謂的「手感」就像是寫文章，需要偶而一點靈感才行。

系統程式領域，主題與範圍一般都較為明確，例如：kernel。軟體設計領域，範圍更大，所涉及的知識也更多，例如：網路、使用者。無論是系統程式，或是軟體設計，都會有共同的背景知識，例如：物件導向(Object-oriented, OO)。

初學 kernel 或 device driver 的開發者，或許不會知道 kernel 本身就是物件導向的設計；實務上，如果專注在工程面(Engineering)，其實也不太需要深入了解 kernel 與物件導向的關係。所以在 kernel 與 device driver 領域，物件導向是 implicit，隱性的。

在軟體設計方面，例如：Android framework，如果不了解 subsystem 的物件導向設計，以及設計模式(Design pattern)，並不太容易看懂其程式碼(Source code)，並且幾乎影響了後續工程工作的進行。所以在 Android framework 領域，物件導向是 explicit，顯性的。

這就是過去幾年為企業進行相關教育訓練時，有些主題我會特別強調 Object-oriented 與 design pattern，有些主題僅介紹機制與作法(practice)的原因。這是老師的責任，評斷並決定主題的切入點以及重心。例如，最近在介紹 Android 的 graphics system 時，我就會特別說明 Surface 與 SurfaceHolder.Callback 的 MVC 模式。

<strong>Implicit of Object-oriented</strong>

所以，並不是作業系統不需要用到物件導向觀念，也不是物件導向觀念是用在大型軟體開發上(有些教科書的說法)。而是，在某些時候，它是相當 implicit 的。原創者(Creator)將物件導向的觀念封裝到非常好，好到使用者(Users/coders)只需要照圖施工，就可以保證成功。最好的例子，就是 Linux kernel。]]></description>
         <link>https://www.jollen.org/blog/2012/04/jollens_afc-1-implicit-of-object-oriented.html</link>
         <guid>https://www.jollen.org/blog/2012/04/jollens_afc-1-implicit-of-object-oriented.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 03 Apr 2012 12:47:32 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Framework Complete (框架大全) 課程發表、外加感想</title>
         <description><![CDATA[就用 Jollen 的 AFC 當做 Android 研究工作的段落吧。在 2007 年底接觸 Android 時，還沒有很用心去投入研究工作，記得當時還在 Openmoko 公司，負責教育市場的推展。到了 2008 年底，Openmoko 社群將 Android 移植到 Neo FreeRunner 後，便開始對 Android 的框架研究產生了興趣。當時，花費比較多時間其實還是在移植(Porting)的層面，除了改改一些小東西，寫點移植手冊外(為了學校推廣、後來沒有發表)，還是很零星地在 Android Framework 研究工作上。

離開 Openmoko 公司後，便創辦 Moko365 公司，給新公司的定位是 Research Company。當時心想，台灣未來勢必走向軟體開發之路。了解軟體的人，一定知道技術研究工作的重要性。畢竟，技術研究工作，是技術開發之母。再加上 Android 龐大又複雜的架構，如果可以有一家公司，專職做「技術研究」，為產業提供研究成果，勢必能對產業有所貢獻。就是這個想法，所以自已做下去了。

術業有專攻。我來做研究與發展(Research and Development)，硬體公司做工程(Engineering)，大家分工把最後的工作，也就是「產品」做好。

在成立 Moko365 後，我就是全職的研究員了。一轉眼，過了三年。要細數 Android 的複雜與龐大，當了三年全職的 Android Researcher，可說是有心酸滿腹。這項工作，對我來說，其實是相當有趣的。在這1000個日子裡，對 Android Framework 所做的研究，不敢講是最完整的，但至少產出相當驚人。

這些成果，終於在上週，以「Jollen 的 Android Framework Complete（AFC、框架大全）」正式推出系列課程。說這是課程，倒不如說是服務，因為總計30門課程，如果一週一門課，也要二年半才能開得完。所以，現在只能用「客戶點餐」的方式，提供「研究成果報告」的服務形式。

這正是 Moko365 公司當初成立的核心精神，一家研究公司。我一直想著，就像「供應鍊」一樣，研究公司必定是軟體產業鍊裡，重要的一個環節。它提供迅速、確實又即時的「研究報告」，讓產品公司，大家不用做重覆的工，而且能節省成本。

一個內部的 Android 研究團隊，假設是 3 個人，這應該是最小的編組了。每個人的年管銷費用（薪水加上辦公室等固定成本），以150萬計（年薪75萬x2），這個研究小組的成本每年就是 450 萬。但是跟研究公司買報告或服務，只要這個費用的五折、或三折，或更低。而且得到的是內容維護更精緻的報告。更重要的是，這種模式風險非常低，也節省很多時間。風險與時間，是更重要的成本。

基於這個理念，三年前成立 Moko365 後投入研究工作，到今天超過了 1000 個研究日，所以在此向大家正式介紹 [<a href="http://www.moko365.com/enterprise/android-framework-complete">Jollen 的 Android Framework Complete</a>]。今年初，我已經將研究與開發工作，從 Android 轉入到另外一個領域，所以 AFC 也算是繳出了一張成績單。

下一個目標？除了新的研究領域外，也想再來寫寫書。

大學時代，當個學生，時間總是感覺特別多，四年時間發表了超過10本的著作。還有一本，當年曾登上天瓏排行榜第一名。算一算離最近一次出書，也快十年了，希望可以逐步整理 AFC 內容，終結可能會有十年沒出書的紀錄 ;-)]]></description>
         <link>https://www.jollen.org/blog/2012/03/jollen-android-framework-complete-launch.html</link>
         <guid>https://www.jollen.org/blog/2012/03/jollen-android-framework-complete-launch.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 17 Mar 2012 00:36:50 +0800</pubDate>
      </item>
            <item>
         <title>HTC硬體規格優勢已不在、應思考產品差異化的價值</title>
         <description><![CDATA[<p style="text-align: left;">這幾天看了MWC 2012的消息，華為與中興在手機領域的大躍進令人配服，還有三星的Galaxy SII可說是壓倒性的勝利，不但是銷售之王，幫助三星拿到2011年的智慧型手機龍頭外，也贏得今年的最佳智能手機獎。對此，心裡特別有感，所以很簡單的寫了一篇分享文；希望大家一起為HTC加油。</p>

<p style="text-align: left;">宏達電(HTC)在2010年MWC推出HTC Desire時，獲得相當不錯的迴響，網路上紛紛以「最強的Android手機」來評價它。當時的三星，在Android手機領域，完全沒有任何建樹。即使是今年震撼MWC的華為(Huawei)，或是令人刮目相看的中興(ZTE)，當年推出的Android手機完全無法與HTC Desire抗衡。在硬體規格上落後HTC一大截。</p>
<p style="text-align: left;">HTC一向都是領先的硬體技術者，在2011 MWC推出Incredible S時，硬體技術仍舊領先業界；今年的MWC，繼續以HTC One X保持領先的硬體技術。HTC One系列更再加碼，以自有的ImageSense軟硬體技術，把手機照像功能再推上一層樓。仕橙部落特別將HTC過去三年來的MWC主打機，做了一個整理。</p>

<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="75">&nbsp;</td>
<td valign="top" width="150">2010 MWC<br /><strong>HTC Desire</strong></td>
<td valign="top" width="150">2011 MWC<br /><strong>HTC Incredible S</strong></td>
<td valign="top" width="150">2012 MWC<br /><strong>HTC One X</strong></td>
</tr>
<tr>
<td valign="top" width="75">作業系統</td>
<td valign="top" width="150">Android 2.1
(Eclair)</td>
<td valign="top" width="150">Android 2.2
(Froyo)</td>
<td valign="top" width="150">Android 4.0
(ICS)</td>
</tr>
<tr>
<td valign="top" width="75">機身厚度</td>
<td valign="top" width="150">11.9mm</td>
<td valign="top" width="150">11.7mm</td>
<td valign="top" width="150">9.29mm</td>
</tr>
<tr>
<td valign="top" width="75">重量</td>
<td valign="top" width="150">150g</td>
<td valign="top" width="150">136g</td>
<td valign="top" width="150">153g</td>
</tr>
<tr>
<td valign="top" width="75">記憶體(RAM)</td>
<td valign="top" width="150">576MB</td>
<td valign="top" width="150">768MB</td>
<td valign="top" width="150">1GB</td>
</tr>
<tr>
<td valign="top" width="75">螢幕規格</td>
<td valign="top" width="150">3.7” / 480x800
AMOLED</td>
<td valign="top" width="150">4” /  480x800
SuperLCD</td>
<td valign="top" width="150">4.7” / 720x1280
SuperLCD</td>
</tr>
<tr>
<td valign="top" width="75">應用處理器</td>
<td valign="top" width="150">單核 1GHz
(Qualcomm)</td>
<td valign="top" width="150">單核 1GHz</td>
<td valign="top" width="150">四核 1.5GHz
(nVidia Tegra 3)</td>
</tr>
<tr>
<td valign="top" width="75">像機</td>
<td valign="top" width="150">5MP
720P 錄影</td>
<td valign="top" width="150">8MP
720P 錄影</td>
<td valign="top" width="150">8MP
1080P 錄影</td>
</tr>
</tbody>
</table>
<p style="text-align: left;">製表：<a href="http://www.moko365.com/" target="_blank">仕橙部落</a></p>
<p style="text-align: left;">從這張表可以發現，HTC在2010年時，硬體技術可說是大幅領先同業，以華為來說，當年所推出的機種，應用處理器也只有528MHz，螢幕規格更差了一大截。到了2011年，HTC Incredible S仍舊領先競爭對手，不過差距已經縮小許多。</p>
<p style="text-align: left;">到了今年，大家從媒體的報導就可以看得出來，HTC One X已經沒有硬體技術的優勢了；除了ImageSense外，華為與中興都已經和HTC並駕齊驅。</p>

<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="180">&nbsp;</td>
<td valign="top" width="180"><strong>HTC One X</strong></td>
<td valign="top" width="180"><strong>Huawei Ascend D quad</strong></td>
</tr>
<tr>
<td valign="top" width="180">預計上市時間</td>
<td valign="top" width="180">2Q’12</td>
<td valign="top" width="180">2Q’12</td>
</tr>
<tr>
<td valign="top" width="180">作業系統</td>
<td valign="top" width="180">Android 4.0 (ICS)</td>
<td valign="top" width="180">Android 4.0 (ICS)</td>
</tr>
<tr>
<td valign="top" width="180">機身厚度</td>
<td valign="top" width="180">9.29mm</td>
<td valign="top" width="180">8.9mm</td>
</tr>
<tr>
<td valign="top" width="180">重量</td>
<td valign="top" width="180">153g</td>
<td valign="top" width="180">130g</td>
</tr>
<tr>
<td valign="top" width="180">記憶體(RAM)</td>
<td valign="top" width="180">1GB</td>
<td valign="top" width="180">1GB</td>
</tr>
<tr>
<td valign="top" width="180">後鏡頭(rear)</td>
<td valign="top" width="180">8MP</td>
<td valign="top" width="180">8MP BSI</td>
</tr>
<tr>
<td valign="top" width="180">前鏡頭(front)</td>
<td valign="top" width="180">1.3MP</td>
<td valign="top" width="180">1.3MP</td>
</tr>
<tr>
<td valign="top" width="180">通訊規格</td>
<td valign="top" width="180">HSPA+ / LTE</td>
<td valign="top" width="180">HSPA+ / LTE</td>
</tr>
<tr>
<td valign="top" width="180">螢幕規格</td>
<td valign="top" width="180">4.7” / 720x1280<br />SuperLCD (312ppi)</td>
<td valign="top" width="180">4.5” / 720x1280<br />LCD (330ppi)</td>
</tr>
<tr>
<td valign="top" width="180">應用處理器</td>
<td valign="top" width="180">nVidia Tegra 3<br />Quad-core (1.5GHz)</td>
<td valign="top" width="180">K3V2 (ARM)<br />Quad-core 1.5GHz</td>
</tr>
</tbody>
</table>
<p style="text-align: left;">製表：<a href="http://www.moko365.com/" target="_blank">仕橙部落</a></p>

<p style="text-align: left;">這倒是給了我們一個警示。2010年到2012年，實質上只有二個整年；華為與中興從大幅落後，到現在迎頭趕上，只花了730天的時間。這說明了，如果2013 MWC還是主打硬體規格，競爭對手之間，已經沒有任何差異性了；就像 [<a href="http://www.moko365.com/enterprise/news-20120229-mwc-2012-htc-new-arrivals" target="_blank">HTC新機功能強大　仍缺”獨賣”價值</a>] 提到的：</p>
<p style="text-align: left;"><em>分析師指出，雖然HTC在這次大會中證實自己仍是手機硬體技術的領導品牌廠，但這並不足以成為強大的競爭優勢，因為這些門檻別人很快就會跨越追上。</em></p>
<p style="text-align: left;">筆者的看法很簡單，HTC已經不需要向誰證明它的硬體技術，或是製造能力了，專注思考「差異化的價值」，一定能做得非常不錯。如果不認真去思考什麼是差異化的價值，那可能完全想不透，為什麼Samsung Galaxy SII能獲得如此迅速又巨大的成功；這可不是因為AMOLED面板被斷貨，這麼簡單的原因。而且，用什麼作業系統，對HTC來說也不是很重要的事情，也不必去再乎Bada作業系統的潛在影響力。</p>]]></description>
         <link>https://www.jollen.org/blog/2012/03/htc-in-mwc-2013.html</link>
         <guid>https://www.jollen.org/blog/2012/03/htc-in-mwc-2013.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Thu, 01 Mar 2012 17:09:40 +0800</pubDate>
      </item>
            <item>
         <title>該來的終於來了：HTML5大戰拉開序幕</title>
         <description><![CDATA[<p style="text-align: left;">Android版的Chrome瀏覽器出現了，Chrome是HTML5發展史的一個重要指標。預計今年HTML5(Chrome)將全面走入各式產品，這宣告純硬體時代正式結束了。未來的產品，少了軟體與雲端的加持，將顯得平傭而無奇。純硬體能走進市場的機會，只會越來越少。手機肯定首當其衝。特別是Android版的Chrome瀏覽器現身了，它將引發新軟體革命嗎？</p>
<img src="http://www.moko365.com/enterprise/wp-content/uploads/misc/html5.jpg" alt="" width="525" height="300" />
<p style="text-align: left;">我們知道，雲端應用目前有二大龍頭：Google與Facebook。Google老大哥的Gmail、相簿、位置服務等，儼然成為一項民生必須品。Facebook則是在社交網路(Social Networking)領域獨佔鰲頭。他們都是網路巨擎，也都是以平臺(Platform)的概念經營。平臺的概念是什麼？簡單說，就是提供開發者API服務。</p>
<p style="text-align: left;">平臺是一個很容易理解的概念，就像大家手上的手機，裡頭安裝了許多使用到Google以及Facebook API的App；這些App都會透過「雲端」，存取其服務。iPhone與Android手機裡的這些「雲端App」，所使用到的核心技術，就是HTML5。這代表著，只要HTML5的規格能開始推出草案(Draft)，並且手機上的HTML5瀏覽器技術更加成熟，手機行業將會展開一場HTML5大戰。HTML5大戰就是雲端運算的戰爭，這肯定是新一波的軟體革命。以HTML5技術，結合網路服務、開發App，並整合至手機，將成為顯學。</p>
<p style="text-align: left;">所以，筆者認為，Android版Chrome的到來，從產業的角度來看，肯定是一個重要指標，具有特別的意義。它將帶領HTML5往前衝刺。其實，Chrome很早就是HTML5的領頭羊了，例如：早在2010年，Google就宣佈以HTML5取代Google Gear技術，從這裡可見一斑。</p>
<p style="text-align: left;">我們可以這樣假設：有了Chrome，雲(Cloud Computing)就更容易放進裝置裡。正因為如此，所以Chrome的出現，有了HTML5大戰的煙焇味。Chrome將加速雲端應用走進手機App，所以手機不能只有硬體功能，硬體廠將面臨新的一波挑戰。</p>]]></description>
         <link>https://www.jollen.org/blog/2012/02/html5-evolution.html</link>
         <guid>https://www.jollen.org/blog/2012/02/html5-evolution.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 13 Feb 2012 21:56:16 +0800</pubDate>
      </item>
            <item>
         <title>台灣ICT產業的出路：雲端與App？（中天新聞2012經濟危機特報）</title>
         <description>中天新聞今天播出陳文茜小姐主持的特別節目「2012經濟危機特報-內閣改組篇」，討論到台灣ICT產業的發展，有一段討論讓我很有共鳴，所以在這裡簡單做的紀錄。這篇文章的內容，完全沒有任何政治立場，純綷就是關心一個與自己切身相關的話題，並分享給大家。

這幾天有二則新聞。第一則是台北市政府App鑑賞期事件的後續，第二則是張委員提到，「政府主導教育雲、醫療雲 ‎」。台灣科技業是否能重返榮耀，或是繼續失落，需要一個強力政策，更需要有眼光的政府團隊。目前看來，「雲端產業」，是重振台灣科技業的主導政策。

今天陳文茜小姐的節目，有二位來賓談到了這個App事件，以及雲端產業的願景。這是和我們非常密切的主題。關於台北市政府的App事件，新科政務委員張善政提到二個重點。第一個重點，我們需要一個合乎時宜的法規，也就是，並不是不去規範App收費，但是不能用一個落伍的法律，去限制一個產業的發展。

其實，在這個事件發生後，網路上有相當多「法律落伍」的聲音。政府去年也表示，可以發佈一個特別法來解決這個問題。但很不幸地，前幾天經濟部的裁決結果，仍支持台北市政府的看法。App事件，或許能定調是法規落後。個人看法是，大陸與韓國有關App商店的法規內容，應能做為修正的藍本。

第二個重點是，張委員認為，法規會的葉慶元先生法律出身，不懂科技，是逐字在執行法律，這是「法匠」；張委員認為，應該協助葉慶元先生了解科技。所以，他有意找葉慶元先生溝通。張善政出身科技業，在科技圏相當有份量，這次辭去Google的工作，到政府單位服務，來自專業領域，果然有一種耳目一新的感覺。

另外，有一段對話頗為有趣。張委員說，可以為新內閣上課，第一堂課就是了解科技，了解雲端產業；宅神朱學恆接腔說，張委員沒空的話，他可以代勞這個手腳工，幫新內閣上課。講到這段時，現場來賓還拍手，...。我想說，有機會的話，我也可以貢獻自已的淺薄能力（此想法純屬虛構，不敢造次）。

關於雲端產業的發展，陳文茜提到韓國的做法，張委員認為，雲端園區，也可以是虛擬園區，只要提供一個「公共雲」，也就是為年輕人打造一個發展的舞台，這樣甚至可以「到宜蘭上午衝浪、下午在房間做App」。張委員認為，除了雲端產業，App產業也是可以納入的一環。

以上紀錄出自「2012經濟危機特報-內閣改組篇」，未來應該可以在 Youtube 上找到影片。上述內容若有誤，也請大家指正。台灣在2012年會臨經濟危機？我想不至於這麼嚴重啦。台灣的App軟體人才，水準很高，一起加油吧。

後記。張善政回應主持人說他不敢想到李國鼎的層次，但是台灣需要你是第二個李國鼎。</description>
         <link>https://www.jollen.org/blog/2012/02/taiwan-2012-ict.html</link>
         <guid>https://www.jollen.org/blog/2012/02/taiwan-2012-ict.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 08 Feb 2012 22:16:36 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] 如何成功 Android 4.0 移植, #2: Early suspend 設定</title>
         <description><![CDATA[本文使用的Linux內核版本是2.6.35.7，若使用其它版本，設定選項的位置可能會有所不同。根據先前的說明，我們將分別設定Early suspend、Quota v2與Framebuffer功能。

關於 Early suspend 的設定，請打開以下功能：

● Power management options -> Wake lock (圖1)
● Power management options -> Wake lock -> Early suspend （圖1)

<img alt="ics-kernel-configs-1.png" src="http://www.jollen.org/blog/2012/02/02/ics-kernel-configs-1.png" width="777" height="511" />
圖1: Wake lock 與 Early suspend 設定

接著，底下有一個項目：

● User-space screen access (圖2)

<img alt="ics-kernel-configs-2.png" src="http://www.jollen.org/blog/2012/02/02/ics-kernel-configs-2.png" width="776" height="510" />
圖2: User-space screen access 設定

將這個功能設定為「Sysfs interface」，意思是在 /sys 目錄裡產生 Framebuffer 驅動程式的 suspend/resume sysfs 檔案。Android 4.0 的 Surfaceflinger 現在會使用到這個功能，沒有開啟的話，Android 開機時會因為無法正常啟動 Surfaceflinger，而導致開機失敗。

<strong>延伸閱讀</strong>

● <a href="http://www.jollen.org/blog/2012/01/android-4-0-porting-1.html">[教育訓練紀錄] 如何成功 Android 4.0 移植, #1: 三個常見的kernel configs問題</a>
]]></description>
         <link>https://www.jollen.org/blog/2012/02/android-4-0-porting-2.html</link>
         <guid>https://www.jollen.org/blog/2012/02/android-4-0-porting-2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Thu, 02 Feb 2012 13:55:24 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] 如何成功 Android 4.0 移植, #1: 三個常見的kernel configs問題</title>
         <description><![CDATA[延續「<a href="http://www.moko365.com/enterprise/ice-cream-sandwich-porting-training" target="_blank">Ice Cream Sandwich (Android 4.0) 移植與框架</a>」課後紀錄，與大家分享一些Android 4.0的移植經驗。移植Android 4.0的第一個階段稱為Bring-up，簡單說，就是要想辦法將Android放到硬體上，並且要能成功開機。

影響是否能Bring-up的關鍵之一，就是kernel的設定。因為驅動程式的關係， 一般認為，使用Linux 3.0系列是比較好的做法。不過，2.6.3x或3.x的版本，都能支援Android 4.0。在這次的課程裡，筆者使用了三個平臺。第一個是MagicLEGO計畫所開發的MagicLEGO開發板，MagicLEGO使用三星的Exynos 4210雙核心處理器。第二個是長高科技開發的DMA-210L開發板，最後一個是devkit8000，這是一個BeagleBoard的複製品。

以上三個平臺，就kernel configs層面來說，需要打開的項目，大約有80%左右的共通性。以下，整理針對執行Android 4.0必要的kernel configs，相信對仍在進行移植工作的朋友，會有一些幫助。

首先，先介紹三個Android 4.0的特性：

● Android 4.0使用Early Suspend

● Android 4.0不支援Virtual Framebuffer

● Android 4.0使用Quota v2

Android 4.0的Surfaceflinger使用到early suspend功能，因此必須將kernel的Early suspend能打開。接著，在Bring-up階段，我們採取穩健做法，先使用Software rendering的方式，讓Android能成功開機，後續再考慮硬體加速的部份。

在Kernel支援Virtual framebuffer的環境下，Software rendering並不能完全正常運作，因此，必須將kernel的Virtual framebuffer功能關閉。另外，Software rendering的方式，將會透過Kernel的framebuffer驅動程式進行繪圖，這部份後續再做說明。

Quota v2是kernel的「netlink」功能，Android的netd會使用到netlink，在設定kernel時，也要將這個功能開啟。以上整理的三個重點，特別是netlink的設定，很頻繁地出現在網路上的論壇，可見這是移植Android 4.0初期經常遇到的問題。

<strong>延伸閱讀</strong>

● <a href="http://www.jollen.org/blog/2011/12/porting-android-4-0-devkit8000.html">移植 Android 4.0 到 Devkit8000 開發板 (OMAP3)，只能開機、沒有硬體加速</a>
● <a href="http://www.jollen.org/blog/2012/01/android-4-0-porting-keys.html">[教育訓練紀錄] Android 4.0 移植與框架課程：會後小記與學習建議</a>]]></description>
         <link>https://www.jollen.org/blog/2012/01/android-4-0-porting-1.html</link>
         <guid>https://www.jollen.org/blog/2012/01/android-4-0-porting-1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Tue, 31 Jan 2012 11:39:18 +0800</pubDate>
      </item>
            <item>
         <title>HTML5在手持裝置將開始爆發式成長</title>
         <description>文／Jollen Chen（原文刊載於 CTimes零組件雜誌2012年2月號）

HTML5標準將開始大舉進入行動裝置市場，這是今年的手機技術重頭戲。撰寫手機App現在有二種選擇了。第一種做法是典型的做法，也就是Native App的開發方式，採用Java或C程式語言撰寫App，在編譯後安裝至手機運行。這種做法的主要缺點是，不跨平臺，也就是，針對Android手機、iPhone機等，都必須各自發展一份程式碼。

第二種做法就是HTML5的做法，採用HTML5標準開發App，有點像是在設計網頁，或是撰寫Web應用程式。可以想像，以後只要把網頁或Web應用程式封裝成App後，就能直接安裝至手機運行。這種做法解決了Native App不能跨平臺的缺點。

我們可以這樣解釋，不管使用什麼作業系統或瀏覽器，都可以瀏覽網頁，所以網頁與Web應用程式本身，都是跨平臺的。同樣地，不管你是使用什麼手機，也不管手機使用的是什麼作業系統，都可以運行同一份HTML5的手機App。

HTML5將要在手持裝置域，呈現大爆發式的成長；因此，有三項關鍵技術，不可不知。

第一、使用HTML5+CSS+JavaScript撰寫Web應用。HTML5是網頁標籤語言的標準，當然，單單使用HTML5並不能開發應用程式，必須搭配CSS與JavaScript來使用。因此，HTML5+CSS+JavaScript就是「HTML5 App」的基礎建設。有些網頁上面有很棒的特效，例如：轉場效果，這些都可以透過JavaScript來完成。

另外，jQuery也是不可或缺的技術。jQuery已經相當的有名，就不必再多說了。直接撰寫JavaScript可能有時很麻煩，這時可以使用jQuery以及眾多的jQuery plugins來完成。

第二、JavaScript引擎的成熟度是關鍵。要在手機上運行HTML5的App，因為將會使用到許多JavaScript程式碼，所以JavaScript的引擎成熟度，以及它的效能是主要關鍵。安裝在手持裝置上的JavaScript 引擎，將成為手持裝置的重要技術。

Android系統早期使用的 JavaScript 引擎稱為 JavaScriptCore (JSC)，JSC 包含在 webkit 中。因為一些原因，Google 也決定開發自已的 JavaScript 引擎，稱之為 V8。技術上，新一代的 V8 引擎效能比 JSC 引擎更好。最新的 Ice Cream Sandwich 已經全面採用 V8 引擎了。V8 引擎的編譯基礎技術稱為 Crankshaft，這項技術可以很有效地改善JavaScript應用程式的效能。

第三、PhoneGap潛力驚人。目前，已經有非常多的App開發者，使用知名的開放源碼專案 PhoneGap，來開發者HTML5的手機App。大家都知道，Adobe已經宣佈放棄行動版的Flash，但是，有一個重要的事情是DreamWaver 5.5。DreamWaver 5.5 的特色之一就是加入 PhoneGap 的支援。

DreamWaver 5.5可以做到令人興奮的一個功能。設計師可以使用DreamWaver 5.5把設計好的「Web」直接封裝成手機Android App，並安裝至手機。不但如此，封裝出來的App還可以上架到Android Market上。

從種種跡象顯示，HTML5+CSS+JavaScript確實已經成為應用軟體開發商的另外一個選擇了。各大作業系統JavaScript引擎的成熟，以及DreamWaver宣佈支援 PhoneGap，還有PhoneGap專案的快速發展，這些現象告訴我們，HTML5標準在手持裝置領域，將開始有爆發式的成長。</description>
         <link>https://www.jollen.org/blog/2012/01/html-5-boost-up-mobile-devices.html</link>
         <guid>https://www.jollen.org/blog/2012/01/html-5-boost-up-mobile-devices.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 19 Jan 2012 20:33:15 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] Android 4.0 移植與框架課程：會後小記與學習建議</title>
         <description><![CDATA[上週三 (1/11) 舉辦的「Ice Cream Sandwich (Android 4.0) 移植與框架」課程，參與情況相當踴躍，覺得非常的感動。所以當然也要使出渾身解數，回報大家的支持。這次的課程有一個比較特別的地方，就是在一些移植工作上，筆者特別將 Android 4.0 的移植工作與 Android 2.3 做比較。

由於 Android 4.0 移植，可以基於 Android 2.3 甚致 Android 3.0 來進行，所以並不需要「從零開始」。基於過去的 Android 移植經驗，可以完成大約 80% 左右的 Android 4.0 移植工作。從學習的角度來看，因為 2.3 與 4.0 的移植技術很許多相同的地方，例如：Product tree 的製作完全相同，因此實際了解 2.3 與 4.0 的「差異」是比較有效率的作法。

另外，同樣是從學習的角度來看。如果是 Android 移植的入門新手，一開始不太需要區分版本，由 Android 2.3 移植開始，也是一個很好的入門點，這會讓學習更單純，例如：不需要考慮 InputReader 的修改；這個專門針對 Android 4.0 的移植工作，未來再補上即可。

<strong>Android 4.0 的 20%</strong>

Android 移植工作，可以區分為二個部份：

• Bring-up
• 周邊移植

Bring-up 指的是「想辦法做到可以開機」，這是移植的第一個重要 Milestone。在開機完成後，再接著處理周邊，例如：Wi-Fi、感測器等。

這次的課程，總共有三位講師。筆者主要負責「Bring-up」的部份，也就是整理如何將 Android 4.0 做到可以開機，課程內容的規劃，鎖定在這「20%」的不同。「可開機」的定義是可以看到 Android 的桌面環境。課堂上，筆者提到了幾個「可開機」的關鍵：

• Kernel configs
• Software rendering
• InputReader (Android) 與 Input device driver (kernel)

接下來的課程紀錄，將會針對每一個關鍵做一個整理。雖然面對不同硬體，Android 4.0 移植工作仍會有些小差異，不過只要處理好這三大關鍵，幾乎都可以成功開機。]]></description>
         <link>https://www.jollen.org/blog/2012/01/android-4-0-porting-keys.html</link>
         <guid>https://www.jollen.org/blog/2012/01/android-4-0-porting-keys.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Tue, 17 Jan 2012 14:03:07 +0800</pubDate>
      </item>
            <item>
         <title>現今軟體開發的一個重要議題：開源授權與私有授權的混用</title>
         <description><![CDATA[文／Jollen Chen（原文刊載於零組件雜誌2012年1月號）

<strong>軟體取代硬體成為主流價值</strong>

觸控手機與平板電腦的成功，正式宣告軟體與網路時代的來臨，這表示硬體獲利模式時代的結束，以硬體為主的商業模式，成為上一個世代的故事了。軟體是產品開發的主角，這已經是大家所公認的主流價值。

因此，可以看到一個事實，台灣有許多硬體公司，已經將軟體列為重點項目，除了利用軟體提升硬體的價值外，也提供軟體服務的新商業模式。

軟體取代硬體，成為主流價值的時代，有更多非技術面議題需要探討。除了專利問題外，在軟體開發的過程中，還有一個幾乎被遺忘的重要議題，在這裡與大家分享。
 

<strong>一個非技術面議題</strong>

現今市場上流通的產品，有很高的比例都是採取開放平臺與開源軟體。我們都知道，軟體太重要了，所以企業開始大量投入軟體研發。但與其說是「軟體研發」，不如說是「軟體工程」；因為，這些軟體有相當高的比例都是開放平臺或開放源碼，意思是，絕大多數，甚致是全部的軟體，都是取之於他人（透過網路），而非自行「研究」與「開發」，所以事實是，我們都是在他人的基礎之上做工（工程）。

也就是說，現今企業「從零開始」開發真正私有程式碼的比例變得相當低。從產品的角度來看，可能有99%的程式碼都是外來，例如，使用Google提供的Android程式碼；又如，網路上數以萬計的開放原始碼計畫 (Open source software)。可能只有1%或更低的比率，是私有的程式碼。

因此，我們將產品上的程式碼分為「公眾財」與「私有財」二大類。開放源碼採用的 GPL 或 Apache 是屬於一種公眾財概念的授權 (License)，無論是取得、散佈、重製或修改等，都要遵守授權規劃。例如：Linux 核心的修改必須遵守 GPL (Version 2) 的授權聲明，確實公開原始程式碼。過去一些企業經常採用「Delay Open」，即產品發佈後，儘量托延公開程式碼的時間，但 [<a href="http://laforge.gnumonks.org/weblog/2011/12/24/#20111224-htc-delays-gpl" target="_blank">這個做法可能仍有一些疑慮</a>]。

透過一些「軟體架構設計」的方式，可以讓企業在公眾財的程式碼裡，加入「私有財」的程式碼。私有財的程式碼，完全由開發商自行撰寫授權條款，即授權方式是自行決定，而不是採用公眾財授權。例如，Android 的 HAL 架構，便具備這樣的空間。<u>架構設計，有時並不只在解決技術問題，有些也在解決一些關鍵的非技術問題。</u>

從以上的說明可以發現，開源軟體的公眾財授權，以及企業為了保護智慧財產權必須採取的私有財授權，以及二者的混合，必須有一定的架構設計，才不致於面臨法律問題，這是台灣業者過去這一年來，普遍忽視的議題。從現況來看，筆者為大陸企業提供這方面建議與咨詢服務已經有一段時間了，台灣業者在這個議題上，仍需加緊腳步。

Happy New Year 2012 !]]></description>
         <link>https://www.jollen.org/blog/2012/01/software-license-issues-floss-and-proprietary.html</link>
         <guid>https://www.jollen.org/blog/2012/01/software-license-issues-floss-and-proprietary.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sun, 01 Jan 2012 09:45:50 +0800</pubDate>
      </item>
            <item>
         <title>硬體廠的軟體經營策略：建立軟體伙伴關係</title>
         <description>文／Jollen Chen（原文刊載於零組件雜誌2011年12月號）

現在已經是軟體的時代了，有一個很有趣的現象在發生，部份軟體公司，「想連硬體設計都一起做」；許多硬體公司，積極想在軟體層面有所突破。從技術上的做法來看，軟體整合是硬體廠尋求突破很好的方向，但技術性太高。因此，若以軟硬結合取代軟硬整合，也就是硬體廠結合軟體公司，成為伙伴關係，或許是另一個不錯方向。

軟硬整合的難度有多高？Steve Jobs打從創立蘋果之初，就決心走軟硬整合的路，今天許多偉大的產品，雖然並不光靠「軟硬整合」技術取勝，但單就技術面來看，這肯定是一條「非常漫長的道路」。因此，硬體廠與軟體公司結合，成為策略合作伙伴，將是比較聰明又有效率的選擇。另外一個原因是，軟硬整合做得好，長期來看，應該要往消費性產品公司的願景邁進。

這半年來，為許多企業進行訓練工作時，可以感受到「決心改變」的氣氛；最特別的是，過去一些被認為是純硬體廠的企業，正努力地尋找好的「軟體策略」；因此，在這裡提出一些個人觀察與想法。

第一、軟硬結合。技術研發上，硬體廠透過軟體公司的協助，可以為硬體建立更多價值，並且也能在新商業模式的建立上有所突破。因此，與軟體公司或是軟體團隊，建立伙伴關係，並且學習新的軟體研發管理模式，是重要的功課。建立與軟體公司的伙伴關係，並且成功打造突破性產品的成功例子，當屬三星的手機產品。伙伴關係是一種共生系統架構（Ecosystem），對硬體廠來說才是最有利的做法，因為可以建立軟體伙伴對自已的認同感。

第二、研發管理。過去的軟體研發，特別在台灣，都是屬於門內做法（In-door），意思是招募自有的軟體工程師，並在內部完成研發專案。現在，有許多專案在初始階段（Initial Development Pase）都是採用戶外做法（Out-door）；簡單說，就是我們談論很久的社群模式，透過外部力量來完成工作。 軟體的研發方法不一樣了，關於這點，或許可以從我個人經營的軟體服務業務來說明；目前與客戶進行的部份軟體專案，客戶也接受了新的做法，讓社群上的個人開發者參與專案。因此，若採用戶外模式，專案的成功關鍵因素，在於是否能建立正確的研發管理方法。

第三、軟體是知識。軟體並不是程式碼，而是「知識」。知識包含想法、創意與專業學科等等。過去在本論壇，也和大家分享過「寫程式並不等於做軟體」的觀點。由於軟體是無疆界的「知識」，因此，策略與專案內容的擬定上，「不應該以硬體平臺做為出發點」；這是過去一直不斷發生的問題，許多計畫都是以硬體角度出發，容易因小失大。簡單說，專案的出發點，以及結果，「都只是在為硬體寫程式」，這是過去近二年觀察到的問題。


一直以來，硬體廠把軟體視為附屬品，或是將軟體公司做為外包廠的現象，在這半年來有了相當大幅度的轉變。這是一個以軟體為主的新時代，台灣硬體廠雖然一開始應變速度慢，起步也較晚，但是觀念的轉換速度卻很快，希望大家以期待的心情來看待。</description>
         <link>https://www.jollen.org/blog/2011/12/create-partnership-with-software-house.html</link>
         <guid>https://www.jollen.org/blog/2011/12/create-partnership-with-software-house.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sat, 31 Dec 2011 11:39:29 +0800</pubDate>
      </item>
            <item>
         <title>Android 瀏覽器與 Webkit 專案心得：AOSP 心酸說不完</title>
         <description>2011歲末時刻，要好好為自已上一堂「Lessons Learned」課程。

今年度有幾個 Android 專案，特別令人印象深刻，其中一個是有關於 Android 瀏覽器與 webkit 的計畫。因為開發專案的需要，修改了 Android 瀏覽器 (Browser) 的程式碼，也對 Webkit 做了些研究，沒想到這整個過程，倒是有點出乎我的意料之外；原本以為這是一個簡單，且能輕易結案的計畫，沒想到踩到 AOSP 的地雷。在這個瀏覽器開發專案接近尾聲時，在這裡分享一點甘苦談。

Google 採用 Webkit 做為 Android 內建瀏覽器的 HTML 引擎，Webkit 是相當知名的 HTML 引擎，由 Apple 公司做了早期的開發，現在則是成為了一個開源計畫，由社群開發者，以及部份公司，共同貢獻程式碼。

這個開發專案需要基於現有的 Android 2.2/2.3 瀏覽器，加入一些功能，並能整合伺服器端的服務，其中一個功能，需要使用到瀏覽器的 Copy/Selection 功能。就如同大家所知道的，Android 2.2 的 WebView 並沒有 Selection API，Android 2.3 在 WebView 加入了 Selection API，「但是卻沒有完整且良好的實作」。

原本天真的以為，「把沒有實作完成的 API 做完」，就可以交卷了，沒想到後續發展，根本沒有辦法寫劇本，到後期則是在兵恾馬亂的情況下，「補洞」加「救火隊」的方式搞定專案。這一切都要從 Android 的設計與實作講起。

部份的 API 設計過於鬆散，有些程式碼的實作也還是 prototype 階段；這就是開發 AOSP 的惡夢，工程師要面對鬆散的設計，以及不完整的實作，到最後只能拋開一切理想化的做法，開始進行捕洞與救火工程，然後希望下一個洞不要出現，眼不見為淨，希望專案早早收尾。

由於廠商需要同時推出 Android 2.2 與 2.3 的平板電腦，所以需要同時開發 2.2 與 2.3 的瀏覽器。但是，二個版本的瀏覽器程式碼有一些差距，為了簡化開發工作，並且讓程式碼更一致性，首先我做了一個工作，就是將 Android 2.3 的瀏覽器 Backport 至 Android 2.2。

這期間遇到一些小問題，例如：Android 2.2 並沒有 EdgeGlow 的功能。為了讓 Android 2.3 的瀏覽器可以在 2.2 上正常執行，也修改了 WebView 以及一小部份的 native webkit。Android 2.3 雖然支援了 selection API，但是並沒有實作「彈出視窗」的功能，在這個部份花了一點時間實作，因為希望儘量不去破壞 WebView 的 behavior，所以對 WebView 做了一次完整的研究。專案開始的初期，花費許多時間在了解 WebView 的設計。
</description>
         <link>https://www.jollen.org/blog/2011/12/android-aosp-browser-webkit-struggling.html</link>
         <guid>https://www.jollen.org/blog/2011/12/android-aosp-browser-webkit-struggling.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 23 Dec 2011 15:44:38 +0800</pubDate>
      </item>
            <item>
         <title>移植 Android 4.0 到 Devkit8000 開發板 (OMAP3)，只能開機、沒有硬體加速</title>
         <description><![CDATA[Google 將 Android 4.0 釋出後，製造了一波移植運動。Linaor 或是 xda-developer 上，都已經發佈 Ice Cream Sandwich 的移植成果。一些大家手邊常見的開發板，像是 PandaBoard 等，也都能運行最新的 Android 4.0 系統了。

在 Android 4.0 AOSP 上線後，手邊也開始了一個移植專案；經過一個多禮拜的努力，就在大有斬獲時，開發板就被 Porting 到壞掉了。不過，Ice Cream Sandwich 移植是一個令人熱血沸驣的工作，怎麼能這樣就休息呢。於是把在上課使用的 devkit8000 開發板拿出來「試試看」，這是我手邊現有的唯一開發板。

雖然 devkit8000 的硬體配置不算頂級（Cortex A8 600MHz 處理器、256M 記憶體），但因為 Linaro 發佈的 Release 11.11 與 11.12 都把 ICS 移植到  PandaBoard 與 BeagleBoard 了，所以我想 devkit8000 「應該」也不成問題。

上週在進行「Android Porting」的教育訓練時，也跟同學提到，「要把 Android 移植到可以開機」，並不是很難的工作，所以，這個任務的目標，就定為「只到可以開機就好」。於是，利用了週末以及今天下午的時間，進行了這項任務。

為了實驗：從 AOSP（Android Open Source Project）到能開機，並不是難事，所以我就從 AOSP 上取得原生的 Android 程式碼，開始了移植作業。Android 4.0 Porting 很有趣，而且也有一點挑戰性。

由於 Android 4.0 使用到 Wakelock 的 Early-suspend 功能，所以必須重新編譯 kernel，把 Power sysfs 打開。原本打算取用 Linaor 的 kernel，不過後來還是決定使用 rowboat 的版本，再加上 devkit8000 的 4.3 吋 LCD Panel 需要打一些補丁，最後是下載了 0xdroid 的 kernel tree，除了有包含 rowboat 的分支外，也維護了 devkit8000 的相關 patch。

工程師初老症第一條：能找到別人維護好、現成可用的 source tree，就不會有自已維護、打補丁的念頭。

不過最後還是對 kernel 做了一點小修改，包含 Touch Screen 的邊緣沒反應等小問題。這個版本目前維護在 moko365 的 github 上。

移植 Android 時，也修改了一些小細節。不過，大致上「編譯後放到板子上」就可以開機了。由於 dexopt 的 verify-and-optimize 所製造出來的 cache 資料量比較大，以及為了方便測試，所以我就把 kernel、ramdisk 與整個 system 都放到 SD Card 上開機。

最後做出一個只能開機的 Android 4.0 + Devkit8000。Graphics 的部份，使用的是 libEGL_android.so，也就是 Software OpenGL，並沒有硬體加速的功能。最後就是影片中看到的：Launcher2 起來後就掛了。所以，要到真正能用，還要再加點工。

<iframe width="560" height="315" src="http://www.youtube.com/embed/lfT2anmjJ5Y" frameborder="0" allowfullscreen></iframe>

在 Po 壞的板子維修完成前，Devkit8000 大概還要再陪我過幾天乾癮。

<strong>12.07 更新：Launcher 順利啟動</strong>

<iframe width="560" height="315" src="http://www.youtube.com/embed/ptGl_p7b6bg" frameborder="0" allowfullscreen></iframe>]]></description>
         <link>https://www.jollen.org/blog/2011/12/porting-android-4-0-devkit8000.html</link>
         <guid>https://www.jollen.org/blog/2011/12/porting-android-4-0-devkit8000.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 05 Dec 2011 16:18:52 +0800</pubDate>
      </item>
            <item>
         <title>Android 4.0 源碼即將登場：產業將再起什麼變化？</title>
         <description>文／Jollen Chen（原文刊載於 CTimes 零組件雜誌，2011年11月號）

經過10個月的等待，最新的Android源碼，千呼萬喚始出來。Android開始源碼專案，稱之為Android Open Source Project（即AOSP)，目前的最新版本是2.3，下一個AOSP將會是4.0。Google跳過Android 3.0的原因，據信是因為「Android 3.0程式碼不適合開源」，從技術面的角度來看，可能是碼源的組織結構尚未建全，以及架構設計並未經過全面驗證。

從目前已公開的Android 4.0 SDK來看，確實是一個「跨裝置」的作業系統。代號為Ice Cream Sandwich的Android 4.0強調「手機與平板」通用，在新特色列表裡，我們也能發現，應用程式面的特色增加不少。事實上，這次Android 4.0的發佈，Google並沒有說明框架或底層技術細節的變動，而是強調應用軟體面的新功能。

Android 4.0可以說是Android發展史的「第三個重要時代」。熱呼呼的Android 4.0源碼即將到手，廠商應該如何調整接下來的研發策略？在此提供一些想法，請不吝指教。

第一、產品公司的利基已經顯現。第三個Android發展時期，即Android 4.0的時代，訴求已經很明顯，那就是「應用軟體」。重視應用軟體，並不等於組織一個應用軟體研發團隊。實際上，Android Market近20萬個軟體，都是最好的「創意庫」。因此，Android 4.0對於產品公司特別有利基點，因為有了自有產品，才有機會專注在應用軟體與網路服務的整合，發展創新特色。

第二、系統廠應消除研發累贅。所謂的研發累贅，廣義上來說，可以是符合以下二個條件的工作：1. 重覆性工作、2. 研發成本過高的活動。重覆性工作視廠商性質不同而定，例如：感測器研發系統廠，「Android 移植」就是一個重覆性工作，因為接下來，廠商將能直接由「社群」或是「處理器原廠」取得完整度很高的參考設計，只需將重心放在感測器本身的整合，以及應用層面的創新等工作即可。研發成本過高的活動，說明在第三點。

第三、研發成本很低或無限大。研發成本過高的活動，內容千羅萬象，在這裡舉一個例子：「技術研究」。技術研究的目的在於「精確掌握接下來的軟體與技術趨勢」，例如：多核心（Multi-core）接下來的主流會是「異質性」或「同質性」模型。這樣的能力通常只有專家型公司辦得到，例如：知名的技術服務公司Infosys就是一個例子。Infosys曾經提過，「當企業無法跟上技術變化，創新過程將變得緩慢」，因為一但走錯研發方向，修正錯誤的代價將極為昂貴。因此，技術研究是自組團隊，或尋求專家公司的協助，需要經營者的智慧。

以上三個想法，主要是推測Android 4.0降臨後，產業現況可能會發生的化學效應，並取其重，當然還會有更多的變化，不只於這三個想法。面臨變化，新策略的制定，過去的經驗是否有幫助，這可能需要省思。企業經理人，可以從彼得杜拉克「巨變時代的管理」一書中找到一些想法，包含「徹底改造」也是巨變時代的一個藥方。
</description>
         <link>https://www.jollen.org/blog/2011/12/android-4-0-ice-cream-sandwich.html</link>
         <guid>https://www.jollen.org/blog/2011/12/android-4-0-ice-cream-sandwich.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 05 Dec 2011 16:05:19 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] Android HAL &amp; Framework 課程範例移植至 Android 4.0</title>
         <description><![CDATA[本課程範例目前已順利移植至 Android 4.0.1 版本。「<a href="http://www.moko365.com/training/android-hal-framework-practice" target="_blank">Android HAL & Framework: 軟硬整合實作訓練</a>」課程，從2009年開辦至今，已經將有二年半的時間了，在這二年多的時間裡，歷經了 Android 1.6/2.1/2.2/2.3 共四個版本，今天也正式踏入了 Android 4.0 版本。

還記得「<a href="http://www.moko365.com/training/android-hal-framework-practice" target="_blank">Android HAL & Framework: 軟硬整合實作訓練</a>」第一次開課是在2009年7月份，在北京紫竹橋附近的訓練教室，大約有50位同學參加。當時，大家是在「不太了解」什麼是 HAL 與 Framework 的情況下來上課，所以似乎是 Android 的超高人氣幫了最大的忙。

這次將課程範例移植至 Android 4.0，其實是一個「無痛」的過程，程式碼實作本身並沒有什麼修改，只須對 Android.mk 做微幅調整，再重新編譯即可。學員若有意將課堂範例移植到 Android 4.0，可參考仕橙3G教室發佈的修改方法，再重新編譯即可。

課程範例的主要訴求是「依循標準架構」來設計，設計如果能符合標準架構，並依循物件導向的觀念，便能得到相當容易維護並移植的程式碼實作。當然這就是這門課程的主軸，「了解 HAL 與 Framework 的架構、設計與原理」。Android 的開發有許多觀念必須事先建立，例如：範例儘可能不去更動 Android 框架的原始碼。由於沒有對 Android 框架程式碼做修改，因此將新功能移植至新版本時，可以節省相當多的時間。]]></description>
         <link>https://www.jollen.org/blog/2011/11/android-hal-framework-porting-android-4-0.html</link>
         <guid>https://www.jollen.org/blog/2011/11/android-hal-framework-porting-android-4-0.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Fri, 18 Nov 2011 15:18:02 +0800</pubDate>
      </item>
            <item>
         <title>[多核心課程紀錄] 多核心軟體開發的關鍵：Thread Object Model</title>
         <description>關於Java virtual machine的Thread object model，理論上的做法是「一個Thread object連繫(Binding)到一個native thread」。從multi-core的角度來看，這是一個很理想的做法，實務上是否有採用這種Thread object model的Java virtual machine呢？答案是，有的；這取決於virtual machine與作業系統的設計。

JDK6搭配Ubuntu 8.04的環境，以及Android 2.3搭配MagicLego的kernel來看，都是上述的thread model。當然，這裡只是以我自已目前使用中的開發環境當例子，不僅只於這二個環境。

為什麼一個Thread object連繫一個Native thread是比較好的做法，適合應用在multi-core的場合，這涉及到多核心的kernel scheduling技術，在後續的文章裡再做討論。基本上，只要您的系統是屬於上述的Thread object model，未來都能提供一個良好的多核心軟體開發環境。

應用程式到框架，框架透過JNI來到C/C++底層，Thread object與Native thread是一對一關係時，作業系統再將指定的Native thread指派到另外一個處理器，接下來就可以得到這樣的效果：Thread object被指派到另外一個處理器了。這就是為什麼應用程式的設計，以及底層的軟體系統，決定了系統是否能實現「多核心運算」的原因。</description>
         <link>https://www.jollen.org/blog/2011/11/multi-core-software-thread-object-model.html</link>
         <guid>https://www.jollen.org/blog/2011/11/multi-core-software-thread-object-model.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 11 Nov 2011 16:11:48 +0800</pubDate>
      </item>
            <item>
         <title>佔領硬體街：軟體人，今天是你們要團結的日子，你們沒有什麼包袱</title>
         <description><![CDATA[「佔領硬體街」，原文出處：<a href="http://www.eetimes.com/electronics-news/4230394/Calling-all-hackers--Occupy-Hardware-Street" target="_blank">Calling all hackers: Occupy Hardware Street</a>。很有趣，因為寫得很有戰帖的味道，但其實是很發人省思的一篇文章。

它的開場白：

<blockquote>This ought to be the era of the software developer.</blockquote>

<blockquote>「這應該是軟體人的時代」</blockquote>

在佔領華爾街後，為什麼要招集軟體高手佔領硬體街？從下一句話，就可以知道，軟體的價值，以及軟體時代真的到來了。原文的 Hacker 是「軟體高手」的意思：

<blockquote>Every week sees a new event courting the lowly hacker. Here are a few recent examples from my day timer: The Android Dev Con, ARM Tech Con, Blackberry Dev Con, Ubuntu Dev Con…and the list goes on.</blockquote>

<blockquote>「每週，都有很多活動，想要拉攏軟體高手，像是 Android Dev Con、ARM Tech Con、Blackberry Dev Con、UBuntu Dev Con。」</blockquote>

所以，不意外的，或許接下來會有 Baba Dev Con、Tizen Dev Conf 吧。拉攏開發者的活動，或是換個角度，「與開發者合作」的能力，是很重要的競爭力。這應該列入現代公司經營管理的一環，公司要建立與開發者的合作能力，而不是去雇用為員工，這個新改變很巨大，因為企業要學習如何與「Hackers」合作。

例如，軟體高手，他（她）可能想要發展自已的 App，並且在 Android Market 上銷售自已的軟體，甚至這些軟體高手，已經有多個熱銷的 App 了。「收編旗下」實在是個不好的主意，要招募他們為員工的可能機會不高，但可以和他們結為伙伴關係。這就是為什麼有些廠商，認為「好的軟體人才難尋」的原因之一。雖然旁邊的釘子很多，但這塊磁鐵吸不到半支。

看到這點，讓我想到，政府部門這陣子很用心在執行「App 軟體人才」的計畫，值得肯定；但方向可能有錯，或許是對「App 軟體人才」的認知不夠。正確的做法，應該是去協助並扶植 App 人才或團隊，形成一個新的產業，就像當初政府發展「竹科」一樣。從政府的角度，只做就業媒合，格局太小，需要全套的政策。

原文提到「Software Bay」的看法，很值得政府來做。在十月份的零組件雜誌專題報導中，我認為「台灣沒有軟體人才的問題」，但「管理軟體人才的方式出問題」是近二年教育訓練的心得。實際上，台灣軟體高手不少，很多軟體高手在國際上都有「好利害」的表現。像是最近在 AppStore 排行榜前十名的熱門軟體，有一個就是台灣的軟體團隊作品。

過去二年，為許多企業做 Android & Linux 教育訓練，觀察「軟體人才」問題也有很長一段時間了。我的心得是「其實軟體人才就在裡面」，這段期間，也遇到不少利害的軟體高手，我這個「老師」和他們的領域相比，相形見絀。例如，過去幾年，代工廠內部產出一批可以客製化 Linux kernel 的工程師，但就像原文提及：

<blockquote>Even Taiwan's notebook makers are hungry for all the low level software architects they can find—especially those who can help them customize Android kernels for their future smartphones and tablets.</blockquote>

代工廠，你們有 Developer 等級的 Linux 高手，為什麼還缺人才，確實值得思考。所以，台灣有軟體高手，產出人才不是重點，怎麼去凝聚這些人，才是重點，怎麼做？這個部份，其實像是創新工場，或是台灣的 Mr. Jamie 都有很不錯的做法。或許學習 Mr. Jamie 的模式，才是政府該做的事情。

原文最後，是我覺得最好玩的一句話：

<blockquote>So, software developers, today is your day to unite. You have nothing to lose but your shackles.</blockquote>

<blockquote>「所以，軟體人，今天是你們要團結的日子，你們沒有什麼包袱，只要解開你的桎梏」</blockquote>

這個「shackle」是什麼？作者的意思應該是「不要怕、要勇敢」，意思是軟體人要站出來做點自已的事情了。因為「Software is becoming the key differentiator in electronics everywhere you turn.」，你的產業或許不把軟體當一回事，就這是不要怕，要拿開 shackles 的意思。如果您是在科技業的軟體人，也參與過一些硬體研發專案，這不需要再多講了，我想我們都有默契。]]></description>
         <link>https://www.jollen.org/blog/2011/11/calling-all-hackers-occupy-hardware-street.html</link>
         <guid>https://www.jollen.org/blog/2011/11/calling-all-hackers-occupy-hardware-street.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 10 Nov 2011 10:15:41 +0800</pubDate>
      </item>
            <item>
         <title>[多核心課程紀錄] 多核心軟體開發的關鍵：Pthread</title>
         <description>任務切割的目的，在於將應用程式裡的計算工作，切割後指派至另一個處理器核心；讓應用程式，能真正使用多核心的計算能力。這就是為什麼多核心軟體的設計，決定了多核心系統效能。上述的觀念，就是「平行處理」。

從應用程式的層面，就要考慮多核心的設計。如何將一個計算工作切割出來，並指派至另一個處理器核心？方式就是使用multi-thread。以Linux作業系統為例，multi-thread程式設計使用一個稱為pthread的程式庫；因此，學習pthread程式設計，就是打好多核心軟體開發的第一個功課。

Android作業系統同樣是使用pthread程式庫，雖然Android的pthread程式庫，與Linux的pthread程式庫「是二個2不同的實作版本」，但同樣是依循POSIX的標準（pthread是POSIX thread的縮寫），因此，有志進入多核心軟體開發的工程師，可以先在Linux系統底下，學習Linux pthread程式設計。

此外，Android應用程式與框架層，採用Java程式語言撰寫，並且採用物件導向的基礎理論。目前所談論的pthread程式設計，則是用C或C++撰寫，我們將透過pthread所產生的thread稱之為native thread。應用程式使用Java語言撰寫，所產生的thread稱為Java thread。Java thread本質上是一個物件，因此也稱為Thread object。

應用程式與框架層的Thread object與更底層的Native thread關係為何？答案是決取於Java Virtual Machine的設計；JVM的Thread model設計，將會影響Java thread的行為，在多核心系統上，Thread model也會影響Java thread的效能。</description>
         <link>https://www.jollen.org/blog/2011/11/multi-core-software-multi-threading.html</link>
         <guid>https://www.jollen.org/blog/2011/11/multi-core-software-multi-threading.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Multi-core Software</category>
        
        
         <pubDate>Tue, 08 Nov 2011 19:09:04 +0800</pubDate>
      </item>
            <item>
         <title>[多核心課程紀錄] 多核心軟體開發的關鍵：任務切割</title>
         <description><![CDATA[距離上次為自已的部落格裡新增文章分類，已經有一年的時間了，這次加入了一個 Multi-core Software 的分類，也代表自已接下來的技術研究重心，將會在多核心軟體開發上投入時間。

就在上週四(11/3)，Moko365與MagicLego團隊共同舉辦了一場「<a href="http://www.moko365.com/enterprise/multi-core-training-basics" target="_blank">Multi-Core 嵌入式開發</a>」研討課程。這次的課程，可以說是多核心軟硬體開發的「電腦概論」課程，不但是多核心開發的第一門課，更是了解「沒有軟體、沒有多核心效能」的基礎課程。針對自已的講題部份，在此做一些簡單的紀錄。

沒有良好的軟體設計，無法發揮多核心處理器的效能。既然如此，多核心軟體的涉及範圍為何？只需要作業系統支援多核心即可嗎？

答案是，多核心處理器需要軟體全面的支援，範圍從應用程式開始，一直往底層，直到作業系統，甚致驅動程式，都有很大的關聯。軟體如何支援多核心處理器，最大的關鍵在於「任務切割與指派」。研討課程當天，另一位講師Frank也利用一個影像處理的例子，說明影像處理如何分割任務，並將不同的任務，分派給不同的處理器。

另一個多核心軟體的關鍵為資料合併(Combine)，Frank同樣也以影像處理做為例子，說明每個處理器在完成計算後，將分別得到的結果(Data)合併成最終計算結果。這個部份，梁老師也利用了一個矩陣運算的例子做了很清楚的說明。

由以上的說明可以了解，資料合併的關鍵，最終在於「如何取用另一個處理器核心的結果(Data)」。在ARM Cortex A8/A9架構裡，這個問題，是透過共用記憶體方式解決。也就是Cortex A8/A9多核心架構，處理器核心間是共用記憶體。共用的記憶體包含二種，第一種是成本較高，但速度較快的 L2 Cache；另一個共用記憶體則是DRAM，它的成本較低、容量較大，但速度較慢。]]></description>
         <link>https://www.jollen.org/blog/2011/11/multi-core-software-task-conquer.html</link>
         <guid>https://www.jollen.org/blog/2011/11/multi-core-software-task-conquer.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Multi-core Software</category>
        
        
         <pubDate>Mon, 07 Nov 2011 18:15:17 +0800</pubDate>
      </item>
            <item>
         <title>Android 4.0 源碼即將登場：產業將再起什麼變化？ </title>
         <description><![CDATA[文／Jollen Chen（原文刊載於「零組件雜誌」2011年11月號）

經過10個月的等待，最新的Android源碼，千呼萬喚始出來。Android開始源碼專案，稱之為Android Open Source Project（即AOSP)，目前的最新版本是2.3，下一個AOSP將會是4.0。Google跳過Android 3.0的原因，據信是因為「Android 3.0程式碼不適合開源」，從技術面的角度來看，可能是碼源的組織結構尚未建全，以及架構設計並未經過全面驗證。

<strong>Android 4.0 跨裝置作業系統</strong>

從目前已公開的Android 4.0 SDK來看，確實是一個「跨裝置」的作業系統。代號為Ice Cream Sandwich的Android 4.0強調「手機與平板」通用，在新特色列表裡，我們也能發現，應用程式面的特色增加不少。事實上，這次Android 4.0的發佈，Google並沒有說明框架或底層技術細節的變動，而是強調應用軟體面的新功能。

Android 4.0可以說是Android發展史的「第三個重要時代」。熱呼呼的Android 4.0源碼即將到手，廠商應該如何調整接下來的研發策略？在此提供一些想法，請不吝指教。

<strong>軟體與產品公司興起、ODM 模式走入尾聲？</strong>

第一、產品公司的利基已經顯現。第三個Android發展時期，即Android 4.0的時代，訴求已經很明顯，那就是「應用軟體」。重視應用軟體，並不等於組織一個應用軟體研發團隊。實際上，Android Market近20萬個軟體，都是最好的「創意庫」。因此，Android 4.0對於產品公司特別有利基點，因為有了自有產品，才有機會專注在應用軟體與網路服務的整合，發展創新特色。

第二、系統廠應消除研發累贅。所謂的研發累贅，廣義上來說，可以是符合以下二個條件的工作：1. 重覆性工作、2. 研發成本過高的活動。重覆性工作視廠商性質不同而定，例如：感測器研發系統廠，「Android 移植」就是一個重覆性工作，因為接下來，廠商將能直接由「社群」或是「處理器原廠」取得完整度很高的參考設計，只需將重心放在感測器本身的整合，以及應用層面的創新等工作即可。研發成本過高的活動，說明在第三點。

<strong>走錯研發方向、修正成本相當昂貴</strong>

第三、研發成本很低或無限大。研發成本過高的活動，內容千羅萬象，在這裡舉一個例子：「技術研究」。技術研究的目的在於「精確掌握接下來的軟體與技術趨勢」，例如：多核心（Multi-core）接下來的主流會是「異質性」或「同質性」模型。這樣的能力通常只有專家型公司辦得到，例如：知名的技術服務公司Infosys就是一個例子。Infosys曾經提過，「當企業無法跟上技術變化，創新過程將變得緩慢」，因為一但走錯研發方向，修正錯誤的代價將極為昂貴。因此，技術研究是自組團隊，或尋求專家公司的協助，需要經營者的智慧。

<strong>彼得杜拉克：「巨變時代、徹底改造」</strong>

以上三個想法，主要是推測Android 4.0降臨後，產業現況可能會發生的化學效應，並取其重，當然還會有更多的變化，不只於這三個想法。面臨變化，新策略的制定，過去的經驗是否有幫助，這可能需要省思。企業經理人，可以從彼得杜拉克「巨變時代的管理」一書中找到一些想法，包含「徹底改造」也是巨變時代的一個藥方。]]></description>
         <link>https://www.jollen.org/blog/2011/11/android-4-0-wave-taiwan.html</link>
         <guid>https://www.jollen.org/blog/2011/11/android-4-0-wave-taiwan.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sat, 05 Nov 2011 18:25:40 +0800</pubDate>
      </item>
            <item>
         <title>AOSP 回來了、Ice Cream Sandwich 將會開源</title>
         <description>經過漫長的等待，AOSP (Android Open Source Project) 終於回來了。這次 kernel.org 被駭事件，導致 AOSP 伺服器中斷服務近三個月，Google 終於帶來好消息。

正好 Android 4.0 (Ice Cream Sandwich) 產品發佈。根據 Google 官方說明，Ice Cream Sandwich 的源碼將在三星 Android 4.0 手機 (Nexus Prime) 上市後公開。由於社群不斷在郵件列表裡發問，這次 Google 官方為大家釋疑，讓社群開發者總算放下一塊大石；Android 4.0 確定會發佈到 AOSP，時間大約在 11 月底前。正確時間還是要看 Nexus Prime 的出貨時間而定。

AOSP 這次也加入了新的權限管理方案，不再使用 kernel.org 提供的伺服器，而是搬回 Google 自已的伺服器，由 Google Git 接手 AOSP 的服務。

現在已經看不到 android.git.kernel.org 的小綿羊了。android.git.kernel.org 被導向到 android.googlesource.com，AOSP 也能正常下載；但目前提供的是舊版 AOSP 源碼 (2.3.7_r1)。由於 Android 3.x 可能無法正常支援手機，因此 Google 並未提供 AOSP；相信 Android 4.0 將會再帶來新的樂趣。</description>
         <link>https://www.jollen.org/blog/2011/10/aosp-is-back-ice-cream-sandwich.html</link>
         <guid>https://www.jollen.org/blog/2011/10/aosp-is-back-ice-cream-sandwich.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 21 Oct 2011 12:30:33 +0800</pubDate>
      </item>
            <item>
         <title>Dennis M. Ritchie (1941-2011)</title>
         <description><![CDATA[<p align="center" style="margin-top:50px;margin-bottom:100px;"><a href="http://cm.bell-labs.com/who/dmr/">
<img alt="Dennis-Ritchie.gif" src="http://www.jollen.org/blog/2011/10/14/Dennis-Ritchie.gif" width="310" height="261" border="0" />
</a></p>]]></description>
         <link>https://www.jollen.org/blog/2011/10/dennis-ritchie-1941-2011.html</link>
         <guid>https://www.jollen.org/blog/2011/10/dennis-ritchie-1941-2011.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Fri, 14 Oct 2011 23:48:18 +0800</pubDate>
      </item>
            <item>
         <title>趨動台灣的硬體產業創新：10個觀念建立開放硬體專案</title>
         <description><![CDATA[趨動台灣的硬體產業創新：10個觀念建立開放硬體專案
(文/Jollen Chen，刊載於零組件雜誌2011年10月號)

硬體產業需要融入「軟體」、「應用」與「社群」的元素，才有創新的力量。台灣的硬體產業如何創新並尋找新的機會，開放硬體是一個很好的做法。台灣硬體業者，在開放硬體運動上，也能扮演重要的角色。以下以簡單的10個觀念，協助台灣硬體廠建立開放硬體專案。

<strong>第一：提供硬體元素 (Hardware Components)</strong>

以Arduino社群為例，它提供一個可供硬體愛好者「DIY」的元素，愛家利用Arduino提供的硬體來連接週邊裝置，例如：電器用品，發揮天馬行空的創意。由開放硬體計畫由銷售的硬體，以簡單易用為原則。

<strong>第二：整合式開發工具 (Integrated Development Environment)</strong>

除了有簡單好用的特性外，軟體也是一大關鍵；最重要的軟體莫過於開放工具。目前開放源碼的軟體開發工具眾多，如果可以讓硬體的開發工具更容易上手，使用並整合至現有的開發工具，肯定是最佳的想法。

<strong>第三：決定程式碼授權與內容授權條款 (FLOSS & CC License)</strong>

開放源碼(FLOSS)的眾多授權條款中，GPL(v2/v3)、BSD系列、Apache 2.0與MIT條款都是主流條款，也廣為使用。內容的授權條款則是以創用CC(Creative Commons)系列為主。不管是開放源碼、開放硬體、開放設計或開放內容，都要以這些授權條款為主。

<strong>第四：創造獨有精神象徵 (Symbols)</strong>

象徵可以是口號、符號、圖形等，或是建立完整的CIS系統。但是，若是借開放名義，行商業之實，很容易產生反效果。因此，並不建議基於企業現有的 CIS 系統，來進行這項活動。

<strong>第五：扮演適當的身份 (Sponsor is good)</strong>

如上述所提，勿假借開放名義行商業之實；但是，並不是要企業扮演慈善家，只有付出或貢獻，企業透過開放硬體社群，目前是為了創造新的商業機會。開放硬體運動是雙向的，硬體廠有貢獻，當然也可以有收獲，以「贊助商」的角色協助營運，並扮演初期創辦者的角色，都是很好的做法。

<strong>第六：社群經理人 (Manage Results not People)</strong>

從企業是開放硬體計畫的「成立者」來看，急於將社群的產出，連接到企業商業利益， 將很容易出現「控制」社群的行為。硬體廠需要了解的是，在開放硬體的計畫裡，沒有經理人：沒有任何計畫需要被管理，沒有任何社群的自發活動需要被管理。

社群與商業利益的良好結合，會是社群能長久經營的關鍵；這項工作也需要專業經理人，但經理人不是駐守在社群裡，而是在企業內部。因此，這裡指的社群經理人，並不是管理社群的人，而是善於收劍社群的產出，將之轉化為商業模式的專家。

簡而言之，企業內部的社群經理人，在於管理產出物，而不是社群本身。

<strong>第七：建立銷售管理 (Create Disty & Resller Program)</strong>

硬體要容易取得，這是開放硬體計畫很重要的事情。硬體不像是軟體，軟體可以很容易透過下載取得。因此，為你的實體硬體建立銷售管道，將是很重要的工作。

開放硬體社群會願意付費購買硬體，供應商當然也可以從中得到合理利潤；只要一切合理，以商業方式銷售硬體，是應該要鼓勵的作法。這是一種正面的循環，硬體廠可以創造合理利潤，玩家又能以合理價格取得硬體。

以 Disty & Reseller Program 來說，建議可以連絡區域性的代理商或代銷商，目前網路上有許多社群導向的線上銷售公司，這些都是第一優先的建議名單。

<strong>第八：獨立網址 (Create .org/.com)</strong>

為開放硬體計畫創造獨立的網站，當然也要為它申請獨立的網址。這點很重要，因為關係到未來行銷或是PR工作。獨立網址，也更容易讓人留下最佳的第一印象。

<strong>第九：了解OSHW文化 (Read OSHW)</strong>

閱讀OSHW(Open Source Hardware)的官方網站，了解OSHW的定義、歷史活動以及成功案例，都是了解開放硬體文化的作法，但都比不上實地參與更有效。要建立開放硬體社群，就不能只是紙上談兵，就算沒有經驗，也要起身而行，求教有經驗的社群，做學做邊又何妨。

<strong>第十：確實開放 (Completely Open)</strong>

相信這是台灣硬體廠內心最掙扎的一點，確實開放，代表要以「創辦者」與「贊助者」的心態，充份開放足夠的技術資訊，例如：Schematics、BOM、Gerber 等，儘量不要讓開發者面臨技術資訊不足的困境。

如果有商業機密的考量，當然可以選擇不公開技術資訊，因為這也是一種正向循環，可以讓硬體廠在商業上得到保障，又能尋找新機會。因此，確實開放的定義是：選擇能「充份公開」的「部份」。

<strong>仕橙部落「開放硬體」專欄</strong>

過去一個月，仕橙部落針對開放硬體也做了一些報導「<a href="http://www.moko365.com/enterprise/sp001-20110929-open-hardware" target="_blank">OSHW 專欄</a>」。]]></description>
         <link>https://www.jollen.org/blog/2011/10/10-principles-to-open-source-hardware-for-taiwan-vendors.html</link>
         <guid>https://www.jollen.org/blog/2011/10/10-principles-to-open-source-hardware-for-taiwan-vendors.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 03 Oct 2011 14:53:03 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] Android 的 JNI 開發，排名第一名的誤用是？</title>
         <description><![CDATA[Java 與 native code 的溝通介面稱為 JNI，這是 Android 底層開發的基本技術。不過，有許多 C 語言遺留下來的壤習慣，讓很多系統程式的開發者，一不小心就把 JNI 的程式碼寫錯。本週將會在一場論壇上說明幾個「誤用 C 語言」的例子，希望對 Android 底層開發的初學者有幫助。

近期在協助一家企業進行 Android 內訓，也遇到工程師問起「Java 如何與 C 傳遞資料」的問題，以傳遞陣列來說，其程式碼的寫法，跟傳統的 C 語言寫法有點不同。

從過去的教育訓練經驗裡也能歸納發現，排行榜第一名的誤用莫過於「陣列傳遞」，當 Java 透過 JNI 傳遞 Array 給 native code 時，native code 必須使用 JNI 的 GetIntArrayRegion() method 來讀取，而不是使用 C Pointer 的做法。

例如：

<pre>
int intArrayAdd(int *num)
{
   int i, sum = 0;
  　
   for (i = 0; i < 10; i++) {
      sum += num[i];
   }
}
</pre>

換成 JNI 的話，應該改寫成：

<pre>
intArraryAdd(JNIEnv *env, jobject obj, jintArray arr)
 {
     jint buf[10];
     jint i, sum = 0;
　
     (*env)->GetIntArrayRegion(env, arr, 0, 10, buf);
　
     for (i = 0; i < 10; i++) {
         sum += buf[i];
     }
     return sum;
 }
</pre>

這裡的概念是，將陣列 copy 到 native code 裡後再使用。相關的完整說明，可參考「The Java Native Interface. Programmer's Guide and Specification」第 3.3 節。
]]></description>
         <link>https://www.jollen.org/blog/2011/09/android-jni-passing-array-use-getintarrayregion.html</link>
         <guid>https://www.jollen.org/blog/2011/09/android-jni-passing-array-use-getintarrayregion.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Tue, 27 Sep 2011 00:21:34 +0800</pubDate>
      </item>
            <item>
         <title>Android 軟體品質管理： 台灣硬體廠如何提升軟體能力</title>
         <description><![CDATA[文／Jollen Chen (原文刊載於零組件雜誌 2011 年 9 月號)

<strong>建立 Android 專用的 Code Review 流程是關鍵</strong>

照顧程式碼品質就像照顧身體，要常常檢查，隨時注意異狀。Android 的開發工作如果要確保可用（Usable）與穩定（Stability），就要做好 Code Review 的工作。根據過去與許多廠商的合作經驗發現，許多關鍵的軟體開發觀念經常被忽略。主要的原因為，大部份的技術開發思惟，都比較偏向硬體與驅動程式方面，或是功能性的實作。

軟體的開發本身就是一項大工程，由 Android 所創造出來的手機作業系統，可能有近 90% 的比例是透過軟體工程的技術與觀念所開發，其它 10% 才是考量硬體層面，或是驅動程式層面。換個角度來看，以台灣硬體廠商的技術水準，如果把軟體工程的技術養成，一定能具備產品開發的實力。以下提供個人的一點建議：台灣硬體廠該如何提升軟體開發能力。

第一、先做再說、確實可行。單獨以 Android 的框架與軟硬整合的角度來看，先設計後實作（Design & Implementation）的方法論可能不適用於台灣的產業環境，因此導入傳統的軟體工程方法論，或許也沒有絕對的必要性；原因是，Android 已經把這些基礎建設都做到一定程度了。在理論與實際間取捨的話，「先實作、後檢視」可能是一種方式。

目前在業界所見的程式碼實作，大多偏重硬體與功能面，在理論面著墨不深，不過這卻是個很好的契機。過去自已的經驗發現，先實作，得到初步可用的程式碼實作後，再考量理論面，進行程式碼調整，其實是可行、有效率的做法。因此，自已也希望能將這個觀念與方法論，提供給客戶參考，甚或協助導入「先實作、後檢視」的作業流程。

<strong>什麼是 Code Review</strong>

第二、實施 Code Review 就對了。軟體的開發工作，都會有 Code Review 的流程。這裡所提的「檢視」即 Code Review。Code Review 是一個很久的觀念了，它在軟體管理（Software Management）的領域裡被詳細討論。

Code Review 是一個系統化的檢查過程，目的是確定程式碼的品質；檢查的過程，是為了找出錯誤、並且修正錯誤，這些錯誤在初階的開發階段（Initial Development Phase）可能不會被發現。這裡的「錯誤」也包含「觀念上的錯誤」、「理論的誤用」等等，因此，能動作（Workable）的程式碼，不見得是正確的程式碼。

<strong>Code Review 是教育訓練的一環</strong>

第三、搭配教育訓練。Code Review 還有另外一個很重要的目的，卻不常被提及，就是「提升開發人員的技能」。Code Review 等於 Improve Software Quality + Improve Developer's Skills。軟體的品質，影響軟體的穩定性；人員的素質，影響軟體的品質。在初階開發階段，可以不必發現理論上的問題，而是下一階段，由資深開發人員協助 Code Review，再進行程式碼調整，以提升軟體品質。這就是「先實作、後檢視」的精神。

最後、其實是一個例子。以 Android Framework 與 Linux 驅動程式為例，主要影響系統穩定性的關鍵在於「Android 框架與 Linux 驅動程式的資料傳遞方式」，即「儲存資料」並「傳遞記憶體」的方式。「記憶體的使用」是影響 Android 與 Linux 整合穩定性的主要因素。Android 底層可能需要以 Memory Heap 來儲存並傳遞大量資料，而非以 C 語言指標（Pointer）的方式進行。

從事硬體發展的研發人員，可以在初階開發階段以 malloc 搭配 C 語言指標，來傳遞硬體資料給 Android 作業系統。但是，必須有 Code Review 人員，協助將初階的實作，修改為 Memory Heap 方式，並以物件觀念傳遞。 重構後的程式碼，可以協助該硬體開發人員，提升他（她）的軟體技能。這種「先開發、後檢視」的作法，就是 Code Review 的精神，也是台灣硬體業提升 Android 程式碼穩定性，以及提升開發人員技能的一個方法。]]></description>
         <link>https://www.jollen.org/blog/2011/09/android-code-review-with-training-is-a-must-for-taiwan-industry.html</link>
         <guid>https://www.jollen.org/blog/2011/09/android-code-review-with-training-is-a-must-for-taiwan-industry.html</guid>
        
        
         <pubDate>Mon, 05 Sep 2011 23:57:43 +0800</pubDate>
      </item>
            <item>
         <title>Motorola 嫁進 Google 大門，台灣硬體廠如何因應，與 Android 的未來</title>
         <description><![CDATA[Google 過去四年來對 Android 應用程式框架的貢獻巨大，可視為「資訊科技整合」的一個代表作，購併 Motorola Mobility Inc 後，除了媒體所討論的專利議題外，Google 更能從 Motorola Mobility 身上，學習到寶貴的軟硬整合經驗，這是更重要的進步。一般認為收購之舉是衝著專利而來。大家比較沒有注意到的是 IBM 也擁有許多作業系統專利，不久前，Google 也「一籃子」收購 IBM 專利，因此，Motorola 嫁進 Google 大門，專利議題應是主因。以下從「非專利」面分享一些個人看法，請不吝指教。

<strong>自有能力 vs 重度依賴</strong>

台灣硬體廠學習軟體的速度緩慢，對 Google 有很高程度的依賴。在 Google 購併 Motorola Mobility Inc 後，紛紛對 Android 的開放性提出質疑，並且認為 Motorola 將取得更多的 Android 資源，其它廠商或將淪為「二等公民」。

台灣硬廠對軟體的高度依賴性並不是現在才開始。從 Wintel 時代開始，就非常依賴微軟視窗作業系統，不過，由於當初時空背景的關係，微軟作業系統是整個 PC 標準重要環節，大家是一個 Ecosystem，因此沒有建立自有作業系統的必要性。

這個從小到大的習慣，開始帶來壞處。就像一個被寵壞的小孩子一般，硬體廠普遍認為 Google 提供完善的 Android 程式碼是順理成章的事情，有了完整的程式碼，以及適當程度的「保護」後，「大家才願意跟著玩」，這或許不是一個很健康的想法。如何建立自已的軟體技術能力，似乎少有人思考。

<strong>回歸自由軟體的精神</strong>

Google 對 Android 的投入與貢獻，不該被忽略。Android 的定位很清楚，有一份 AOSP 版本，Google 也不斷在內部開發更棒的新版本。AOSP 是 Google 在開放平臺與自由軟體的重要貢獻之一，這就是 Android Open Source Project 的目的，貢獻源碼，提供自由軟體，「但這並不表示 Google 有責任為硬體廠付出任何責任」，韓國三星電子近期強力要求旗下部門強化軟體競爭力，這才是台灣產業要認真思考的方向。

事實上，AOSP 僅只是 Google 對自由軟體的貢獻之一，而不是唯一。Google 也提供 AJAX 程式庫，也有免費的 CSS 字型，也支持 jQuery 等重要的自由軟體專案，「聽說 Google 將封閉 Android 程式碼」，並不是健康的想法。如果了解 Google 在自由軟體工程領域的貢獻，就會了解，「只是先關起門來把事情做好」，而不是對外關起大門。

<strong>朝向開放 API 搭配部份開放源碼</strong>

事實上，要取得目前最新版本的 Android 程式碼不是高牆。在 Android ecosystem 裡，Google 很清楚自已所扮演的角色：「Android Product Manager」，因此，Google 有責任了解廠商的「產品理念」，才能扮演好自已的角色。

封閉程式碼，與做好 Product Manager 的工作，格局差異太大，前者並不是大格局做法，實際上目前也未聽聞 Android 有這項計畫。個人看法是，Google 接下來應會加速開放 API 以及部份程式碼的腳步。過去 Android 產品是一種失控的狀態，為避免重蹈覆徹，完全開放源始碼短期可能性應不高。

有了底層的程式碼、HAL 技術架構等，透過軟體工程技術，將開放 API 整合至產品並不困難。開放 API 已足夠，並不需要完全開放源碼才能做產品。早在 Android 1.6 版時代，Google API 就以 Open API（封裝後的二進位程式碼）的形式提供在 AOSP 裡。

<strong>看看 Arduino、回頭想想自已</strong>

Arduino 是一個開放源碼的 I/O Board，可用來連接外部控制器或週邊裝置，如：馬達。Arduino 是一個知名的開放源碼專案，它做硬體，同時整合軟體並提供一個開發環境。這項硬體技術，對台灣的硬體廠來說，只是小兒科。

Google 於今年五月發表的 Android Open Accessory Development Kit（ADK）就是基於 Arduino 專案及其硬體。台灣的硬體技術水準很高，零件廠夠多夠大，但對 Android 的影響力卻不及 Arduino 專案，很明顯地是經理能力出現問題：缺乏開放平臺觀念、軟體與社群社交等能力。

<strong>Lession Learing</strong>

這二天有一些合作廠商，因 Google 購併 Motorola Mobility Inc 表現出不安，提出的許多疑問其實也很合理，但 Google 並沒有照顧硬體廠的責任。AOSP 是自由軟體的精神，儘快建立自已的軟體實力，靠自已最實在，就算基於 AOSP 也能做出好東西，一些主要的 Android 手機製造商已經學到這一課。

再者，台灣主要的硬體廠，也都早已取得 Android 3.x 程式碼，並不是無法取得最新的程式碼，只是過去太重度依賴 Google 的 Android 團隊，這是比較大的問題。如果連 Android 3.x 的程式碼都無法取得，封閉之說才有那麼一些道理。

上述想法並沒有任何預設主場，僅只是想法分享。過去幾年為許多台灣硬體廠提供 Android 技術服務，其實大家也都很有學習的企圖心，只是我們還需要一些時間，多一點正面想法，接著拭目以待囉。]]></description>
         <link>https://www.jollen.org/blog/2011/08/google-to-acquire-motorola-mobility-impact-of-taiwan-hardware-industry.html</link>
         <guid>https://www.jollen.org/blog/2011/08/google-to-acquire-motorola-mobility-impact-of-taiwan-hardware-industry.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Thu, 18 Aug 2011 22:39:23 +0800</pubDate>
      </item>
            <item>
         <title>淺談 GNU/Linux 與 Android/Linux 的差異：Android 非常需要 Permissive License 的授權模式</title>
         <description><![CDATA[上週五受台北科技大學資工系的邀請，發表了一個小時的演講，講題是「淺談 GNU/Linux 與 Android/Linux 的差異」，如同題目所要表達的，「Android+Linux」並不是「GNU+Linux」。傳統的 Embedded Linux 系統是基於 GNU/Linux 開發，因此在這次的演講裡，也做了一個「Embedded Linux 不等於 Android+Linux」的結論。

這個題目相當有趣，因為涉及非技術面的議題。在真槍實彈開發 Android 產品前，認識我們使用的 Android/Linux 系統是很重要的前置作業。這次的講題涉及層面較廣，細節也很多，小弟特別從二個層面切入：技術面談 Android/Linux Toolchain、非技術面談 FLOSS 授權。

<strong>Android 的商業模式需要 Permissive License</strong>

在 FLOSS 授權方面，並不是「不同授權的 FLOSS 軟體」都能「任意合併使用」，在了解各授權的特性與「合併使用」議題前，有必要先了解現有授權的類型。現今較廣為使用的 FLOSS（Free and Libre Open Source Software）授權有：GPLv2、GPLv3、LGPL 系列、Apache 2.0、BSD 系列、MIT 等等。

上述的授權條款可分為三大類型：

<ul>
<li>1. Permissive License</li>
<li>2. Weak Copyleft</li>
<li>3. Strong Copyleft</li>
</ul>

Copyleft 的授權模式主要在限制軟體不能以 proprietary software 形式散佈，copyleft 的授權模式又分為 weak 與 strong 二種；例如 GPLv2 是屬於 strong copyleft 的授權，大大地限制軟體不能以 proprietary 的形式散佈。

Permissive license 則允許軟體以 proprietary software 的形式散佈，開發 Android 需要從產品面思考，不能單方面只思考技術面，從產品面來看，Android 程式碼非常需要 permissive license 的授權模式，因為有許多核心技術或是關鍵的軟硬整合實作，需要以 proprietary software 的形式散佈。]]></description>
         <link>https://www.jollen.org/blog/2011/07/android-needs-permissive-license-to-distribute-products.html</link>
         <guid>https://www.jollen.org/blog/2011/07/android-needs-permissive-license-to-distribute-products.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 11 Jul 2011 18:42:24 +0800</pubDate>
      </item>
            <item>
         <title>台灣廠商與老闆們，可以枉尺直尋的心態投入「架構研究」工作</title>
         <description><![CDATA[先前談了有關「架構分析工程師」的重要性，在這裡再分享「為什麼研究架構」是一項重要的工作，也值得業界投入。

架構分析與研究還有一個很重要的目的：找出問題、解決問題，透過較為有系統的方式找出問題的起因，並研究解決問題的有效方法。在研發工作上，可以大幅縮短開發時間。從專案的角度來看，有二個好處：

<ul>
<li>縮短自行摸索時間。在有經驗者的引領下，從學習、訓練、實習、經驗累積到開發案參與，都有明確的方法；在技術工作上，可立即了解每個技術議題所涉及的背景知識。在真正專案開始後，能往正確的方向前進。</li>

<li>解決問題。系統廠普遍存在一個老問題，就是「try-and-error」的解決問題方法。從架構分析面著手，能了解問題可能發生的層面，逐步縮小範圍，以找出解決問題的方法。從實務角度來看，「Research」是為了解決問題，完成工作任務。</li>
</ul>

因此，「架構分析」是幫助我們「解決問題」與「縮短摸索時間」的良方。學術上的「做研究」是為了學問，但業界做研究，很大一部份是為了解決問題，目的大不同。因此，研究工作、研究型軟體公司，或是顧問公司，將會扮演重要的角色。

<strong>研究工作在追求更大範圍的收獲</strong>

不過是縮短摸索時間，或是解決問題，關鍵都在「時間」這項資源上。架構分析導向的問題解決過程，確實能加快解決問題的時間。反之，有時在解決技術上的問題後，常常會有走了很多冤枉路的感覺，「要是當初知道這樣做，早就解決問題了」，可不是嗎？

不過是自行投入技術研究工作，或是尋求與技術研究公司合作，雖然一開始會有「沒有產出」，或是「不夠具體化」的感覺，也會感覺前期的投資沒有回收；但是，如果看得更長遠，枉尺而直尋，宜若可為也。最後，總結看法並簡單翻譯，「研究工作長遠來看，當可創造新的營收來源」，因為研究者能以創作心態做產品，製造者是以成本心態做產品。]]></description>
         <link>https://www.jollen.org/blog/2011/07/view-of-architecture-research-for-taiwan-industry.html</link>
         <guid>https://www.jollen.org/blog/2011/07/view-of-architecture-research-for-taiwan-industry.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Wed, 06 Jul 2011 16:15:46 +0800</pubDate>
      </item>
            <item>
         <title>培養 Android 軟體人才： 從落實軟體工程教育開始</title>
         <description><![CDATA[文／Jollen（原文刊載於零組件雜誌 2011 年 6 月號）

「開發 Android」是台灣科技業的全民運動了。未來幾年，如果要尋求更大的突破，並提升整體軟體開發能力，根本的做法與策略為何？個人看法是「落實軟體工程的基本教育」。以下是個人對於「軟體人才培養」的看法與心得，請不吝指教。

提昇 Android 軟體能量，我們的當務之急是：培養一批「架構分析」的工程師。架構分析需要考量的層面較廣，包括技術面與產品面。軟硬整合的工作，幾年前訴求的是硬體驅動程式與系統軟體，主要工作是效能的提昇與優化，這是從硬體層面思考的軟硬整合。從產品層面重新思考軟硬整合，涉及的層面會放大至中間軟體（Middleware、Application Framework）、應用程式（Application）以及網路服務等。

架構分析的重點工作之一，在於了解 Application Framework 與驅動程式（硬體）間的關係，使用各種現有的技術來整合系統，並提出更好的架構設計方案。架構分析的技術屬於「軟體工程」領域，而不是硬體或系統程式領域。但具備驅動程式與硬體經驗，可以幫助工程師找出更好的架構設計方法。

 Android 是軟體框架的技術。台灣廠商得天獨厚的優勢在於，過去積累大量的IC設計、硬體主板、驅動程式與系統軟體經驗，若能補足軟體框架的分析與設計能力，未來競爭力將有很大的想像空間。因此，導入架構分析技術，培養架構分析工程師，理論上能讓我們發揮這項得天獨厚的能力，建立獨特的競爭力。這也是三星（Samsung）正在積極進行的工作。

落實軟體工程教育是根本，必須從教育做起。軟體工程所討論的「軟體框架」就像資訊科學的「位元運算」一樣，屬於通識學科。軟體框架技術，重度依賴這些基礎學科：物件導向（OO）、物件導向語言（OOP）、物件導向分析與設計（OOAD）、分層架構設計、設計模式（Design Patterns）等，這些知識缺一不可。

<strong>軟體框架 (Software Framework ) 的四大特性</strong>

以下針對軟體框架做簡單的說明，以做為研究「軟體框架」的起步點。軟體框架具備四項特性：控制點反轉、預設行為、不可修改性與擴充性。第一、控制點反轉（Inverse of control），整體來看，控制應用程式執行流程的人，是軟體框架，而不是應用程式本身的函數呼叫關係。這與一般循序式的結構化語言（如：C語言）很不同。

第二、預設行為（default behavior），軟體框架本身都有預先設定好的行為。這些行為通常都是預先定義好後，才釋出軟體框架。所謂的「行為」範圍廣大，例如同步呼叫、非同步呼叫、阻塞式I/O等。

第三、不可修改性，這是軟體框架相當重要的觀念。以 Android 為例，其 Application Framework 的程式碼「不能」修改，開發者「不能直接改 Code」。意思是我們下載 AOSP（Android Open Source Project）程式碼後，不能直接修改框架層的程式碼。若直接修改 Android 應用框架程式碼，在編譯時期便會出現警告訊息。直接修改 Android 應用框架程式碼也會造成 API 相容性的問題，可能無法通過 CTS，導致產品無法上市，不可不重視。

最後是擴充性，在不能直接修改軟體框架的前提下，我們如何「加入自已的功能」至 Android 框架呢？正確的方法是以覆載（override）方式進行擴充（extend）。覆載是物件導向領域的基本知識，也是開發 Android 應用框架的重要技術。撰寫「應用程式」補上軟體框架所沒有的功能，也是「擴充性」的另外一個做法。現在正在進行 Android 開發工作的我們，具備這四個基本觀念了嗎？從以上的介紹便能發現，未來如果要在 Android 應用框架開發上有所突破，軟體工程教育就必須要落實，畢竟這是軟體開發的基本學科。]]></description>
         <link>https://www.jollen.org/blog/2011/06/android-software-engineering-education.html</link>
         <guid>https://www.jollen.org/blog/2011/06/android-software-engineering-education.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Wed, 29 Jun 2011 23:24:40 +0800</pubDate>
      </item>
            <item>
         <title>台灣，軟體意識抬頭了！Android 大功臣與 3個 M 型現象</title>
         <description>文／Jollen Chen（原文刊載於零組件雜誌 2011 年 5 月號）

新一波的產業核心競爭力，有一個最簡單的答案，那就是「軟體」。軟體不只是「程式碼」與「技術」的代名詞，而是代表了整個新時代，從產品面來看，就是「創意」、「社群」與「網路」。「軟體提升附加價值」、「軟體才是產品的核心」等口號，不再是「下一波」產業的趨勢，而是「現在進行式」了。

近期台灣許多大廠，不約而同，大舉招募「軟體工程師」，說明軟體反客為主，將在「硬體成本競爭」時代自創格局。從「人力資源」的角度來看，經營教育訓練市場多年，最近也發現到許多過去沒有的有趣現象：人才移動很M型，自有開發能力很M型，建立軟體能力意識很M型。

第一、要人、找人 vs 找不到人。這是過去幾年沒有過的現象，可以說是「幾乎所有」的科技廠，都在設法招募優秀的軟體人員，以提升公司的軟體能力。軟體相關的招募需求旺盛，前所未見。從目前開放出來的軟體職務，以及招募現況來看，呈現相當兩極的現象。頂尖人才或即戰力，幾乎都往少數科技大廠移動，更多廠商開放出來的職務則是乏人問津，顯見「軟體」的新時代，公司的號召力、形象、品牌、活力與產品等，才是吸引人才的關鍵。

第二、委外、再委外 vs 自有開發能力。因為Android前景大好，有不少軟體外包的案件，為數可觀；廠商也大舉採取委外方式，「設法解決一些技術問題」。不過，有一點可以讓我們思考的現象是，有軟體開發「丟包」需求的，幾乎都是中小型廠商，甚至是一些二線品牌，從另一個角度來看，我們應該是設法提升自我的軟體開發能量，建立自已的核心價值，委外不再是面對軟體時代的良好解決方案。有能力自製軟體的，大多數都是大型產品公司，或是小型新創軟體公司。台灣品牌找深圳山寨做「代工」，就是很好的例子。

第三、內部軟體人才的培養。從事教育訓練多年，很明顯感受到，這一年公司對內部軟體人員養成的重視。不單單只是從這一年接受內訓委託案的數量來看，更可以從廠商心態轉變來看。很令人感到興奮的是，廠商不但有強烈的「建立軟體團隊」意識，更願意投入資源，培養內部團隊。大部份具規範的廠商，積極採取內部培養的方式建立軟體能力，這與中小型廠商的「委外」做法，大相逕庭。現在的現象是，有人積極培養軟體能力，也有人認為仍必須做成本競爭，軟體只是附件。

最後，這是個人看法。不過是招募、委外或內部養成，都能有效地解決「軟體開發面」的問題，但就像我們知道的，軟體是「網路」、「應用」、「創意」、「社群」、「產品」等多面向的技術，除了開發技術外，產品規劃也是很重要的能力。台灣一些品牌廠的成功經驗，是值得我們借鏡與學習的。從這個角度來看，提升產品經理人對軟體的認知，才能擺脫「硬體思惟」，擺脫「成本、規格與 BOM」的硬體勝利方程式 (Hardware)；有了軟體，應該來做真正的「產品 (Product)」。</description>
         <link>https://www.jollen.org/blog/2011/05/ctimes-2011-5-taiwan-android-software-human-resource.html</link>
         <guid>https://www.jollen.org/blog/2011/05/ctimes-2011-5-taiwan-android-software-human-resource.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 30 May 2011 23:29:21 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Leak 開發解密, #4: Don&apos;t Create Unnecessary Objects in Main Thread</title>
         <description><![CDATA[在 Dev Guide 裡還有另外一個重要的觀念：避免產生不必要的物件。這個觀念真的很重要，如果要讓程式的效能更好，不斷在 Main Thread 裡產生物件是 bad idea。原因是 Garbage Collection。

Android 的 Dalvik 虛擬機在初始化時，會建立 Main Thread。Main Thread 負責眾多任務，其中之一是 Garbage Collection，也就是負責做資源回收，把用不到的物件回收再利用。如果我們不斷在 Main Thread 裡產生物件，Main Thread 很可能忙著幫我們收拾垃圾，當 Main Thread 花費太多心思幫我們撿垃圾，處理其它工作的效率就會變差，例如：UI 事件處理。

所以，這告訴我們一個重要的觀念：「不要產生不必要的物件」，難怪 Dev Guide 在講「Designing for Performance」時，把這個觀念放在首位。有些 Class 在設計時，提供了多種 instantiate（實例化）方法，以 android.os.Message 為例，我們可以這樣做：

<blockquote>Message msg = new Message();</blockquote>

也可以這樣做：

<blockquote>Message msg = Message.obtain();</blockquote>

透過 Message.obtain() 做實例化，可以重用先前的 Message object：

<blockquote>Return a new Message instance from the global pool. Allows us to avoid allocating new objects in many cases. (--Android SDK Reference)</blockquote>

如果要在 Main Thread 裡以 Message 方式處理「按鍵事件」，較好的寫法就是：

<pre>
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		...
		mHandler.sendMessage(Message.obtain()); // 可避免產生不必要的物件
		...
	}
</pre>
	
要避免，也不要在 Main Thread 裡產生不必要的物件。

<em>Android Leak 開發解密系列，整理自 Jollen 過去在 Android 領域的經驗，以「Do & Don't Do」形式分享一些重要但常被略的開發觀念。轉載請全文引用，並註明出處 (Jollen's Blog, <a href="http://www.jollen.org/blog">http://www.jollen.org/blog</a>)，謝謝您對數位內容著作權的支持。</em>]]></description>
         <link>https://www.jollen.org/blog/2011/05/android-leak-3-dont-create-unnecessary-object-in-main-thread.html</link>
         <guid>https://www.jollen.org/blog/2011/05/android-leak-3-dont-create-unnecessary-object-in-main-thread.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 29 May 2011 23:56:54 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Leak 開發解密, #3: Don&apos;t Access the Android UI toolkit at Worker Threads </title>
         <description><![CDATA[這是 Android 官方「Dev Guide」裡提到的重要級觀念，但經常被忽略，因此在這裡做個簡單的說明。

許多 Android 應用程式經常發生「undefined」與「unexpected」的錯誤，很多錯誤的起因都是違反了這個觀念。在 Android Dev Guide 裡提到，每一個 Android process 都有一個「UI thread」負責處理 UI 事件，為了避免 ANR，應用程式開發者應該建立 Java thread 來執行像是 long operation 等的工作，由應用程式自行產生的 thread，稱為「worker thread」。Worker thread 有時也稱為 secondary thread 或 child thread。但問題發生了，一些應用程式在 woker thread 裡操作 Android UI toolkit。

所謂的 Android UI tookit 就是 android.widget 與 android.view 二個 package。以下是 Dev Guide 上的範例片斷：

<pre>
public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}</pre>
(Source: Android Dev Guide)

範例中的 mImageView 物件就是 android.widget.ImageView。這是不對的寫法。根據 Dev Guide 上的說明，採用以下的方式可修正此問題：

<ul>
<li>Activity.runOnUiThread(Runnable)</li>
<li>View.post(Runnable)</li>
<li>View.postDelayed(Runnable, long)</li>
</ul>

使用 View.post() 將 'Runnable' 放到 message queue 裡即可解決此問題。Message queue 裡的 'Runnable' 會在 UI thread 上執行。

<em>Android Leak 開發解密系列，整理自 Jollen 過去在 Android 領域的經驗，以「Do & Don't Do」形式分享一些重要但常被略的開發觀念。轉載請全文引用，並註明出處 (Jollen's Blog, <a href="http://www.jollen.org/blog">http://www.jollen.org/blog</a>)，謝謝您對數位內容著作權的支持。</em>]]></description>
         <link>https://www.jollen.org/blog/2011/05/android-leak-3-dont-access-android-ui-toolkit-at-worker-threads.html</link>
         <guid>https://www.jollen.org/blog/2011/05/android-leak-3-dont-access-android-ui-toolkit-at-worker-threads.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 01 May 2011 22:36:40 +0800</pubDate>
      </item>
            <item>
         <title>矽島論壇(2011.04)，Honeycomb到位：Android成為平板作業系統的關鍵特色</title>
         <description><![CDATA[<h2>Honeycomb到位：Android成為平板作業系統的關鍵特色</h2>
<h3>多項重要技術的推出，讓Honeycomb成為真正的平板電腦作業系統。</h3>

文／Jollen Chen（原文刊載於零組件雜誌2011年4月份）

一場iPad 2的發表會上，賈伯斯以「copycats」來形容Android平板電腦大軍，著實給了一記當頭棒喝。不過這也是事實。在iPad取得大成功後，Android大軍很快地轉向了平板電腦市場。原本定位在智慧型手機的Android 2.0作業系統，也開始進行專門針對平板電腦的版本開發。如今，Android 3.0平板電腦終於正式開賣了，讓我們從技術角度來看，讓Android成為真正平板作業系統的是哪些關鍵特色。

首先，不得不提的重要技術就是「多核心支援」，這是Android 3.0的重要特色，也是Android首次支援多核心架構。Android 3.0在Dalvik VM與Bionic均做了修改，讓Android能開始支援多核心，讓Android在雙核心的Cortex-A9處理器上可以有更好的表現。

第二，新的UI框架，這是Android 3.0的重點。Android 3.0開發的目的是為了支援較大的螢幕，例如：平板電腦。因此，Android 3.0在UI框架方面做了大幅度的修改。例如：加入了Fragments功能。Fragments，這個新的設計，可以將Activity切割成不同的subcomponent，可應用在需要「multipane」的UI開發上。在iPad上也常見此類型的應用程式。

第三，Animation與Clipboard也是Android 3.0的更新重點。為了讓UI操作有更佳的體驗，Android 3.0加入了全新的動畫框架。針對「平板」的使用特性，Android 3.0加了新的Clipboard框架，即剪貼功能。從產品的角度來看，Clipboard是平板電腦的一項重要功能，它讓應用開發者可以為自已的應用程式加入基本的編輯功能。過去在中小尺吋的手機產品上，要讓應用程式提供基本的編輯功能並不是很方便，因此手機軟體是以「操作」為主；現在，在大尺吋的Android平板上，已經能加入基本的編輯功能了。

最後，3D運算也是Android 3.0平板的重要功能。除了支援更佳的硬體加速外，Android 3.0還加入了一項稱之為Renderscript的技術。透過Renderscript技術，應用程式開發者可以很方便地以撰寫script的方式進行3D運算；Renderscript被儲存為*.rs檔案，並且透過Android SDK開發工具編譯為bytecode形式，並打包在*.apk裡。

Renderscript是為了高效能的3D rendering與運算所開發的技術。Renderscript的語法非常類似C語言（實際上是C99標準），透過這項技術，開發者可開發更有視覺效果的平板軟體；例如，電子書軟體，可能就會大量採用這項技術。Renderscript的技術其實早在Android 2.1（Eclair）就已存在，當時的Renderscript技術尚未發展更熟，編譯器也是基於acc來發展，因此較沒有被重視。

如今，Honeycomb已經有發展成熟的Renderscript技術，也改用效能較佳的LLVM編譯技術，Renderscript API與相關開發工具也已經公開在Android 3.0 SDK裡，想見未來Honeybom平板電腦將有比Android手機更豐富的視覺效果。

另外，Android 2.3/3.0的區隔，可以從技術面來看。Android 2.x針對中小尺吋螢幕的應用，例如：手機；Android 3.0針對大尺吋螢幕的應用，例如：平板電腦。中小尺吋與大尺吋的分界技術上沒有明顯的界線，從產品的角度來看，一般認為是以7吋做為分界。

因此，使用10吋面板的Android平板電腦，就採用Android 3.0作業系統。 從產品面來看，以單核心為主的產品或中小尺吋裝置，仍會使用Android 2.3；以多核心為主的產品，或大尺吋裝置，將會採用Android 3.0作業系統。未來的產品開發，將是Android 2.3/3.0並行。

另一個值得一提的非技術面議題是，「Android註冊商標的使用權未來可能也會更有規範」。這從Android 3.0將可能「更晚」推出AOSP找到線索。目前，Google採取CTS（Compatibility Test Suite）的做法來規範「Android」註冊商標使用權，未能通過CTS測試的裝置，都不能自稱為Android裝置。這項做法其實是很令人肯定的。CTS可以保障產品的技術品質，沒有通過品質測試的產品，若以Android裝置名義上市，使用者若產生不好的觀感，對Android的名聲也是一種打擊。

Renderscript技術、新的UI框架、新的Animation框架、Clipboard功能、Multipane UI、支援多核心ARM等重要技術，讓Honeycomb成為真正的平板電腦作業系統。]]></description>
         <link>https://www.jollen.org/blog/2011/04/many-important-features-of-honeycomb.html</link>
         <guid>https://www.jollen.org/blog/2011/04/many-important-features-of-honeycomb.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sat, 09 Apr 2011 12:12:14 +0800</pubDate>
      </item>
            <item>
         <title>矽島論壇（2011.03）：解析 Android 今年的重要技術發展</title>
         <description>（原文刊載於零組件雜誌2011年3月）

今年是智慧型手機與平板電腦的大年，重量級的國際品牌，幾乎都已進入這個戰場。除了應用處理器與面板外，還有幾個重要的關鍵零組件，包括：DRAM、FLASH與感測元件，也是觀察要點。軟體部份，近期最令人感興趣的莫過於 Nokia 事件，若 Nokia 確定投靠微軟陣營，對Symbian與 MeeGo的影響力無非是一個重傷害，Android也確定會成為主角。讓我們來「細看」這個主角幾個重要的發展藍圖。

第一、強化Web Application支援性，過去也在本論壇裡介紹過的Mobile Widget也是相同的技術。Mobile Widget是OPhone作業系統提供的特色，從各版本的Android發展歷程顯示，Web Application的支援將是未來重要的發展重心。基於WebView元件所打造的Web Application功能，是未來重要的手機應用軟體技術。

第二、視窗化。Android 2.x作業系統的應用程式，採取「瀏灠式」的架構，也就是每個應用程式的畫面，就像是一張「頁面」。在手機上操作應用程式，就好像在閱讀並切換頁面。針對較大螢幕的產品來說，「視窗式」瀏灠比較能符合過去使用者的習慣，也較為適合「多工式」作業系統。因此，Android3.x作業系統加入了視窗化的架構，讓使用者可以同時操作多個「視窗」而非頁面。

不過，Mac OS的發展趨勢做法有點不同。使用在MacBook Air上的作業系統是視窗式瀏覽，使用在iPad上的作業系統除了採取頁面式瀏灠外，也加入許多「平板專用特色」。現在，這些特色將被移植到MacBook Air產品裡，讓平板專用特色也能在「個人電腦」型的產品上使用。

第三、SDK。產品開發化將提供自有特色的API成為重要特色。在 Android 2.3 SDK 裡，大家都能發現「Samsung Mobile Add-ons」，這是Samsung針對Galaxy系列產品所開發的API，使用這些API能開發自有特色的應用程式。在Android作業系統框架裡加入API，並提供客製化的SDK並非難事。但重點不在於如何製作客製化SDK，而是在「讓API突顯硬體特色」。

第四、Native 化。客製化API目的是呈現產品特色，因此軟硬整合技術是關鍵。客製化API另一個目的是提供給「開發者」，因此API的「意圖」尤為重要。從軟硬整合的角度來看，Android將會有更大幅度的更新，特別是「Native 化」。Android 2.3 在部份硬體單元（Component）做了一些架構調整。隨著 AOSP 程式碼的大幅進步，硬體廠需要加強對 Android HAL 的技術掌握度。

為什麼在過去的 Android OS 裡，HAL 的架構或程式碼如此陽春？簡單講，「就是還沒有發展成熟。」但今年Android將會有大幅度的進展。Android 應用程式存取硬體的做法，大致分為二個路徑：Android Service 與 Native Service。Native Service往上的架構是Application Service，Application Service是一個統稱，目的就是提供API。

最後、封閉源碼。這點是過去不斷討論到的觀念。雖然Google透過AOSP（Android Open Source Project）提供Android程式碼，但有更多的私有程式碼是由各家廠商所發展，因此現象是「更多的實作都是封閉源碼」，AOSP上的程式碼最多只是參考實作。廠商想要更早推出產品，等待AOSP並不是好辦法，因為實作的時間不容易評估，也處於完全被動狀態。Android 在技術與產業的變化有時還真摸不著頭緒，例如：Nokia 過去在 Android 作業框架的「貢獻度」也是榜上有名的，今年將是精采又奇妙的一年。</description>
         <link>https://www.jollen.org/blog/2011/03/android-in-2011-technology.html</link>
         <guid>https://www.jollen.org/blog/2011/03/android-in-2011-technology.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Wed, 30 Mar 2011 23:10:21 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android HAL 技術解析, #9: 理想中的完整架構</title>
         <description><![CDATA[一年多前，「<a href="http://www.jollen.org/blog/android_os/">Android 的 HAL 技術</a>」系列日記闡釋了 Android HAL 的觀念與基本設計方法；在小弟的「Android HAL & Framework: 軟硬整合實作訓練 」訓練規劃中，也對 Android Service 與 HAL 做了完整的介紹。

現在，再度回到 Android HAL 的主題；主要是 Android 2.3 在部份硬體單元（Component）做了一些架構調整。隨著 AOSP 程式碼的大幅進步，硬體廠需要加強對 Android HAL 的技術掌握度，這是十萬火急的工作了。因為，硬體廠唯有具備發展高品質軟硬整合程式碼的能力，才能在 Android 產品開發工作上具備競爭力。

<strong>「Native 化」</strong>

過去在「<a href="http://www.jollen.org/blog/2009/11/android-hal-android-native-service.html">Android 的 HAL 技術, #3: 小探Android Service與Native Service</a>」裡提及 Android 應用程式存取硬體的做法，大致分為二個路徑：Android Service 與 Native Service。在「Android HAL & Framework: 軟硬整合實作訓練 」裡，分別以「紅色路徑」與「綠色路徑」來表示。

在日記「<a href="http://www.jollen.org/blog/2011/01/andriod-23-sensorservice-native.html">Android 2.3 的更新：SensorService 的 Native 化</a>」裡提到的「Native 化」會成為日後 Android 作業系統發展的趨勢之一。「也就是紅色路徑慢慢往綠色路徑移動」。

<strong>理想中的 Android HAL 架構</strong>

要了解這項發展趨勢，可以從理想中的 Android HAL 架構說起，先在腦海裡建立大方向圖（Big Picture）。根據 Android 各版本的發展，可以推測出 Android HAL 將朝向下圖的架構來發展。

這個架構不是憑空想像，事實上也不是 Android 作業系統的創舉。

研究作業系統的朋友可以知道，Hardware Abstraction Layer (HAL) 的觀念已經行之多年了，在許多重量級的 OS 裡都能看到。為什麼在過去的 Android OS 裡，HAL 的架構或程式碼如此陽春？簡單講，「就是還沒有發展成熟。」

<img src="http://www.jollen.org/blog/2011/02/21/a-complete-android-hal-architect.png" />
圖一：理想且完整的 Android HAL 架構

根據我們的研究，理想中且完善的 Android HAL 架構包含四個部份：

<ul>
 <li>Service Layer 屬於 Application Framework 層，提供 Application 存取硬體的「服務」。理論上，這些服務是以 instance 的形式存在。</li>
 <li>Hardware Abstraction Layer 介於 Application Framework 與 Driver Framework 之間，以抽象形式存取硬體。理論上，HAL 層的存在形式有三種 - library (module) / server / communication bus。</li>
 <li>Native Servier 屬於整個 Android 作業系統最底層。理論上，它應該在 HAL 層之下，也是 HAL 不會是我們想像中「隔絕 kernel 的最下層」，存在形式為 daemon。
 <li>Driver Framework 屬於 kernel-space 層，也就是包含在 Linux kernel 裡，「非常 long-term 來看」也可以是其它 OS。理論上，Driver Framework 是以 instance 的形式存在。
</ul>

Service Layer 實作在 Application Framework 層，為了提供更上層（Application）存取硬體而設計，因此，在有些研究上，也稱之為「Application Service」；等同於過去我們所講的 Android Service。Application Service 最多就是提供 API 而已。

這個架構並非一種創新，因為在許多重量級的作業系統裡都能看到這樣的設計，例如：Mac OS X。統一架構，讓硬體存取一致化，實作整齊劃一。不過，以上的觀點都只是理論，目的是推測 Android 在這個部份的發展輪廓。

實務上，看到的程式碼與理論研究總是有點落差。現行的 AOSP 程式碼實作，還是與我們所討論的架構不一樣；至於未來如何發展，只能密切與 AOSP 同步。螢幕上的程式碼，背後總是有許多思惟邏輯，有些能從程式碼推敲一二，更多則是沒有文獻記載，技術研究的樂趣在於發覺這些內涵。]]></description>
         <link>https://www.jollen.org/blog/2011/02/android-hal-complate-architecture-outlook.html</link>
         <guid>https://www.jollen.org/blog/2011/02/android-hal-complate-architecture-outlook.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 21 Feb 2011 21:15:36 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Leak 開發解密, #2: Play Music in Secondary Thread</title>
         <description><![CDATA[根據先前所討論過的觀念，我們把 "play" music 的功能設計在 secondary thread 裡。Android SDK 使用 java.lang.Thread 來建立 thread，使用 secondary thread 來產生 MediaPlayer 的 instance，並撥放音樂。Music Player v2 設計圖如下：

<img alt="musicplayer-v2-threading.png" src="http://www.jollen.org/blog/2011/01/11/musicplayer-v2-threading.png" width="666" height="472" />
圖一：Music Player v2 設計

針對 Music Player 個案，因為 MediaPlayer.create() 是以同步 (Synchronous) 方式準備 MediaPlayer，所以有出現 ANR 的機會。在早期 (2008 年) 的 MediaPlayer 設計中，因為 .create() 的實作上有點問題，遇到比較大的音樂檔時，.create() 花費的時間可能較久，形成一個 long operation。雖然後續版本已經改善這個問題，不過如果我們的資料來源不是檔案 (file) 的話，對 UI 就會有很不好的影響。

因此，採用 secondary thread 的方式來把 Music Player 設計的更好，避免可能產生的問題。這個範例衍生的議題是關於 association 的設計。假設我們想在「撥完音樂後」回報 MusicPlayerUI，我們可以讓 secondary thread 以 callback 方式回報 MusicPlayerUI，不過這不是一個好設計。

Music Player v2 的設計，MusicPlayerUI 成為一個 navigable object，意思是，實作上，我們可能會採取以下的實作（pseudo code）：

Thread thr = new Thread(this);

因為不理想，所以暫時不進行程式碼實作。現在，我們已經將撥音樂的動作放到 secondary thread 裡了，接下來我們會想重構 association 的設計。

<em>Android Leak 開發解密系列，整理自 Jollen 過去在 Android 領域的經驗，以「Do & Don't Do」形式分享一些重要但常被略的開發觀念。轉載請全文引用，並註明出處 (Jollen's Blog, <a href="http://www.jollen.org/blog">http://www.jollen.org/blog</a>)，謝謝您對數位內容著作權的支持。</em>]]></description>
         <link>https://www.jollen.org/blog/2011/01/android-leak-2-play-music-secondary-thread.html</link>
         <guid>https://www.jollen.org/blog/2011/01/android-leak-2-play-music-secondary-thread.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 11 Jan 2011 19:05:11 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Leak 開發解密, #1: Dont&apos; Play Music in UI Thread</title>
         <description><![CDATA[設計音樂撥放器應用程式時，如何正確地進行「Play Music」的動作？在按下 Button 後立即進行音樂撥放，是可行 (work) 的做法，但卻不建議這樣設計。在類似 <em>onButtonPressed()</em> 的 event callback 裡直接撥音樂會有「UI 成本」產生，意思是可能對 UI 反應造成不利影響。

設計一個簡單的 Music Player 界面如下：

<img alt="musicplayer-v1-ui.png" src="http://www.jollen.org/blog/2011/01/10/musicplayer-v1-ui.png" width="320" height="480" />
圖一：Music Player

以 Android SDK 2.3 為例，我們可使用 android.view.View.OnClickListener 以及 android.media.MediaPlayer 設計出「不建議」的音樂撥放器。

<img alt="musicplayer-v1-ood.gif" src="http://www.jollen.org/blog/2011/01/10/musicplayer-v1-ood.gif" width="676" height="327" />
圖二：Bad Design: Music Player v1

<font color="#ff0000">以下以 pseudo code 呈現圖二的實作：</font>

<pre>
public class MusicPlayerUI extends Activity implements OnClickListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
　
        Button playBtn = (Button) findViewById(R.id.playBtn);
        Button stopBtn = (Button) findViewById(R.id.playBtn);
　
        playBtn.setOnClickListener(this);
        stopBtn.setOnClickListener(this);
    }
　
	public void onClick(View v) {
		Access Audio Hardware and get it ready.
		Control Audio Hardware and start music.
	}
}
</pre>

以 MediaPlayer 來實作音樂撥放功能，完整程式碼片斷如下：

<pre>
public class MusicPlayerUI extends Activity implements OnClickListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
　
        Button playBtn = (Button) findViewById(R.id.playBtn);
        Button stopBtn = (Button) findViewById(R.id.playBtn);
　
        playBtn.setOnClickListener(this);
        stopBtn.setOnClickListener(this);
    }
　
	public void onClick(View v) {
		// TODO Auto-generated method stub
		MediaPlayer p = MediaPlayer.create(this, R.raw.test);
		p.start();
	}
}
</pre>

在了解 Android Process 模式後會發現，在 UI thread 裡做撥音樂的動作，會對 UI 反應有不利影響。<font color="#ff0000">以 MediaPlayer.create() 為例，在實例化 MediaPlayer 時，會去讀取音樂檔，並預先將檔案準備好，這個動作由 .prepare() 完成。由於 .prepare() 是同步式的實作，所以在 UI thread 裡不適合進行這項操作。</font>

一般來說，我們不會在 UI thread 裡做控制（control）或硬體存取（hardware access）的操作。我們將利用以下二種方式來設計 Music Player：

1. 使用 secondary thread 執行「撥」的動作
2. 使用 separated process 執行「撥」的動作

Secondary thread 指的是由應用程式本身建立的 thread；由系統自動建立的 thread 稱為 main thread。在設計 iPhone 應用程式，或是撰寫 C# 時，也經常使用到 main thread 與 secondary thread 的觀念。Don't play music in UI thread 可說是通識觀念。Android Leak #2 將說明 how to do。

<strong>Revision history</strong>

* 2011.1.11: 加入一段 pseudo code 輔助說明。根據網友 allstars 的意見，加強 MediaPlayer 例子說明，之前寫得太簡單，造成困擾了，sorry!

<em>Android Leak 開發解密系列，整理自 Jollen 過去在 Android 領域的經驗，以「Do & Don't Do」形式分享一些重要但常被略的開發觀念。轉載請全文引用，並註明出處 (Jollen's Blog, <a href="http://www.jollen.org/blog">http://www.jollen.org/blog</a>)，謝謝您對數位內容著作權的支持。</em>]]></description>
         <link>https://www.jollen.org/blog/2011/01/android-leak-1-dont-in-ui-thread.html</link>
         <guid>https://www.jollen.org/blog/2011/01/android-leak-1-dont-in-ui-thread.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 10 Jan 2011 15:20:11 +0800</pubDate>
      </item>
            <item>
         <title>Dalvik VM 與 JVM 差異比較：Zygote 與 Class Preload</title>
         <description><![CDATA[Android 2.3 框架源碼釋出後，開始有必要對 Dalvik VM 做深入的研究。建議可以由 Dalvik VM 與 Java VM 的差異起步。Dalvik VM 與 Java VM 雖然字面上都是「Java 虛擬機」，但內部設計並不完全相同，存在一些差異。最中最重要的差異，就是 Dalvik VM 加入了 Zygote 的設計。

在過去許多的演講場合，不斷提到 Zygote 的觀念，在 [<a href="http://www.jollen.org/blog/2010/04/android-concept-1-zygote-mode.html">Jollen 的 Android 觀念解析, #1: Zygote Mode</a>] 日記裡也曾簡要提及。Zygote 負責幾項重要的工作：

1. Listening Socket (Forking child process)
2. Preload Resource
3. Preload Class
4. Start System Server
5. Enter Zygote Fork Mode

與 JVM 最大的不同是，Dalvik VM 透過 Zygote 進行「Class Preloading」。意思是，把絕大部份的「Java class file」載入記憶體。Java class file 被打包成 *.jar 檔，Java class file 就是 Java library，提供 Android 應用程式與框架所需的 API。Zygote 所載入的 class file，幾乎包含所有的 API，當然，大部份自已都用不到，因此稱之為「preload」，也就是「預載」。透過 preload，讓 Android 應用程式在載入時，不需要重覆「class loading」的動作，除了加快應用程式啟動速度外，也達到許多效果。

Class preloading 是 Dalvik VM 最重要的特色之一，也是與 JVM 不同之處。Class preloading 是在 Android 裝置開機時進行，可能產生的不良效應之一就是「開機變慢」，不過，已經有一些方法可以解決這個問題。在了解 Dalvik VM 與 Zygote 後，便能開始深入 Dalvik VM 的 heap 原理。]]></description>
         <link>https://www.jollen.org/blog/2011/01/dalvik-vm-jvm-zygote-class-preload.html</link>
         <guid>https://www.jollen.org/blog/2011/01/dalvik-vm-jvm-zygote-class-preload.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 04 Jan 2011 10:34:20 +0800</pubDate>
      </item>
            <item>
         <title>Android 2.3 的更新：SensorService 的「Native 化」</title>
         <description><![CDATA[近期在進行 Android 2.3 的新框架程式碼研究，Android 2.3 在 Platform (Framework) 部份包含了許多重大的更新，其中一個部份就是 SensorService 改寫成 Native Service 形式。在 Android 2.2 以前的框架，SensorService 包含在 SystemServer 裡，實務上，可能也會對 SensorService 做小幅度改寫，以增進效能，或是將 SensorService 獨立成為一個 process。

在 Android 2.3 裡的 SystemServer 已經找不到 SensorService 了，這個重要的 Android Service 被改寫成 Native Service。「如何將 Android Service 改寫為 Native Service」，以及「Native Service」的開發，從 Android 2.3 開始，將成為重量級主題。由於本週即將進行「<a href="http://www.moko365.com/training/android-hal-framework-practice" target="_blank">Android HAL & Framework: 軟硬整合實作訓練</a>」課程，利用元旦假期，也順利完成課程以及教材的更新，將開始著重 Native Service 的講解，並透過實例解說 Native Service 的開發。

由於 Android 2.2/2.3 可能是併行的關係，而非取代關係。因此，Android 2.2 以及 Android 2.3 的學習必要性很高；意思是，最好能由 2.1/2.2 的框架開發開始學習。了解 Android 2.1/2.2 的 SensorService 架構，再對 Android 2.3 的 SensorService 進行了解，除了可比較其設計與實作差異外，也能知道「效能改進之道」。了解過去 SensorService 架構與實作上的不足，以及 Android 2.3 的改寫，解決了什麼問題。

Android 2.3 的 libhardware 沒有太大變動。從 Anroid 2.1/2.2 開始的開發者，可以由 Android 2.3 的 SensorService 做為「更新知識」的進入點。]]></description>
         <link>https://www.jollen.org/blog/2011/01/andriod-23-sensorservice-native.html</link>
         <guid>https://www.jollen.org/blog/2011/01/andriod-23-sensorservice-native.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 03 Jan 2011 16:45:07 +0800</pubDate>
      </item>
            <item>
         <title>Android 軟體開發的思惟與建議 </title>
         <description>（原文刊載於零組件雜誌，2010年12月）

2011年是Android揮軍平板電腦的重要一年。

AP+OS+APP主導產品開發，AP（Application Processor）應用處理器是產品主板（PCBA）的靈魂，主要的功能是用來執行作業系統。OS（Operating System）的重要性在於它提供應用軟體的執行環境，並負責驅動主板上的所有硬體。APP（Applications）則是基本作業系統所撰寫的應用程式。

作業系統有近二十年來，不斷蓬勃發展，並採取社群模式開發的Linux kernel。應用程式部份，有Google提供的AOSP（Android Open Source Project）以及Android API標準。應用程式開發者基於標準API撰寫各式有創意的應用程式，產品開發商或硬體製造商，可基於AOSP的架構以及API標準發展產品。對產品開發商的優點是，基於AOSP架構與API標準所發展的產品，可搭載「現有的 Android 應用程式」。意思是，Android Market或第三方來源的各種Android應用軟體，「很早就為我們的產品準備好了」。

因此，AP+OS+APP的產品公式，可等價於AP+Android Application Framework+Developers。這讓產品開發的思惟很不同，但也可以很傳統，取決定產品本身的定義。以下是幾點「很不同」的想法，提供大家參考指教。

第一、Android Application Framework的開發強調「相容性」。這個相容性並不是傳統上的「硬體相容」或是「舊版本軟體相容」，而是「API相容」。如同上述所提，當一個 Android框架無法開發到API相容時，「很可能多數的現成軟體都無法正常執行」。開發Android產品不是只為了硬體，而是要支援網路上「現成的各種軟體」。消費者可能無法接受一個API不相容的Android產品。大部份的應用開發者都基於標準Android SDK做開發，此時，API不相容的產品，會讓這些應用軟體無法執行。因應這個問題，Google提出了CTS套件，希望廠商開發的 Android 框架與產品都可以通過CTS（Compatible Test Suite）測試。

第二、開發軟體是「設計導向思惟」。寫程式（Coding）並不等於做軟體（Software），寫code可以很straight forward，意思是，大家可以通往直前，不受任何限制地自由發揮，程式碼怎麼寫，很自由心證。但是做軟體就很不同了。以Android框架的開發為例，寫code要考慮架構，要先做設計（OOD），要驗證設計的正確性，同時也要達到重用（Design Reuse）框架設計的要求；所以開發Android框架，是在一套系統化且制式的規模下進行，寫code受到規範。過去硬體商寫code是為了驅動硬體，或驗證硬體，現在要擔綱軟體開發的工作，coding的思惟就要改變。

第三、這是開放平台。開放平台（Open Platform）與開源軟體（Free and Open Source Software）是二個概念。開放平台代表開放API給開發者使用，或是開放Platform Builder供製造商使用，製造商很可能無法取得內部的實作源碼（Implementation source），取而代之的是一個configurable的環境。意思是說，AOSP版本的程式碼很可能永遠都是reference code，廠商自已的implementation也不會公開源碼。Android裡的Launcher是reference Launcher，Android裡的rild也只是reference code；大部份implementation是reference implementation，不是workable或useable code。所以，不能只顧著等候AOSP的釋出，也不能渴望著取得所有的源碼。強化開發能力，動手發展AOSP成為好用的自有版本，才是務實之道。

2011年是Android揮軍平板電腦的重要一年， 要知道製造商在Android的研發儲備能量，這將是重要的觀察指標。</description>
         <link>https://www.jollen.org/blog/2011/01/android-software-development.html</link>
         <guid>https://www.jollen.org/blog/2011/01/android-software-development.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 03 Jan 2011 13:06:18 +0800</pubDate>
      </item>
            <item>
         <title>AMOLED 的戰爭，從產品面看二零一一</title>
         <description>AMOLED 應用在智慧型手機以及移動裝置，明年 (2011) 勢必有大幅的成長，影響力也將更為擴大。面對這個已經成形的趨勢，更值得討論的應該是解決方案，因為 AMOLED 並非不可替代，也不是難以入門；反之，有一點比較另人擔心的是，若經過長時間的媒體曝光，「讓 AMOLED 變成消費者心中的神聖規格」，這才是難以解決的問題。AMOLED 三星公司不是獨家。最關鍵的轉折點在金融海潚時期，因為經費、成本以及產業前景問題，讓這項技術停滯，沒有堅持下去，這是台灣比較可惜的地方。以下是小弟的一些個人看法，請不吝指教，有不合適的地方，也請來函指正。

AMOLED 是 OLED 的一種，適合用在手持裝置，同時也有色彩較鮮艷的優勢，但並不是一種「完美」的技術，當然也有許多缺點；AMOLED 並不神聖；反倒有一點被發展成「必備的手機規格」，成為一種市場行銷名詞。從技術面來看，TFT 與 AMOLED 同樣都是 24-bit 的數位輸出；因此主要的差異在硬體的表現。

從產品規格的角度來看 TFT 與 AMOLED，我們取同一個品牌生產的產品來比較。3.5 吋 TFT 與 AMOLED 的尺吋 (outline) 當然是差不多，高度大約在 86mm 左右，寬度則是 51mm 上下。但厚度則是有比顯的差異，3.5 吋 AMOLED 厚度大約在 1.5 之間，同樣尺吋的 TFT 則是大約在 2.0。因此，設計薄型手機時，初步會選用 AMOLED。亮度方面，這款 3.5&quot; TFT 的單位亮度為 380 (cd/m2)，同品牌尺吋的 AMOLED 則是 300 (cd/m2)，與典型亮度相差不多。

但是對比度就有比較大的差異了。這款 AMOLED 的對比度為 2000， 同品牌尺吋的 TFT 則是 1000。對比度越高，顏色的漸變層次越明顯，這就是為什麼 AMOLED 會讓人感覺到色彩較鮮艷的原因。TFT 的對比度隨著尺吋越大，對比度可能變低，例如同品牌的 7 吋 TFT 對比度只有 700；但同樣 7 吋 AMOLED 對比度則是「破萬」。實際上，目前 AMOLED 的滲透率並不算高，因此，2010 年才是 AMOLED 真正的「元年」。從產品規格的角度來看，戰線將從智慧型手機延伸到平板電腦，特別是採用 7 吋 AMOLED 高鮮艷色彩的平板電腦，這會是重要的決戰點。7 吋 AMOLED 的厚度大約在 1.0 左右，比 3.5 吋的 AMOLED 更「薄」。

另外，供應鍊的關鍵在於，小尺吋 AMOLED 的產能幾乎都由三星開出 (SMD)，台灣的產能初期落差較大。目前 SMD 的月產能為 300 萬片，2011 年中 (Q2)，將達到 3000 萬片，可望解決目前量產不足的問題，各大手機品牌將可望導入至產品。可以確認的是，AMOLED 明年的強烈需求，將從手機延伸到平板電腦，也就是，3.5~4.2 吋與 7 吋的二種主流規格。三星藉由 AMOLED 搶攻智慧型手機市場的戰略已經很清楚了，其它手機品牌是否能取得 AMOLED 供貨，關鍵在 SMD 公司這 3000 萬片的月產能，是否能滿足三星自家需求。7 吋平板電腦市場，今年三星已經開始佈局了， 是否再以 7 吋 AMOLED 攻城掠地，相當值得觀察。

從這個角度來看，AMOLED 的關鍵似乎不在技術問題，而是產能問題。「今年六月宏達電曾推出一款手機Legend，就是採用AMOLED面板，上市時雖然頗受好評，但才賣了不到三個月就面臨斷貨問題，幕後的操控者就是三星。 『三星對我們總量管制！』一位宏達電主管氣憤表示。」（引述自商業周刊）。台灣要急起直追的機會是非常高的，友達就擁有 AMOLED 的相關專利。戰略上可以採取自創規格的方式，而不是「跟進規格」，4.2 到 4.8 吋的 WVGA 可能是一個不錯的規格，因為在 TFT 規格上，這樣的尺吋已經慢慢被消費者接受，喜愛度也慢慢提升。</description>
         <link>https://www.jollen.org/blog/2010/12/amoled-war.html</link>
         <guid>https://www.jollen.org/blog/2010/12/amoled-war.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">產品開發</category>
        
        
         <pubDate>Thu, 16 Dec 2010 17:54:05 +0800</pubDate>
      </item>
            <item>
         <title>Android 2.3 終於來囉：新的多媒體框架、NFC、NativeActivity 等等</title>
         <description><![CDATA[Android 2.3 這次終於如傳聞中的日期釋出了，許多重要的新特色也出現在這個重要的版本裡了。官方網站上有完整的 [<a href="http://developer.android.com/sdk/android-2.3-highlights.html">Android 2.3 Platform Highlights</a>]，網路新聞也有不少的介紹；在這裡簡單聊一下一些看法。

Android 2.3 已經來到 API Level 9，有許多有趣的更新，像是加入了 VoIP、NFC 與 Download Manager，還有強化了遊戲開發的功能。多媒體框架部份也有重要的更新。新的 Multimedia Framework 已經可以完全取代 OpenCore。新產品開發上，已經能去除 OpenCore 並使用 Android 2.3 的 VP8/WebM 新技術，理論上，多媒體的效能以及應用將有大幅度的進展。

在遊戲開發部份，因為有不少的 native code 開發需求，這次 Android 2.3 也加入了 android.app.NativeActivity 幫助遊戲開發者撰寫 native code，例如：存取 OpenSL ES、audio，以及其它 NDK 裡的 native API。Dalvik 也加入了 StrictMode debugging 的功能，對於效能以及記憶體使用的統計分析，相信有很大的幫助。

不過，API 終究是 API，需要應用開發者花費巧思；platform 還是 platform，需要實作硬體驅動程式，以及精緻的軟硬整合工作。過去硬體廠老是被 AOSP 拉著跑，每當新的 AOSP 出現，就要花費力氣進行實作，或是整合至硬體；過去許多製造商已經累積了許多程式碼與實作，只要能做到 API 相容，並符合架構，相信能重用程式碼的機會相當高。因此，理論上，將 Android 2.1/2.2 更新至 Android 2.3 將不再這麼花費力氣，速度也將更快，至少不再像過去，需要幾個月的實作與測試時間。

從產品面來看，三星的 Nexus S 應該是值得期待的，會有許多更先進的軟硬整合技術出現。像是：Nexus S 內嵌 NFC 晶片，是很值得研究的技術。還有像是 Nexus S 的顯示面板，採用三星最強悍的 AMOLED 技術，在戶外也可以有很棒的表現，相信是很重要的亮點：搭配 480x800 畫面解析度的 4 吋面板，很適合用來撥放高清視頻 (HD)。Nexus S 還跟 Google 服務有很好的整合，總之，趕快來買一支吧。]]></description>
         <link>https://www.jollen.org/blog/2010/12/android-2-3-nexus-s-announced.html</link>
         <guid>https://www.jollen.org/blog/2010/12/android-2-3-nexus-s-announced.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Wed, 08 Dec 2010 18:31:56 +0800</pubDate>
      </item>
            <item>
         <title>山寨市場是常規市場：下一波的市場新機會 </title>
         <description>將山寨市場正名為「常規市場」，這種的現象是一種新的社會文化。

2010年又到了尾聲，傳聞中的Android 3.0版本以及Android平板電腦，都將在明年成為科技業的重頭戲之一。將近一年前，在本論壇也提及二個主題：「發展自有Android分支」以及「山寨軍品牌革命：下一個山寨影響力」。根據Google對Android未來的發展策略，以及對廠商的支援政策來看，發展自有Android分支已經成為一項必要的工作。

在山寨軍品牌革命這篇評論裡，提到Android帶來的機會，以及山寨的轉型方向。隨著Android手機以及相關產品的大舉佔市，以及山寨大軍一年多來的努力，我們發現「山寨市場」以及「山寨文化」確實有一些顯著的改變。許多山寨商已經擁有自有的Android分支，但我們從更大的層面來放大觀察「山寨」：山寨改變了社會文化。以下是針對「山寨」所做的一些觀察，以及延續性的討論，請不吝指教。

針對「山寨」這個名詞，許多人都有「一個層面」上的誤解，即「山寨就是盜版」。即先前提到的：在抄襲與非正統的「模式」下所開發與製造的手機，就被暱稱為「山寨機」，意謂拷貝與強取之意。大陸知名山寨觀察家「阿甘」在他的著作「山寨手機幕後真相」裡指出，「山寨機」成為一種「社會文化」。

在阿甘先生的著作裡提出這樣的看法。過去的大型企業，做習慣了「規模經濟」，由於一些因素，這些企業在發展穩定後，便開始「貴族化」，這與山寨文化是非常鮮明的對比。山寨機經過幾年的發展，居然開始影響社會文化，讓山寨起家的「草根階層」也能和以大企業為代表的「精英階段」相互競爭。從產品的角度來看，這是個性化與「私品牌化」的潮流。

由山寨所帶起的社會文化改變，最鮮明的現象就是「個人與團隊」的快速堀起。這點在阿甘先生的著作裡也有觀察。有技術的個人，或是有能力的團隊，掌握了關鍵的資源或技術，因此萌生自立門戶的想法。關鍵的資源包含：市場、人力、商業模式、社會資源等等。單純從技術面來分析，Android帶動巨大的機會，許多優秀的Android開發小公司，都是個人自立門戶下的成果。這就是山寨機崛起的關鍵因素：眾多的個人或小團隊。

因此，把山寨定位為盜版集團，可能是過時的看法了。維基百科、Linux kernel以及山寨機本質本並無差別，他們都是以一種「籠統」、「非正規組織」、「個人」、「螞蟻大軍」等模式所建構出來的；從十多年前開始，透過網際網路的推波助灡與影響，這種新的社會文化就開始形成了。所以，就如同阿甘先生在他的著作裡的看法，「山寨企業將顛覆傳統的競爭法則」；自由軟體或 Android給了技術人員很好的機會。

在十月份的「2010 Android社群平臺開發大會」上，小弟也發表了一個淺見。「山寨市場」或「類山寨市場」，與「正規市場」完全衝突，但山寨機廠商不但國際化也品牌化，許多山寨廠商的產品品質也和正規產品不相上下了，許多更已經「正規化」。因此，不如將山寨市場正名為「常規市場」，這種「非大企業也能為之」的現象是一種新的社會文化，許多山寨廠商都能直接和大公司建立商業關係，這代表精英階層的鬆動；阿甘先生在其著作裡所做的觀察，令人發省。從Android或自由軟體的技術面深入觀察，將角度提升到社會層面，將會發現許多有趣的改變。(原文刊載於「零組件雜誌 2010.11月」）</description>
         <link>https://www.jollen.org/blog/2010/11/china-the-next-wave-of-new-android-market-opportunities.html</link>
         <guid>https://www.jollen.org/blog/2010/11/china-the-next-wave-of-new-android-market-opportunities.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">產品開發</category>
        
        
         <pubDate>Mon, 29 Nov 2010 12:51:13 +0800</pubDate>
      </item>
            <item>
         <title>簡介 Android Camera 三大功能區塊</title>
         <description><![CDATA[近期為客戶進行 Android Camera 的顧問服務，主要的任務是改善 Android 在拍照方面的效能，撰寫了一些相當基本的觀念介紹，在此與大家分享。Android 的 Camera 系統大概念上可分成三大區塊：

1. android.hardware.Camera
2. Camera Service
3. Camera HAL + videodev

其中最核心的 Camera Service 預設是實作在 SystemServer 裡。控制 Camera 硬體的 HAL 部份，目前大多採用 Linux 的 videodev 介面來擷取視訊。以下簡介 Android Camera 的三大功能區塊。

<strong>1. android.hardware.Camera</strong>

屬於 Java 層，Camera 應用程式可透過 Camera.open() 來取得 android.hardware.Camera 的實例化 (instance)，並且可實作 Camera.PictureCallback 介面來取得「照片影像」。Camera.PictureCallback 提供二種照片資料：JPEG Data 與 Raw Data。這部份的細節可參考 Camera.takePicture()。

<strong>2. Camera Service</strong>

Android 的 Camera Service 重要程式碼實作於 libcameraservice.so，對於 Android 平臺的移植者來說，可忽略 Camera Service 的內部細節，直接實作 Camera HAL 即可驅動 Camera 硬體。但對於 Camera Service 的設計，以及實作細節進行一些研究，對於改善現有 reference code 的效率會有一些幫助。關於 Camera Service 的設計細節，留待後續做說明。

<strong>3. Camera HAL</strong>

驅動 Camera 硬體最關鍵的部份。在 Google 官方的 Android Platform Developer's Guide 文件裡指出，平臺移植者可實作 CameraHardwareInterface 來驅動 Camera 硬體。CameraHardwareInterface 是 Android 的 Camera HAL 設計，開發者可參考 Android 裡的 CameraHardwareStub.c 範例，以了解 CameraHardwareInterface 的實作原理。

Camera HAL 裡設計了二個 thread，分別是：preview thread 與 picture thread。Picture thread 在取得影像資料後，以 callback 方式將 JPEG data 或 raw data 往 Java 層傳遞。如上述，Camera 應用程式透過實作 Camera.PictureCallback 取得影像資料。]]></description>
         <link>https://www.jollen.org/blog/2010/11/android-camera-block-diagram.html</link>
         <guid>https://www.jollen.org/blog/2010/11/android-camera-block-diagram.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 25 Nov 2010 20:41:05 +0800</pubDate>
      </item>
            <item>
         <title>Android 產品開發工作談：嵌入式系統技術講求整機開發</title>
         <description><![CDATA[嵌入式系統畢竟是一個需要高度「軟硬整合」的技術工作，這和過去台灣所熟悉的「PC」是很不一樣的工程。過去把 PC 模組化，大家可以分工生產個別模組，組裝後，安裝作業系統以及軟體。把這個思惟用在嵌入式系統開發上，可能會產生一些盲點。

以 Android 作業系統為例，廠商都能取得 reference code，以及 Android 使用許多開放源碼專案的成果，因此技術人員能進行更精緻的技術工作，例如：針對處理器優化編譯器。過去，把硬體做好，再安裝微軟作業系統，是台灣習慣的模式，但廠商無法取得源碼，最多只能使用 API 進行開發，所以直接取得能支援硬體的作業系統，是微軟模式的特色。

近期與許多經理人洽談 Android 產品的合作，發現微軟習慣真是如影隨形，以舊思惟思考新的產品，可能有許多盲點。產品研發的根本是技術，尚未脫離技術工作階段時，把專業的工作交給專業，是最基本的態度與責任。以下是小弟的一些個人看法，希望在此與大家分享，讓專案的成敗由技術決定，而不是身段與心態。請不吝指教。

<strong>嵌入式系統技術講求整機開發</strong>

如同一開始提的，嵌入式系統是非常講求細節的工作，所有的細節是為了讓軟體與硬體有更好的整合性，效能與穩定性的期中考卷，技術工作者必須努力交出及格的成績單。嵌入式系統的技術細節，往往超乎技術人員自已的想像。

過去的 PC & NB 研發工作，努力把硬體做好，用更快的處理器，把作業系統安裝到硬體上。「直接把軟體安裝在硬體上」，不管是硬體工程師，或軟體工程師，都不需要考慮軟硬體間的細節。所以，PC & NB 是一種不必過度講究開發細節的工作。

現在把 Android 放到 ARM 平臺上，開發手機，不但需要把 Android 提供的 reference code 做實作，更要進行許多細節的工作。幾個大細節像是：

1. Android 提供的 reference Launcher 夠不夠用？（穩定性、反應速度等）
2. Android 提供的 prebuilt toolchain 夠不夠用？（符號處理、指令集等等）
3. 開機速度。

還有更多的小細節需要我們努力，例如：PNG24 在 PNG8 的處理有失真問題 (GIF)。不同的硬體，細節不會相同，例如這裡提到的 PNG24 與 PNG8 處理問題。所有的細節是為了讓「整機」有更棒的表現，例如避免 PNG24 不出現失真，這個失真會是肉眼可見的問題，不該被忽略。

我們是一個專注嵌入式系統的研發團隊，開發「整機」讓我們全心在「這個硬體」上處理各種軟體細節。Android 產品也是如此。一開始就定位「整機研發」，因為這是技術的本質，嵌入式系統考慮硬體來做軟體，所以軟體綁硬體，是技術所導致的結果。

在我們 A 機器上表現良好的軟體，直接安裝到 B 機器，我們的軟體可能是 dirty software：達不到好的穩定性，也做不到好的速度，或許只有部份功能可以正常。因此，身為一個專業的技術工作者，當您遇到客戶提出「給我們源碼與硬體設計」，「我們要自已改硬體」，「我們自已改Driver」，「應用程式可以用外包方式處理」，等典型要求時，最好為自已爭取一段時間，靜下心來思考專案的可行性。

請不要把我的 Android framework 軟體直接「安裝」在您的硬體上。

技術學習者也應該更務實，以「Embedded Linux」來說，我們應該有耐心地去了解每個單元的設計、運作原理與架構，因為細節就是在處理這些技術，而不是學習操作或是 how-to。

取得參考設計，在硬體上加入模組，更換零件，把源碼當護身符，「因為樣機都可以跑了、改改東西不難」，是良方還是毒藥，需專業判斷，而不是籠統的認定。例如：2個G-Sensor還是3個G-Sensor，是不同的二件事情。商業周刊第1191期的標題「忘記會做微軟　不然就等死」，看起來聳動，但也真實；但是，我們必須往好處想，因為我們面臨的將不會是技術問題，而是心態問題。《待續》]]></description>
         <link>https://www.jollen.org/blog/2010/11/android-embedded-system.html</link>
         <guid>https://www.jollen.org/blog/2010/11/android-embedded-system.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Wed, 17 Nov 2010 11:41:58 +0800</pubDate>
      </item>
            <item>
         <title>又說 Android 2.3 將透過 OTA 更新了</title>
         <description><![CDATA[「<a href="http://www.androidpolice.com/2010/11/07/oha-team-member-confirms-gingerbread-version-as-2-3-hints-at-dev-nexus-one-versions-receving-an-ota-update-in-the-next-few-days/" target="_blank">OHA Team Member Confirms Gingerbread Version As 2.3, Hints At Dev Nexus Ones Receiving An OTA Update In The Next Few Days</a>」說把 Nexus One 準備好，因為 Android 2.3 將於近日內釋出。根據文章裡的消息來看，最有可能釋出的是 Android 2.3 SDK，時間是在 11 月 11 日。至於將令各界人仰馬翻的 AOSP 目前依然狀況未明。不過總結目前的謠言來看，下一個版本 Gingerbread 是 2.3 而不是 3.0。之前謠傳的 Nexus Two 現在「據說」硬體出了問題，無法如期推出。最近真是太有趣了。

Gingerbread 除了大幅改善使用者介面外，也加入了 WebM 技術，很值得期待；Android 多媒體架構與支援程度，等到 AOSP 釋出後需要了解一下，是否有較大的設計變更。WebM 導入後，Android 的影音串流技術預期將進入新的里程碑。其它像是 Flash 以及 HTML5 等，只能等到 AOSP 釋出才知道了。 ]]></description>
         <link>https://www.jollen.org/blog/2010/11/android-23-ota.html</link>
         <guid>https://www.jollen.org/blog/2010/11/android-23-ota.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">產品開發</category>
        
        
         <pubDate>Wed, 10 Nov 2010 00:21:23 +0800</pubDate>
      </item>
            <item>
         <title>最近的二個八卦：Android 2.3 與 Nexus Two</title>
         <description><![CDATA[最近有關 Android 的八卦消息還挺多的。Gingerbread 是 Android 3.0 的代號，不過上週的小道消息指出，Gingerbread 應該是 Android 2.3 而不是 3.0。這應該不是 Android 在玩版本遊戲，未來 Android 的版本如何釋出與命名，關係重大。

Android 即將由手機延伸至平板電腦，這個重要的版本里程碑落在哪一個數字，現在眾說紛云，不過很可能是 Android 3.5。如果是這樣，Android 2.3 應該只是 2.2 版的一個 update，算是對手機用途的 Android 版本有個交待。

跟 Android 2.3 有關的另一個八卦是，十一月初，Google 很可能將發表 Nexus Two，並交由 Samsung 代工。延續先前的看法「<a href="http://www.ctimes.com.tw/news/ShowCols.asp?O=201007011401114982" target="_blank">Nexus One是下架 還是承先啟後</a>」，Android 在線上市集（Android Market）已經有了一些重要的改變，理論上應該有個 2.x 的版本，擔負整合新授權機制與產品展示的工作；很期待 2.3 能達陣。

以產品面來看，Android 2.3（如果真有其事）與 Samsung 合作，很可能是在 AMOLED 以及 Touch UI 部份尋求突破；以 Nexus One 的精神來看，Nexus Two 的展示意味應該也很濃厚。「如果」Nexus Two 真的投向三星的懷抱，展示「硬體設計」的可能性又高了一些。這是從技術發展的角度「做出的猜想」，純屬個人憶測，不一定會中獎。]]></description>
         <link>https://www.jollen.org/blog/2010/10/android-gingerbread-nexus-two.html</link>
         <guid>https://www.jollen.org/blog/2010/10/android-gingerbread-nexus-two.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">產品開發</category>
        
        
         <pubDate>Fri, 29 Oct 2010 23:32:19 +0800</pubDate>
      </item>
            <item>
         <title>長江三號 Android 手機來囉：來自瑞芯微電子（Rockchip）與中一無線（ZinnWireless）</title>
         <description><![CDATA[前陣子幫大家簡略介紹過 [<a href="http://www.jollen.org/blog/2010/08/zinn-mobi-android-gsm-phone.html" target="_blank">長江二號</a>] Android 智能手機，由於一些原因，長江二號將不進行量產，轉而全力投入長江三號的研發；現在再來簡略地向大家更新一下狀態。我們的 [<a href="http://www.google.com/search?q=%E9%95%B7%E6%B1%9F%E4%B8%89%E8%99%9F+android&hl=zh-TW&client=firefox-a&hs=IHD&rls=org.mozilla:zh-TW:official&source=lnt&tbs=qdr:m&sa=X&ei=dTyqTLWWHsbpOdCrockM&ved=0CA4QpwU" target="_blank">長江三號</a>] 智能手機，在八月時，大致有了原型；第一批大量測試機，也在九月中陸續出廠，目前正在進行密集的產品與測試工作。

長江三號是由瑞芯微電子（Rockchip）與中一無線（ZinnWireless Inc）共同推出的 Android 智能手機，也是第一支採用中國芯的 Android 手機。過去曾在香港電子展（今年4月份），以及德國 IFA 會場現身過：

<a href="http://cnbeta.com/articles/121197.htm" target="_blank">[多图]3D+3G+Android 瑞芯IFA2010火爆直击</a>

其實，網路上已經有許多大大小小的相關消息了，只是因為機器還在研發階段，「官方」的部份幾乎沒有任何直接的公開訊息。想先認識我們的長江三號（三仔）的朋友，可以從下面的報導開始：

<a href="http://www.cnbeta.com/articles/118130.htm" target="_blank">瑞芯微RK28新机 - 中一无线iZiNN CJ-3(长江3号)抢先报道</a>

或是先從我們簡陋的小網站開始 [<a href="http://www.Zinn.Mobi" target="_blank">Zinn.Mobi</a>]，很抱歉，大家實在都太忙了，我們近期會儘快為大家更新「三仔」的消息。更多有關長江三號的消息，接下來，我們都會發佈到 zinn.mobi 的網站上；不過，小弟的 Blog 也會做一些簡單的轉載以及說明。

目前長江三號將進入量產，以及商務階段；歡迎閱讀 Engaget 的文章，簡單了解一下量產機的狀況：

<a href="http://www.cnbeta.com/articles/122021.htm" target="_blank">N8的系统换成Android如何：中一长江3号量产开盒</a>

再跟大家宣佈一則好消息。由於長江三號將進入量產，以及商務階段，因此，我們也會陸續進行一些公開活動。首先，在10月11日的北京通訊展上，我們的合作伙伴「瑞芯微電子」將現場展示長江三號產品機，歡迎大家前往會展，給我們支持喔。

接著，在10月21-22舉辦的「<a href="http://www.moko365.com/2010-android/" target="_blank">2010 Android 平臺社群開發大會</a>」活動上，ZinnWireless（深圳中一無線軟件有限公司）也會親自為大家介紹長江三號，中一無線的經營團隊，也會到場與大家交流。介紹機器的部份，將由小弟來操刀介紹，不嫌棄的朋友請給我們指教與意見。小弟也會簡單說明，ZinnWireless 的 Android 技術發展構想。ZinnWireless 將開始供應全球第一款 Android PCBA 量產解決方案，並「結合技術咨詢以及教育訓練」，請大家拭目以待囉。

目前，大家的心力都在研發以及產品工作上，有關長江三號的最新消息，要先請大家從網路上收看了；後續再幫大家發佈官方的「實機展示影片」。]]></description>
         <link>https://www.jollen.org/blog/2010/10/rockchip-zinnwireless-android-cj-3.html</link>
         <guid>https://www.jollen.org/blog/2010/10/rockchip-zinnwireless-android-cj-3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">產品開發</category>
        
        
         <pubDate>Tue, 05 Oct 2010 04:21:59 +0800</pubDate>
      </item>
            <item>
         <title>平板電腦熱潮來臨，Android 不缺席，出自深圳的 Android Pad</title>
         <description><![CDATA[深圳特產再一樁，道地的 Android 平板電腦。外觀不錯，使用起來還算及格。這款 Android Pad 的使用手感仍不及 iPad，但是從 Android 的技術角度來看這個板子，還是很有趣的。這是在深圳出現的 Android 平板電腦，左邊是真正的 iPad，右邊則是 Android 平板電腦的包裝。

<img src="http://jimg.jollen.org/2010/09/apad-1.jpg" alt="Android PAD" />

包裝盒已經說明這款產品的身世了，這是「Android 平板電腦」，外觀有點像 iPad，事實上根本是一模一樣的。

<img src="http://jimg.jollen.org/2010/09/apad-2.jpg" alt="Android PAD" />

Android 平板電腦比 iPad 更小巧，經過一天的隨身使用，發現其實攜帶性很不錯，放在背包裡頗為方便，而且重量較輕。最重要的面板與觸控面板部份，不需要再多說了，明眼人一看就知道囉。iPad 深邃鳥黑的顯示屏，帶來的是最佳的視覺表現，這不是 Android Pad 這個有點白汒汒的白內障顯示屏可以抗衡的。

正版 iPad 的 LED 背光顯示可以說是它最不可或缺的特色。9.7 吋的 LED 背光 IPS 顯示屏，支援超廣視角，以及 multi-touch，讓 iPad 的視覺與操作達到很棒的同步。iPad 讓大腦、視覺與手指操作達到一種匪夷所思的同步，Android 平板大概還要花很長一段時間，才能達到這個境界。

<img src="http://jimg.jollen.org/2010/09/apad-3.jpg" alt="Android PAD" />

從側面來比較，厚度非常接近；特別的是在材質方面，觸感還不錯，比起一些厚重的塑膠感平板電腦，這塊 Android 平板的整體質感確實不錯，目前為止還另人滿意。

<img src="http://jimg.jollen.org/2010/09/apad-4.jpg" alt="Android PAD" />

再近看，如果只看這個角度，其實已經可以達到以假亂真的效果了。

<img src="http://jimg.jollen.org/2010/09/apad-5.jpg" alt="Android PAD" />

外觀設計流著 iPad 的圓滑設計血液，再加上又是深圳出品，早早就被貫上「山寨 iPad」的雅號了。山寨 iPad 的喇叭設計在底部，試用後「音質」可以算得上及格。

<img src="http://jimg.jollen.org/2010/09/apad-6.jpg" alt="Android PAD" />

這款 Android 平板電腦，使用的是 Rockchip 的處理器（RK2808）。目前，已經有幾家深圳的 Android 研發公司，準備推出搭載 Rockchip 新一代處理器的 Android 平板電腦了；讓我們拭目以待吧。
]]></description>
         <link>https://www.jollen.org/blog/2010/09/android-pad-rockchip-rk2808.html</link>
         <guid>https://www.jollen.org/blog/2010/09/android-pad-rockchip-rk2808.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
                  <category domain="http://www.sixapart.com/ns/types#category">產品開發</category>
        
        
         <pubDate>Sat, 18 Sep 2010 23:49:34 +0800</pubDate>
      </item>
            <item>
         <title>Android Telephony &amp; RIL: 通訊系統架構與實作，課後小記</title>
         <description><![CDATA[Android Telephony 以及 RIL 是一個重要的議題，內容主要在談論 Android 的電話系統框架，以及 RIL (Radio Interface Layer) 的實作。由於 Android 提供的 RIL 幾乎沒有實作 Modem 端的功能，也缺乏像是 Data Multiplexer 的實作，因此，研究 Telephony 以及實作 RIL 成為了 Android 手機開發的關鍵技術。

在經過一段相當長時間的規劃與調整後，終於在日前成功開設「Android Telephony & RIL: 通訊系統架構與實作」課程，本程也在9月12日順利結訓。這是截至目前為止，在參與過的課程規劃案中，技術複雜度較高的題目。

課程內容除了採集過去開發 Android 手機的經驗外，也將 Telephony & RIL 做了很完整的研究，目標是以深入淺出方式，介紹這個有意思的主題。希望這門課程，能協助學員開發 Android 手機的通訊功能。

<img src="http://images.moko365.com/images/coursegallery/tpe-telephony-20100912-1_b.jpg" />
圖：GPRS 上網 (Data Multiplexer) 實作討論

總計4天的課程裡，對 Telephony Service、PhoneProxy、PhoneInterfaceManager、RIL、Phone Service 等重要的通訊系統設計做了全面的解說，並以產品開發的實務經驗進行實作討論，將複雜又龐大的 Android Telephony 框架以及 RIL (Radio Interface Layer) 架構，做了整體的分析。

實作討論部份，針對實務面常見的實作議題做了許多討論，例如：

1. 實作 Android 未完成的 USSD (簡碼服務) 功能
2. 加入 SetMute 音量開關功能至 Android 通訊系統
3. Mux 實作 (資料多工) 討論、GPRS 上網、PDP
4. Timed callbacked 以及 synchronous AT I/O stream 討論等

此外，課程也將通訊系統使用到的架構設計觀念做了很清楚的說明；課程也將 Android 通訊系統使用到的 Design Pattern 做了整理，例如：Abstract Factory Pattern、Factoy Method Pattern、Proxy Method Pattern 以及 Singleton Pattern 等，都放入課程裡做說明。期望能告訴學員，Android 的開發最好以「設計再實作」的角度出發。]]></description>
         <link>https://www.jollen.org/blog/2010/09/android_telephony_ril.html</link>
         <guid>https://www.jollen.org/blog/2010/09/android_telephony_ril.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Mon, 13 Sep 2010 00:32:54 +0800</pubDate>
      </item>
            <item>
         <title>Nexus One 晉升 Android Developer Phone 3</title>
         <description>Nexus One 雖然在日前停止網路銷售，不過，根據幾天前在的 Android Developer Blog 上的一則新聞來看，Nexus One 榮獲「Android Developer Phone」頭銜。也就是，雖然 Nexus One 下架了，但改以 ADP3（Android Developer Phone 3）的名稱對開發者進行銷售。第一代 Android Developer Phone 就是由 T-Mobile G1（aka HTC Dream）換名而來，並且做了解鎖動作；Android Developer Phone 2（aka G2 or HTC Magic）於去年 11 月推出，同樣也是 Unlocked Phone。ADP3 則是將 Nexus One 下架上架再解鎖（Unlocked）。

這是 Android 開發者的一大福音（除了它的價格之外），Google 總是提供好用的開發者手機（Developer Phone）。ADP3 的購買方法還是一樣，需要 Android Developer 帳號。</description>
         <link>https://www.jollen.org/blog/2010/08/nexus-one-became-adp3.html</link>
         <guid>https://www.jollen.org/blog/2010/08/nexus-one-became-adp3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 10 Aug 2010 23:55:20 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Booting 解析, #3: 製作 Android Bootchart</title>
         <description><![CDATA[前一則日記提到的 Bootchart 是典型的開機量測工具，主要能進行開機過程以及開機時間的量測。由於 Bootchart 的原理是取代 init process 或是內建在 init process 裡，所以只能取得 initial script 的開機過程報告。不過，這已經很有幫助了。

<strong>關於 Android Bootchart</strong>

以下是一份使用 Bootchart 所製作的 Android 開機流程圖。過去有一些以 C 重寫 Bootchart 的專案，而 Android 也有一份 C re-implementation，放置於 [<a href="http://android.git.kernel.org/?p=platform/system/core.git;a=blob;f=init/bootchart.c;h=f72fcaaca06ea42e077ea984017dccbdfeb8a38c;hb=35237d135807af84bf9b0e5b8d7f8633e58db6f5" target="_blank">system/core/init/bootchart.c</a>]。由此可知，Android init 已經內建一份 C re-implement 的 Bootchart。

<a href="http://www.jollen.org/blog/2010/07/28/bootchart-2.png" target="_blank"><img alt="android bootchart" src="http://www.jollen.org/blog/2010/07/28/bootchart-2.png" width="640" border="0" /></a>
圖一：使用 Bootchart 製作的 Android 開機流程圖

以下說明如何製作 Android Bootchart。

<strong>1. 編譯 Android Bootchart</strong>

Android 的 init process 雖然已內建 Bootchart，但編譯系統預設並不會將 Bootchart 編譯至 init 裡。因此，需要重新編譯 init.c 才能加入 Bootchart：

<pre>
jollen@android:~/try/mokoid_elcair-20100511$ touch system/core/init/init.c
jollen@android:~/try/mokoid_elcair-20100511$ . build/envsetup.sh 
including vendor/aosp/vendorsetup.sh
jollen@android:~/try/mokoid_elcair-20100511$ m INIT_BOOTCHART=true PRODUCT-dma6410xp-eng -j 8
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=2.1-update1
TARGET_PRODUCT=dma6410xp
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=ECLAIR
============================================
</pre>

完成後，使用 Android 模擬器開啟製作好的 image file。

<strong>2. 設定 Bootchart Timeout 時間</strong>

要讓 Bootchart 在「下一次開機」時開始運作，以進行取樣並紀錄開機過程，需要指定 Timeout 時間：

<pre>
$ adb shell 'echo 120 > /data/bootchart-start'
$ adb shell 'mkdir /data/bootchart'
</pre>

重新啟動模擬器。對 /data 目錄進行的變動，「可以」寫回 userdata.img 裡。這裡指定 Timeout 時間為 '120' 秒；實際測試時，可以視情況自由調整。

<strong>3. 取得 Bootchart 紀錄檔</strong>

在 Android 系統裡的 /data/bootchart 取得開機紀錄檔：

<pre>
linux@android:~/android/mokoid$ adb shell
# ls -l /data/bootchart
-rw-rw-rw- root     root          389 2010-07-28 11:06 header
-rw-r--r-- root     root            0 2010-07-28 11:06 kernel_pacct
-rwxr-xr-x root     root       589824 2010-07-28 11:08 proc_diskstats.log
-rwxr-xr-x root     root      2293760 2010-07-28 11:08 proc_ps.log
-rwxr-xr-x root     root       196608 2010-07-28 11:07 proc_stat.log
# 
</pre>

接下來，必須取出這幾個檔案，並製作成漂亮的統計圖檔。貼心的 Android 已經幫我們準備好一個 script 檔了，因此，先切換到 system/core/init 目錄下，直接執行 grab-bootchart.sh：

<pre>
linux@android:~/android/mokoid$ cd system/core/init/
linux@android:~/android/mokoid/system/core/init$ ./grab-bootchart.sh 
7 KB/s (389 bytes in 0.048s)
1439 KB/s (405771 bytes in 0.275s)
2014 KB/s (3861856 bytes in 1.872s)
1936 KB/s (979391 bytes in 0.493s)
look at bootchart.tgz
</pre>

最後的統計報告存放於 /tmp/android-bootchart/bootchart.tgz。

<strong>4. 製作精美 Bootchart 報告</strong>

Bootchart 包含一個以 Java 寫成的圖表製作工具，因此，還是必須取得原始的 Bootchart 套件。在 Ubuntu 環境下，可以用 apt 直接安裝：

<pre>$ sudo apt-get install bootchart</pre>

接著，將 bootchart.tgz 製作成圖檔：

<pre>$ java -jar /usr/share/bootchart/bootchart.jar /tmp/android-bootchart/bootchart.tgz
Parsing /tmp/android-bootchart/bootchart.tgz
Wrote image: ./bootchart.png
</pre>

最後，得到如圖一的精美報告。接下來的工作，就是對 Bootchart 的內容進行分析。

<strong>延伸閱讀</strong>

2010.07.28: <a href="http://www.jollen.org/blog/2010/07/jollen-android-booting-column-1.html">Jollen 的 Android Booting 解析, #1: 整體開機流程</a>
2010.07.29: <a href="http://www.jollen.org/blog/2010/07/jollen-android-booting-column-2.html">Jollen 的 Android Booting 解析, #2: 關於開機的評估</a>
分享您的 Android Bootchart 筆記：<a href="http://jollen.org/wiki/Build_Android_Bootchart">Build Android Bootchart</a>]]></description>
         <link>https://www.jollen.org/blog/2010/08/jollen-android-booting-column-3.html</link>
         <guid>https://www.jollen.org/blog/2010/08/jollen-android-booting-column-3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 03 Aug 2010 11:15:46 +0800</pubDate>
      </item>
            <item>
         <title>Android GSM Phone：長江二號 (CJ-2) 開機囉</title>
         <description><![CDATA[執行 Android 2.1 的智能手機「長江二號」開機囉。目前使用起來還算順暢，雖然 Engineering Build 的開機時間有點長，不過能看到 Linux 小企鵝，是一件興奮的事情。

<object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/CakdoDHGMRM&amp;hl=zh_TW&amp;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/CakdoDHGMRM&amp;hl=zh_TW&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object>

長江二號（CJ-2）是使用 Rockchip RK2808 的 Android GSM 智能手機，目前正在測試中的「長江三號」是令人更興奮的版本。長江二號的團隊（Zinn.Mobi）正在很努力地催生三號中，希望 3G 版本也能儘快與大家見面。]]></description>
         <link>https://www.jollen.org/blog/2010/08/zinn-mobi-android-gsm-phone.html</link>
         <guid>https://www.jollen.org/blog/2010/08/zinn-mobi-android-gsm-phone.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 02 Aug 2010 13:25:22 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Booting 解析, #2: 關於開機的評估</title>
         <description><![CDATA[針對「開機過程」的評估，可以採取個別擊破的方式；針對三個不同的開機階段，分別進行開機過程的評估。評估開機過程的典型做法，當然是測量「開機時間」。

<strong>確認訴求</strong>

費勁進行開機時間的評估，最首要的目的當然是「快速開機」，想辦法讓開機速度加快。策略上，因為手機是一種重視使用者經驗的產品，所以「儘早顯示桌面環境」就是一個好想法。採用「一大堆」非同步的做法，可以達到很不錯的效果。簡單來說，儘速顯示桌面環境的目的，就是造成開機很快的「假象」。

因此，快速開機在智慧型手機產品端，也可以歸類到 User Experience 主題。

<strong>確認方向</strong>

OS-Level 的部份，包含 Linux kernel 本身的開機時間測量，在此先行略過。針對 Android-Level 的開機時間測量，可以採用廣受歡迎的工具 [<a href="http://www.bootchart.org/" target="_blank">Bootchart</a>] 來製作；對使用者來說，進入 Zygote Mode 時，已經是處於桌面環境下了，因此理論上也能先略過這個階段。

<strong>關鍵部位 (Critical Parts)</strong>

<img src="http://www.jollen.org/blog/2010/07/28/android-booting-2.gif" />
圖一：由 Android-Level 切入、尋找議題

如圖一。綜合上述，對 Android-Level 的開機過程進行評估，就是我們的首部曲，也是第一個研究方向。傳統的評估方式，是測量其開機時間；接下來，將會以知名的 Bootchart 來進行這項工作。]]></description>
         <link>https://www.jollen.org/blog/2010/07/jollen-android-booting-column-2.html</link>
         <guid>https://www.jollen.org/blog/2010/07/jollen-android-booting-column-2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 29 Jul 2010 21:12:45 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Booting 解析, #1: 整體開機流程</title>
         <description><![CDATA[Android 開機流程，是一個很值得詳細討論的主題；近期，也正在進行相關的技術工作，因此簡單整理一些相關資料，和大家分享。了解「整體開機流程」，是最重要的第一門課。我們將開機劃分為三大階段：

1. OS-Level，由 Bootloader 載入 Linux kernel 後，開始進行 kernel 本身的初始化，並載入 built-in 的驅動程式。Kernel 完成開機後，載入 init process，切換至 user-space 後，結束 kernel 的循序過程（sequence），進入排程模式（process scheduling）。

2. Android-Level，由 init process 開始，讀取 init.rc 並啟動重要的外部程式，例如：servicemanager、Zygote 以及 SystemServer。

3. Zygote-Mode，Zygote 啟動完 SystemServer 後，進入 Zygote Mode，在 Socket 等候命令。隨後，使用者將看到一個桌面環境（Home Screen）。桌面環境由一個名為 [<a href="http://android.git.kernel.org/?p=platform/packages/apps/Launcher.git;a=summary" target="_blank">Launcher</a>] 的應用程式負責提供。

整體開機流程如圖一所示。

<img src="http://www.jollen.org/blog/2010/07/28/android-booting-1.gif" />
圖一：Android 整體開機流程圖

初探 Android 開機技術的朋友，建議可以先行閱讀 init.rc 檔案，並了解 Android 的 init.rc 語法。Android init language 可參考 [<a href="http://pdk.android.com/online-pdk/guide/bring_up.html" target="_blank">PDK</a>] 的說明。

<strong>延伸閱讀</strong>

2010.04.24: <a href="http://www.jollen.org/blog/2010/04/android-initrc-setprop.html">
Jollen 的 Android 系統管理雜記, #3: init.rc 與 setprop</a>]]></description>
         <link>https://www.jollen.org/blog/2010/07/jollen-android-booting-column-1.html</link>
         <guid>https://www.jollen.org/blog/2010/07/jollen-android-booting-column-1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Wed, 28 Jul 2010 14:20:44 +0800</pubDate>
      </item>
            <item>
         <title>文藝復興運動，軟體我能創作</title>
         <description><![CDATA[                              <p>自由，而不是免費啤酒 (註1)。</p>

<blockquote><em>Rinascimento, FOSS or FLOSS (free/libre/open source software) </em></blockquote>

<p>神權至上的時期，被稱為「黑暗時期」，在 14 世紀興起的「文藝復興」運動，打破這個現象。也就是說，黑暗時代，神權至上，可能有很多像是「魔法」的東西，或是很多霍格華茲魔法學校（註：哈利波特的學校）；文藝復興後，很多近代科學開始發展，生活在此時期後的人類，哈利波特稱之為麻瓜（Muggle，指沒有魔法的人）。</p>

<p>在 13 世紀晚期，義大利中產階段因為生活上的富足，開始尋求更優雅與時尚的生活，於是萠生「脫離神權」的想法。知名詩人「但丁（1265-1321）」的出現，被認為是義大利文藝復興時代的開始。於是，「個人主義」，也就是文藝復興運動的重要產物之一，開始發展。許多耳熟能詳的「大師」，也在文藝復興時期開始出現，例如：米開朗基羅（<a href="http://www.epochtimes.com/b5/6/6/21/n1186091.htm" target="_blank"><strike>忍者龜</strike></a>）；天文學、物理學、數學、生物學等，高中時期最令我頭痛的近代科學，有了重要的發展。</p>

<p>用近代的網路現象來比喻，文藝復興運動很像是 Web 2.0。出現很多新現象，例如：個人主義的興起，每個人都可以自由創作，發表成果，這裡的「個人主義」指的是「好的」個人主義，例如：分享、討論、知識交換，而不是自私的這種個人主義。</p>

<p>文藝復興時期，經典創作「聖彼得大教堂」花了 120 年的時間建造，由多位大師共同創作而成，很像是 Web 2.0 時代的「共筆」或是「協作」。Linux kernel 是網際網路時代，社群協作的重要創作之一。今天我們使用的 Android 手機，都是運行於 Linux kernel 軟體之上。</p>

<p>聖彼得大教堂，肯定是背包客必定點名的景點，這是天主教最神聖的地方。聖彼得大教堂花了這麼久的時間才完工，當然不可能是由同一位建築師獨立完成，參與聖彼得大教堂設計的建築師有勃拉芒特、拉斐爾、米開朗基羅、小莎迦洛，這也是聖彼得大教堂的特色；現今，自由軟體界，稱重要且具代表性的 developer 為「大神」。感謝以上四位大神的共同協作，讓我們在 700 年後的今天，能親眼目睹這個偉大的建築，能欣賞到令人嘆為觀止的繪畫與雕刻。</p>

<p>文藝復興運動，就像 Internet/WWW 的出現，以及後來的 Web 2.0 與開放源碼運動（Open Source Movement）一樣，影響全人類的生活與文化發展。近年來，手機也從封閉走向開放，以及「個人創作手機軟體」的運動發展，相信只要再 1~2 年的時間，我們的生活與文化，都會產生很大的改變。</p>

<p>自由軟體，打破「軟體黑暗時期」；每個人都能在手機上創作內容，也將打破「手機黑暗時期」。軟體黑暗時期？據可靠資料，可能指微x時期。</p>

<p>註1：這句話來自於 Richard M. Stallman 大師，對於自由軟體的自由「Free」之 [<a href="http://en.wikipedia.org/wiki/Gratis_versus_Libre" target="_blank">解釋</a>]。</p>
]]></description>
         <link>https://www.jollen.org/blog/2010/07/rinascimento-floss.html</link>
         <guid>https://www.jollen.org/blog/2010/07/rinascimento-floss.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sun, 18 Jul 2010 23:12:49 +0800</pubDate>
      </item>
            <item>
         <title>Android SystemServer 對 Linux 驅動程式程式碼風格的影響，一個簡單的概念</title>
         <description><![CDATA[今天為一家國際手機廠進行 Android 底層相關的內訓，課堂中提到一個概念，就是基於 Android 的 SystemServer 架構，可以藉由「架構面」改善過去 Linux 驅動程式在 Rentrant Code 議題面的程式碼模式。藉由模式的調整，可改善程式碼的效率。

過去，多個 Process 同時（Concurrency）存取同一份 Linux 驅動程式時，若驅動程式進行 Re-scheduling 操作，驅動程式的 Driver Method 就會重覆進入，因此需要考量做同步控制。

若是藉由 SystemServer 的架構，也就是「Single Process 存取驅動程式」，就能簡化重覆進入的議題。如下圖所示。

<img alt="android-linux-driver-model.png" src="http://www.jollen.org/blog/2010/07/08/android-linux-driver-model.png" width="378" height="454" />

這是一個很簡單的模型，在 Middleware 層面，透過「限制應用程式的模式」來達到簡化重覆進入的議題。以 Android 作業系統為例，SystemServer 可以扮演這個「Single Process」的角色。當然，也可以設計成一個獨立的「My SystemServer」，根據對問題的分析來決定要修改 SystemServer，或是建立 My SystemServer。

對應用程式來說，只需要透過 IPC 與 SystemServer 溝通即可。當 SystemServer 需要交付資料（Data）給應用程式（Clients）時，可以採取下列二種方式：

1. Message & Handler
2. 註冊 Listener、由 SystemServer Callback

目前，正在進行「Android HAL & Framework：軟硬整合實作訓練」課程的第三次修訂，將會加入這個主題，以實例介紹 Android 框架如何影響 Linux 驅動程式的程式碼寫作風格，希望能在驅動程式的程式碼優化方面進行討論。]]></description>
         <link>https://www.jollen.org/blog/2010/07/how-android-systemserver-change-code-style-of-linux-driver.html</link>
         <guid>https://www.jollen.org/blog/2010/07/how-android-systemserver-change-code-style-of-linux-driver.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 08 Jul 2010 00:02:36 +0800</pubDate>
      </item>
            <item>
         <title>最近的新玩具，研發中的 Android 手機</title>
         <description><![CDATA[最近有關 Android 的大新聞，大概就是 Froyo 的發佈消息了。有在關心 Android 部落格的朋友應該都已經都看到這則消息了，Android 2.2（Froyo）的程式碼已經正式發佈，除了釋出大家最關心的 Dalvik JIT Compiler 外，也加入了 V8 Javascript Engine。又有工作要做囉。

小弟也順勢發佈一張即將推出的一款 Android 樣機照片，目前這款樣機已進入試產階段，不過，這是一個解決方案平臺，並不是正式發行的終端產品。

<img alt="zinn-phone.jpg" src="http://www.jollen.org/blog/2010/06/30/zinn-phone.jpg" width="253" height="434" />

未來，使用這個平臺，將可以達到一些快速開發的需求。在這款平臺上，也會進行一些 Enhanced API 的開發，以協助廠商開發自有特色的應用軟體。目前正在持續進行產品的規劃，以及後續的開發準備工作。希望能加速技術面的發展，未來才能繼續和大家分享最新進展。]]></description>
         <link>https://www.jollen.org/blog/2010/06/my-new-android-solution.html</link>
         <guid>https://www.jollen.org/blog/2010/06/my-new-android-solution.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Wed, 30 Jun 2010 23:46:33 +0800</pubDate>
      </item>
            <item>
         <title>MeeGo 1.1 展新頁，首屆 MeeGo 研討會將於11月份舉辦</title>
         <description><![CDATA[MeeGo 開始 1.1 版的開發了。

今年二月份才剛宣佈的 MeeGo 作業系統，已經進入 1.1 版的重要階段了。由 Moblin 與 Maemo 二個 software stack 所合成的 MeeGo 作業系統，最初的焦點是放在 Netbook 硬體，主要在提供更好的使用者經驗。

根據 MeeGo 的 roadmap 指出，今年十月將釋出 MeeGo 1.1。新版本的 MeeGo 將會佈署重兵在「Handset」裝置，令人期待，相信會是一個重要的發行版本。另外，手機作業系統整合「Web Runtime」已經是一個大趨勢了，MeeGo 1.1 也不缺席，根據 MeeGo 官方發佈的 Roadmap 來看，MeeGo 1.1 將加入 Web Runtime 功能，更令人引頸期盼。

目前，在 Android 作業系統上發展 Web Runtime 最著名的專案是 JIL 實驗室的「JIL Mobile Widget」技術。筆者過去曾經撰文「<a href="http://www.ophonesdn.com/article/show/184" target="_blank">JIL Mobile Widget: 我的第一堂课</a>」介紹過 JIL Mobile Widget 的概念。目前已經搭載 JIL Mobile Widget 技術的手機平臺，就是知名的「OPhone」作業系統。

「Web Runtime」簡單來說，「就是以 HTML 5 來撰寫手機應用程式」，也就是改變了手機應用開發的模式。例如：「把 jQuery + CSS 拿來寫手機程式」，就是典型的代表。

MeeGo 從六月份開始進入「Open Development Builds」階段，同時，「Handset User Experience Develoipment」也進入開放發展階段。MeeGo 官方也於6月22日宣佈，第一次的 MeeGo 研討會將於今年11月15-17日，一連三天於愛爾蘭舉辦，相信會是重要的一場發佈活動。]]></description>
         <link>https://www.jollen.org/blog/2010/06/meego-get-new-page-started-in-handsets.html</link>
         <guid>https://www.jollen.org/blog/2010/06/meego-get-new-page-started-in-handsets.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 23 Jun 2010 22:44:47 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 系統管理雜記, #4: C 如何取得 property</title>
         <description><![CDATA[上一則日記提到 Android property 的設定。在 Android 作業系統裡，取得 property 是很重要的工作。不管是 Android 框架層（使用 Java 語言），或是 Native 層（使用 C/C++ 語言），都可以看到讀取 property 的程式碼。

以下是一段簡單的範例程式，用以說明如何用 C 來讀取 Android 系統的 property：

<pre><blockquote>/*
 * Copyright (C) 2010 The Mokoid Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
  
#define LOG_TAG "LED Stub"
  
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;
#include &lt;assert.h&gt;
  
#include &lt;jni.h&gt;
#include &lt;mokoid/led.h&gt;
#include &lt;cutils/properties.h&gt;
  
#define HAL_DEFAULT_VARIANT     "default"
  
static const char *variant_keys[] = {
    "ro.hardware",  /* This goes first so that it can pick up a different file on the emulator. */
    "ro.product.board",
    "ro.board.platform",
    "ro.arch"
};
#define HAL_VARIANT_KEYS_COUNT  (sizeof(variant_keys)/sizeof(variant_keys[0]))
  
int main()
{
    int i;
    int status;
    char prop[PATH_MAX];
  
    status = -EINVAL;
  
    for (i = 0; (status != 0) && (i < HAL_VARIANT_KEYS_COUNT); i++) {
        if (property_get(variant_keys[i], prop, NULL) == 0) {
	    continue;
        }
        printf("Your property is: %s = %s\n", variant_keys[i], prop);
    }
  
  return 0;
}</blockquote></pre>

上述範例，是由 libhardware 的程式碼修改而成。HAL (即 libhardware) 會讀取以下四個 property：

<ul><li>ro.hardware</li>
<li>ro.product.board</li>
<li>ro.board.platform</li>
<li>ro.arch</li></ul>

習慣上，我們會以 "ro.product.board" 做為 HAL 模組的 "product name"，ro.product.board 決定 HAL 模組的命名方式。例如，當 ro.product.board 為 "mokoid" 時，我們就必須將 HAL 模組命名為 *.mokoid.so，並存放於 system/lib/hw 目錄下。]]></description>
         <link>https://www.jollen.org/blog/2010/05/android-getproperty-using-c.html</link>
         <guid>https://www.jollen.org/blog/2010/05/android-getproperty-using-c.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 21 May 2010 18:19:15 +0800</pubDate>
      </item>
            <item>
         <title>更新 MokoidBoard (DMA-6410L) 至 Android 2.1 (Eclair)</title>
         <description><![CDATA[MokoidBoard 是基於 DMA-6410L (S3C6410) 的一款開發板，目前使用在仕橙3G教室的大部份訓練課程裡，目前已經將 Android 2.1 (Eclair) 移植至 MokoidBoard 上。開發板使用者或是課程學員，同樣請使用 [<a href="http://www.jollen.org/blog/2010/02/mokoid-project-is-up.html" target="_blank">Mokoid</a>] product tree 即可編譯。

<strong>升級 Android 2.1 步驟如下</strong>

以下步驟提供給 MokoidBoard 使用者參考。依此步驟即可將您的 MokoidBoard 升級 Android 2.1。

<img src="http://www.jollen.org/blog/2010/05/11/mokoidboard-eclair-4.jpg" />
圖一：Android 2.1

<img src="http://www.jollen.org/blog/2010/05/11/mokoidboard-eclair-5.jpg" />
圖二：Home Screen

<strong>1. 準備 Android 1.6 原始碼</strong>

請備妥 MokoidBoard CD，將 Android 1.6 (Donut) 取原始碼取出，並解壓縮。

<strong>2. 取得 Mokoid 分支</strong>

將 Mokoid 分支取出，擺放到 vendor/ 目錄下：

<blockquote>$ svn co http://mokoid.googlecode.com/svn/trunk/ mokoid
</blockquote>
Android 以 product tree 方式維護分支與編譯程式碼，請確保 mokoid 分支存在於 vendor/ 目錄下。

<strong>3. 取得 DMA-6410L 的 LED Stub</strong>

Mokoid 上的 LED Stub 為骨架，依開發板硬體，須填入 system call 程式碼。文後附上一個給 DMA-6410L 的 LED Stub 原始碼。

<strong>4. 保留 BatteryService 實作</strong>

請注意，由於 Android 2.1 的 BatteryService native 實作上做了變更。因此，我們先將 1.6 版的 BatteryService natvie 實作備份下來：

<blockquote>$ cp frameworks/base/services/jni/com_android_server_BatteryService.cpp /tmp</blockquote>

待更新至 2.1 版後，再將 1.6 的實作放回 source tree。Android 2.1 在 BatteryService native 端，主要的變動是修改了 sysfs 的讀取方式。

<strong>4. 更新至 Android 2.1 版</strong>

依照 [<a href="http://source.android.com/download" target="_blank">Android 網站</a>] 上的說明，安裝 repo。接著使用 repo 直接取回 Android 2.1 的程式碼即可：

<blockquote>$ cd &lt;path-to-your-android-source&gt;/<br />
$ repo sync</blockquote>

將 com_android_server_BatteryService.cpp  放回 source tree：

<blockquote>$ cp /tmp/com_android_server_BatteryService.cpp frameworks/base/services/jni/com_android_server_BatteryService.cpp
</blockquote>
<strong>5. 編譯 Mokoid Product Tree</strong>

編譯 Android image：

<blockquote>$ make PRODUCT-dma6410xp-eng
</blockquote>
<strong>6. 打包 ramdisk.img</strong>

將編譯完成的 ramdisk.img 包裝成 u-boot 格式：

<blockquote>$ mkimage -A arm -O linux -T ramdisk -C none -a 0x50800000 -n "ramdisk" -d ramdisk.img ramdisk-uboot.img
</blockquote>
將 system.img 與 ramdisk-uboot.img 拷貝到 tftp 根目錄下，並設定好開發板的 tftp 環境。

<strong>7. 更新至 Android 2.1</strong>

最後將 system.img 與 ramdisk-uboot.img 燒錄至 MokoidBoard 即可：

<pre>U-Boot 1.1.6 (Jan 21 2010 - 08:58:14) for SMDK6410                              
                                                                                
                                                                                
CPU:     S3C6410@666MHz                                                         
         Fclk = 666MHz, Hclk = 166MHz, Pclk = 83MHz, Serial = CLKUART (ASYNC Mo 
Board:   SMDK6410                                                               
DRAM:    128 MB                                                                 
Flash:   0 kB                                                                   
NAND:    128 MB                                                                 
In:      serial                                                                 
Out:     serial                                                                 
Err:     serial                                                                 
Initialise LCD with values                                                      
Hit any key to stop autoboot:  0                                                
<strong>SMDK6410 # run system   </strong>                                                        
dm9000 i/o: 0x30000300, id: 0x90000a46                                          
MAC: 00:40:5c:26:0a:5b                                                          
could not establish link                                                        
TFTP from server 10.0.1.24; our IP address is 10.0.1.25                         
<strong>Filename 'system.img'.                                                          </strong>
Load address: 0x50008000                                                        
Loading: T ####################################
...
done                                                                            
Bytes transferred = 59174016 (386ec80 hex)                                      
                                                                                
NAND erase: device 0 offset 0xa00000, size 0x4300000                            
Erasing at 0x4ce0000 -- 100% complete.                                          
OK                                                                              
                                                                                
NAND write: device 0 offset 0xa00000, size 0x386ec80                            
                                                                                
Writing data at 0x40b8800 -- 100% complete.                                     
 59174016 bytes written: OK       
<strong>SMDK6410 # run ramdisk_uboot         </strong>                                           
dm9000 i/o: 0x30000300, id: 0x90000a46                                          
MAC: 00:40:5c:26:0a:5b                                                          
could not establish link                                                        
TFTP from server 10.0.1.24; our IP address is 10.0.1.25                         
<strong>Filename 'ramdisk-uboot.img'.  </strong>                                                 
Load address: 0x50008000                                                        
Loading: ################################                                       
done                                                                            
Bytes transferred = 158894 (26cae hex)                                          
                                                                                
NAND erase: device 0 offset 0x900000, size 0x100000                             
Erasing at 0x9e0000 -- 100% complete.                                           
OK                                                                              
                                                                                
NAND write: device 0 offset 0x900000, size 0x100000                             
 1048576 bytes written: OK          </pre>

<img src="http://www.jollen.org/blog/2010/05/11/mokoidboard-eclair-1.jpg" />
圖三：更新完成、啟動 MokoidBoard

<img src="http://www.jollen.org/blog/2010/05/11/mokoidboard-eclair-2.jpg" />
圖四：執行 LedTest 範例、LED 燈初始化狀況為全滅

<img src="http://www.jollen.org/blog/2010/05/11/mokoidboard-eclair-3.jpg" />
圖五：點亮 LED 1

<strong>DMA-6410L 的 LED Stub 原始碼</strong>

以下是 led.c 的原始碼，支援 DMA-6410L 的 LED 控制：

<pre>/*
 * Copyright (C) 2009 Mokoid Open Source Project
 * Copyright (C) 2009 Moko365 Inc
 * 
 * Author: Jollen Chen &lt;jollen@jollen.org&gt;
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#define LOG_TAG "MokoidLedStub"
 
#include &lt;hardware/hardware.h&gt;
 
#include &lt;fcntl.h&gt;
#include &lt;errno.h&gt;
 
#include &lt;cutils/log.h&gt;
#include &lt;cutils/atomic.h&gt;
 
#include &lt;mokoid/led.h&gt;
 
/**
 * Definition of kernel-space driver.
 */
#define	LED_DEVICE	"/dev/led"
#define	LED_C608	1
#define	LED_C609	2
 
static int led_device_close(struct hw_device_t* device)
{
	struct led_control_device_t* ctx = (struct led_control_device_t*)device;
 
	if (ctx) {
		close(ctx->fd);
		free(ctx);
	}
	return 0;
}
 
static int led_on(struct led_control_device_t *dev, int32_t led)
{
	int fd;
 
	LOGI("LED Stub: set %d on.", led);
	fd = dev->fd;
 
	switch (led) {
		case LED_C608: 
			ioctl(fd, 1, &led);
			break;
		case LED_C609:
			ioctl(fd, 1, &led);
			break;
		default:
			return -1;
	}
 
	return 0;
}
 
static int led_off(struct led_control_device_t *dev, int32_t led)
{
	int fd;
 
	LOGI("LED Stub: set %d off.", led);
	fd = dev->fd;
 
	switch (led) {
		case LED_C608: 
			ioctl(fd, 2, &led);
			break;
		case LED_C609:
			ioctl(fd, 2, &led);
			break;
		default:
			return -1;
	}
 
	return 0;
}
 
static int led_device_open(const struct hw_module_t* module, const char* name,
        struct hw_device_t** device) 
{
	struct led_control_device_t *dev;
 
	dev = (struct led_control_device_t *)malloc(sizeof(*dev));
	memset(dev, 0, sizeof(*dev));
 
	dev->common.tag =  HARDWARE_DEVICE_TAG;
	dev->common.version = 0;
	dev->common.module = module;
	dev->common.close = led_device_close;
 
	dev->set_on = led_on;
	dev->set_off = led_off;
 
	*device = &dev->common;
 
	/*
         * Initialize Led hardware here.
         */
        dev->fd = open(LED_DEVICE, O_RDONLY);
	if (dev->fd < 0) 
	    dev->fd = 2;   /* Error Handler */
 
	led_off(dev, LED_C608);
	led_off(dev, LED_C609);
 
success:
	return 0;
}
 
static struct hw_module_methods_t led_module_methods = {
    open: led_device_open
};
 
const struct led_module_t HAL_MODULE_INFO_SYM = {
    common: {
        tag: HARDWARE_MODULE_TAG,
        version_major: 1,
        version_minor: 0,
        id: LED_HARDWARE_MODULE_ID,
        name: "Sample LED Stub",
        author: "The Mokoid Open Source Project",
        methods: &led_module_methods,
    }
 
    /* supporting APIs go here. */
};</pre>

新版的 led.h 設計：

<pre>/*
 * Copyright (C) 2009 Mokoid Open Source Project
 * Copyright (C) 2009 Moko365 Inc
 * 
 * Author: Jollen Chen &lt;jollen@jollen.org&gt;
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#include &lt;hardware/hardware.h&gt;
 
#include &lt;fcntl.h&gt;
#include &lt;errno.h&gt;
 
#include &lt;cutils/log.h&gt;
#include &lt;cutils/atomic.h&gt;
 
/***************************************************************************/
 
struct led_module_t {
   struct hw_module_t common;
};
 
struct led_control_device_t {
   struct hw_device_t common;
 
   /* attributes */
   int fd;
 
   /* supporting control APIs go here */
   int (*set_on)(struct led_control_device_t *dev, int32_t led);
   int (*set_off)(struct led_control_device_t *dev, int32_t led);
};
 
/***************************************************************************/
 
struct led_control_context_t {
	struct led_control_device_t device;
};
 
#define LED_HARDWARE_MODULE_ID "led"</pre>

<strong>下載原始碼</strong>

<ul>
<li><a href="http://www.jollen.org/blog/2010/05/11/led.dma6410xp.c" target="_blank">led.c</a></li>
<li><a href="http://www.jollen.org/blog/2010/05/11/led.h" target="_blank">led.h</a></li>
</ul>
]]></description>
         <link>https://www.jollen.org/blog/2010/05/update-mokoidboard-to-eclair.html</link>
         <guid>https://www.jollen.org/blog/2010/05/update-mokoidboard-to-eclair.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 11 May 2010 12:24:20 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 系統管理雜記, #3: init.rc 與 setprop</title>
         <description><![CDATA[今天在上海進行「Android Framework & HAL 軟硬整合」培訓課程，課程中提到 init.rc 的用途，因此在此做一個紀錄。init.rc 是 Android 作業系統的 initial script，在開機時由 init 讀取並執行 init.rc 裡的命令。Android 的 init.rc 使用的的語法稱為 Android init language，有別於傳統 Embedded Linux 採用 shell script 的方式。

在 init.rc 裡找到類似以下的命令片斷：

<pre>on boot
    setprop ro.FOREGROUND_APP_ADJ 0
    setprop ro.VISIBLE_APP_ADJ 1
    setprop ro.SECONDARY_SERVER_ADJ 2
    ...</pre>

以上是一個動作（action）區段的設定，說明如下：

1. on boot 表示在開機時（boot）觸發此動作區段裡的所有命令。
2. setprop 是設定 Android property 的命令。

上述提及的「動作區段」設定格式如下：

<pre>on &lt;trigger&gt;
   &lt;command&gt;
   &lt;command&gt;
   &lt;command&gt;</pre>

當 "trigger" 為 "boot" 時，表示「開機觸發」。一個動作區段裡，可以有任意個命令（command），每個命令獨立於一行。最常見，也最重要的命令就是 'setprop'。'setprop' 用來設定 'property' 的值，property 有點像是系統的「環境變數（environment variable）」。其命令格式如下：

<pre>setprop &lt;name&gt; &lt;value&gt;</pre>

例如：

<pre>setprop ro.product.device dma6410xp</pre>

表示「ro.product.device = "dma6410xp"」的意思。Android 系統有非常多 property，這些 property 都是 Android 作業系統本身在使用的重要變數，例如：上例的「ro.product.board」就是給 HAL 使用的重要變數。]]></description>
         <link>https://www.jollen.org/blog/2010/04/android-initrc-setprop.html</link>
         <guid>https://www.jollen.org/blog/2010/04/android-initrc-setprop.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 24 Apr 2010 21:57:27 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 觀念解析, #1: Zygote Mode</title>
         <description>Android 作業系統開機時，會經由 init.rc 來啟動許多外部程式，其中有一個最重要 process 稱為 Zygote。Zygote 是 Android 的 monitor process，它主要負責二項工作：

1. 啟動 system server
2. 執行 Android 應用程式

「System Server」是由 Zygote 所建立的另外一個 process，建立 system server 的方式是使用典型的 Linux system call - fork()。當 Zygote 成功建立 system server 後，便進入 socket listening 模式。在此模式下，zygote 會監聽（listen）由 socket 所傳入的「命令」，並依據命令的內容啟動 Android 應用程式。

Zygote 啟動外部 Android 應用程式的方式，同樣是使用 Linux kernel 所提供的 fork() system call。因此，在 socket 做 listening，並依據命令來 fork() 並執行外部 Android 應用程式，稱之為「Zygote Mode」。</description>
         <link>https://www.jollen.org/blog/2010/04/android-concept-1-zygote-mode.html</link>
         <guid>https://www.jollen.org/blog/2010/04/android-concept-1-zygote-mode.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 05 Apr 2010 15:14:32 +0800</pubDate>
      </item>
            <item>
         <title>iSuppli公佈2009年全球手機出貨量</title>
         <description><![CDATA[iSuppli在2月25日公佈了2009年手機出貨量研究報告，其中包含了關於全球市場與中國市場的統計。簡單摘錄重點數字如下：

1. 2009年全球整體手機出貨量為12億部
2. 承上，中國整體出貨量為4.04億部，佔整體份額33.7%，達三之一
3. 承上，首次來到4.0億部的規模
4. 承上，以成長率來看，到二零一二年，中國整體出貨量將來到5.0億部

<img alt="2009-phone-global-market.png" src="http://www.jollen.org/blog/2010/03/26/2009-phone-global-market.png" width="463" height="340" />

5. 在中國的手機業產裡，「手機設計」公司是相當重要的一個環節。二零零九年，由手機設計公司出貨的手機為2.44億部，佔中國整體出貨量的60%，意謂著，有超過一半的手機是由手機設計服務公司出貨。

當然，上述的4.04億部，還包含了「外銷」手機；若以「本土銷售」來計算，數量大約是2.4億部。2010年本土銷售預測成長為11%，即2.66億部，這是feature phone加smart phone的數字，若只計算smart phone的銷售，2010年smart phone的出貨量是2600萬部以上。近期曾看到有關smart phone與feature phone的成長預測報告，但未有正式數據，故不引用。

<strong>中國的手機設計服務公司扮演大推手</strong>

這些手機設計服務公司的商業模式是，直接為白牌或本土品牌提供設計方案，例如：手機板 (PCBA)。手機設計服務公司具備高效率與客製化服務能力，成為中國手機產業的重要成長推手。]]></description>
         <link>https://www.jollen.org/blog/2010/03/isuppli-2009-phone-global-market.html</link>
         <guid>https://www.jollen.org/blog/2010/03/isuppli-2009-phone-global-market.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Fri, 26 Mar 2010 16:50:13 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Framework in a Nutshell：台北場演講順利舉辦</title>
         <description><![CDATA[幾個月前的一則日記，紀錄了「<a href="http://www.jollen.org/blog/2010/01/jollen-android-in-a-nut-shell.html">堅果殼精神</a>」的起源，經過數個月的籌劃，第一場「Jollen 的 Android Framework in a Nutshell」演講終於順利結束。距離 [<a href="http://www.jollen.org/blog/2010/01/android-framework-in-a-nuthsell-preview.html">Android Framework in A Nutshell 講題規劃完畢</a>]，到第一場演講正式舉辦，間隔近一個半月的時間，感謝協助本活動的幾位幕後人員，才能使這個活動順利進行。

這次的演講，除了以提及的「堅果殼」精神分享技術研究心得外，也在實現另外一個自已的理念：以半課程形式呈現演講。

<img src="http://www.moko365.com/project/android-framework-taipei/Android_Taipei_files/shapeimage_1.png" />

半課程形式的意思是，以演講的舉辦形式，加上課程形式的內容，融合演出。演講活動，是啟發思考不可或缺的活動，一場好的演講，有時扣人心弦、有時發人省思，講者也能儘情演出、暢談自已的理念；課程或教育訓練，則是在傳遞知識，設法以授課技巧或教育方法，達到潛移默化的效果，這是授業者的工作。形式與目的大有不同。

如果「演講」又要能達到一部份「課程」的效果，就必須專注在內容本身，也就是著重於技術本身的說明；同時，事前的妥善規劃也很重要。主題的規劃必須客觀大於主觀，才能有課程的感覺。再者，這種形式，讓講者能以更輕鬆的方式進行，就像演講般，帶入一些個人想法，或是加入一些經驗談。

當天也向與會朋友預告了「Dalvik VM in A Nutshell」，希望工作之餘，繼續經營這顆堅果殼。

<strong>延伸閱讀</strong>

* 2010.1.3: <a href="http://www.jollen.org/blog/2010/01/jollen-android-in-a-nut-shell.html">「Jollen 的 Android Framework in a Nutshell」演講</a>
* 2010.1.26: <a href="http://www.jollen.org/blog/2010/01/android-framework-in-a-nuthsell-preview.html">「Jollen 的 Android Framework in a Nutshell 演講」講題規劃完畢</a>
* 2010.2.1: <a href="http://www.jollen.org/blog/2010/02/android-framework-foxconn.html">「Android Framework Introduction」講座</a>]]></description>
         <link>https://www.jollen.org/blog/2010/03/jollen-android-framework-nutshell-taipei.html</link>
         <guid>https://www.jollen.org/blog/2010/03/jollen-android-framework-nutshell-taipei.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Wed, 24 Mar 2010 11:49:36 +0800</pubDate>
      </item>
            <item>
         <title>山寨軍品牌革命：下一個山寨影響力</title>
         <description>文／Jollen Chen（原文刊登於 CTimes零組件雜誌2010年3月號）

因為聯發科的解決方案、低價手機、不斷創新的外觀設計、龐大的內需市場以及技術螞蟻大軍等要素，造就第一個深圳手機產業的奇觀「山寨機」。在抄襲與非正統的「模式」下所開發與製造的手機，就被暱稱為「山寨機」，意謂拷貝與強取之意。

過去也曾經在本論壇提到的本土品牌廠天宇朗通，是以山寨機起來，轉型為本土品牌製造商的代表。過去一年來，因為受到 Android 開放平臺的影響，讓這批所謂的山寨大軍開始思考國際化與品牌化的道路。Android 作業系統讓這些手機商感受到「自主」的力量，這個力量可以由以下二個技術層面來討論。

第一、開放的作業系統，讓山寨手機商開始有了能「自由使用」的軟體；使用這個開發軟體，便可以不會「直接」受到軟體原廠的控制與限制，在產品開發上有了更大的自由度。第二、開放的作業系統，讓山寨手機商能開始真正思考，「構建自有軟體團隊」的可行性與做法；自有的軟體團隊，可以針對市場或客戶的需求，進行開發與軟體的客製化。

由此看來，「使用上的自由」以及「針對市場做軟體客製化」是山寨轉型需要掌握的二個力量；另人振奮的是，山寨大本營所在的深圳，有了更成熟的產業與資金環境，給了新創自有品牌產品一個難得的好機會。過去山寨品牌化都是個案，現在山寨品牌化已經變成一個現象，氛圍已經形成，許多山寨機製造商對於「自有產品」、「自主解決方案」以及「自有品牌」展現強烈的企圖力，未來將成為不可漠視的力量，山寨大軍的轉型，成為下一個手機產業的關鍵影響力。

山寨品牌化，以及走向國際銷售，供應鍊管理是首要加強的能力。借助台灣供應鍊及供應鍊管理能力，可使山寨品牌化的腳步跨出大步。具體實行上，提昇供應鍊端的價值是必要工作，為不致淪為單純的零件供應商，可以思考幾個具體的做法，由小地方逐步完善。單純就技術面來看，幾個提昇價值的具體做法如下。

第一、提供關鍵零組件更精緻的軟硬整合服務，以服務增加本身價值，並思考開源模式的助益或可能帶來的影響力。第二、提供更完整的平臺解決方案，形式上更像是一個 turnkey key solution 或是公板，這方面台灣有許多掌握關鍵技術的硬體廠，都有很不錯的做法。第三、為產品製造應用軟體，模式上可以和硬體綁定銷售。

山寨大軍若能成功帶起品牌革命，成功走向品牌化與國際化，這個影響力對大家都是有助益的，若能正面看待、樂觀因應，這又是一個開放平臺潮流裡的一個大機會。</description>
         <link>https://www.jollen.org/blog/2010/03/shenzen-branding-road.html</link>
         <guid>https://www.jollen.org/blog/2010/03/shenzen-branding-road.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Tue, 23 Mar 2010 18:02:12 +0800</pubDate>
      </item>
            <item>
         <title>HAL Stub 的測試程式範例：Led.c</title>
         <description><![CDATA[近期進行有關 MokoidBoard 的平臺開發，MokoidBoard 的目的是打造一個「Android 框架與底層」專用的學習平臺，主板的部份是基於 Samsung S3C6410 處理器。目前除了計畫以 S3C6410 打造手機方案外，還有一個一直很想實現的理念：教育訓練方面，提供品質良好、架構完善的範例程式碼。一些 dirty code 對於初步學習，並了解硬體是很有幫助的；但入了門，總是要持續進步、精益求精，研讀架構完善的高品質程式碼，就是煅煉火候的好方法。

<img alt="mokoid-led.jpg" src="http://www.jollen.org/blog/2010/03/17/mokoid-led.jpg" width="496" height="672" />

目前在 MokoidBoard 上提供的 LedTest 範例，是透過 ServiceManager、LedService 以及 HAL Stub 等觀念所設計的「LED 控制程式」。如圖，LedTest 執行時，會出現一個巨大按鈕，按了後，便會將開發板上的第一個 LED 燈點亮。

範例程式碼並不難讀，比較難懂的是 Android 框架與 HAL 架構的觀念，還有一些設計原理。這些觀念，在上週的 Android Framework in a Nutshell 演講做了一個整體性的介紹。

在開發階段，「如何測試 HAL Stub」其實是另一個很重要題目。因此，隨著 MokoidBoard 進入最後整理階段，Mokoid 範例也將會提供一個 Led.c 測試程式，讓我們能在開發階段以 native 方式進行「HAL Stub 的 API 驗證」，如圖所示。

Led.c 是一個 native 執行檔，執行時會透過 HAL 取得 LED Stub，並以 direct call 方式直接測試 Stub 裡的 API 實作，藉此驗證 Stub 與 kernel-space driver 的硬體控制功能；在未來的演講或是課程裡，將會加入一些「測試程式開發」的主題，請大家不吝指教。



]]></description>
         <link>https://www.jollen.org/blog/2010/03/write-hal-stub-testbench.html</link>
         <guid>https://www.jollen.org/blog/2010/03/write-hal-stub-testbench.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Wed, 17 Mar 2010 22:52:37 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 系統管理雜記, #2: Java Package 與 Jar File 對應設定</title>
         <description><![CDATA[經由 [<a href="http://mokoid.googlecode.com" target="_blank">Mokoid</a>] 範例，我們可以學習到擴充（Extent）Android 框架的做法。搭配 Product Tree 的方式，我們將 LedManager 與 LedService 二個類別編譯成獨立的 jar 檔（mokoid.jar），mokoid.jar 會被 Android build system 自動佈署到 system.img 裡（system/framework/mokoid.jar）。

因為 mokoid.jar 裡的類別沒有做 preload，並不是「preload class」，所以需要額外的系統設定，才能讓 Android 作業系統找到 LedManager 與 LedService 二個類別。在 Mokoid 範例中，找到一個名為 com.mokoid.server.xml 的設定檔，內容如下：

<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;permissions>
    &lt;library name="com.mokoid.server"
            file="/system/framework/mokoid.jar"/&gt;
&lt;/permissions&gt;</pre>

此設定檔的作用為：指定 com.mokoid.server 的相對應 jar 檔。「com.mokoid.server」是 Java package（library name）、mokoid.jar 是檔案。透過 com.mokoid.server.xml 來設定其對應關係，並將此檔案置於 /etc/permissions 目錄下，這是基本的 Android 系統管理。

<strong>延伸閱讀</strong>

<ul>
<li>2010.2.8: <a href="http://www.jollen.org/blog/2010/02/mokoid-project-is-up.html">MOSP: Mokoid Project (Mokoid Open Source Project) 上線</a></li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2010/03/java-package-jar-file-mapping.html</link>
         <guid>https://www.jollen.org/blog/2010/03/java-package-jar-file-mapping.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 02 Mar 2010 22:45:09 +0800</pubDate>
      </item>
            <item>
         <title>Override Context.getSystemService()</title>
         <description><![CDATA[Context.getSystemService() 是一個很重要的 API，也是「Android 應用程式控制硬體」的起點。在一個開發項目中，如何擴展 getSystemService() 的實作成為一個重要的課題。

幾天前與客戶進行技術討論時，適巧討論到這個議題，因此在這裡做一個簡單的紀錄與大家分享。應用程式要存取手機上的 Sensor 裝置時，須取得 SensorManager 物件，程式寫法如下：

<pre>public class mokoidSensor extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        　
        SensorManager sensor = (SensorManager)getSystemService(SENSOR_SERVICE);
        sensor.getSensors();
        /* Do something. */
    }
}</pre>

如果手機上有一個「馬達」裝置，程式碼的寫法，依此邏輯來推論，設想的程式碼會是這樣：

        MotorManager motor = (MotorManager)getSystemService(MOTOR_SERVICE);

只是，AOSP 上的程式碼並無 Motor Service 硬體服務，因此除了加入 MotorManager 與 MotorService 設計外，也要擴充 getSystemService() API。實際能採行的做法很多，這裡討論二種方式：

1. 土方法
2. 符合架構

土方法是一個簡單的方式，直接到 [<a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob_plain;f=core/java/android/app/ApplicationContext.java;hb=HEAD" target="_blank">ApplicationContext.java</a>] 裡把程式碼改掉。這個方式簡單有效，不費勁。

符合架構的方法，是基於「Application Framework 不能修改」的前提下來進行設計。也就是，不要變動 Context 的設計、也不要修改 ApplicationContext 實作。AOSP 的 [<a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob_plain;f=core/java/android/app/Activity.java;hb=HEAD" target="_blank">Activity.java</a>] 以 override 的方式，展示了這個做法：

<pre>    @Override
    public Object getSystemService(String name) {
        if (getBaseContext() == null) {
            throw new IllegalStateException(
                    "System services not available to Activities before onCreate()");
        }
　
        if (WINDOW_SERVICE.equals(name)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }</pre>

因此，改用以下的設計：

<pre>public class mokoidActivity extends Activity {
    ...
    @Override
    public Object getSystemService(String name) {
　
        if (MOTOR_SERVICE.equals(name)) {
            MotorManager mMotorManager = new MotorManager();
            return mMotorManager;
        }
        return <strong>super.getSystemService(name);</strong>
    }	
	...
}</pre>

應用程式的部份也要做修改：

<pre>public class HelloWorld extends <strong>mokoidActivity</strong> {
}</pre>

目前為止，這只是一個想法，尚未實作驗證。

]]></description>
         <link>https://www.jollen.org/blog/2010/02/override-context-getsystemservice.html</link>
         <guid>https://www.jollen.org/blog/2010/02/override-context-getsystemservice.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 26 Feb 2010 22:12:24 +0800</pubDate>
      </item>
            <item>
         <title>Android Framework「專案啟動」顧問方案：技術、工程與管理 Start-up 服務</title>
         <description><![CDATA[為什麼要研究 Android Framework？這是一個軟體工程以及專案管理面的問題。Android Framework 是 Application Framework（應用軟體框架），所謂的 Framework 定義上指的是未完成（incomplete）或是不完善（not ready-to-use）的軟體程式庫（嚴格來說是 class library）。

近期收到有關「如何發展 Android 產品」的需求有增多的趨勢，而要解決的最核心問題就是「研究 Android Framework」。雖然目前與一些訓練單位合作，提供許多 Android Framework 方面的課程，但因為都是屬於純技術面，還缺少軟體工程以及專案管理面的內容，尚有不足的地方。

有鑑於此，花費近一個月的時間，整理了一套「標準顧問方案」為企業客戶提供這方面的 On-site 服務，以補齊不足之處。這套顧問方案共分為 5 個層面，並定名為「Android Framework 專案啟動顧問服務」，詳細說明如下。

<strong>Framework 是未完成品</strong>

「Framework 是參考實作、未完成品。」這是小弟過去在許多演講場合，和大家分享的觀念。

1. Framework is incomplete.

Framework 本身是不可用的，需要強化或填寫 framework 的空白，並設計相對應的應用程式，此時 framework 才能有作用。從學術上的定義來看，framework 本身是 class library，這套 class library 以物件導向的抽象觀念來設計，提供可重用（resuable）或可抽換的單元（component），並提供一套應用程式流程（flow）與反向控制點（Inverse of Control）。

2. Framework is not ready-to-use.

Framework 是 class library，不是一個作業系統。Android Framework 是一套 class library，Android 搭配 Linux kernel 成為 Android OS。因此，framework 必須搭配作業系統核心，並且完成產品端的軟體開發；此處所指的「軟體開發」經常性的工作有2項：1. 補齊驅動程式，2. 並且整合驅動程式與 application framework。

以上說明讓我們了解一點，「廠商需要著手完成收尾工作」，並且 framework 本身是採用物件導向觀念設計的 class library，這說明了研究 Android Framework 內部設計的重要性。

<strong>Android Framework 留下的空白</strong>

因為不完整，所以需要了解我們倒底要填寫什麼空白、擴充哪些功能、開發哪些 abstract class 等等，以建立完整可用的 Android Framework。通常，這是一項針對「產品規格」所進行的工作，即 Specification 階段就該完成的工作。

在專案啟動前期（in the begining）若沒有完善這項工作，將可能影響後面的實作工作（implementation）。

<strong>Framework 只能擴充不要修改</strong>

一個嚴格的軟體工程方法裡，針對 application framework 的完善工作，需要基於「基礎版」進行後續的程式碼撰寫，但是，這個「撰寫」（coding）的工作應該儘量避免修改原始的 framework 實作。即 application framework 具備 non-modifiable 的特性。

Framework 是一個物件導向設計的 class library，因此採用 override 方式就可以實現「擴充」框架的做法；了解 Android Framework 現有設計（OOD）並進行擴充，是重要的工程技術。

<strong>實現 3M 分支維護策略管理程式碼</strong>

Framework 是實體的物體（physical objects），例如：以 JAR 檔形式存在。Framework 不是抽象的，所以像是 GoF 便是一種非實體的觀念。如何在 Android Framework 原始碼裡，將擴充出來的部份產生實體物體，最基本的做法是建立獨立的 JAR 檔。當我們擴充或重用 Android Framework 時，就會面臨三個問題：1. 有哪些實體需要建立？2. 以及如何建立？3. 並且如何在不修改 Android Framework 程式碼的前提下完成這些工作？

過去在課程或演講經常所提出的「3M 分支維護策略」就是一個簡易的解決方案。

<strong>建立 Android Framework 開發主機</strong>

為客戶建立一台專門的開發主機，是最後一項工作。除了提供範例與開發環境外，也會給予一個基本的訓練。

<strong>咨詢方案規劃</strong>

Jollen's Consulting 工作室聯合幾位不同領域的專家，在 Android Framework 的專案研發上做了一些討論，並且提出一個針對企業的咨詢方案，大綱如下：

《1 .觀念入門》Framework 是未完成品
《2. 實務應用》Android Framework 留下的空白
《3. 設計理論》Framework 只能擴充不要修改
《4. 分支管理》實現 3M 分支維護策略管理程式碼
《5. 開發環境》建立 Android Framework 開發主機

這是一套咨詢方案（Consulting），對象是「有意採用 Android 發展產品、想要正確並有效起步」的企業客戶。這套方案的方向是以「專案管理」、「產品開發」以及「軟體工程」做為出發點，雖然只是一個提供 Start-up 的 consulting 服務，但過去實行的成效良好，因此，將本方案公佈在這裡，期望 Jollen's Consulting 的專家團隊，能在「企業導入 Android 工程技術」上有所貢獻。過去的做法，經常是採購實驗板，並自行摸索；本方案提供的是「Start Up」服務，比起自行摸索的做法，更有效率、也更整體。

本方案過去服務過大陸與美國的企業。一些做法，目前也應用在自有的開發項目上。本方案目前的做法是以2-3天的 On-site 顧問服務方式進行，客戶端建議的參與人員必須包含1名專職的PM。本方案的顧問團隊，也包含了 2 位具備 PMP 資格的專案管理師，能為大家在開發上的專案管理提供一些觀念。
]]></description>
         <link>https://www.jollen.org/blog/2010/02/consulting-android-framework-project-startup.html</link>
         <guid>https://www.jollen.org/blog/2010/02/consulting-android-framework-project-startup.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Sun, 14 Feb 2010 15:48:36 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 系統管理雜記, #1: 關於 android.uid.system 與 AID_SYSTEM</title>
         <description><![CDATA[在 [<a href="http://mokoid.googlecode.com" target="_blank">Mokoid</a>] 的 LedTest 範例裡，找到 [<a href="http://code.google.com/p/mokoid/source/browse/trunk/apps/LedTest/AndroidManifest.xml" target="_blank">AndroidManifest.xml</a>] 檔案。這個檔案為應用程式的「交貨清單」；在開發 LedTest 的過程中，我們加入了一個屬性如下：

<pre>&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mokoid.LedTest"
    <font color="#ff0000">android:sharedUserId="android.uid.system"</font>&gt;
</pre>

原來，ServiceManager 會去檢查應用程式的權限；Android 作業系統會根據 UID 做權限管制，這裡所講的 UID 就是 Linux 系統管理面所討論的 User ID，即使用者 ID。在 [<a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob_plain;f=cmds/servicemanager/service_manager.c;hb=HEAD" target="_blank">frmeworks/base/cmds/servicemanager/service_manager.c</a>] 裡，找到這段實作：

<pre>int svc_can_register(unsigned uid, uint16_t *name)
{
    unsigned n;
    　
    if ((uid == 0) || (uid == AID_SYSTEM))
        return 1;
　
    for (n = 0; n < sizeof(allowed) / sizeof(allowed[0]); n++)
        if ((uid == allowed[n].uid) && str16eq(name, allowed[n].name))
            return 1;
　
    return 0;
}
　
int do_add_service(struct binder_state *bs,
                   uint16_t *s, unsigned len,
                   void *ptr, unsigned uid)
{
    struct svcinfo *si;
//    LOGI("add_service('%s',%p) uid=%d\n", str8(s), ptr, uid);
　
    if (!ptr || (len == 0) || (len > 127))
        return -1;
　
    if (!svc_can_register(uid, s)) {
        LOGE("add_service('%s',%p) uid=%d - PERMISSION DENIED\n",
             str8(s), ptr, uid);
        return -1;
    }
    ...
}</pre>

AID_SYSTEM 被定義為 1000，即 system server 的 UID。從上述的實作可以了解，ServiceManager 會去檢查應用程式的 UID，當 UID 不符規定時，便無法執行 do_add_service()。

也就是：當應用程式的 UID 不是 1000 時，是沒有權限新增 Android Service 的。所以，在 AndroidManifest.xml 裡加上 android:sharedUserId 屬性的目的在於此：將應用程式的 UID 定義為 android.uid.system 即 1000，程式即可具備新增 Android Service 的權限。

以 Mokoid 所提供的範例為例，「因為我們是在 Android 應用程式裡啟動 Android Service」，因此要特別留意這個部份。典型的新增 Android Service 做法是修改 frameworks/base/services/java/com/android/server/SystemServer.java 檔案，但是，「因為 3M 分支維護策略的理念是儘量避免更動原始的 Android 程式碼」，所以我們採取這種「Start LedService in a seperated process.」的做法。細節請參考 Mokoid 範例。]]></description>
         <link>https://www.jollen.org/blog/2010/02/android-uid-system.html</link>
         <guid>https://www.jollen.org/blog/2010/02/android-uid-system.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 09 Feb 2010 15:46:07 +0800</pubDate>
      </item>
            <item>
         <title>MOSP: Mokoid Project (Mokoid Open Source Project) 上線</title>
         <description><![CDATA[為了能在「<a href="http://www.jollen.org/blog/2010/01/android-framework-in-a-nuthsell-preview.html">Jollen 的 Android Framework in a Nutshell 演講</a>」上搭配範例做講解，因此「特製」了一個 LedManager 範例；此範例原來是「<a href="http://www.moko365.com/training/android-hal-development-and-practice">Android 框架與驅動程式整合: HAL 原理與實作訓練</a>」培訓課程的實作範例，目前計畫以此範例的部份內容做為演講材料。歡迎有意參加 Jollen 的 Android Framework in a Nutshell 演講的朋友，事先下載 Mokoid 程式碼。

Mokoid 提供一個 LedTest 範例程式，同時，LedTest 也是 Mokoid 專案目前所提供的第一個「近乎完整」的範例。請大家參考 apps/LedTest/src/com/mokoid/LedTest/ 裡的程式碼。Mokoid 專案的第二個範例，計畫是「MotorManager」，目前仍處於測試階段，預計在《台北場》演講時釋出。

Mokoid 的目的是提供「教學開發板」一套完整的 architect code 供實驗課程使用，或是搭配正課做原理教學，當然，首要目的是應用在自已的培訓課程上。主要的理念是「去除使用 dirty code 教學的風氣」，不過，可能需要不少時間來實踐這個理想。

Mokoid Project 網址：<a href="http://mokoid.googlecode.com">http://mokoid.googlecode.com</a>

]]></description>
         <link>https://www.jollen.org/blog/2010/02/mokoid-project-is-up.html</link>
         <guid>https://www.jollen.org/blog/2010/02/mokoid-project-is-up.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 08 Feb 2010 22:55:00 +0800</pubDate>
      </item>
            <item>
         <title>「Android Framework Introduction」講座</title>
         <description><![CDATA[Android Framework 技術的重要性日漸提高，研究 Android Framework 架構或是內部結構成為一項重要的工作。明天（2/2）受邀至鴻海土城民生廠進行一場訓練課程，主題是「Android Framework Introduction」；議題雖然是「introduction」，但突然有個想法，希望能做更深入的 introduction。正好近期在整理研究資料，所以把抓出的議題也和大家分享。計畫介紹的技術主題（Features）如下：

<strong>1. Android Framework Features</strong>

<ul><li>SystemServer & ServerThread</li>
<li>Main Thread</li>
<li>android.app.Activity 與 android.app.Service</li>
<li>Android Process 模式</li>
<li>ServiceManager 與 getSystemService API</li>
<li>JNI & Native Method</li>
<li>Blocking & Long Operations</li>
<li>VMThread & Thread</li>
<li>Zygote 與 Linux fork System Call</li></ul>

<strong>2. Android Framework Development</strong>

<ul><li>Manager API</li>
<li>Proxy Object</li>
<li>Remote Object</li>
<li>Remotable Object</li>
<li>IInterface & IServiceManager</li></ul>

原本構想中的 introduction 是以 Android 的架構圖為主，逐一介紹每一層的關係以並做 source code 的導讀；不過，後來想了一下，因為只有 3 個小時的時間，再加上大家對「概念」可能都已經有一定程度的了解了，所以再做這種描述性的簡介，似乎意義不大。

於是，改採介紹「Features」的方式，將 Android 重要的技術點做「點擊式」的說明。「明天過後」歡迎大家來函索取這次的講稿，待未來講義更加完善時，再放置到網路上分享。]]></description>
         <link>https://www.jollen.org/blog/2010/02/android-framework-foxconn.html</link>
         <guid>https://www.jollen.org/blog/2010/02/android-framework-foxconn.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 01 Feb 2010 23:58:01 +0800</pubDate>
      </item>
            <item>
         <title>「Jollen 的 Android Framework in a Nutshell 演講」講題規劃完畢</title>
         <description><![CDATA[從去年就開始構思的「Jollen 的 Android Framework in a Nutshell」演講，終於在上週六正式完成講題規劃了。初版講稿也開始進入檢查階段，第一場演講預估在二月底或三月初舉行，詳細的時間與地點，稍後將發佈於演講活動官網 [<a href="http://www.jollen.org/androidframework/">Jollen's Android Framework in a Nutshell 演講</a>]。

<a href="/androidframework/"><img src="http://www.jollen.org/androidframework/banner.png" border="0" /></a>

「In a Nutshell」是本演講的核心理念，也是規劃講稿時所依循的精神；關於堅果殼精神，可參考先前日記 [<a href="http://www.jollen.org/blog/2010/01/jollen-android-in-a-nut-shell.html">「Jollen 的 Android Framework in a Nutshell」演講</a>] 的說明。

講題的規劃將以一個實例「揭開」Android 框架的黑盒子、將 Android 結構做展開的動作，並分別介紹每個層面的技術重點；希望藉由這個精心規劃的演講活動，能幫助對 Android 框架與系統底層有興趣的朋友，有效率地掌握技術重點。]]></description>
         <link>https://www.jollen.org/blog/2010/01/android-framework-in-a-nuthsell-preview.html</link>
         <guid>https://www.jollen.org/blog/2010/01/android-framework-in-a-nuthsell-preview.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 26 Jan 2010 00:22:44 +0800</pubDate>
      </item>
            <item>
         <title>OPhone SDN 徵文「JIL Mobile Widget: 我的第一堂课」</title>
         <description><![CDATA[去年 [<a href="http://www.ophonesdn.com/" target="_blank">OPhone SDN</a>] 舉辦了一個徵文活動，因為對 BAE 技術的高度興趣以及好感，特別撰文，以另外一種角度來表達 JIL Mobile Widget 技術的定位，希望以深入淺出方式，將這個實用的好技術介紹給大家。稿件有幸被接受，請大家不吝指教 [<a href="http://www.ophonesdn.com/article/show/184;jsessionid=D31E09EEC5BFDC720E04D1CE507099AC" target="_blank">JIL Mobile Widget: 我的第一堂课</a>]。BAE 與 JIL Mobile Widget 的關係，在文末也做了簡單說明。]]></description>
         <link>https://www.jollen.org/blog/2010/01/ophone-sdn-jil-mobile-widget.html</link>
         <guid>https://www.jollen.org/blog/2010/01/ophone-sdn-jil-mobile-widget.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Mon, 25 Jan 2010 13:57:25 +0800</pubDate>
      </item>
            <item>
         <title>首屆海峽Android技術及產業合作發展研討會：會後雜記</title>
         <description><![CDATA[這個月15日到北京參加「<a href="http://www.android-collaboration.com/" target="_blank">首屆海峽Android技術及產業合作發展研討會</a>」，結果正值北京二十年來難得一見的大寒，白天的氣溫最低還來到了-10度C，整個城市都可以看到白雪，到小商店還買到了「結冰礦泉水」，實在是一個有趣的經驗。

關於這次會議的實況，在研討會官網以及媒體上，都有很詳細的報導，所以這裡僅補上一點會議雜記。這次的會議，下午場分做三個track（產業、技術與人才培養），我在技術與人才場各有一個session。在技術場部份，針對「Android對手機的技術趨勢與機會」做了演說，人才培養場則是討論有關創業機會的議題。

「Browser-based」是Android手機的技術趨勢之一，這是大家都不陌生的名詞，整合cloud computing到手機上，這也是一個關鍵技術。技術分會場中，我特別舉JIL的Mobile Widget技術做實例，介紹了這個技術帶來的優點，以及BAE將如何創造一個新的手機應用開發模式。

這次會議，根據估計，與會人數大約有500人。在會議的前幾天，正逢沸沸洋洋的Google事件，與一些朋友聊天時，不免談論到這個話題；不過，大家普遍認為這個事件並不影嚮Android的技術推展。以會議的討論氣氛來看，影響確實不大。

<img alt="android-beijing.jpg" src="http://www.jollen.org/blog/2010/01/25/android-beijing.jpg" width="464" height="337" />

在「人才培養」分會場的部份，來自中正大學羅習五老師實驗室的「現場成果展」吸引不少人的目光，特別是「螢光棒」的創意應用，連媒體都特別來報導了。可見，「創意」是Android應用開發不能缺少的一個要素。]]></description>
         <link>https://www.jollen.org/blog/2010/01/android-collaboration-beijing.html</link>
         <guid>https://www.jollen.org/blog/2010/01/android-collaboration-beijing.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Mon, 25 Jan 2010 13:43:48 +0800</pubDate>
      </item>
            <item>
         <title>「Jollen 的 Android Framework in a Nutshell」演講</title>
         <description>在堅果殼裡「In a nut shell」是一句英文片語，表示「簡單地說」的意思，與中文的「總而言之」有異曲同工之妙。如果上網搜尋，還可以找到這個片語來源：知名的長篇史詩「伊利亞德（Iliad）」曾被製作成體積迷你的手抄版本，正因為這個迷你的手抄版本小到能放進堅果殼裡（in a nut shell），因此這個片語就被引申為「簡單地說」之意。

知名的電腦書出版商O&apos;reilly就出版了一系列的「in a nut shell」。這個系列書藉的特色正如其名「簡潔、不托泥帶水」，「一頁抵十頁、一部抵十部」或許能說明堅果殼書的特色；意思是說，別人100頁的篇幅，往往堅果殼只需要10頁就足矣。事實上，這應該不誇張，因為現在很多書就喜歡拉篇幅、撐厚度；有些「用來佔地盤」的系列著作就更不用提了。不過，我們不該是以這種評論的角度，把堅果殼書跟其它書拿來做比較；因為，要出版一本著作，也不是簡單的事情，任何書都有它的參考價值、還有作者日以夜繼的心血在裡面，這些心血都應該被尊重。這樣的形容，單純為了說明「堅果殼書」的特色。

「寫一本堅果殼書。」是我很早的目標，雖然過去也寫了許多書，但都還達不到我想像中的堅果殼書標準。希望有朝一日能達成這個目標。

前天的日記曾提到，去年利用了七個月的時間在做「巡迴」，不過這是平常工作之餘的活動；「研究」佔了我日常工作大約50%-60%的時間，透過課程巡迴與演講，除了可以分享部份研究心得外，也有助於調整後續的研究進度。在這七個月的時間裡，大致上都把Android Framework的內部設計以及原理做了研究以及整理，對於整體框架的實作，也準備了一份簡報，希望可以用「堅果殼」的精神，化繁為簡，用一種簡而易懂的方式，將複雜的Android框架設計放進堅果殼裡。

有鑑於此，今年希望規劃大約3場演講，親身說法，為大家說明堅果殼裡的內容。

如何把龐大的Android框架，簡單表達，儘量簡化而保持原始精神，確實是一大挑戰，但也很有趣。希望「Jollen 的 Android Framework in a Nutshell」演講，能成功挑戰這個難題。目前正在規劃第1場堅果殼演講，歡迎大家提供意見供活動小組參考。活動時間將另行公佈，期望與大家交流，並不吝指教。
</description>
         <link>https://www.jollen.org/blog/2010/01/jollen-android-in-a-nut-shell.html</link>
         <guid>https://www.jollen.org/blog/2010/01/jollen-android-in-a-nut-shell.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sun, 03 Jan 2010 18:54:11 +0800</pubDate>
      </item>
            <item>
         <title>Android 的 HAL 技術, #8: 實作 HAL Stub</title>
         <description><![CDATA[承日記「<a href="http://www.jollen.org/blog/2009/12/android-hal-stub-implementation.html">Android 的 HAL 技術, #6: 小結 HAL stub 實作步驟</a>」與「<a href="http://www.jollen.org/blog/2009/12/android-hal-get-proxy-object.html">Android 的 HAL 技術, #7: 取得 Proxy Object</a>」。在了解基本的觀念，以及架構上的設計後，接著就可以開始實作 HAL Stub 了。以下是 LED Stub 的實作範例，將程式碼儲存為 led.c：

<pre>static struct hw_module_methods_t led_module_methods = {
    open: led_device_open
};
　
const struct led_module_t HAL_MODULE_INFO_SYM = {
    common: {
        tag: HARDWARE_MODULE_TAG,
        version_major: 1,
        version_minor: 0,
        id: LED_HARDWARE_MODULE_ID,
        name: "Simple LED module",
        author: "The Mokoid Open Source Project",
        methods: &led_module_methods,
    }
　
    /* supporting APIs go here */
};</pre>

這段程式碼實作了圖1的設計， led_module_t 是 Stub 的主體結構（或稱為主類別），其符號名稱須取名為 HAL_MODULE_INFO_SYM，不可更改。任何的 Stub 主類別名稱都須命名為 HAL_MODULE_INFO_SYM。

<a href="http://www.jollen.org/blog/2010/01/02/hw_module_methods_t.gif" target="_blank"><img src="http://www.jollen.org/blog/2010/01/02/hw_module_methods_t.gif" width="480" border="0" /></a>
圖1：LED Stub的設計(OOD)

幾個重要的欄位如下：

<ul><li>tag：須指定為 HARDWARE_MODULE_TAG</li>
<li>id：指定為 HAL Stub 的 module ID，我們的範例為”LED”</li>
<li>methods：struct hw_module_methods_t，為 HAL 所定義的「method」</li>
<li>struct hw_module_methods_t 是由 HAL 定義的標準介面，目前的 AOSP（Android Open Source Project）實作裡包含一個”open”介面</li>
</ul>

"Open" 是一個介面（interface），這意味著 HAL Stub 必須實作此介面，這個部份更是 HAL 的重點。]]></description>
         <link>https://www.jollen.org/blog/2010/01/android-hal-stub-design-implement.html</link>
         <guid>https://www.jollen.org/blog/2010/01/android-hal-stub-design-implement.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 02 Jan 2010 23:31:08 +0800</pubDate>
      </item>
            <item>
         <title>二零一零新年快樂：新希望與期許！</title>
         <description>新的一年，祝福大家心想事成。開放源碼(Open Source)與開放平臺(Open Devices)是相當有意思的二個主題，今年，二零一零年，希望可以認識更多志同道合的朋友；也祝福大家，技術進步、研究豐碩。

今年，需要投注更多心力到Android相關的技術研究工作，以及產品規劃。去年，確實是頗為有趣的一年，自已也投入了相當多時間在技術研究、廠商交流以及教育訓練工作上。七個月多的時間，不斷巡迴台北、上海、深圳、北京四大城市，在這些城市進行Android以及Embedded Linux的培訓工作，其間，也和許多技術人員，以及廠商有許多交流，甚致深入的合作。

這段過程中，令人感受最深的，便是「快速成長」四個大字。

「二零零九年，大陸總計賣出一千三百萬輛台汽車。」有一天在前往北京首都國際機場的出租車上，所聽到的一段新聞。大陸目前約有一百家汽車廠，一般消費者，從下訂到取車，可能需要長達三個月的時間。在一些主要城市，整條路上的汽車經銷商，甚致沒有一家有「現貨」可供銷售。

若以每年中國經濟成長率超越美國約5%來計算，中國的GDP在二零二零年，將超過美國。以每年8%來計算，到時生產總額也將再翻一番。今年，二零一零年，中國也要做好GDP超越日本的準備。不過，畢竟經濟這件事也不是做為技術工作者的我，能通盤了解的，除了平日讀讀新聞、看看發佈數據外，還是專注在技術本業上更實際一些，所以，在這裡分享一個技術工作上的看法，供大家參考指教。

有關BAE的應用，今年可能得到大幅度的份額增長。BAE的全名是Browser based Application Engine。這並非很新潮的技術，在另外二大手機作業系統：Windows Mobile與Symbian上，也都有這樣的應用。BAE其實不只是在手持裝置上，在Web Service或是個人電腦上，也都能看到它的踪跡。

重視開放平臺的BAE應用，把它加入產品設計與規劃的要項，是過去我給一家廠商提出的建議。過去，Android Market是個棘手問題，但現在開始，情況會有所改變。這是獨立應用軟體開發商或獨立品牌的好機會。Android Market的發展看起來將有很大的突破； 目前，在我所參與的一個技術工作上，已經將資源的分配由框架與底層的驅動程式，部份轉換到應用(Applications)的開發與研究工作上，就是希望在應用開發上加重比重。

上述提到的BAE技術，因為改變應用軟體開發的模式，也要多加的重視。

去年十二月，在教育部嵌入式軟體聯盟的一場開放手機研討會上，我邀請了北京的eoeAndroid社群做了一次遠端視訊連線，為現場同學做了一些Android方面的介紹。「社群是開放平臺的靈魂。」因為Android技術，出現了許多技術社群，「熱情」是他們的共通點。從eoeAndrod的談話中，可以感受到這點，以及技術研究的精神「自身投入、明確務實」。台灣的0xlab也是一個典範，我們看見了追求技術卓越的熱情與貢獻，還有務實的精神。

因為Android的出現，eoeAndroid團隊一些人辭去他們原本的工作，一起為建立社群而努力。在周遭所認識的朋友裡，他們並不是唯一有這種堅定決心的工作者。 回顧過去的工作，深刻感受到不同城市間的工作文化、技術發展重點或是想法，都有很不一樣的特色；技術研究是個有趣的工作，給自已的期許是，當一個稱職的執行者，努力並有效率地完成專案。</description>
         <link>https://www.jollen.org/blog/2010/01/happy-new-year-2010.html</link>
         <guid>https://www.jollen.org/blog/2010/01/happy-new-year-2010.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Fri, 01 Jan 2010 15:14:36 +0800</pubDate>
      </item>
            <item>
         <title>Android 的 HAL 技術, #7: 取得 Proxy Object</title>
         <description><![CDATA[延續上一則日記的介紹，在完成HAL Stub的實作後，緊接著的工作就是撰寫Native Service。

談了許多「Android Service」以及「HAL Stub」，這裡再補充一點。Android作業系統啟動時，會執行一個process稱為servicemanager。Servicemanager process負責管理、提供並保存「Android Service」。Android Service為Java層，因此接下來會透過JNI來呼叫C/C++層的Native Service。

廣義來說，Native Service也提供Runtime的功能，即Core Library層。Runtime的重要工作之一為「取得HAL Stub所提供的API」，因此這是撰寫完整Native Service的前哨站。

<strong>什麼是 Proxy Object？</strong>

Native Service呼叫HAL的hw_get_module() API取得stub物件，即HAL_MODULE_INFO_SYM。

HAL會去尋找HAL Stub檔案，HAL Stub是以*.so檔的形式存在，並佈署於/system/lib/hw目錄下。HAL會根據module ID以及”ro.product.board”去尋找相對應的*.so檔，以我們的LED範例來說，HAL會回傳回led_module_t結構的物件（an instance of led_module_t class）給Native Service。

我們把HAL回傳給Native Service的資料稱為「Stub Object」或是「Proxy Object」，即先前所提及的「代理人」觀念。Native Service透過代理人與 Linux 驅動程式溝通。這個過程的觀念如圖1所示。

<img src="http://www.jollen.org/blog/2009/12/26/hal-get-proxy-object.gif" width="480" />
圖1：取得Proxy Object

hw_get_module()，這是HAL所提供的API，也是實作HAL Stub最重要的一個API，不過另人驚奇的地方是，這也是目前AOSP裡HAL層所提供的唯一一個API。
]]></description>
         <link>https://www.jollen.org/blog/2009/12/android-hal-get-proxy-object.html</link>
         <guid>https://www.jollen.org/blog/2009/12/android-hal-get-proxy-object.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 26 Dec 2009 23:36:55 +0800</pubDate>
      </item>
            <item>
         <title>Android 的 HAL 技術, #6: 小結 HAL stub 實作步驟</title>
         <description><![CDATA[在討論了不少基本概念後，在這裡小結一下 HAL stub 的實作步驟。HAL stub 的起頭是「繼承 HAL 的 struct hw_module_t」，這是 HAL stub 的設計理念，除了讓架構的條理分明外，也容易做後續擴充與維護。以下改用實用上的習慣用語，小結一下 HAL stub 實作步驟，並提供一段例。

<strong>HAL Stub 實作步驟(Implementation)
</strong>

1. 設計自已的wrapper data structure

* 編寫led.h
* 定義 struct led_module_t
* 框架提供的 struct hw_module_t 必須放在第一個 field、並取名為 common
* 請參考 hardware/hardware.h

2. led_module_t的意義

宣告初始化時期(new object)的 supporting API、在 constructor 裡會使用到。

3. 定義 led_control_device_t

宣告控制時期的 supporting API、在 Manager API 裡會使用到。設計上的細節在後續文章再做整理。

4. 每個 HAL stub 都要宣告 module ID

5. 宣告 Stub operations 並實作 callback functions

Stub 的 operations 結構符號名稱須取名為 HAL_MODULE_INFO_SYM、此符號名不可更改。

<strong>範例：led.h</strong>

<pre>#include &lt;hardware/hardware.h&gt;
   
#include &lt;fcntl.h&gt;
#include &lt;errno.h&gt;
   
#include &lt;cutils/log.h&gt;
#include &lt;cutils/atomic.h&gt;
   
/*****************************************************************************/
   
struct led_module_t {
   struct hw_module_t common;
};
   
struct led_control_device_t {
   struct hw_device_t common;
   /* supporting control APIs go here */
   int (*set_on)(struct led_control_device_t *dev, int32_t led);
   int (*set_off)(struct led_control_device_t *dev, int32_t led);
};
   
/*****************************************************************************/
   
#define LED_HARDWARE_MODULE_ID "led"</pre>
   
實作上的細節在這裡不再多做說明，關於設計上的細節將另行整理。
]]></description>
         <link>https://www.jollen.org/blog/2009/12/android-hal-stub-implementation.html</link>
         <guid>https://www.jollen.org/blog/2009/12/android-hal-stub-implementation.html</guid>
        
        
         <pubDate>Fri, 04 Dec 2009 22:49:08 +0800</pubDate>
      </item>
            <item>
         <title>Android 的 HAL 技術, #5: 繼承 HAL 的 struct hw_module_t</title>
         <description><![CDATA[撰寫 HAL stub 除了要具備系統程式（systems software）的觀念外（這是基礎），「思考方式的改變」也是重要的一堂課。

<strong>思考方式哪裡不同？</strong>

實作 HAL stub 的首要工作是「繼承 struct hw_module_t 抽象型別」。Class（類別）屬於一種抽象型號（ADT）。

首先，引入最重要的標頭檔（header file）：

<blockquote>#include &lt;hardware/hardware.h&gt;</blockquote>

接著，再定義一個「MODULE ID」。這個 mdoule ID 將會被 HAL 層用來尋找 HAL stub。我們舉最簡單的裝置類型「LED」來做為範例：

<blockquote>#define LED_HARDWARE_MODULE_ID "led"
</blockquote>
繼承 HAL 的 struct hw_module_t 抽象型別（即 base class 的概念），並取名為 struct <strong>led</strong>_module_t（即 derived class）：

<blockquote><pre>struct led_module_t {
   struct hw_module_t common;
};</pre></blockquote>

以資料結構的角度來看，這裡的做法只是宣告了一個抽象資料型別（Abstract Data Type），以提升程式碼的結構化特性。但是，這裡需要以架構的角度來解釋，「Android HAL規定不要直接使用struct hw_module_t」，原文的意思是要我們做類別繼承。

<strong>實作繼承</strong>

在C語言裡實作繼承的方式，大致如下：

1. 宣告一個 data structure 將原始的基本結構包裝起來
2. 將原始的基本結構放在第一個 field

因此，可以思考如下：

<blockquote><pre>struct led_module_t {
   struct hw_module_t common;
   　
   /* Place attributes here. */
　
   /* Place methods here. */
};</pre></blockquote>

唯一，這裡的實作在OO特性上，缺乏像是public與private的封裝性。但這裡的重心是，以OO的方式思考，會如何改變過去的 C 程式寫作習慣？最明顯的地方是，<strong>程式碼的寫作風格有了很大的改變</strong>。]]></description>
         <link>https://www.jollen.org/blog/2009/12/android-hal-derived-c-language.html</link>
         <guid>https://www.jollen.org/blog/2009/12/android-hal-derived-c-language.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 01 Dec 2009 22:09:14 +0800</pubDate>
      </item>
            <item>
         <title>HAL stub的商業價值與想法</title>
         <description><![CDATA[<strong>HAL stub是與硬體關係最密切的軟體</strong>

HAL stub因為是以獨立的*.so檔形式，佈署於/system/lib/hw目錄下，同時，執行時期（runtime）是以提供操作（provide operations）的方式來運作，所以可以達到HAL的目的之一，即界面（interface）的設計。

再加上HAL stub是與硬體關係最密切的軟體，所以讓我們有了不同的思考。

<strong>Android分支(branch)工廠</strong>

定義好界面再實作stub，這樣的架構讓stub，也就是*.so檔，可以元件化（component）。HAL以物件導向的思考方式設計與實作。元件可以抽換或重用，再配合過去提到的「Android分支建立」觀念，「HAL stub 服務工廠」的想法便油然而生。

<strong>提供HAL(與*.so)代工服務與解決方案</strong>

由驅動程式專業公司進行設計與開發，即軟體服務的一種想法，基於：

1. HAL stub軟體可封閉源碼，因此也較利於商業模式推展
2. 提供關鍵IC零組件封閉原始碼的HAL stub軟體 (Proprietary software)
3. 不但是「軟硬整合」，也是Android綁定硬體平臺的一種策略

當然，想法往往都是「有感而發」，現實面能不能執行又是一回事了。   ;-)]]></description>
         <link>https://www.jollen.org/blog/2009/11/hal-proprietary_software.html</link>
         <guid>https://www.jollen.org/blog/2009/11/hal-proprietary_software.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sun, 29 Nov 2009 14:20:17 +0800</pubDate>
      </item>
            <item>
         <title>Android 的 HAL 技術, #4: Android Service 與 HAL Stub</title>
         <description><![CDATA[目前為止，我們提了「HAL Stub」的觀念，了解到 stub 是一種代理人的觀念，架構設計上，採取「provide operations」與「callback」機制，而不是採用「module」即 library 的 direct function call 做法。

接著，又提到「採取Service架構的方式」。在講解HAL stub的實作細節前，需要大略了解一下Android Service與HAL stub的關係；因為，架構設計上是「透過Android Service取得HAL stub提供的operations」。

取得HAL stub operations的程式碼實際上是實作在Native Service裡，相關的實作細節留待後續再做說明。

<strong>Android Service與HAL stub的關係</strong>

應用程式（Application）使用了Manager API，Manager API經由Android Service來到Native Service層，最後Native Service層再引用（invoke）HAL stub。這個過程，總共經過了以下3個介面，如下圖。

<img alt="android-service-hal.png" src="http://www.jollen.org/blog/2009/11/28/android-service-hal.png" width="423" height="329" />

1. 這是一個 remote interface，應用程式與Android Service在不同的 process 上執行。

2. 這是 Java native interface，實作上透過 JNI table 來對應 native method 與 native function。

3. 理論上，這是 HAL 層，實作上則是使用 HAL API 先取得 stub operations，再由 native service callback stub。
]]></description>
         <link>https://www.jollen.org/blog/2009/11/android-hal-android-service-hal.html</link>
         <guid>https://www.jollen.org/blog/2009/11/android-hal-android-service-hal.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 28 Nov 2009 21:56:57 +0800</pubDate>
      </item>
            <item>
         <title>Android 的 HAL 技術, #3: 小探Android Service與Native Service</title>
         <description><![CDATA[前二篇教學提到「採用Service架構整合HAL的做法」。這裡再針對HAL如何採用Service架構與框架整合做一個概念的介紹。

Android的Service分為二種：Android Service與Native Service。

<strong>Android Service</strong>

Android Service又稱為Java Service，是實作在框架層（framework）裡的「Server」。這裡所講的「Service」是System Service，又稱為Server，與應用程式設計上所討論的Service（android.app.Service）不同。Android Service以Java撰寫。

<strong>Native Service</strong>

Native Service則是實作在Runtime層裡的Server。架構設計上，我們有二個選擇，一個是實作Android Service、再透過JNI與HAL stub溝通；另一個選擇是，跳過Android Service，讓Application（Manager API）直接與Native Service溝通。

未來的Android發展趨勢，應會以第二種做法為主，即Manager API直接與Native Service溝通，以達到更好的效能表現。

<strong>延伸閱讀</strong>

* 2009.10.14: <a href="http://www.jollen.org/blog/2009/10/android-hal-service-introduction.html">Android 的 HAL 技術, #2: 採用Service架構方式</a>
* 2009.10.08: <a href="http://www.jollen.org/blog/2009/10/android-hal-status-report.html">Android 的 HAL 技術, #1: 簡介與發展現況</a>]]></description>
         <link>https://www.jollen.org/blog/2009/11/android-hal-android-native-service.html</link>
         <guid>https://www.jollen.org/blog/2009/11/android-hal-android-native-service.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 27 Nov 2009 01:59:51 +0800</pubDate>
      </item>
            <item>
         <title>Symbian釋出microkernel</title>
         <description><![CDATA[今天重要事，與開放手機有關，但不是Android的新聞，「Symbian」幾天前發佈的新聞稿指出「<a href="http://symbian.org/media/news/pr2009_10.php">EPL RELEASE OF MICROKERNEL DEMONSTRATES PROGRESS TOWARDS OPEN SOURCE GOAL</a>」。

自從Nokia收購Symbian其餘股權後，便開始計畫將Symbian開放、成為一個開放手機平臺，經過一段時間，Symbian Foundation幾天前達成一個重要的里程碑。如上述新聞稿所述，Symbian的microkernel（EKA2）以EPL（Eclipse Public License）釋出部份套件的原始碼，其中包含了Hardware Services套件。

開發者現在可以由[<a href="http://developer.symbian.org/wiki/index.php/Category:Kernel_&_Hardware_Services">developer.symbian.org</a>]下載原始碼，並且可使用模擬器（Qemu）進行試驗。

一個比較有趣的地方是，在眾多的FOSS授權裡，Symbian Foundation選擇以EPL授權釋出原始程式碼。EPL與GPLv2或v3授權都是不相容的，其中一點是，只有該軟體的擁有人（owner）或是已經得到了owner的許可，其他人（指contributors）才能將程式碼放到其它FOSS授權的軟體裡使用。也就是說，我們不能自已把EKA2的程式碼，放進Linux kernel或BSD kernel裡使用。此外，貢獻者（contributors）也不能暱名發佈程式碼，在EPL的條款裡，貢獻者需要具名。

另外一個EPL與現有其它FOSS授權不同的地方是「衍生著作（derivative work）」的定義。EPL不以程式庫的連結（linking）來定義衍生著作，而是以美國著作權法上的說明來定義。也就是，以獨立形式（例如module）實做了一個EPL軟體上所沒有的功能時，這個module就不算是一個衍生著作。

是否為衍生著作關係到授權條款，只要是衍生著作都必須是EPL授權。也就是，自已設計的獨立模組可以隨意授權。


]]></description>
         <link>https://www.jollen.org/blog/2009/10/symbian-release-microkernel.html</link>
         <guid>https://www.jollen.org/blog/2009/10/symbian-release-microkernel.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Sun, 25 Oct 2009 22:28:48 +0800</pubDate>
      </item>
            <item>
         <title>Android 的 HAL 技術, #2: 採用Service架構方式</title>
         <description><![CDATA[在上一篇日記裡，我們介紹了Android HAL的大概念。接下來，將慢慢地進行細部的分析與研究。首先，我們先針對HAL的現況先做討論，後續再針對HAL的設計提出觀念。

<img alt="HAL-service-jni.png" src="http://www.jollen.org/blog/2009/10/14/HAL-service-jni.png" width="415" height="549" />
圖1：採用Service架構與不採用Service架構

如圖1，應用程式存取驅動程式的過程，可區分為以下二種：

<ul><li>採用Service架構方式</li>
<li>不採用Service架構方式</li>
</ul>

採用Service架構方式是比較標準的做法，即圖上藍色線的部份；紅色線的部份為非Service架構式的做法。先前，在「Android 驅動開發關鍵技術：HAL及移植要領」演講中最後所提出的一個LED範例，即是一種非架構式的做法，簡報上有一段範例程式碼，歡迎下載參考。

<div style="width:425px;text-align:left" id="__ss_2173827"><a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/jollen/android-hal-introduction-libhardware-and-its-legacy" title="Android HAL Introduction: libhardware and its legacy">Android HAL Introduction: libhardware and its legacy</a><object style="margin:0px" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=android-os-driver-hal-r2-091009031110-phpapp01&stripped_title=android-hal-introduction-libhardware-and-its-legacy" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=android-os-driver-hal-r2-091009031110-phpapp01&stripped_title=android-hal-introduction-libhardware-and-its-legacy" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object><div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;">View more <a style="text-decoration:underline;" href="http://www.slideshare.net/">documents</a> from <a style="text-decoration:underline;" href="http://www.slideshare.net/jollen">Jollen Chen</a>.</div></div>

採取Service架構的方式，是建議的做法，當然因為這是標準架構，也應該採用。

Service在Android框架裡的角色是「存取底層硬體」，往上層的話，可以和應用程式溝通。因此，採用標準的Service做法，好處是在資料存取（data communication）的處理較為系統化。這方面，Android提供了標準的處理架構，後續再進行討論。

圖上的「core libraries」即是Service程式碼的實作，也就是，Android應用程式透過JNI（Dalvik）來到Service這一層，再透過Service載入*.so檔；而非標準做法則是讓應用程式直接透過JNI來載入*.so檔。

<strong>不使用Service架構</strong>

紅色的過程，因為不使用Service架構，因此「框架整合」的工作量比較小，甚致大部份的實作都不需要改動框架本身。這樣的做法，就有點像是「跳過framework」的方式；相對的，此時應用程式開發者需要考慮的設計議題就比較多。

例如，當遇到block operation時，是否需要獨立的Java thread來處理？]]></description>
         <link>https://www.jollen.org/blog/2009/10/android-hal-service-introduction.html</link>
         <guid>https://www.jollen.org/blog/2009/10/android-hal-service-introduction.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Wed, 14 Oct 2009 18:17:29 +0800</pubDate>
      </item>
            <item>
         <title>Android 的 HAL 技術, #1: 簡介與發展現況</title>
         <description><![CDATA[Android 的 HAL（硬體抽像層）是 Google 因應廠商「希望不公開源碼」的要求下，所推出的新觀念，其架構如下圖。雖然 HAL 現在的「抽象程度」還不足，現階段實作還不是全面符合 HAL 的架構規劃，不過也確實給了我們很好的思考空間。

<img alt="android-hal.jpg" src="http://www.jollen.org/blog/2009/10/08/android-hal.jpg" width="500" height="378" />
圖1：Android HAL 架構規劃

這是 Patrick Brady (Google) 在2008 Google I/O 所發表的演講「<a href="http://sites.google.com/site/io/anatomy--physiology-of-an-android">Anatomy & Physiology of an Android</a>」中，所提出的 Android HAL 架構圖。從這張架構圖我們知道，HAL 的目的是為了把 Android framework 與 Linux kernel 完整「隔開」。讓 Android 不至過度依賴 Linux kernel，有點像是「kernel independent」的意思，讓 Android framework 的開發能在不考量驅動程式的前提下進行發展。

在 Android 原始碼裡，HAL 主要的實作儲存於以下目錄：

1. libhardware_legacy/ - 過去的實作、採取程式庫模組的觀念進行
2. libhardware/ - 新版的實作、調整為 HAL stub 的觀念
3. ril/ - Radio Interface Layer

在 HAL 的架構實作成熟前（即圖1的規劃），我們先就目前 HAL 現況做一個簡單的分析。另外，目前 Android 的 HAL 實作，仍舊散佈在不同的地方，例如 Camera、WiFi 等，因此上述的目錄並不包含所有的 HAL 程式碼。

<strong>HAL 的過去</strong>

<img alt="android-hal-libhardware_legacy.png" src="http://www.jollen.org/blog/2009/10/08/android-hal-libhardware_legacy.png" width="330" height="264" />
圖2：Android HAL / libhardware_legacy

過去的 libhardware_legacy 作法，比較是傳統的「module」方式，也就是將 *.so 檔案當做「shared library」來使用，在 runtime（JNI 部份）以 direct function call 使用 HAL module。透過直接函數呼叫的方式，來操作驅動程式。

當然，應用程式也可以不需要透過 JNI 的方式進行，直接以載入 *.so 檔（dlopen）的做法呼叫 *.so 裡的符號（symbol）也是一種方式。

<strong>HAL 的現實狀況</strong>

<img alt="android-hal-libhardware.png" src="http://www.jollen.org/blog/2009/10/08/android-hal-libhardware.png" width="328" height="313" />
圖3：Android HAL / libhardware

現在的 libhardware 作法，就有「stub」的味道了。HAL stub 是一種代理人（proxy）的概念，stub 雖然仍是以 *.so 檔的形式存在，但 HAL 已經將 *.so 檔隱藏起來了。Stub 向 HAL「提供」操作函數（operations），而 runtime 則是向 HAL 取得特定模組（stub）的 operations，再 callback 這些操作函數。這種以 indirect function call 的實作架構，讓 HAL stub 變成是一種「包含」關係，即 HAL 裡包含了許許多多的 stub（代理人）。Runtime 只要說明「類型」，即 module ID，就可以取得操作函數。

<strong>HAL 的未來發展？</strong>

新的 HAL 做法，傾向全面採用 JNI 的方式進行。也就是，在 Android 的架構中，修改 Android runtime 實作（即「Core Library」），在取得 HAL 模組的 operations 後再做 callback 操作。將 HAL 模組完全放在 HAL 裡面。

]]></description>
         <link>https://www.jollen.org/blog/2009/10/android-hal-status-report.html</link>
         <guid>https://www.jollen.org/blog/2009/10/android-hal-status-report.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 08 Oct 2009 02:05:19 +0800</pubDate>
      </item>
            <item>
         <title>走入雲端的開發套件</title>
         <description><![CDATA[在 Linux for Devices 上的一則新聞「<a href="http://www.linuxfordevices.com/c/a/News/SSV-SSVECC-eSOMEK1ECC-and-eSOMEK1ECC/">Embedded development kits link up to Amazon cloud</a>」，開發板也趕流行，跑上雲端。

這是以 Debian Linux 為基礎的 Embedded Linux 開發套件，並且可以連接 Amazon 的 S3 cloud service。有趣的地方是，文中以「Embedded Cloud Computing」的方式來形容，令人有不同的發想空間。「Embedded Cloud Computing」是「連上雲端的嵌入式硬體模組」的概念。「自動化裝置」是一種嵌入式系統，讀了這篇新聞後，讓人有另外一種像想。

如果能把雲端服務，嵌入到一些自動化裝置裡，因為這類型的裝置通常不經由人類操作，所以等於進入了「設備自動連結雲端」的時代。自動化裝置可以主動將擷取的裝置，送上雲端、儲存、分析並加以應用。例如，設計一個可隨時發送座標的隨身模組。]]></description>
         <link>https://www.jollen.org/blog/2009/10/embedded-cloud-computing.html</link>
         <guid>https://www.jollen.org/blog/2009/10/embedded-cloud-computing.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 08 Oct 2009 01:46:55 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#31: R.drawable 應用-如何使用NinePatch圖檔</title>
         <description><![CDATA[上一篇日記介紹了「如何製作NinePatch圖檔」，因此有讀者問到「如何寫程式」，因此特別補上這篇教學。

<strong>開始寫程式: HelloNinePatch</strong>

範例HelloNinePatch的實作方式如下。

Step 1. 建立一個新的Android專案，命名為HelloNinePatch。

Step 2. 將arrow.9.png托曳(drag)到HelloNinePatch專案裡的「res/drawable」目錄下。如圖1。

<img alt="ninepatch-res-1.png" src="http://www.jollen.org/blog/2009/09/20/ninepatch-res-1.png" width="244" height="208" />
圖1: 將arrow.9.png放進res/drawable資料夾

Step 3. 修改UI（res/layout/main.xml），設計出上一篇教學(#30)裡的圖2畫面。main.xml的內容如下。

<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    &gt;
&lt;Button
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="small world"
    android:textSize="12sp"
    android:background="@drawable/arrow"
    /&gt;
&lt;Button
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="big world"
    android:textSize="24sp"
    android:background="@drawable/arrow"
    /&gt;
&lt;Button
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="super world"
    android:textSize="48sp"
    android:background="@drawable/arrow"
    /&gt;
&lt;/LinearLayout&gt;</pre>

這裡的做法是，在UI上擺放Button元件，並設定Button上的文字及大小。透過「android:background」屬性的設定，我們將Button的背景設定為「@drawable/arrow」，即「drawable資源（drawable/目錄下）裡的arrow圖檔」，Android框架會去找到arrow.9.png檔案。

因為arrow.9.png是一張NinePatch圖檔，因此會隨著Button上的文字大小延展。

Step 4: 完成HellNinePatch

程式碼不需要做任何修改，直接執行HelloNinePatch專案即可。
]]></description>
         <link>https://www.jollen.org/blog/2009/09/jollen-android-programming-31.html</link>
         <guid>https://www.jollen.org/blog/2009/09/jollen-android-programming-31.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 20 Sep 2009 22:39:33 +0800</pubDate>
      </item>
            <item>
         <title>清華大學Android種子教學培訓課程: Day1 上課紀錄</title>
         <description><![CDATA[<img alt="android-nthu-day1.png" src="http://www.jollen.org/blog/2009/09/08/android-nthu-day1.png" width="580" height="327" />

第一天的「Android種子培訓課程」以應用開發為主軸，介紹了Android應用程式入門的課程。現場有許多大學的老師，因此在課程進行中，特別給了一些建議。針對Android應用程式入門教學來說，做「API走訪式」的教學比較無法切中正題，而且也讓課程顯得乏味。因此，我給了二點小建議：

1. Android應用的教學，以「應用程式的模式」為重心，因為Android應用程式的入門重點在於Activity/Service、R.java、XML layout、XML attributes、View（UI）、Intent等「模式」的觀念，所以最好不要採取API介紹式的教學方法

這裡的所提的「模式」即「撰寫程式」的方法。

2. 透過Android應用程式建立對框架（Framework）的基本認識。Android作業系統的一大重點在於「框架的設計」，而對應用程式進行較深度的分析，是了解框架設計的一個好方法。

例如，今天在課堂中所提到的「應用程式向框架取得（get）物件（object）」，而不是「應用程式自行建立物件（"new"一個object）」的觀念。另外一個觀念，則是「應用程式要避免建立物件」，這是Android Dev Guide提到的「為效能而設計」的第一個條文。

第二天的課程將會開始介紹Android開發平臺，並展示一些「Lab」範例。]]></description>
         <link>https://www.jollen.org/blog/2009/09/nthu-android-training-day1.html</link>
         <guid>https://www.jollen.org/blog/2009/09/nthu-android-training-day1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 08 Sep 2009 01:58:41 +0800</pubDate>
      </item>
            <item>
         <title>深耕校園、 清華大學Android種子教學培訓課程</title>
         <description><![CDATA[<strong>Android校園計畫</strong>

產學官三界過去在Android的發展做了很多腦力激盪的工作，這是一個很好的現象，有助於Android熱潮的延續，以及各界對Android作業系統的重視。提到Android研發能量的建立，最根本的治理方式是「深耕校園」。

因此，現階段要務是「深耕校園」。實際做法建議是「建立種子」。建立種子師資或種子學生，透過導入課程至大學課程的方式，讓大一與大二的學生「認識」並「開始接觸」Android作業系統；種子學生的建立，可以鎖定大三以上、研究生或是以實驗室為單位，做為培訓對象。這個階段的做法，可以建立採取「種子培訓營」的方式進行。

學生如果能被適當的訓練，就能將把創造力，或是想像力，轉換成「具體」的成果。一個實際的例子是，如果能訓練非資訊本科系的學生撰寫Android應用程式，學生才能有能力把想法轉換成「程式原型」、才能展現創意。因此「深耕校園」的目標應該是要更「普及」，而不是只導入本科系。

活動詳情可參閱<a href="http://esw.cs.nthu.edu.tw/">嵌入式軟體聯盟網站</a>，以下是課程資訊。希望藉由自已在教育訓練方面的經驗，貢獻棉薄之力。

<strong>課程簡介</strong>
	
本課程的目的是培訓大專校院之Android種子教學師資。課程針對Android系統，由底層框架、驅動程式、移植、到應用開發，做一完整的介紹，並利用OpenMoko FreeRunner手機為實驗平臺，實際動手實作。整個課程設計以符合業界實際需要為重點，藉由本課程之培訓，讓學校能充實Android研發能量，未來達到產學結合的效果。

時間：98年9月7日 (星期一) 至9月10日 (星期四)
地點：清華大學資訊電機館資工系3F 328室
資格：國內各大專校院教師或學生，對Android系統之教學及開發有濃厚興趣者
名額：40人
費用：NT$ 2,500 (含午餐、設備使用費、講義；報名後通知繳費)
主辦單位：教育部顧問室嵌入式軟體聯盟、清華大學電腦與通訊科技研發中心、清華大學資訊工程學系
協辦單位：Jollen’s Consulting、清華大學OpenLab
聯絡人： 	吳少文小姐  Tel: 03-5742410  E-mail: swwu@mx.nthu.edu.tw

]]></description>
         <link>https://www.jollen.org/blog/2009/08/android-campus-plan-taiwan.html</link>
         <guid>https://www.jollen.org/blog/2009/08/android-campus-plan-taiwan.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 29 Aug 2009 11:36:16 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#30: R.drawable 應用-製作NinePatch圖檔</title>
         <description><![CDATA[<strong>什麼是NinePatch圖檔</strong>

NinePatch是一種「可延展」的PNG圖檔。NinePatch圖檔的用途是製作「可隨文字大小縮放」的圖片，如圖1。

<img alt="ninepatch-1.png" src="http://www.jollen.org/blog/2009/08/22/ninepatch-1.png" width="320" height="480" />
圖1: 文字背景可隨著文字大小縮放

NinePatch是很有用的圖片格式，可做為widget的「背景圖」。如圖１的範例，其應用程式的設計如下：

<ul><li>文字部份使用TextView元件</li>
<li>使用TextView的XML attribute來設定文字大小</li>
<li>使用TextView的XML attribute來設定一張背景圖</li>
<li>使用NinePatch圖片做為背景圖，如此一來背景圖就可以隨著文字大小縮放</li></ul>

首先，第一個工作就是「製作NinePatch圖檔」，方式如下。

Step 1. 準備一張原始的PNG圖檔，如圖2。

<img alt="ninepatch-2.png" src="http://www.jollen.org/blog/2009/08/22/ninepatch-2.png" width="128" height="105" />
圖2: 原始PNG圖檔（arrow.png）

Step 2. 啟動Android提供的draw9patch工具，直接執行Android SDK tools/目錄下的draw9patch執行檔即可，如圖3。

<a href="http://www.jollen.org/blog/2009/08/22/ninepatch-3.png" target="_blank"><img alt="ninepatch-3.png" src="http://www.jollen.org/blog/2009/08/22/ninepatch-3.png" width="480" border="0" /></a>
圖3: Android SDK提供的draw9patch工具（點擊看原圖）

Step 3. 開啟原始PNG圖檔，編輯圖檔，如圖4。

<a href="http://www.jollen.org/blog/2009/08/22/ninepatch-4.png" target="_blank"><img alt="ninepatch-4.png" src="http://www.jollen.org/blog/2009/08/22/ninepatch-4.png" width="480" border="0" /></a>
圖4: 開始編輯圖檔（點擊看原圖）

<strong>如何編輯NinePatch圖檔</strong>

NinePatch圖檔的製作方式是「繪製二條線」，分別在原始圖檔的上方與左方繪出二條「黑線」，黑線所交集的區域即為「可延展」區域。如下圖的粉紅色區域。

<img alt="ninepatch-5.png" src="http://www.jollen.org/blog/2009/08/22/ninepatch-5.png" width="433" height="427" />
圖5: 定義延展區

「可延展區」是Android框架用來擺放文字的區域，換句話說，文字(TextView)只會被放置在粉紅色區域<strike>，並且擺放的原則是「對準粉紅區域的中心點」，即上下置中、左右也置中</strike>。非「可延展區」，即綠色部份，並不會隨著文字的大小<strike>縮放</strike>延展。所以：

1. 綠色區域是固定大小區域
2. 粉紅色區域是可延展區域、文字擺放於此

圖中的「二條黑線」是怎麼畫出來的呢？方式如下。

Step 4. 移動「Zoom」調整圖檔比例，讓「斑馬線」的大小能適中，如圖6。

<img alt="ninepatch-6.png" src="http://www.jollen.org/blog/2009/08/22/ninepatch-6.png" width="598" height="498" />
圖6: 調整比例

Step 7: 畫黑線

斑馬線是用來畫黑線的區域，怎麼畫黑線呢？用滑鼠點斑馬線就可以了。要怎麼刪除黑線上？按住「Shift」再點斑馬線即可。斑馬線很不好點，所以如果不是要特意訓練操作滑鼠的技巧以及考驗眼力，善用「Zoom」功能可以幫助黑線的繪製。

勾選「Show patches」選項，即可顯示粉紅色區域，如圖7。

<img alt="ninepatch-7.png" src="http://www.jollen.org/blog/2009/08/22/ninepatch-7.png" width="542" height="451" />
圖7: 即時顯示可延展區

在draw9patch的右邊也會有縮放的展示圖。

Step 8: 完成NinePatch圖檔

儲存完成的NinePatch圖檔，draw9patch會自動將圖檔的副檔名儲存為*.9.png。完成NinePatch圖檔後，就可以開始寫程式了。

* Update: 2009/9/20 - Android擺放文字於NinePatch圖檔上的原則「並非對準粉紅區域的中心點」，已刪除此句。]]></description>
         <link>https://www.jollen.org/blog/2009/08/jollen-android-programming-30.html</link>
         <guid>https://www.jollen.org/blog/2009/08/jollen-android-programming-30.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 22 Aug 2009 23:05:27 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#29: HelloIntentWallpaper - 背景圖選擇器</title>
         <description><![CDATA[Android提供「SET_WALLPAPER」的內建Intent，當框架收到這個Intent時，就會啟動「背景圖選擇器」，讓我們選取新的背景圖。送出SET_WALLPAPER intent的程式寫法如下：

<pre>        Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER);
        startActivity(Intent.createChooser(intent, "Select Wallpaper"));</pre>

根據Reference Guide的說明，SET_WALLPAPER會啟動一個內容選擇器，所以同時地，我們先呼叫createChooser()方法建立一個內容選擇器後，再送出Intent。

<strong>完整程式碼: HelloIntentWallpaper.java</strong>

<pre>package com.moko.hellointentwallaper;
　
import com.moko.hellointentwallaper.R;
　
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
　
public class HelloIntentWallpaper extends Activity implements View.OnClickListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        　
        Button button = (Button)findViewById(R.id.set_wallpaper);
        button.setOnClickListener(this);
    }
　
    public void onClick(View v) {
<strong>        Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER);
        startActivity(Intent.createChooser(intent, "Select Wallpaper"));</strong>
    }
}</pre>

<strong>UI設計: layout/main.xml</strong>

<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    &gt;
	&lt;TextView  
	    android:layout_width="fill_parent" 
	    android:layout_height="wrap_content" 
	    android:text="@string/hello"
	    /&gt;
    &lt;Button android:id="@+id/set_wallpaper"
        android:layout_width="wrap_content" android:layout_height="wrap_content" 
        android:text="@string/set_wallpaper"&gt;
        &lt;requestFocus /&gt;
    &lt;/Button&gt;
&lt;/LinearLayout&gt;</pre>

<strong>執行結果</strong>

<img alt="hellointentwallpaper-1.png" src="http://www.jollen.org/blog/2009/08/22/hellointentwallpaper-1.png" width="320" height="480" />
圖1: HelloIntentWallpaper主程式

<img alt="hellointentwallpaper-2.png" src="http://www.jollen.org/blog/2009/08/22/hellointentwallpaper-2.png" width="320" height="480" />
圖2: 「背景圖」選擇器]]></description>
         <link>https://www.jollen.org/blog/2009/08/jollen-android-programming-29.html</link>
         <guid>https://www.jollen.org/blog/2009/08/jollen-android-programming-29.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 22 Aug 2009 22:52:26 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#28: HelloIntentSelect - 內容選擇器(Content Chooser)</title>
         <description><![CDATA[上一篇教學提到如何利用Intent實作「自動撥放」程式。而另外一個較具代表性的Intent應用就是「內容選擇器」。例如，要怎麼實作一個音樂撥放機呢？先說明音樂撥放器HelloIntentSelect範例的使用情境如下：

1. 執行HelloIntentSelect後，出現一個「撥放音樂」的按鈕
2. 按下按鈕後，選擇一個音樂檔撥放

實作「選擇音樂檔」的做法是使用Intent的「chooser」觀念。程式做法如下：

1. 建立action為ACTION_GET_CONTENT的Intent：

　Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

2.設定Intent的mime type，例如：設定Intent的mime type為聲音檔案：

　intent.setType("audio/*");

3.建立內容選擇器並送出Intent：

　startActivity(Intent.createChooser(intent, "Select music"));

以上的程式是根據Android的Reference Guide寫出來的。當Intent action為ACTION_GET_CONTENT時，表示要根據mime type來「取得內容」(get content)，因此呼叫setType()方法，來定義內容的mime type。

定義好mime type後，再呼叫createChooser()方法來產生能取得此mime type內容的「選擇器」，簡單說，就是一個「檔案選取程式」。

利用Android的Intent觀念，我們以不到十行的程式碼實作了一個音樂撥放器。

<strong>Action的屬性寫法與常數寫法</strong>

Intent的action寫法有二種。第一種寫法是屬性(attribute)寫法，例如第一個HelloIntentDialer範例：

　dial.setAction("android.intent.action.CALL");

在「android.intent.action」套件(package)裡，定義了action的屬性。第二種寫法是常數寫法，例如第二個HelloIntentSelect範例：

　Intent intent = new Intent(Intent.ACTION_GET_CONTENT);

在「Intent」類別(class)裡，定義了action的常數。在記憶的技巧上，可以用「xxx對於到ACTION_xxx」的方式記。例如：

CALL（android.intent.action.CALL）就是ACTION_CALL（Intent.ACTION_CALL）。

又如，所以當我們講「GET_CONTENT」時，指的就是android.intent.action.GET_CONTENT；同理，講ACTON_GET_CONTENT時，就是代表Intent.ACTION_GET_CONTNET。所以「GET_CONTENT」與「ACTION_GET_CONTNET」代表相同的Intent。

<img alt="hellointentselect-1.png" src="http://www.jollen.org/blog/2009/08/07/hellointentselect-1.png" width="320" height="480" />
圖1: HelloIntentSelect主程式

<img alt="hellointentselect-2.png" src="http://www.jollen.org/blog/2009/08/07/hellointentselect-2.png" width="320" height="480" />
圖2: 出現一個Content Chooser:「音樂選擇器」
]]></description>
         <link>https://www.jollen.org/blog/2009/08/jollen-android-programming-28.html</link>
         <guid>https://www.jollen.org/blog/2009/08/jollen-android-programming-28.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 07 Aug 2009 23:13:25 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#27: 使用ACTION_CALL實作自動撥號: HelloIntentDialer</title>
         <description><![CDATA[HelloIntentDialer是一個自動撥號程式，執行時會自動撥號到指定的電話。這樣的程式要怎麼寫呢？先看到HelloIntentDialer.java的完整程式如下： 

<pre>package com.moko.hellointentdialer;
　
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
　
public class HelloIntentDialer extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        　
        Intent dial = new Intent();
        dial.setAction("android.intent.action.CALL");
        dial.setData(Uri.parse("tel:119"));
        startActivity(dial);        
    }
}</pre>

因為permission的關係，所以也要在AndroidManifest.xml裡加上「CALL_PHONE」的權限。AndroidManifest.xml的完整內容如下：

<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.moko.hellointentdialer"
      android:versionCode="1"
      android:versionName="1.0"&gt;
    &lt;application android:icon="@drawable/icon" android:label="@string/app_name">
        &lt;activity android:name=".HelloIntentDialer"
                  android:label="@string/app_name"&gt;
            &lt;intent-filter>
                &lt;action android:name="android.intent.action.MAIN" /&gt;
                &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
            &lt;/intent-filter&gt;
        &lt;/activity&gt;
    &lt;/application&gt;
    &lt;uses-sdk android:minSdkVersion="3" /&gt;
    &lt;uses-permission android:name="android.permission.CALL_PHONE" /&gt;
&lt;/manifest>&gt;</pre>

這個範例相當簡單，但足以說明Intent的核心精神了。程式說明：

1. 先產生一個Intent物件：

　Intent dial = new Intent();

2. 設定Intent的action為「android.intent.action.CALL」，這是一個內建的action：

　dial.setAction("android.intent.action.CALL");

內建action「CALL」需要附帶一筆資料，而資料的寫法是使用URI格式：

　dial.setData(Uri.parse("tel:119"));

4. 「CALL」是一個activity action，所以呼叫startActivity()向Intent送給框架：

　startActivity(dial);   

這個範例的概念並不難，「送出一個帶有內建action的Intent給框架，因為action為CALL，所以框架會去啟動撥號activity並打電話。」]]></description>
         <link>https://www.jollen.org/blog/2009/08/jollen-android-programming-27.html</link>
         <guid>https://www.jollen.org/blog/2009/08/jollen-android-programming-27.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 07 Aug 2009 11:25:30 +0800</pubDate>
      </item>
            <item>
         <title>CTimes 矽導論壇：從Android看小筆電的機會</title>
         <description>文／陳俊宏(Jollen Chen)
Contact: jollen@jollen.org
（原文刊載於零組件雜誌2009年5月份）

技術端須補缺口 產品端須達到實用性

社群開發者將Android移植到EeePC後，興起一股「Android小筆電」討論風潮。幾個月後，市場上刮起了一陣開發Android小筆電的新聞。現在，幾家品牌大廠，對Android小筆電市場更是磨刀霍霍。對於這陣Android小筆電的風潮要如何解讀？在此分享個人的觀察與想法，請不吝指教。

Android小筆電的概念是由開發者社群所帶來的。網路上的技術玩家，將 Android 移植到 EeePC 701，並將這個新概念放到YouTube上分享。隨後，網路上開始出現討論，社群網站也接著進行報導，經由社群的推波助瀾，主流媒體也開始進行報導，接著，就像現在我們所看到的現象，Android小筆電開始吸引廠商的注意，大家都認為這是一個有不錯商機的產品。對於Android小筆電產品的熱潮，廠商與使用者的期待與想像也是一個主因。從技面的角度來看，Android小筆電仍有使用介面（UI）上的疑慮。由於Android介面的設計預設對象為手機，因此在小筆電上的畫面表現較不理想，操作方面亦同。Android小筆電在技術端仍需填補一些缺口。

但是，我們不仿從另一個「非技術面」的角度來思考。基於「使用應用程式、上網」為概念的小筆電，看來仍是微軟作業系統的天下，Linux小筆電在市場上起不了大作用的原因是「使用習慣」的問題。因為，使用者免不了將Linux小筆電與微軟系統的小筆電拿來比較。Linux上有OpenOffice 辦公軟體，但微軟系統小筆電上有使用者更習慣的Office套裝軟體；Linux上有Thunderbird電子郵件軟體，但微軟系統小筆電有使用者更習慣的Outlook軟體；不論是上網、電子郵件還是即時通訊，Linux小筆電上的「應用程式」都讓使用者操作得很沒有安全感。

基於「網路服務」概念的Android平臺，因為與微軟系統的小筆電有很大的差異性，因此似乎存在不錯的機會。Android平臺不基於 Linux桌面技術，我們不能將OpenOffice軟體，或是Firefox瀏覽器安裝在Android小筆電上，正好能與現有的小筆電做出相當大的市場區隔。因此有一個很好機會是，我們可以讓使用者很自然地不將Android小筆電與微軟系統小筆電拿來做比較。差異性與區隔性，讓Android小筆電浮現市場價值。

總結來看，現階段Android小筆電仍處於技術玩票階段，要讓Android介面適用在小筆電產品上仍有一些技術距離；Android技術平臺與典型的Linux桌面環境截然不同，Linux或微軟的應用程式，無法「移植」到Android平臺上，但這個技術上的大不同，正好給了Android 小筆電「差異化」的想像空間。產品定位方面，把Android做為取代微軟系統的想法，反而讓Android小筆電失去差異性；應將Android平臺做為「產品使用區隔」的有效工具。未來Android若真的能開發出適合小筆電的版本，一定是個很酷產品。</description>
         <link>https://www.jollen.org/blog/2009/08/android-netbook-opportunity.html</link>
         <guid>https://www.jollen.org/blog/2009/08/android-netbook-opportunity.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Wed, 05 Aug 2009 17:48:31 +0800</pubDate>
      </item>
            <item>
         <title>CTimes 矽導論壇：經營Android軟體設計服務、需要不同的思考</title>
         <description>文／陳俊宏(Jollen Chen)
Contact: jollen@jollen.org
（原文刊載於零組件雜誌2009年8月份）

軟體委外設計服務 - 百年老店、全新感受

委外服務（outsourcing）在科技產業是一種普遍的商業模式，透過委外取得解決方案，以及後續服務是產品開發的一種做法，委外服務供應商透過「接案」的方式獲取利潤。特別是在軟體方面，有許多代表性的「軟體代工」廠，讓軟體代工成為委外服務的代表作。

隨著Android被大量關注以及採用，會有越來越多的「Android設計服務」公司出現，透過觀察，確實也有很多專門承接Android軟體專案的公司、小團隊或個人，這是不令人感到意外的現象。在討論Android設計服務前，不如先來思考一下近來當紅的線上軟體商店模式。

Android Market提供的商業模式是收取「安裝」費用，即使用者下載的拷貝(copy)數決定了軟體開發商的營收，雖然Android Market還沒有像App Store這樣成熟，但是「線上軟體商店」的模式幾乎已經被確立了，「應該要做market」的公司，也都宣佈他們的market方案了。這種賺取拷貝數的商業模式最簡單沒有負擔，可以說是比微型創業更小型的創業方式，唯一的考量的是流通在外的裝置(device)數量，因為裝置流通越多，軟體被下載的機會與次數越高。

再談到委外開發的模式，什麼樣的公司，會有「Android應用軟體委外設計」的需求？無疑是想要「bundle」特殊軟體在「產品」上的公司，希望有些軟體在機器出廠時就被安裝，在這樣的前提下，就有二個想法。第一，我們可以單純提供設計服務、交付成果，再由廠商預裝到裝置上，這時「安裝數量」與收費就沒有關係；第二，可以提供收取權利金的方式，安裝到裝置的數量，決定收費，等於收取拷貝數的費用。

對設計服務公司而言，採用以拷貝數為基礎的模式，是比較好的模式，但是這種模式能不能被廠商接受？衡量的依據之一是「解決問題」，或是「提供價值」。一項只能解決問題（技術難題）的服務，大體上只能以服務收費為主；而一個能提高產品價值的軟體，就能收取拷貝數的錢。在這裡所討論的是Android的設計服務，在其他領域，銷售「關鍵技術」是一個很好的商業模式。

Android應用軟體設計商，還要具備的一個思惟是，Android應用軟體是可以被安裝到「任何」的Android裝置上的；因此，為單一廠商或單一產品設計軟體，收取服務費用，還不如自已擁有(own)該軟體的所有權利，並透過market銷售，讓使用者安裝我們的軟體到不同的Android裝置。

但是，在線上軟體商店賣軟體也是辛苦的工作。一些應用軟體開發商，從線上軟體商店賺到的錢是相當微薄的，對人數較多的公司而言，market的模式還無法支撐營運。看來，現階段定位為Android應用軟體開發商的公司，除了採取最容易的「設計服務」外，要以拷貝數為商業模式的基礎，確實要有一些非常的策略才行了。</description>
         <link>https://www.jollen.org/blog/2009/08/android-outsourcing-business-strategy.html</link>
         <guid>https://www.jollen.org/blog/2009/08/android-outsourcing-business-strategy.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Wed, 05 Aug 2009 17:35:34 +0800</pubDate>
      </item>
            <item>
         <title>CTimes 矽導論壇：四個步驟、建立你的Android競爭力</title>
         <description>文／陳俊宏(Jollen Chen)
Contact: jollen@jollen.org
（原文刊載於零組件雜誌2009年7月份）

創建自有版本 打開你的競爭力

過去品牌商面臨的一個問題是沒有自已的軟體。所謂的「自已的軟體」並不是拿別人的軟體來使用，或是視取得的開放源碼軟體為自有軟體。Android是一個作業系統，微軟的Windows Mobile也是一個作業系統，他們二者的「本質」上有什麼不同？Windows Mobile是由微軟所「擁有」的軟體，無法「自由」取得；而Android是開放源碼的軟體，每個人都可以透過網路自由取得，但是你擁有它嗎？

取得不代表擁有。建立自已的版本，能控制並掌握整個框架的開發，才叫做擁有。擁有了自已的Android軟體，你就能修改它，並做出任何你想要的版本，取得(get)與擁有(own)意義上有很大的不同。過去Linux以及開放源碼給人的迷思是，由於軟體能自由取得，並做修改，因此不需要付費購買微軟的產品。下載Linux核心與開源軟體，故事才正要開始，首先要面臨到的便是工程的部份。沒有自有的Linux技術團隊，只要假以他手，將專案外包，而工程化後的Linux系統，自已仍無法掌握，照著自已的意思儘情地修改。

如何擁有自已的軟體，以下是個人建議，請您指教。擁有自有Android軟體的第一個步驟是：建立Android進化版本、即自已的分支。將Android框架的原始(original)原始碼(source code)建立一個新分支，也就是自已名字的版本。Google所提供的Android作業系統是「原生版本」，而自有的分支則是「進化版本」。例如：調整Android框架的實作，以加入自有的特性(features)，讓自有的進化版與原生版有所差異。試想，當我的Android進化版可以提供更炫麗的操作介面(UI)時，使用原生版本的產品便失去了市場性。最佳典範就是 HTC Sense。HTC Sense是HTC手機的專用UI，針對Android手機，HTC Sense能提供更棒的使用性(usability)。

第二、建立商標。Android作業系統採用Apache授權(Linux kernel除外)，而不是較為普及的GPL授權，所以Android作業系統對於商標(trademark)的建立是相當有助益的。商標是企業的一項價值，商標代表「這是我的東西」。當有差異性的Android版本能關閉原始碼，並建立註冊商標時，代表的是一個重要的里程碑：「這是屬於我的Android版本」。最佳典範，一樣是HTC Sense(tm)。

第三、適度貢獻與關閉原始碼。Android作業系統是開放平臺，開放平臺技術開發講求貢獻。OHA聯盟也是如此。廠商要能持續對OHA聯盟有所貢獻，而提交Android框架的程式碼是一個做法。OHA聯盟發給會員的門票並非終身有效，因為仍有被趕出大門的例子。另外，基於自有版本提供一套SDK是非常不錯的做法，例如OMS SDK就是一個典範。

第四、建立應用程式。基於市場與產品建立應用程式，以搭配產品，這是Android作業系統帶來的絕佳機會。是否自創品牌，當然也是一個考量，端看應用程式的價值以及特殊性。進化版Android目前來看，可以有二個發展題目。第一是結合服務的客製化版本，例如：OMS針對China Mobile服務做大量的客製化。第二個是針對UI與使用性做客製化版本，例如上述的HTC Sense。

廠商欲採用Android作業系統，並開發產品，但若不思考如何建立自有的能力，以及創造自有的Android進化版本，是非常可惜的一件事情。最大的盲點在於「外包能解決所有問題」的思維；反之，專案外包(out sourcing)不會是Android能帶來的商業價值，解決方案(solution)才是。針對個人開發者的部份，Android作業系統給開發者(developers)最好的舞臺是Android Marketing，因為越多的拷貝數量(即下載數)代表軟體越有價值。</description>
         <link>https://www.jollen.org/blog/2009/08/android-product-4-steps.html</link>
         <guid>https://www.jollen.org/blog/2009/08/android-product-4-steps.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Wed, 05 Aug 2009 17:33:38 +0800</pubDate>
      </item>
            <item>
         <title>CTimes 矽導論壇：Android產品開發的關鍵五力</title>
         <description>文／陳俊宏
jollen@jollen.org
（原文刊載於零組件雜誌2009年6月份）

開放平臺讓我們看見了新機會 關鍵能力培植才能掌握機會

Android手機產品開發的重要能力培植，是搭上Android商機列車的入場卷。在Android手機產品開發，以及技術養成方面，有哪些重要的關鍵能力呢？以下針對這段時間所做的觀察，以及個人的心得，提出五個重要的指標能力，請您不吝指教。

關鍵力一：界面設計。過去談智慧型手機時，大家都了很多的力道在UI設計上，主要的原因除了智慧型手機都是觸控式螢幕的規格外，也受到iPhone很大的影響。Android的「制式界面」比較沒有獨特之處，因此「客製界面」將成為Android手機以及其他Android產品的重要特色。採用OMS系統(Open Mobile System)的oPhone手機，就在UI設計上下了很大的功夫。

關鍵力二：工業設計。手機工業設計(ID)包含手機的體積以及外觀，由於「手機」的特點是「經常攜帶」、「高移動性」以及「時尚配件」，所以手機是否夠輕薄、易攜帶，會是消費者重要的考量點。此外，有些手機代表的是一種時尚配件，或是身份地位的象徵，因此在工業設計的能力也影響到手機的銷售。對Android手機來講，工業設計不但重要，甚致還要更高一層來搭配有特色的UI設計。

關鍵力三：底層技術能力。研究Android作業系統底層技術的目的之一為「培養Android移植技術的能力」。任何Android應用程式都需要底層的支援，才能順利運作在硬體平臺上，而這也正是Android產品開發的核心能力；Android產品開發階段，不但一個高品質的硬體，也需要將Android作業系統移植到該硬體上並進行調校，才能讓Android應用程式在產品上順利執行。

關鍵力四：軟硬整合。上述的「移植能力」並不等於「軟硬整合」的能力。移植的目的是為了讓Android作業系統能在目標裝置上順利執行，並且驅動程式部份也能正常運作。軟硬整合的工作則是希望透過驅動程式的支援，讓Android Framework能發揮硬體的特色，因此如何擴充framework並實作自已的Activity與Service，以及修改framework，將是技術面的關鍵。

關鍵力五：行銷溝通力。手機產業本身就是一個特殊的產業，除了通路行銷(channel marketing)以及電信通路(carrier)的做法外，直銷(direct sale)也會是Android新興品牌手機的一大機會。Android是人人可取得的開放手機作業系統，新的產品概念，可幫助新興品牌進入利基市場，而採取直接銷售，例如透過網路，將是利基品牌手機很有影響力的一個做法。在產品行銷方面，如何與社群(community)以及終端用戶(end-user)溝通，並精確傳遞產品概念，以及經營品牌形象，就成為非常重要的關鍵能力。

與技術面有關的關鍵力三，以及關鍵力四，說明了Android作業系統與過去的Linux手機平臺的不同之處。Android是一個很健全(strong）的Application Framework，framework的擴充與開發技術是重要的技術。以大方向來看，加入新的 library 時需要擴充 application framework。

Android framework 的實作，部份需要考量硬體的規格，例如: Surface Manager 需要考量是否有 GPU 或硬體加速。但 Android application 建立一個 Surface holder時，若是將 Surface 的類型設定為 SURFACE_TYPE_HARDWARE，就必須針對硬體加速與 DMA 做移植工作。這個例子舉Android基本技術能力的重要性。

界面設計(UI)、工業設計(ID)、底層技術(Internals)、軟硬整合(Integration)與行銷溝通力(Communications)，共五個關鍵能力。UI與ID是比較抽象的能力，需要天生好手或是有經驗的設計師來協助；底層技術與軟硬整合，是比較具體的能力，可以採用培訓來初步形成，並透過實務經驗來加強，或是採取伙夥合作(partnership)的方式來補足；最後的行銷溝通力，則是一半抽象一半具體，具體的部份是行銷方面有一些標準方法與工具，抽象部份是如何有創意的利用這些標準方法與工具，來傳達正確的產品訊息給消費者。</description>
         <link>https://www.jollen.org/blog/2009/08/android-product-5-power.html</link>
         <guid>https://www.jollen.org/blog/2009/08/android-product-5-power.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Wed, 05 Aug 2009 17:28:49 +0800</pubDate>
      </item>
            <item>
         <title>Android的Launcher研究：客製化桌面UI</title>
         <description><![CDATA[<strong>前言</strong>

能取得Android OS原始碼，並修改裡頭的內容，有時候也頗有樂趣。最近和幾位朋友聊到「Android框架的改造」，以及如何吸引對Android框架技術有興趣的同好一起交流的議題；我個人認為，一開始如果能丟出一個比較有樂趣的議題，或許可以有拋磚引玉的效果。

上週在北京進行Android培訓課程時，與eoeAndroid社群也進行了想法的交流，由於大家都體認到Android底層技術的重要性及其價值，而且eoeAndroid社群裡也有許多技術好手，所以就和eoeAndroid的創辦人靳岩兄有了一個共同主持研究Android底層技術「同好小組」的想法，希望能透過社群的方式，集合大家的智慧，一起把底層技術研究清楚。

因為要讓大家能有焦點，所以「發題」很重要，這個工作就由落在我身上了。由於第一次希望題目能簡單，並且有趣一點，至少要能達到發球的效果，吸引大家開始關心Android底層技術，所以原則是：希望能用最簡單的方式、讓大家體驗修改底層的樂趣。

<strong>題目說明: Launcher</strong>

第一次的題目是「Launcher」的修改。

Launcher就是Android的應用程式啟動器，Launcher的功能還包含：桌面的切換、應用程式快捷(shortcut)功能、背景圖(Wallpaper)功能等等。因此，修改Launcher可以改變一些很深層的UI功能。

在Android的桌面最下方，有一個圖示，按下後可以拉出應用程式圖示清單，這是Launcher提供的功能。這一次，因為我們覺得這個Launcher的圖示太製式化了，越看越不好看，所以想要修改一下，換張圖，要怎麼做到呢？

<strong>範例展示</strong>

例如，圖1是原始的圖示；圖2是修改後的圖示。

<img alt="launcher-1.png" src="http://www.jollen.org/blog/2009/07/17/launcher-1.png" width="320" height="480" />
圖1: 原始圖示

<img alt="launcher-2.png" src="http://www.jollen.org/blog/2009/07/17/launcher-2.png" width="320" height="480" />
圖2: 幫Launcher妝扮一下

<strong>實作說明 1/4: 取得Android原始碼與EeePC移植</strong>

這個功能並不難做，事實上，完全不用寫程式。只要把圖檔重做就可以了。只不過前提是，要知道：

1. 如何取得Android OS原始程式碼
2. 如何編譯Android OS

最簡單的做法是：

1. 下載Android原始碼後、取得EeePC的移植(product)
2. 編譯「TARGET_PRODUCT」為eee_701
3. 由於Launcher都是用Java語法寫成的，所以不會有架構(ARM/x86/...)的問題，編譯後可以取得Launcher.apk；APK套件是不分處理器平臺的

先學會如何由Android原始碼編譯出eee_701的image，才有辦法繼續進行。

<strong>實作說明 2/4: 修改圖檔</strong>

在Android原始碼的 packages/apps/ 目錄裡，存放了Android內建的應用程式原始碼，Launcher是Android的一個應用程式，所以從這裡找到它的原始碼，並進行修改工程。

切換到以下目錄：

&lt;android source&gt;/packages/apps/Launcher/

接著要修改src/目錄下的內容，還是res/目錄下的內容呢？圖檔屬於Android的「resource」，因此當然是到res/目錄下找到我們要的圖檔。

切換到以下目錄：

&lt;android source&gt;/packages/apps/Launcher/res/

又看到了一大堆目錄，圖檔的部份存放於：

<ul><li>drawable-land/ - landscope 模式的圖檔</li>
<li>drawable-port/ - portrait 模式的圖檔</li></ul>

我們先改一下portrait模式的圖檔。找到drawable-port/tray_handle_normal.png檔案如下：

<img alt="tray_handle_normal-1.png" src="http://www.jollen.org/blog/2009/07/17/tray_handle_normal-1.png" width="320" height="56" />

就是它了，換掉，把圖檔換成這個：

<img alt="tray_handle_normal-2.png" src="http://www.jollen.org/blog/2009/07/17/tray_handle_normal-2.png" width="320" height="56" />

換好後重編Android即可。一行程式都不用改。

<strong>實作說明 3/4: 安裝Launcher.apk</strong>

重編Android原始碼，接著可以在out/target/product/&lt;product name&gt;/system/app/找到Launcher.apk套件。把Launcher.apk安裝到AVD(Android 模擬器)裡做測試，方法如下：

1. 先啟動一個AVD
2. 執行adb將Launcher.apk手動安裝到AVD裡，指令如下：

$ adb install -r &lt;your-path&gt;/Launcher.apk 

成功後可看到以下畫面：

<pre>338 KB/s (837376 bytes in 2.417s)
        pkg: /data/local/tmp/Launcher.apk
Success</pre>

<strong>實作說明 4/4: 重開機</strong>

已經完成了，直接重開即可。「重開」是把AVD重新啟動，不是把電腦重新開機 ;-)

<strong>應用與討論</strong>

歡迎大家上傳你的作品、或是貼圖與大家分享，方式是透過eoeAndroid社群的討論區：

<a href="http://www.eoeandroid.com/forumdisplay.php?fid=54">http://www.eoeandroid.com/forumdisplay.php?fid=54</a>

如果有更詳細的Launcher研究心得，或是希望能針對Launcher進行討論，歡迎至eoeAndroid的討論區發文。]]></description>
         <link>https://www.jollen.org/blog/2009/07/android-os-launcher-app.html</link>
         <guid>https://www.jollen.org/blog/2009/07/android-os-launcher-app.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 17 Jul 2009 03:13:34 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#26: 強大的Intent機制</title>
         <description><![CDATA[<strong>什麼是Intent(意圖)？</strong>

強大的事件處理「Intent」(意圖)是Android很強大的一種機制。

在 Android 應用程式框架中，有一個非常聰明的事件處理機制，稱之為「Intent」。Intent（意圖）的作用與事件(event)很像，但與傳統的事件處理仍然有些差異。傳統的事件處理，講求的是「處理者（handler）的觸發」，當一事件發生時，便callback讓事件的處理者，或是直接將該事件轉送（forward）給應用程式，由應用程式決定處理方式。

在「Intent」這樣的事件處理觀念裡，Android 試圖將事件解釋為「應用程式的意圖」或是「使用者的意圖」，並試著去解釋該意圖的目的，若 Android 系統本身能理解應用程式的意圖，便會「自行」去處理該意圖所應執行的工作。

Android的做法是，讓每個意圖（Intent）都帶有一個動作（action），並根據不同的動作去行動。

<strong>關於前述教學提到的Intent</strong>

在前面的教學裡，我們用到二次Intent如下：

1. 自行定義一個Intent、設定Service可接收此Intent，並透過「送出Intent給框架」的方式，請框架啟動該Service

2. 使用Android內部定義的動作「ACTION_VIEW」，來「檢視」(view)一個「URL」資料，當框架看到內部定義的ACTION_VIEW動作時，便「自行」處理該Intent；處理的方式是啟動WebView並連上網站

以前述的教學為例，使用內建的動作“ACTION_VIEW”就可以很容易做出一個「啟動瀏覽器(WebView類別)上網」的應用程式。

透過這二個例子我們知道，Intent的動作可以是自行定義與框架內部定義二種。Android框架的Intent有很多方便實用的「內建動作」，以下我們說明Android內建Intent的美麗之處。

<strong>Android內建的Intent Action</strong>

Android的框架確實是讓每個Intent都包含了一個動作，就稱為action。

為了讓大家更容易了解Intent的基本觀念，我們採用「體驗」的方式來說明如何使用內建的Action。現在，我們列舉以下三個情境，並分別實作其範例：

HelloIntentDialer: 啟動撥號器(dialer)並撥號
HelloIntentMusic: 使用者按下「Select Music」後，可以由音樂清單裡選擇音樂並撥放
HelloIntentWallpaper: 啟動Android內建的「背景圖選擇器」，讓使用者更換背景

第二個範例”HelloIntentMusic”其實是ApiDemo裡的範例，而且是很容易能了解Intent內涵的好程式。

除了action外，Intent還可以包含另外一項資訊「data」。

Intent的action指定這個Intent的「動作」是什麼，框架會依指定的動作進行處理；有些action可以附帶一筆資料，這個資料是以Uri的格式撰寫，在HelloIntentDialer的範例會再做說明。

內建的Intent有哪些呢？請參考Android Reference Guide中的Intent類別說明。上述三個範例分別使用以下三個action：

1. ACTION_CALL: 撥號
2. ACTION_GET_CONTENT: 啟動內容選取器
3. ACTION_SET_WALLPAPER: 設定Wallpaper

在進行範例講解前，可以先行閱讀Intent類別的說明。ACTION_CALL是一個內建的action，我們只要產生一個Intent物件，並定義其「action」為ACTION_CALL即可通知框架「打電話」。

Android內建的action是相當實用的應用開發機制，同時也是Android OS最具代表性的機制之一。Android內建的Intent action分為二種：

1. Activity Action: 啟動Activity的action
2. Broadcast Action: 透過廣撥器處理的action

第一種action是activity action，用途是通知框架啟動Activity，這裡提出的三個範例，都是使用activity action。Broadcast action將在Broadcast的教學裡再做說明。

* Update: 2009/08/07]]></description>
         <link>https://www.jollen.org/blog/2009/07/jollen-android-programming-26.html</link>
         <guid>https://www.jollen.org/blog/2009/07/jollen-android-programming-26.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 16 Jul 2009 12:27:36 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#25: HelloAppWidgetProvider.java 程式碼說明</title>
         <description><![CDATA[<strong>HelloAppWidgetProvider.java 程式碼說明</strong>

<img alt="HelloAppWidgetProvider.png" src="http://www.jollen.org/blog/2009/07/12/HelloAppWidgetProvider.png" width="638" height="333" />
圖1: HelloAppWidgetProvider的設計

圖1是目前我們的HelloAppWidget範例設計，說明如下：

<ul><li>onUpdate(): 收到ACTION_APPWIDGET_UPDATE廣撥時，框架會callback此method</li>
<li>onDelete(): 收到ACTION_APPWIDGET_DELETE廣撥時，框架會callback此method</li>
<li>AppWidgetManager: 管理App Widget的類別</li></ul>

先前，在AndroidManifest.xml裡我們讓HelloAppWidgetProider類別可以接收ACTION_APPWIDGET_UPDATE廣撥事件；ACTION_APPWIDGET_UPDATE是最主要的App Widget事件，當AppWidgetProvider被要求為App Widget提供”RemoteView”時，就會收到這個事件。

<strong>什麼是RemoteViews？</strong>

什麼是RemoteView呢？先看一下框架的設計，如圖2。

簡單來說，「RemoteViews」就是表示UI的類別。res/layout/main.xml描述了應用程式的UI，UI裡當然包含許多組件(Widget)，而在先前的教學裡講到了一個觀念「Android應用程式的UI就是一個View tree」，view tree就是「View Hierarchy」。

總結來說，RemotViews是一個用來表示View Hierarchy的類別。透過RemoteViews可以找到UI裡的每一個組件。

<a href="http://www.jollen.org/blog/2009/07/12/RemoteViews.png"><img alt="RemoteViews.png" src="http://www.jollen.org/blog/2009/07/12/RemoteViews.png" width="933" height="1021" border="0" /></a>
圖2: RemoteView的設計（點擊看全圖）

<strong>程式說明: HelloAppWidgetProvider.java</strong>

onUpdate()的程式實作：

<blockquote><pre>    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;
        for (int i=0; i&lt;N; i++) {
            int appWidgetId = appWidgetIds[i];
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }</pre></blockquote>

說明如下：

1. onUpdate()負責更新「已經安裝」在桌面上的App Widget內容，因此我們實作一個updateAppWidget()來進行真正更新的工作

2. onUpdate()的第二個參數為AppWidgetManager，這是一個「管理AppWidgetProvider」的類別，我們必須透過框架callback本方法時回傳給我們的AppWidgetProvider物件，來更新桌面上的App Widget

3. onUpdate()的第三個參數appWidgetIds陣列，存放需要更新的App Widget ID；框架會將需要更新的App Widget之ID回傳給onUpdate()，程式必須負責「更新每一個需要更新的App Widget。」

更新App Widget的方式是透過AppWidgetManager來完成，程式實作：

<blockquote><pre>    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
    	CharSequence text;
    	      
    	text = "www.jollen.org";
    	        
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
        views.setTextViewText(R.id.appwidget_text, text);
        
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}</pre></blockquote>

說明如下：

1. 透過UI layout取得自已的「View Hierarchy」(UI)，以前面介紹的RemoteViews物件表示

2. 如圖2，呼叫RemoteView的setTextViewText()方法，修改UI裡的「R.id.appwidget_text」組件，變更文字內容

3. 呼叫AppWidgetProvider的updateAppWidget()方法，更新我們所指定的App Widget，將其UI更新為RemoteView的UI

4. updateAppWidget()的第二個參數為RemoteView，即說明1.取得的UI，說明2.修改了此UI裡的”R.id.appwidget_text”組件，最後透過App Widget Manager更新App Widget的UI]]></description>
         <link>https://www.jollen.org/blog/2009/07/jollen-android-programming-25.html</link>
         <guid>https://www.jollen.org/blog/2009/07/jollen-android-programming-25.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 12 Jul 2009 14:10:25 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#24: AndroidManifest.xml-加入App Widget的程式資訊</title>
         <description><![CDATA[<strong>AndroidManifest.xml-加入App Widget的程式資訊</strong>

AndroidManifest.xml檔案的用途在前面的教學裡介紹過了。以下是其完整內容：

<blockquote><pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.moko.hellowidget"
      android:versionCode="1"
      android:versionName="1.0"&gt;
    &lt;application android:icon="@drawable/icon" android:label="@string/app_name"&gt;
<strong>        &lt;receiver android:name=".HelloAppWidgetProvider"&gt;
            &lt;meta-data android:name="android.appwidget.provider"
                    android:resource="@xml/appwidget_provider" /&gt;
            &lt;intent-filter&gt;
                &lt;action android:name="android.appwidget.action.APPWIDGET_UPDATE" /&gt;
            &lt;/intent-filter&gt;
        &lt;/receiver&gt;</strong>
    &lt;/application&gt;
    &lt;uses-sdk android:minSdkVersion="3" /&gt;
&lt;/manifest&gt;</pre></blockquote>

說明如下：

1. 在&lt;application&gt;裡加入&lt;receiver&gt;標籤，指定android:name屬性為主要的provider類別，即”HelloAppWidgetProvider”，請注意，「.」表示後面的字串為一個「類別名稱」，不要忽略了這個重要的小數點

2. 在&lt;receiver&gt;裡加入&lt;meta-data>標籤，指定android:resource屬性為App Widget的資源檔名稱，以我們的範例來說，就是「@xml/appwidget_provider」，即「xml目錄下的appwidget_provider.xml檔案」

3. 在&lt;receiver&gt;裡加入&lt;intent-filter&gt;標籤，讓我們的App Widget可以接收APPWIDGET_UPDATE事件(event)

一個很簡單的App Widget完成了。接下來，就是針對HelloAppWidgetProvider.java程式碼做細部說明。]]></description>
         <link>https://www.jollen.org/blog/2009/07/jollen-android-programming-24.html</link>
         <guid>https://www.jollen.org/blog/2009/07/jollen-android-programming-24.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 11 Jul 2009 23:57:29 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#23: HelloAppWidgetProvider.java-實作App Widget供應者</title>
         <description><![CDATA[<strong>HelloAppWidgetProvider.java-實作App Widget供應者</strong>

程式碼已經在前面的教學裡展示過了，當時只提到一個很基本的重點：使用AppWidgetProvider類別。在這裡，我們先說明設計的部份，才能了解程式如何實作。程式碼的說明稍後再做補充。

<img alt="AppWidgetProvider.png" src="http://www.jollen.org/blog/2009/07/11/AppWidgetProvider.png" width="798" height="822" />
圖1：設計App Widget

從圖1的設計裡可以知道(配合查詢Android Reference文件)，當程式繼承了AppWidgetProvidr類別後，也繼承了二個主要的method：

<ul><li>onUpdate()</li>
<li>onDelete()</li></ul>

App Widget使用AppWidgetProvider類別，即App Widget的「供應者」，供應什麼東西給誰呢？可以想像成是，我們的應用程式，供應App Widget給Android桌面。

到目前為止，我們知道只需要AppWidgetProvider即可實成一個很陽春的App Widget。而完整的App Widget應該包含三個單元(unit)：

1. Provider：此處說明的「供應者」
2. Configure：App Widget的設定單元，用途是提供一個「介面」供使用者輸入資料
3. Receiver：繼承自BroadcastReceiver的單元，即廣播接收器，用來接收Android框架所送出的事件(event)

在後續的教學裡，我們會繼續說明configure與receiver單元的觀念與實作。
]]></description>
         <link>https://www.jollen.org/blog/2009/07/jollen-android-programming-23.html</link>
         <guid>https://www.jollen.org/blog/2009/07/jollen-android-programming-23.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 11 Jul 2009 23:54:34 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#22: main.xml-描述App Widget的UI</title>
         <description><![CDATA[<strong>main.xml-描述App Widget的UI</strong>

這個檔案在前面的教學裡介紹過了，它的主要用途是描述UI。我們想要設計一個能嵌進桌面，並顯示文字的App Widget，因此必須使用Android的TextView類別。

以下是main.xml的完整內容：

<blockquote><pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    &gt;
&lt;TextView  
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:id="@+id/appwidget_text"
    android:textColor="#ff000000"
    /&gt;
&lt;/LinearLayout&gt;</pre></blockquote>

我們的App Widget使用LinearLayout來安排佈局，而UI為一個TextView物件。在這裡，我們將此TextView物件的id定義為”appwidget_text”。]]></description>
         <link>https://www.jollen.org/blog/2009/07/jollen-android-programming-22.html</link>
         <guid>https://www.jollen.org/blog/2009/07/jollen-android-programming-22.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 11 Jul 2009 23:52:53 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#21: appwidget_provider.xml-描述App Widget屬性的資源檔</title>
         <description><![CDATA[以下分別說明HelloAppWidget的實作，以及技術重點。

<strong>appwidget_provider.xml-描述App Widget屬性的資源檔</strong>

這個檔案主要描述App Widget的幾個屬性：

<ul><li>長度(width)</li>
<li>高度(height)</li>
<li>更新頻率</li>
<li>UI layout檔</li></ul>

以下是appwidget_provider.xml的完整內容：

<blockquote><pre>&lt;?xml version="1.0" encoding="utf-8"?>
   
&lt;appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="85dp"
    android:minHeight="30dp"
    android:updatePeriodMillis="3000"
    android:initialLayout="@layout/main"
    &gt;
&lt;/appwidget-provider&gt;</pre></blockquote>

說明如下：

1. &lt;appwidget-provider&gt;標籤定義App Widget的屬性
2. android:minWidth定義寬度
3. android:minHeight屬性定義長度
4. android:updatePeriodMillis定義App Widget的更新頻率，Android框架每隔這段時間，會callback AppWidgetProvider類別的onUpdate()事件；此屬性的時間單位為1/1000秒，以上述的定義來說，等於3秒鐘的時間(3000/1000=3)
5. android:initialLayout屬性指定此App Widget的UI layout定義檔，”@”符號在Android的XML定義檔案，代表「目錄」之意，因此”@layout/main”表示「layout目錄下的main.xml檔案」

以上共四項屬性，是App Widget最基本的屬性，必須良好定義。其中android:updatePeriodMillis屬性可省略，代表不更新App Widget，即Android框架將不callback appWidgetProvider類別的onUpdate()事件。

onUpdate()事件負責更新App Widget的顯示內容。

設計App Widget的第一件工作，就是定義它的大小，以及更新頻率。由於手機的螢幕比較小，再加上桌面的空間有限，因此就要很小心定義App Widget的長度以及寬度。

在Android的Dev Guide文件裡，有一個App Widget設計原則的章節，描述了App Widget的標準大小；當然，這只是建議，我們可以任意定義App Widget的大小，因此不依照Google提供的設計原則也不會有什麼問題。但是，若是能遵循設計原則的指示，桌面的空間安排會較有效率，桌面的整體呈現也會比較美觀。

App Widget的設計需要考量螢幕的方向，若是直向顯示(portrait)，則App Widget的大小建議如下：

<a href="http://www.jollen.org/blog/2009/07/11/portrait_sizes.png"><img alt="portrait_sizes.png" src="http://www.jollen.org/blog/2009/07/11/portrait_sizes.png" width="939" height="566" border="0" /></a>
(圖片來源：Android Dev Guide；點擊看全圖)

橫向顯示(landscape)的設計建議如下：

<a href="http://www.jollen.org/blog/2009/07/11/landscape_sizes.png"><img alt="landscape_sizes.png" src="http://www.jollen.org/blog/2009/07/11/landscape_sizes.png" width="1003" height="484" border="0" /></a>
(圖片來源：Android Dev Guide；點擊看全圖)

在後面的教學裡，我們會再詳細說明App Widget的美工設計原則。

接下來要接著進行的工作，即是在”@layout/”裡建立main.xml檔案，以描述App Widget的UI。]]></description>
         <link>https://www.jollen.org/blog/2009/07/jollen-android-programming-21.html</link>
         <guid>https://www.jollen.org/blog/2009/07/jollen-android-programming-21.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 11 Jul 2009 23:46:12 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#20: 如何設計一個小型的App Widget？</title>
         <description><![CDATA[Android的ApiDemo範例庫提供了一個很不錯的App Widget範例；不過，對初學者來說，這個範例可能稍嫌繁瑣。在這裡另外提供一個HelloAppWidget範例如下：

<blockquote><pre>/* 範例：HelloAppWidget.java */
package com.moko.hellowidget;
  
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.widget.RemoteViews;
  
public class HelloAppWidgetProvider extends AppWidgetProvider {
  
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;
        for (int i=0; i&lt;N; i++) {
            int appWidgetId = appWidgetIds[i];
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }
      
    public void onDeleted(Context context, int[] appWidgetIds) {
    }
  
    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
            int appWidgetId) {
    	CharSequence text;
      	
    	text = "www.jollen.org";
      	
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.main);
        views.setTextViewText(R.id.appwidget_text, text);
  
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}</pre></blockquote>

一個很簡單的App Widget就是只需要這麼幾行程式碼，HelloAppWidget範例就是先前的App Widget操作示範使用的範例，HelloAppWidget會在桌面嵌進一個TextView組件，並顯示”www.jollen.org”字串。

由此範例可以了解，App Widget使用到Android提供的AppWidgetProvider類別，建議可以先行快速瀏覽Android Reference文件，以了解此類別的大略用法。

<strong>App Widget的設計流程</strong>

設計一個App Widget的流程，並不是由寫程式開始，所以上述的範例程式，並不是首要的重點。實作一個App Widget的過程，用到了過去教學裡的所有觀念，因此對以下的流程述描有不了解的地方，可以再回頭覆習過去的教學。

App Widget設計流程：

1. 規劃App Widget的大小以及更新時間，在res/xml/裡新增一份XML文件，命名為appwidget_provider.xml
2. 規劃App Widget的UI，修改res/layout/main.xml
3. 撰寫App Widget主程式，如上例
4. 編輯AndroidManifest.xml，設定App Widget可接受App Widget的更新事件：android.appwidget.action.APPWIDGET_UPDATE

換個角度來看，設計一個陽春版的App Widget至少需要以下4個檔案：

<ul><li>res/xml/appwidget_provider.xml</li>
<li>res/layout/main.xml</li>
<li>src/<package name>/HelloAppWidgetProvider.java</li>
<li>AndroidManifest.xml</li></ul>

有了設計流程後，接下來一一說明每個步驟的實作，以及技術重點。]]></description>
         <link>https://www.jollen.org/blog/2009/07/jollen-android-programming-20.html</link>
         <guid>https://www.jollen.org/blog/2009/07/jollen-android-programming-20.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 10 Jul 2009 18:00:56 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#19: 什麼是App Widget？</title>
         <description><![CDATA[App Widget是Cupcake(Android 1.5)所提供的一個功能，這是一個很實用而且能有很大創意想像空間的功能。什麼是App Widget呢？請看底下的操作示範。

在Android桌面長壓約3秒，出現一個選單，如圖1。

<img alt="app-widget" src="http://www.jollen.org/blog/2009/07/10/app-widget-1.png"  />
圖1：新增項目至桌面

2. 選擇「Widget」，加入”HelloWidget”

<img alt="app-widget" src="http://www.jollen.org/blog/2009/07/10/app-widget-2.png"  />
圖2：加入自行設計的Widget

桌面上出現了一個「Widget」

<img alt="app-widget" src="http://www.jollen.org/blog/2009/07/10/app-widget-3.png"  />
圖3：在Android桌面上出現我們自已設計的App Widget

<img alt="app-widget" src="http://www.jollen.org/blog/2009/07/10/app-widget-4.png"  />
圖4：加入了音樂撥放器App Widget至桌面

這就是App Widget的應用，可以將一個小塊程式(program piece)嵌入到桌面上。App Widget也是一種UI組件，先前所介紹的TextView、WebView等也泛稱為Widget，二者在應用上的差異該怎麼思考呢？以下是幾點看法：

1. App Widget是有生命的UI組件，他會自動更新本身的內容
2. Widget是沒有生命的UI組件，它不會自我更新，只能等待使用者的操作
3. 應用上，App Widget能提供不斷更新的內容，很適合用來設計天氣、時鐘、新聞等主動式應用程式
4. Widget應用上只用來製作UI，而UI因為只能等待使用者來操作，所以過去我們所撰寫的Android應用程式都是屬於被動式應用程式

讓App Widget能「主動」更新自身內容的方法是透過一個「時間觸發裝置」，Android框架會根據我們設定的時間間隔，不斷地callback我們的App Widget。後續將再說明App Widget的做法，並解釋這個部份。]]></description>
         <link>https://www.jollen.org/blog/2009/07/jollen-android-programming-19.html</link>
         <guid>https://www.jollen.org/blog/2009/07/jollen-android-programming-19.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 10 Jul 2009 17:46:00 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#18: 佈景（Theme）初體驗</title>
         <description><![CDATA[上一節提到佈景（theme）是可以大範圍套用的UI美化功能，其套用範圍為「整個螢幕」，從程式碼的角度來看，佈景可以套用到以下二個範圍：

<ul><li>整個應用程式（application）</li>
<li>整個activity</li>
</ul>

接下來，我們以一個很簡單的例子，來說明如何套用佈景到application。在一些應用，我們可能不想要顯示視窗標題（title），怎麼做出這個功能呢？利用佈景設定的方式即可達成。以下是實作方法。

在styles.xml裡加入以下內容：

<pre>&lt;?xml version="1.0" encoding="utf-8"?>
&lt;resources>
    &lt;style name="myTheme">
    	&lt;item name="android:windowNoTitle">true</item>        
    &lt;/style> 
&lt;/resources></pre>

修改AndroidManifest.xml，在<application>標籤裡加上「theme」屬性：

<pre>&lt;?xml version="1.0" encoding="utf-8"?>
&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.moko.hellotheme"
      android:versionCode="1"
      android:versionName="1.0.0">
    &lt;application android:icon="@drawable/icon" android:label="@string/app_name"
    	<strong>android:theme="@style/myTheme"</strong>>
        &lt;activity android:name=".HelloTheme"
                  android:label="@string/app_name">
            &lt;intent-filter>
                &lt;action android:name="android.intent.action.MAIN" />
                &lt;category android:name="android.intent.category.LAUNCHER" />
            &lt;/intent-filter>
        &lt;/activity>
    &lt;/application>
&lt;/manifest></pre>

執行結果：

<img alt="theme-1.png" src="http://www.jollen.org/blog/2009/06/21/theme-1.png" width="320" height="480" />
圖1: HelloTheme的執行結果

在這個範例裡，我們並沒有修改任何的程式碼，其原理是透過佈景設定的方法。定義佈景的方式與定義樣式（styles）相同，同樣是在styles.xml裡以&lt;item>標籤來定義。

以下是使用HelloTheme的說明：

1. &lt;item>的name屬性為android:windowNoTitle時，表示定義是否要顯示視窗標題，在此設定為true，表示不要有視窗標題
2. 在&lt;application>標籤裡加上theme屬性，將佈景套用到應用程式

佈景除了能套用到應用程式外，也能套用到activity。如何套用佈景到activity呢？只要在&lt;activity>裡加入theme屬性即可，做法與&lt;application>相同。]]></description>
         <link>https://www.jollen.org/blog/2009/06/jollen-android-programming-18.html</link>
         <guid>https://www.jollen.org/blog/2009/06/jollen-android-programming-18.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 21 Jun 2009 13:30:00 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#17: 樣式設計（Styles）初體驗</title>
         <description><![CDATA[在這篇教學裡，我們將用一個非常簡單的範例來初步體驗Android的「styles」功能。

<strong>什麼是樣式（Styles）？</strong>

Android的樣式設計（style）是一個很重要的功能，因為它可以讓應用程式裡的元件（widget）「長」得跟別人很不一樣。樣式設計的使用規定如下：

<ul><li>在Android專案裡以XML資源檔來定義「樣式」</li>
<li>一個Android專案可以定義多個樣式</li>
<li>讓widget套用其中一個樣式</li>
</ul>

Android的styles功能，主要的對象是widget，樣式是為了套用到widget上；另外Android還提供佈景（theme）功能，可以做更大範圍的套用。

<strong>如何定義樣式</strong>

定義樣式的方式如下：

1. 在Android專案的「res/values」資料夾裡建立styles.xml樣式定義檔。如圖1。

<img alt="styles-1.png" src="http://www.jollen.org/blog/2009/06/20/styles-1.png" width="525" height="592" />
圖1: 建立styles.xml

2.在styles.xml裡定義樣式，以下是一個範例：

<blockquote><pre>&lt;?xml version="1.0" encoding="utf-8"?>
&lt;resources>
    &lt;style name="myText">
        &lt;item name="android:textSize">18sp&lt;/item>
        &lt;item name="android:textColor">#880&lt;/item>
    &lt;/style>
&lt;/resources></pre></blockquote>

styles.xml的寫法說明如下：

1. 在 &lt;resource>標籤裡定義資源項目， &lt;style>標籤用來定義樣式資源
2.  &lt;style>的name屬性定義此樣式的名字，widget使用此名字以套用樣式
3.  &lt;item>標籤定義此樣式的內容
4.  &lt;item>的name屬性為android:textSize時，表示定義此樣式的字體大小，在此設定字體大小為18sp
5.  &lt;item>的name屬性為android:textColor時，表示定義此樣式的字體顏色，在此設定字體顏色為#880（RGB）
6. 更多的樣式屬性，請參考Android Reference

定義好樣式後，就可以讓widget套用樣式。

<strong>Widget如何套用樣式</strong>

如何讓widget套用上述定義的「myText」樣式，方法很簡單。還記得UI layout檔main.xml嗎？只要在widget的項目裡，加上style屬性，並指定樣式名稱即可。以下是HelloStyles範例：

<blockquote> <pre>&lt;?xml version="1.0" encoding="utf-8"?>
 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
 &lt;TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Hello, this is HelloStyles."
<strong>    style="@style/myText"</strong>
    />
 &lt;/LinearLayout></pre></blockquote>

「@style/myText」表示要指定一個style的名稱，此名稱為myText。

執行結果：

<img alt="styles-2.png" src="http://www.jollen.org/blog/2009/06/20/styles-2.png" width="320" height="480" />
圖2: HelloStyles的執行畫面]]></description>
         <link>https://www.jollen.org/blog/2009/06/jollen-android-programming-17.html</link>
         <guid>https://www.jollen.org/blog/2009/06/jollen-android-programming-17.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sat, 20 Jun 2009 15:20:45 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#16: Event Listener的用法: 以Click Listener為例</title>
         <description><![CDATA[<strong>Event Listener的用法: 以Click Listener為例</strong>

以Android所提供的View.OnClickListener來說明程式實作方法。一個較為良好的實作方法是在我們的Acitivty類別裡實作View.OnClickListener介面，即：

<pre>import android.view.View;
  
public class HelloClickListener extends Activity implements View.OnClickListener {
   ...
}</pre>

每一個View都可以註冊一個event listener，當Android框架收到「click」事件後，便回呼event listener的callback method。以Button類別（按鈕元件）為例，當我們想要處理使用者觸控按鈕的事件時，就要呼叫Button類別的setOnClickListener()方法來註冊click listener。上述的實作方方法是，直接在我們的Activity類別HelloClickListener裡實作View.OnClickListener，因此上述Button類別的click listener為「this」。

上述的實作觀念，可用圖1來表示。

<img alt="HelloClickListener.png" src="http://www.jollen.org/blog/2009/06/18/HelloClickListener.png" width="385" height="393" />
圖1: HelloClickListener類別實作View.OnClickListener介面

註冊click listener的程式碼如下：

<pre>   public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Button button = (Button)findViewById(R.id.btn);
        button.setOnClickListener(this);
    }</pre>

在onCreate()裡先找到Button元件，它的click listener為this為，接著在我們的Activity類別裡實作onClick()。onClick()方法的程式碼如下，我們以Toast類別來回應訊息給使用者：

<pre>   public void onClick(View v) {
        Toast.makeText(
                this,
                "Yes.",
                Toast.LENGTH_LONG).show();  
    }</pre>

<strong>完整程式碼: HelloClickListener.java</strong>

<pre>package com.moko.helloclicklistener;
   
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import android.view.View;
   
public class HelloClickListener extends Activity <strong>implements View.OnClickListener</strong> {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Button button = (Button)findViewById(R.id.btn);
        button.setOnClickListener(this);
    }
    
<strong>    public void onClick(View v) {
        Toast.makeText(
                this,
                "Yes.",
                Toast.LENGTH_LONG).show();  
    }</strong>
}</pre>

<strong>執行結果</strong>

<img alt="clicklistener-1.png" src="http://www.jollen.org/blog/2009/06/18/clicklistener-1.png" width="320" height="480" />
圖2: HelloClickListener的執行結果

當使用者觸碰畫面上的按鈕時，便以Toast類別在畫面上顯示「Yes」。

]]></description>
         <link>https://www.jollen.org/blog/2009/06/jollen-android-programming-16.html</link>
         <guid>https://www.jollen.org/blog/2009/06/jollen-android-programming-16.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 18 Jun 2009 23:18:30 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#15: 什麼是事件監聽器(Event Listener)？</title>
         <description><![CDATA[學會產生基本的UI後，接著就要學習UI的事件處理(UI Events)，才能讓UI與使用者「互動」。

<strong>什麼是事件監聽器(Event Listener)</strong>

UI的使用者事件處理，即View如何處理使用者的操作，是一個重要的課題。View是重要的類別，它是與使用者互動的前線；在Android框架的設計中，以事件監聽器（event listener）的方式來處理UI的使用者事件。

Android框架提供了非常良好的UI事件處理機制。先前的教學提到，View是繪製UI的類別，每個View物件都可以向Android框架註冊一個事件監聽器。每個事件監聽器都包含一個回呼函數（callback method），

這個回呼函數（callback method）主要的工作就是回應或處理使用者的操作。

<strong>Event Listener: 以Click Listener為例</strong>

以「使用者觸碰（touch）」的動作來說，當View要處理使用者觸碰的事件時，就要向Android框架註冊View.OnClickListener事件監聽器；當「touch」事件發生時，Android框架便回呼事件監聽器裡的回呼函數。

View.OnClickListener是click listener，故名思意，這是UI的「Click動作監聽器」；當使用者對View進行Click操作時（即觸控畫面上的UI元件），Android框架便會回呼這個View.OnClickListener的回呼函數。

View.OnClickListerner的回呼函數為OnClick()。

這裡所提到的監聽器泛指event listener，主要用來「監聽」使用者的各種動作。除了View.OnClickListener外，Android框架還有以下的event listener（及其callback method）：

<ul><li>View.OnLongClickListener: onLongClick()</li>
<li>View.OnFocusChangeListener: onFocusChange()</li>
<li>View.OnKeyListener: onKey()</li>
<li>View.OnTouchListener: onTouch()</li>
<li>View.OnCreateContextMenuListener: onCreateContextMenu()</li>
</ul>

另外一種處理UI事件的機制為事件處理器（event handler），event handler與event listener是不一樣的二種處理機制。在自訂Android component的教學裡，再介紹這個部份。]]></description>
         <link>https://www.jollen.org/blog/2009/06/jollen-android-programming-15.html</link>
         <guid>https://www.jollen.org/blog/2009/06/jollen-android-programming-15.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 18 Jun 2009 23:15:37 +0800</pubDate>
      </item>
            <item>
         <title>Garmin-Asus的nuvifone G60改用Android作業系統</title>
         <description><![CDATA[CNET ASIA上的一則報導[<a href="http://asia.cnet.com/crave/2009/06/16/android-to-replace-garmin-asus-current-linux-platform/" target="_blank">Android to replace Garmin-Asus' current Linux platform</a>]指出，<strike>Garmin-Asus的nuvifone G60將改採Android作業系統</strike>。

<img alt="nuvifone-g60.jpg" src="http://www.jollen.org/blog/2009/06/17/nuvifone-g60.jpg" width="500" height="375" />
(圖片來源：CNET)

2009年二月，Garmin與Asus正式宣佈策略聯盟，並以「Garmin-Asus」雙品牌策略進行行銷。nuvifone G60是Garmin-Asus雙品牌行銷策略下的第一個產物，nuvifone G60則是以導航功能為主軸的手機。

<font color="#ff0000">根據報導指出，Garmin-Asus現有的Linux平臺將只使用在G60裝置上，未來的裝置會採用Windows Mobile或者是Android作業系統。</font>

<strike>如同Garmin的PND產品都是採用Linux作業系統一樣，原本nuvifone G60也計畫採用Linux作業系統，</strike>不久前，在<a href="http://chinese.engadget.com/2009/05/26/garmin-asus-nuvifone-g60-hands-on/" target="_blank">engadget</a>上的報導也出現實機照片<strike>；但是，隨著CNET這則報導的出現，整個開發計畫是否有了改變，頗令人好奇。</strike>

<strike>Android原本就對Google Map有很好的支援，再加上nuvifone G60是以導航以及地圖應用為主的手機，若是改採Android作業系統，也是一個合理的做法。</strike>

* Update: 2009/6/19
]]></description>
         <link>https://www.jollen.org/blog/2009/06/garmin-asus-nuvifone-g60-android.html</link>
         <guid>https://www.jollen.org/blog/2009/06/garmin-asus-nuvifone-g60-android.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Wed, 17 Jun 2009 10:46:29 +0800</pubDate>
      </item>
            <item>
         <title>Linux 2.6.30 釋出</title>
         <description><![CDATA[Linux 2.6.30於2009年6月9日釋出，Linux kernel的發展進入了Linux 2.6.3x的時代。最近一年的 Linux 2.6核心發展有相當重大的進展，除了幾個知名大廠不斷貢獻程式碼外，新產品的開發，也帶動Linux kernel的快速發展。

Linux 2.6.30加入了新的filesystem：

1. NILFS2

一種log-structured filesystem，由John K. Ousterhout與Fred Douglis於1988年提出的設計，主要針對high write throughput的應用。

2. POHMELFS (Parallel Optimized Host Message Exchange Layered File System)

一個分散式平行處理的檔案系統，在讀寫操作方面，根據[<a href="http://www.ioremap.net/projects/pohmelfs" target="_blank">POHMELFS</a>]官方的<a href="http://www.ioremap.net/node/134" target="_blank">數據</a>指出，POHMELFS的效能比NFS還好。

3. DST（Distributed STorage）

一個具高效能與可信賴的網路儲存檔案系統。

4. EXOFS（Object-Based Storage Devices）

支援OSD protocol的檔案系統。

5. FS-Cache

這是一個網路檔案系統（networking filesystem）的cache layer，FS-Cache可以將網路檔案系統的資料 cache 在磁碟裡。

<strong>其他更新</strong>

另外，Intel也貢獻了fastboot（快速開機）程式碼，過去kernel在開機時花費許多時間在處理 I/O 上，例如：儲存裝置的I/O，由Intel貢獻的fastboot以asynchronous function call的觀念解決此問題。其原理在kernel/async.c裡的註解有很清楚的說明：

<pre>14 /*
15
16 Goals and Theory of Operation
17
18 The primary goal of this feature is to reduce the kernel boot time,
19 by doing various independent hardware delays and discovery operations
20 decoupled and not strictly serialized.
21
22 More specifically, the asynchronous function call concept allows
23 certain operations (primarily during system boot) to happen
24 asynchronously, out of order, while these operations still
25 have their externally visible parts happen sequentially and in-order.
26 (not unlike how out-of-order CPUs retire their instructions in order)
27
28 Key to the asynchronous function call implementation is the concept of
29 a "sequence cookie" (which, although it has an abstracted type, can be
30 thought of as a monotonically incrementing number).
31
32 The async core will assign each scheduled event such a sequence cookie and
33 pass this to the called functions.
34
35 The asynchronously called function should before doing a globally visible
36 operation, such as registering device numbers, call the
37 async_synchronize_cookie() function and pass in its own cookie. The
38 async_synchronize_cookie() function will make sure that all asynchronous
39 operations that were scheduled prior to the operation corresponding with the
40 cookie have completed.
41
42 Subsystem/driver initialization code that scheduled asynchronous probe
43 functions, but which shares global resources with other drivers/subsystems
44 that do not use the asynchronous call feature, need to do a full
45 synchronization with the async_synchronize_full() function, before returning
46 from their init function. This is to maintain strict ordering between the
47 asynchronous and synchronous parts of the kernel.
48
49 */</pre>

關於 fastboot 的做法，在LWN上的一篇文章[<a href="http://lwn.net/Articles/314808" target="_blank">An asynchronous function call infrastructure</a>]有很不錯的介紹。

Red Hat也貢獻了二個新的system call：preadv()與pwritev()。其他更多Linux 2.6.30的變更，可參考[<a href="http://kernelnewbies.org/LinuxChanges" target="_blank">kernelnewbies</a>]上的說明。]]></description>
         <link>https://www.jollen.org/blog/2009/06/linux-2-6-30-announced.html</link>
         <guid>https://www.jollen.org/blog/2009/06/linux-2-6-30-announced.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Tue, 16 Jun 2009 10:56:10 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#14: 什麼是對話盒 (Dialog)？如何建立對話盒？</title>
         <description><![CDATA[<strong>最少的元件、最舒適的介面</strong>

自從有了圖形化應用程式之後，對話盒（dialog）一直是元老級的元件（widget）；智慧型手機開始流行後，對話盒仍然是手機介面的重要圖形元件。

在Apple的iPhone問世後，觸控螢幕（touch screen）一直是智慧型手機的標準規格，因此傳統的滑鼠點擊（click）式介面，並不完全適合手指觸控的操作方式，再加上手機觸控螢幕尺吋較小，因此手機應用程式的介面設計，已經與傳統的桌面環境相當不同。

Android的元件庫考量了小尺吋的觸控螢幕，在基本元件的設計上，Android也為使用者做了很體貼的考量。以Android手機應用程式來說，經常使用的元件已經不再像過去的點擊式系統那麼多又複雜；以使用性的角度來看，常被使用的元件如下：

* 選單（Menu）
* 對話盒（Dialog）
* 快顯訊息（Toast）

使用以上三個元件，以及其「變化形」，就能建構一個好用的應用程式介面；再加上Android針對上述的手機操作特性，對其元件庫做了很好的使用設計，因此使用很少的元件，也能提供使用者一個舒適好用的操作介面。

<strong>何謂對話盒？</strong>

對話盒，故名其思，是一個讓應用程式與使用者「對話」的元件。應用程式透過對話盒與使用者進行下述的對話：

* 詢問問題：使用者回答 Yes/No
* 詢問偏好：使用者選擇自已偏好的項目，可以是單選，也可以是複選
* 說明狀態：讓使用者知道應用程式目前的狀態，例如：顯示「處理中」、「載入中」等訊息

Android提供的對話盒物件為android.app.Dialog，實作上繼承自Dialog的AlertDialog物件是最基本的對話盒物件。使用AlertDialog對話盒，可以詢問使用者問題，也可以詢問使用者偏好。接下來介紹AlertDialog對話盒的設計方法。

<strong>建立AlertDialog對話盒</strong>

延續「HelloMenu」範例，現在我們想要加入以下的使用情境：

* 使用者按下Menu鍵
* 使用者觸壓 “New Message” 選項
* 出現對話盒、詢問使用者 “Yes/No”

由以上的使用情境來看，應該在onOptionsItemSelected()裡判斷到R.id.new_message項目時，在UI上建立一個對話盒。以下是修改後的onOptionsItemSelected()完整程式碼，完整範例名稱為HelloAlertDialog：

<pre>public boolean onOptionsItemSelected(MenuItem item) {
    	int item_id = item.getItemId();
    	
    	switch (item_id){
    		case R.id.new_message: 
	    	        AlertDialog.Builder builder = new AlertDialog.Builder(this);
	    	        
	    	        builder.setMessage("Also post your message to Twitter?");
	    	        builder.setCancelable(false);
	    	        
	    	        builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
	    	        	public void onClick(DialogInterface dialog, int id) {
	    	        	}
	    	        });
	    	        
	    	        builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
	    	        	public void onClick(DialogInterface dialog, int id) {
	    	        	}
	    	        });   
	    	        
	    	        AlertDialog alert = builder.create();
	    	        alert.show();
    			break;
    		case R.id.quit: 
                Toast.makeText(
                        this,
                        "Going to quit.",
                        Toast.LENGTH_LONG).show();    			
    			break;
    		default: return false;
    	}
    	return true;
 }</pre>

<img alt="dialog-1.png" src="http://www.jollen.org/blog/2009/06/10/dialog-1.png" width="300" height="433" />
圖1: Android的AlertDialog對話盒

產生AlertDialog物件的說明：

1. 產生AlertDialog.builder物件（dialog builder），這是一個用來建立對話盒內容的產生器物件

2. 設定dialog builder的顯示訊息-builder.setMessage()

3. 設定對話盒能不能被「取消」-builder.setCancelable()

4. 利用dialog builder在對話盒裡產生二個按鈕「Yes與No」-builder.setPositiveButton()與builder.setNegativeButton()
使用dialog builder來建立AlertDialog物件，AlertDialog是真正的對話盒物件-builder.create()

5. 將AlertDialog顯示在UI上-alert.show()

產生「Yes按鈕」的程式如下：

<pre>builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
	public void onClick(DialogInterface dialog, int id) {
		}
});</pre>

呼叫builder.setPositiveButton()方法建立一個「正面（Yes）」的按鈕，參數說明如下：

1. 第一個參數：顯示在按鈕上的文字
2. 第二個參數：指定 click listener

每一個按鈕都需要一個click listener，當使用者觸壓按鈕時，click listener便被回呼。android.content.DialogInterface類別提供click listener物件。]]></description>
         <link>https://www.jollen.org/blog/2009/06/jollen-android-programming-14.html</link>
         <guid>https://www.jollen.org/blog/2009/06/jollen-android-programming-14.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Wed, 10 Jun 2009 14:56:50 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#13: 快顯訊息 android.widget.Toast</title>
         <description><![CDATA[<p>Toast是Android提供「快顯訊息」類別，使用時請import以下套件：</p>

<blockquote><pre>import android.widget.Toast;</pre></blockquote>

<p>這是一個很好用的類別，特別是在初步建立Android應用程式的控制或行為時，可以輔助我們進行初步的測試工作。</p>

<p>配合上述的選單範例，我們將onOptionsItemSelected()回呼函數實作修改如下：</p>

<blockquote><pre>    public boolean onOptionsItemSelected(MenuItem item) {
    	int item_id = item.getItemId();
    	
    	switch (item_id){
    		case R.id.new_message: 
                Toast.makeText(
                        this,
                        "Please enter your message."
                                + " Your message is at max 255 characters.",
                        Toast.LENGTH_LONG).show();
    			break;
    		case R.id.quit: 
                Toast.makeText(
                        this,
                        "Going to quit.",
                        Toast.LENGTH_LONG).show();    			
    			break;
    		default: return false;
    	}</pre></blockquote>
    	
<p>此範例以Toast快顯訊息類別來顯示簡短訊息，以驗證上一個選單範例的功能是否正常。</p>

<img alt="toast-1.png" src="http://www.jollen.org/blog/2009/06/04/toast-1.png" width="354" height="679" />]]></description>
         <link>https://www.jollen.org/blog/2009/06/jollen-android-programming-13.html</link>
         <guid>https://www.jollen.org/blog/2009/06/jollen-android-programming-13.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 04 Jun 2009 00:20:03 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#12: 如何建立選單 Menu</title>
         <description><![CDATA[<p>Android應用程式的UI可以使用XML來定義，這個部份在前面的教學裡介紹過。要定義Android應用程式的選單，我們同樣可以使用XML來做描述，請看以下的說明。</p>

<strong>建立 Menu 步驟</strong>

<p>1. 建立選單的XML檔</p>

<p>在Android專案的res/目錄下新增一個menu/子目錄，然後建立options_menu.xml文件。</p>
<img alt="menu-1.png" src="http://www.jollen.org/blog/2009/06/03/menu-1.png" width="589" height="674" />
<p>圖1: 建立menu/目錄</p>

<img alt="menu-2.png" src="http://www.jollen.org/blog/2009/06/03/menu-2.png" width="525" height="592" />
<p>圖2: 建立options_menu.xml文件</p>

<p>2. 以XML定義選單內容</p>

<p>在options_menu.xml檔案裡，定義我們想要的選單內容。以下是一個範例：</p>

<blockquote><pre>&lt;menu xmlns:android="http://schemas.android.com/apk/res/android">
    &lt;item android:id="@+id/new_message"
          android:title="New Message" />
    &lt;item android:id="@+id/quit"
          android:title="Quit" />
&lt;/menu></pre></blockquote>

<p>3. 將選單加入應用程式</p>

<p>要如何在應用程式啟動時加入我們定義好的選單呢？在onCreateOptionsMenu()事件裡以MenuInflater類別將定義好的選單加入應用程式：</p>

<blockquote><pre>public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.options_menu, menu);
    return true;
}</pre></blockquote>

<p>在這個範例裡，我們使用到二個類別：Menu與MenuInflater，因此記得import這二個套件：</p>

<blockquote><pre>import android.view.Menu;
import android.view.MenuInflater;</pre></blockquote>

<strong>執行結果</strong>

<p>按下手機上的Menu鍵後，出現我們所設計的選單，如圖3。</p>

<img alt="menu-3.png" src="http://www.jollen.org/blog/2009/06/03/menu-3.png" width="900" height="752" />
<p>圖3: HelloMenu範例的選單畫面</p>

<strong>處理選單</strong>

<p>最後一個問題是，當使用者觸壓選單上的選項時，Android應用程式要如何處理？方法是透過onOptionsItemSelected()事件：</p>

<blockquote><pre>    public boolean onOptionsItemSelected(MenuItem item) {
    	return true;
    }</pre></blockquote>

<p>當此事件被回呼時，Android框架傳入被觸壓的選項物件，其類別為MenuItem；請import此套件：</p>

<blockquote><pre>import android.view.MenuItem;</pre></blockquote>

<p>前述的教學提到，Android應用程式編譯時，會自動產生R類別，即描述UI的類別。我們所定義的選單UI也會被放到R類別裡，如下：</p>

<blockquote><pre>package com.moko.hellomenu;

public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int icon=0x7f020000;
    }
<strong>    public static final class id {
        public static final int new_message=0x7f060000;
        public static final int quit=0x7f060001;
    }</strong>
    public static final class layout {
        public static final int main=0x7f030000;
    }
    public static final class menu {
        public static final int options_menu=0x7f050000;
    }
    public static final class string {
        public static final int app_name=0x7f040001;
        public static final int hello=0x7f040000;
    }
}</pre></blockquote>

<p>以下是處理MenuItem的程式範例：</p>

<blockquote><pre>    public boolean onOptionsItemSelected(MenuItem item) {
    	int item_id = item.getItemId();
    	
    	switch (item_id){
    		case R.id.new_message: break;
    		case R.id.quit: break;
    		default: return false;
    	}
    	return true;
    }</pre></blockquote>

<p>呼叫MenuItem的getItemId()方法，可取得該選項的ID，如此一來便能得知使用者所觸壓的選項。</p>

<strong>完整程式列表</strong>

<blockquote><pre>/* 範例檔名：HelloMenu.java */
package com.moko.hellomenu;

import android.app.Activity;
import android.os.Bundle;
<strong>import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;</strong>

public class HelloMenu extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    
<strong>    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.options_menu, menu);
        return true;
    }</strong>
    
    public boolean onOptionsItemSelected(MenuItem item) {
    	int item_id = item.getItemId();
    	
    	switch (item_id){
    		case R.id.new_message: break;
    		case R.id.quit: break;
    		default: return false;
    	}
    	return true;
    }
}</pre></blockquote>]]></description>
         <link>https://www.jollen.org/blog/2009/06/jollen-android-programming-12.html</link>
         <guid>https://www.jollen.org/blog/2009/06/jollen-android-programming-12.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Wed, 03 Jun 2009 17:21:32 +0800</pubDate>
      </item>
            <item>
         <title>「Android Porting Highlights」簡報上線</title>
         <description><![CDATA[上週受邀至「首屆亞太區 Android 技術大會」發表演說，由於大會希望能多著重在技術層面的主題，因此整理了過去研究 Android/FreeRunner 的一些資料，並將「重點」部份做了一次概念性的說明。Android 的分支（branch）是以「產品」的概念做維護，因此若在目前 Cupcake 能支援的平臺（architecture）上做移植的話，是一個較簡單的工作，只需要在 vendor/ 裡新增自已的 product 並修改 AndroidBoard.mk、AndroidProducts.mk 以及 BoardConfig.mk 即可完成一個 Board/Product 的新分支。

關於底層的部份，以 armv4（如 s3c2443）的 architecture 為例，將重要的工作項目做了整理式的說明。在此提供簡報電子檔下載 [<a href="http://www.jollen.org/slides/apac-android-porting-r2.pdf">Android Porting Highlights</a>]。]]></description>
         <link>https://www.jollen.org/blog/2009/05/android-porting-highlights.html</link>
         <guid>https://www.jollen.org/blog/2009/05/android-porting-highlights.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Fri, 22 May 2009 10:55:37 +0800</pubDate>
      </item>
            <item>
         <title>中國移動 OPhone 現身：採用 OMS 系統的 Android 手機（實機附圖）</title>
         <description><![CDATA[中國版的 Android 系統 OMS 現身 (China's Android OS，OMS - Open Mobile System)。

日前一則新聞 [<a href="http://news.chinatimes.com/CMoney/News/News-Page-content/0,4993,11050701+122009051000204,00.html" target="_blank">大陸OPhone商機 台商幕後推手</a>] 以及 [<a href="http://news.cnyes.com/stock/dspnewsS.asp?fi=\NEWSBASE\20090511\WEB782&vi=32214&date=20090511&time=10:54:45&pagetype=usastock&subtype=home&cls=usastock_totalnews" target="_blank">中移動5月中下旬發佈Ophone手機 主介面已可上網流覽</a>] 報導了中國移動（China Mobile）所推出的 OPhone 手機，採用 OMS 作業系統。本週在北京與 OMS（Open Mobile System）的開發商「<a href="http://www.borqs.com" target="_blank">播思通讯（BORQS）</a>」人員餐敘，OMS 的開發者也拿出了 OMS 的參考設計（reference design）實機讓現場朋友實機操作。

OMS 採用的正是 Google 的 Android 作業系統，如同上述報導所提，OMS 是 BORQS 與 Google 共同合作開發的中國版 Android 作業系統。OMS 專門針對中國移動做了許多客製化的應用程式。由於機會難得，因此特別拍了一些照片，在這裡跟大家分享。另外也看到了另外一支「很快就要上市」的 Android 手機，同樣也是採用 OMS 系統，但礙於一些因素，無法多做說明，當然也沒辦法照像了。

<img alt="China Mobile OPhone" src="http://www.jollen.org/blog/2009/05/19/china-mobile-ophone-1.jpg" />
主畫面的部份比 G1 還美觀，整體 UI 給我的感覺很不錯。

<img alt="China Mobile OPhone" src="http://www.jollen.org/blog/2009/05/19/china-mobile-ophone-2.jpg" />
看得出來 OMS 在介面設計上下了一些工夫。

<img alt="China Mobile OPhone" src="http://www.jollen.org/blog/2009/05/19/china-mobile-ophone-3.jpg" />
最特別的地方是 OMS 有「桌面切換」的功能，整體操作性做得不錯。

<img alt="China Mobile OPhone" src="http://www.jollen.org/blog/2009/05/19/china-mobile-ophone-4.jpg" />
某一個選單 UI。

<img alt="China Mobile OPhone" src="http://www.jollen.org/blog/2009/05/19/china-mobile-ophone-5.jpg" />
找了一下，果然沒錯，有導航功能！

<img alt="China Mobile OPhone" src="http://www.jollen.org/blog/2009/05/19/china-mobile-ophone-6.jpg" />
找到了手機電視功能。

<img alt="China Mobile OPhone" src="http://www.jollen.org/blog/2009/05/19/china-mobile-ophone-7.jpg" />
訊息雖然微弱，不過畫面還挺流暢的。

<img alt="China Mobile OPhone" src="http://www.jollen.org/blog/2009/05/19/china-mobile-ophone-8.jpg" />
音樂撥放器。

<img alt="China Mobile OPhone" src="http://www.jollen.org/blog/2009/05/19/china-mobile-ophone-9.jpg" />
雖然是參考設計，不過外觀（ID）做得算不錯。

用過 T-Mobile G1 以及 OPhone 後，總結來看，OPhone 的介面給我的感覺更好：

1. 操作性流暢許多
2. 多桌面（主屏）切換功能比 G1 更加好用
3. 觀看手機電視時畫面非常流暢、介面佈署（UI layout）還不錯 
4. 整體的視覺效果更華麗、畫面更美觀

隨著越來越多的 OPhone 新聞出現，這款由中國自主開發的「O1」頗令人期待，這是 Android 作業系統繼「G1」後，最令人期待的另一個事件。

OPhone 的出現，還代表了一件重要的事情。過去電信營運商，並不掌握手機軟體平臺的技術能力，因此「為自已量身打造」手機軟體、結合自家的服務，並推出實體手機，是一件不容易的事情。如今，藉由「Open Mobile System」的出現，電信營運商能取得開放的手機平臺，並自行發展手機服務應用程式。這是首次，電信營運商能真正掌握整體手機軟體的技術。掌握了電信、用戶、服務與手機軟體等關鍵能力，看來，一些有趣的事情正要開始發生。對於 OPhone 的出線，中國移動扮演了一個重要的角色，身為最重要的電信營運商，此舉必然有登高一呼的效應。]]></description>
         <link>https://www.jollen.org/blog/2009/05/china-mobile-ophone-oms.html</link>
         <guid>https://www.jollen.org/blog/2009/05/china-mobile-ophone-oms.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Tue, 19 May 2009 22:57:08 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android Porting 手札 #1: Android 移植概觀</title>
         <description><![CDATA[本週六將於北京舉行的「Android 技術大會」上發表有關「Android 移植」的技術演說，配合該演講，最近將陸續整理一些筆記以搭配講稿供與會朋友參考。

<strong>Android 的技術優點</strong>

Android 平臺的好處是「將開發者侷限在應用層（application level）」的開發，並透過一個設計良好的 application framework 將 library 層「包裝起來」。傳統 GNU/Linux 系統的「開源模式」是「從裡到外」全面開放，應用程式來自四面八方，每個應用程式底層使用到的 library 並不相同，這讓 Linux 平臺的軟體發展容易失控，造成 Linux distribution 上雖然收錄了豐富的應用程式，但相對的也要包山包海地納入非常多的 shared library。

Android 雖然也採用了其他 open source 的專案成果，但 Android 以很聰明的方式，解決傳統 Linux 開放手機平臺的「相依性」問題，這也是過去長久以來，匯整使用（leverage）開放源碼專案開發產品的大問題。Application framework 採用 Java 程式語言，並軟性的將開發者限制在 application level 是 Android 解決上述技術難題的一個關鍵。

網路上有著數以萬計的 Free & Open Source Software 專案，而被 Android 採納的 FOSS 專案僅有約 60 個左右，比起傳統 Linux distribution 必須收錄上千個套件的數量來看，Android 未來若能發揚光大，能是扮演「收斂」開源軟體發展模式的推手。

傳統的 Embedded Linux 系統程式基於 GNU libc 以及大量的相依程式庫（library dependencies），因此很容易有「牽一髮而動全身」的問題出現。例如：某一個library的API變動（可能是函數改名或移除）將使得其它程式庫與應用程式執行錯誤，這時就必須修改原始程式碼並重新編譯才能解決問題。

這個問題的主因，是因為 Linux 系統是採取動態程式庫（shared libraries）的機制，程式庫的變更雖然只需要「抽換」掉動態程式庫檔，但是應用程式在執行時，才會產生「無法載入符號」的錯誤，除非是「定期」進行「系統重編譯」，否則很難即時修正此錯誤。

Android 的底層並無太複雜的「程式庫相依」問題，這使得 Android 可以比較容易將系統與 IDE 開發工具做整合。在標準 C 程式庫（C library）方面，Google 則是採用 BSD 授權實作了一份適合手機系統使用的版本，無疑是一個值得稱許的做法。

由上述的分析來看，Android 平臺在系統層（library、kernel）的移植工作將不再花費工程人員大量的時間，同時就系統層的調校工作來看，也能有更具體明確的調校項目。

<strong>移植項目概述</strong>

從 Google 發佈的標準 Android 系統架構圖來說，Application framework 以及 application 二層並沒有重要的移植工作需要進行，以下以系統架構的角度來簡介 Android 移植技術的重要事項。

1. Application

應用程式層並移植的工作需要進行，但是因為 Android 每個版本的 API level 都不一樣，因此需要進行 API 相容性的測試。例如，Android 1.1 的 API level 為 2、Android 1.5 的 API level 為 3，需要根據 Google 發佈的 API change 文件進行 application 的相容性測試。

2. Application Framework

以大方向來看，加入新的 library 時需要擴充 application framework；Android framework 以 JNI 呼叫下一層的 library，但是 application 不直接呼叫 library，因此讓 Android framework 的設計更嚴謹。

3. Library

Android 的「external library」裡包含部份現有的 FOSS 專案成果，有些 library 的實作為 machine-dependent，針對 machine-dependent 的實作必須修改其程式碼。例如，android.media.MediaPlayer 的底層為 OpenCore 程式庫，而 OpenCore 的 MP3 decode 部份演算法以 assembly 實作。

4. Dalvik VM

JNI 與 interpreter 是 Android runtimer（Dalvik VM）的移植重點。新版本的 Cupcake（Android 1.5）在 JNI 的部份加入了 x86 的支援，interpreter 目前則是支援 armv4、armv5te 以及 x86。

5. Bionic

Bionic 是 Android 專屬的小型客製化 C library，主要是由 BSD C library 移植而來。支援 Linux kernel 的重要實作，像是：system call、dynamic linker & loader、thread 等等都是檢查的重點。Bionic 裡有一個 libthread_db 是基於 Linux futexes 的 thread 實作，很適合像是手機這樣的小型系統使用。

6. 其它

Android framework 的實作，部份需要考量硬體的規格，例如：Surface Manager 需要考量是否有 GPU 或硬體加速。但你在 Android application 建立一個 Surface holder 時，若是將 Surface 的類型設定為 SURFACE_TYPE_HARDWARE，就必須針對硬體加速與 DMA 做移植工作。

<strong>小結</strong>

Android 平臺的移植技術仍屬重要，主要的技術能力著重於 Android framework（例如：加入新的 external library）以及 Linux device driver 上。本次 Android 技術大會除了介紹 Android 移植的重點（highlights）以及具體實作方向外，也會展示近期的小小實作成果。

雖然一開始提到，Android 平臺的好處是「將開發者侷限在應用層（application level）」的開發，但是 Android framework 與 Android 移植的能力仍然重要，畢竟這是直接影響產品開發的關鍵力之一。

]]></description>
         <link>https://www.jollen.org/blog/2009/05/android-porting-introduction.html</link>
         <guid>https://www.jollen.org/blog/2009/05/android-porting-introduction.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 10 May 2009 21:58:29 +0800</pubDate>
      </item>
            <item>
         <title>Android Day 活動紀錄：Android 應用程式新手入門訓練</title>
         <description><![CDATA[<a href="http://www.androidday.com" target="_blank"><img alt="android-day-banner.jpg" src="http://www.jollen.org/blog/2009/05/08/android-day-banner.jpg" width="630" height="292" border="0" /></a>

上週六（5/2）舉辦「<a href="http://www.androidday.com">Android Day</a>」訓練活動，上午的活動是「Android 的機會」議程，下午舉辦了一場免費的 Android 訓練課程。Android Day 訓練活動的目的除了希望可以認識朋友，並面對面與大家交換不同的想法外，也希望可以幫助對 Android 應用程式有興趣的朋友，能一天就入門 Android 應用程式設計。對於想初步了解 Android 應用程式設計方法的朋友相當有幫助；透過 Android Day 訓練課程希望能幫助大家節省一開始的自學時間。

<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/dZvElmQiR7I&hl=zh_TW&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/dZvElmQiR7I&hl=zh_TW&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object>

上午的議程，由小弟我、高煥堂老師與 David（gOS 執行長）以自由論壇形式，與大家討論 Android 產品端與推廣方面的想法。由於 Android 的 middleware 技術（包含調校、移植等）對產品的發展是一個重要的技術能力，因此高煥堂老師提出一個 shared object（shared library）工作小組的計畫，此外，高老師也針對 middleware 的觀念做了一些說明；David 也從「授權」的角度來分析，為什麼採用 APL（Apache License）授權的 Android 平臺，比起過去以 GPL 授權為主的桌面 Linux 與 Mobile Linux 有更好的商業機會，以及更多元的商業模式。

在推廣方面，現場討論到了亞太地區的分工與交流。日本與韓國對於 Android 技術的參與熱度並不比台灣低，因此如何善用台灣「硬體設計」的優勢，並結合 Android 設計服務，來創造新的機會，成為當天討論的一個想法。

下午則是由小弟主講的「Android 應用程式新手入門訓練」訓練活動，由於現場大部份的朋友都沒有安裝 Eclipse + Android Development Toolkit 的經驗，因此一開始利用一些時間，介紹 Android 開發環境的安裝方式；在主要課程方面，則是以「<a href="http://www.jollen.org/blog/2009/04/android-day-package-v1.html">Android Day Package -- Android 應用程式新手入門</a>」講義與範例做為教材，介紹 Android 應用程式的主要觀念（例如：Activity、View 等）。希望這種形式的活動，對大家入門 Android 有幫助。

下週（5/16-17）將在上海與北京舉行的第一屆 Android 技術大會，將會與日本與韓國的專家現場進行討論，這次的技術大會，除了介紹一些重要的 Android 技術議題外，也會是讓大家交流與互動的好場合。]]></description>
         <link>https://www.jollen.org/blog/2009/05/android-day-event-report.html</link>
         <guid>https://www.jollen.org/blog/2009/05/android-day-event-report.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Wed, 06 May 2009 23:20:18 +0800</pubDate>
      </item>
            <item>
         <title>Android Netbook 行不行：從產品角度來思考</title>
         <description>社群開發者將Android移植到EeePC後，興起一股「Android小筆電」討論風潮。Android小筆電的概念就是這樣來的；由開發者給市場的一個考題。市場上一陣Android小筆電產品的新聞，幾家品牌大廠，對Android小筆電市場更是磨刀霍霍。對於這陣Android小筆電的風潮要如何解讀？在此分享個人的觀察與想法，請不吝指教。

Android小筆電的熱潮，起之於玩家對技術的好奇心；對於Android小筆電產品的討論，則是廠商與使用者的期待與想像。從技面的角度來看，Android小筆電仍有使用介面（UI）上的疑慮，尚不足以產品化。由於Android介面的設計預設對象為手機，因此在小筆電上的畫面表現較不理想，操作方面亦同。Android小筆電上仍有技術缺口。

基於「應用程式」概念的小筆電，因是「使用習性」的問題，使用者還是喜歡微軟的作業系統，Linux小筆電還是佔不到便宜。

使用者免不了將Linux小筆電與微軟系統的小筆電拿來相提並論。Linux上有OpenOffice辦公軟體，但微軟系統有使用者更習慣的Office套裝軟體；Linux上有Thunderbird電子郵件軟體，但微軟系統有使用者更習慣的Outlook軟體；無論是上網、電子郵件還是即時通訊，Linux小筆電上的「應用程式」都讓使用者操作得很沒有安全感。

從另外一個角度來思考。

基於「網路服務」概念的Android平臺，因為與微軟系統的小筆電有很大的差異性，因此似乎存在不錯的機會。Android平臺不基於Linux桌面技術，我們沒有辦法將OpenOffice軟體，或是Firefox瀏覽器安裝在Android小筆電上，正好與現有的小筆電有很大的差異性，也給了新產品定位的大空間。

產品定位方面，把Android做為取代微軟系統或Linux桌面的想法，反而讓Android小筆電失去了這個差異性；當大家開始討論Android小筆電上能不能執行辦公室軟體時，誰知道，會不會又像Linux小筆電產品般的結果。

Android平臺的產品可以擁有更多的區隔性，因此機會之一是尋找或創造不同的產品「使用概念」。現階段Android小筆電仍處於技術玩票性質，這是由玩家帶起的概念。要讓Android小筆電產品化仍要補齊技術上的不足。</description>
         <link>https://www.jollen.org/blog/2009/04/android_netbook_product_concept.html</link>
         <guid>https://www.jollen.org/blog/2009/04/android_netbook_product_concept.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Sun, 19 Apr 2009 15:05:43 +0800</pubDate>
      </item>
            <item>
         <title>出現搭載 Android 平臺的 PMP 產品</title>
         <description><![CDATA[LinuxDevices.com 報導了一則很酷的消息 [<a href="http://linuxdevices.com/news/NS3193160405.html">Android-based PMP to ship in October</a>]。 一家名為 [<a href="http://www.giinii.com/movit_detail.html" target="_blank">GiiNii</a>] 的公司，將在 10 月份開始銷售使用 Android 平臺的 PMP（portable media player）；該公司在明年的 1 月份也會銷售 Android 平臺的 DPF（digital picture frame）。

<img src="http://www.giinii.com/images/products/Lrg/movit/Lrg_Movit-mini-blk-angle-home.jpg" />
（圖片來源：GiiNii）

這台名為 Movit Mini 的 PMP 實際上就是一個「MID」概念的產品，但 GiiNii 並不將 Movit Mini 稱為 MID，而是將它定位為 PMP。Movit Mini 不同於市面的泛 MID 產品，GiiNii 重新做了產品定位：

1. 外觀更輕薄，這意味著 Movit Mini 比 MID 更具攜帶性（mobile）

2. 螢幕尺吋更小，傳統的 MID 是 4.8 吋，Movit Mini 縮小為 4.3 吋

3. 畫面解析度降為 480x272 

不管是 PMP 或 MID，因為都具備觸控螢幕的功能，因此比 Netbook 更適合搭載 Android。報導中也指出，Movit Mini 因為使用 Android 平臺，因此這是一個具備「能存取 Google apps」的 PMP，頗為有趣的產品。

現今的 MID 大多使用 Intel Moblin 平臺（處理器當然使用 Intel 自已的 Atom），因此除了 UI 方面的差異外，似乎比較難表現 MID 本身的產品差異性，當然產品定位也就比較「搖晃」一點。
]]></description>
         <link>https://www.jollen.org/blog/2009/04/movit_mini_android_pmp.html</link>
         <guid>https://www.jollen.org/blog/2009/04/movit_mini_android_pmp.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 19 Apr 2009 14:30:08 +0800</pubDate>
      </item>
            <item>
         <title>「Android Day Package -- Android 應用程式新手入門」</title>
         <description><![CDATA[<p>「Android Day Package -- Android 應用程式新手入門」整理了這陣子我在研討會的演講材料，包含：</p>
<ol>
	<li>簡報一份</li>
	<li>Android 入門教學文件共11集</li>
	<li>範例程式4例</li>
</ol>
<p>因為研討會是一天的演講活動，因此這些內容很適合新手做為「學習 Android 應用程式」的入門教材，大約只需要一天的時間，就能初步了解 Android 
的開發工具使用，並了解 Android 的應用程式模式，故取名為「Android Day Package」，期望能提供一個「Android 
新手一天入門」的教學套件。請不吝指教。</p>
<p>簡報的部份是受零組件雜誌邀請，進行一天的 Android 演講活動，所特別製作的簡報；範例則是參考 Android SDK 
所撰寫的實例，範例是配搭簡報進行實例講解所使用的程式碼。[<a href="http://tw.jollen.org/android/android-day-v1.zip">下載 
Android Day Package</a>] 後，可搭配以下共11份教學文件學習；以下的教學文件是為製作簡報時的筆記，特別整理成一份教學文件與大家分享。</p>
<p><b>課程主題</b></p>

Android Day Package 提供以下的課程主題。

1. 開放手機平台發展現況 (1hr)
‧開放手機平台陣營 
‧授權模式比較 
‧市場現況 
‧行銷與推廣策略 

2. Android 入門 (1.5hr)
‧安裝 SDK 
‧Android模擬器 
‧Android開發工具 (ADT) 
‧Android除錯工具 (ADB) 
‧Hierarchy Viewer 

3. Android應用程式模式 (2hr)
‧Android Framework 
‧Activity 
‧Service 
‧BroadcastReceiver 
‧Process types 
‧Views, ViewGroup 
‧Design Screen 
‧AndroidManifest.xml 
‧Intents 

4. Android應用程式開發 (1hr)
‧Hello, Moko 範例程式 
‧Openmoko Neo FreeRunner手機安裝 
‧安裝 apk套件 
‧使用 Neo FreeRunner實機展示 

<p><b>Android 教學</b></p>
<a name="top">
<ul class="archive-list">
	<li class="archive-list-item">2009.01.19: </a>
	<a href="http://www.jollen.org/blog/2009/01/jollen-android-programming-11.html">
	Jollen 的 Android 教學,#11: AndroidManifest.xml 的用途是什麼？</a> </li>
	<li class="archive-list-item">2009.01.19:
	<a href="http://www.jollen.org/blog/2009/01/jollen-android-programming-10.html">
	Jollen 的 Android 教學,#10: 如何檢查 Service 是否已啟動？使用 Android 除錯器</a> </li>
	<li class="archive-list-item">2009.01.12:
	<a href="http://www.jollen.org/blog/2009/01/jollen-android-programming-9.html">
	Jollen 的 Android 教學,#9: 啟動 Service - startService()</a> </li>
	<li class="archive-list-item">2009.01.12:
	<a href="http://www.jollen.org/blog/2009/01/jollen-android-programming-8.html">
	Jollen 的 Android 教學,#8: 沒有 UI 的 Service</a> </li>
	<li class="archive-list-item">2009.01.08:
	<a href="http://www.jollen.org/blog/2009/01/jollen-android-programming-7.html">
	Jollen 的 Android 教學,#7: 如何讓文字並排顯示 - TableLayout</a> </li>
	<li class="archive-list-item">2009.01.05:
	<a href="http://www.jollen.org/blog/2009/01/jollen-android-programming-6.html">
	Jollen 的 Android 教學,#6: WebView 體驗與 findViewByID</a> </li>
	<li class="archive-list-item">2009.01.04:
	<a href="http://www.jollen.org/blog/2009/01/jollen-android-programming-5.html">
	Jollen 的 Android 教學,#5: 使用 View 的 XML 屬性</a> </li>
	<li class="archive-list-item">2009.01.04:
	<a href="http://www.jollen.org/blog/2009/01/jollen-android-programming-4.html">
	Jollen 的 Android 教學,#4: 使用 XML 安排 UI</a> </li>
	<li class="archive-list-item">2008.12.29:
	<a href="http://www.jollen.org/blog/2008/12/jollen-android-programming-3.html">
	Jollen 的 Android 教學,#3: 第一個 Android 專案</a> </li>
	<li class="archive-list-item">2008.12.29:
	<a href="http://www.jollen.org/blog/2008/12/jollen-android-programming-2.html">
	Jollen 的 Android 教學,#2: “Hello Moko” - Activity 與 View 的關係</a> </li>
	<li class="archive-list-item">2008.12.29:
	<a href="http://www.jollen.org/blog/2008/12/jollen-android-programming-1.html">
	Jollen 的Android 教學,#1: Android 應用程式模式</a> </li>
</ul>

Revision: 2009/04/19]]></description>
         <link>https://www.jollen.org/blog/2009/04/android-day-package-v1.html</link>
         <guid>https://www.jollen.org/blog/2009/04/android-day-package-v1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 12 Apr 2009 11:46:32 +0800</pubDate>
      </item>
            <item>
         <title>Linux Input Device 介紹: APIs</title>
         <description><![CDATA[Linux 的 Input Device 是重要的一個 subsystem，在進行實例介紹前，先大略了解一下相關的 API。

<strong>Linux Input Device</strong>

﻿input.c是Linux的”input”驅動程式，主要支援鍵盤與滑鼠的輸入；input.c介面有趣的地方是採用了事件（event）的方式來處理輸入，以下是input.c介面重要的資料結構與函數：

* struct input_dev
* void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
* void input_register_device(struct input_dev *);
* void input_unregister_device(struct input_dev *);
* void input_register_handler(struct input_handler *);
* void input_unregister_handler(struct input_handler *);

Linux 的input機制可用來實作「虛擬鍵盤」或「虛擬滑鼠」，只要呼叫input_event()將輸入資料發佈給input handler即可。

struct input_dev是用來描述輸入事件的重要資料結構，其原型宣告如下：

<div>
struct input_dev {

	void *private;

	int number;
	char *name;
	unsigned short idbus;
	unsigned short idvendor;
	unsigned short idproduct;
	unsigned short idversion;

	unsigned long evbit[NBITS(EV_MAX)];
	unsigned long keybit[NBITS(KEY_MAX)];
	unsigned long relbit[NBITS(REL_MAX)];
	unsigned long absbit[NBITS(ABS_MAX)];
	unsigned long mscbit[NBITS(MSC_MAX)];
	unsigned long ledbit[NBITS(LED_MAX)];
	unsigned long sndbit[NBITS(SND_MAX)];
	unsigned long ffbit[NBITS(FF_MAX)];
	int ff_effects_max;

	unsigned int keycodemax;
	unsigned int keycodesize;
	void *keycode;

	unsigned int repeat_key;
	struct timer_list timer;

	int abs[ABS_MAX + 1];
	int rep[REP_MAX + 1];

	unsigned long key[NBITS(KEY_MAX)];
	unsigned long led[NBITS(LED_MAX)];
	unsigned long snd[NBITS(SND_MAX)];

	int absmax[ABS_MAX + 1];
	int absmin[ABS_MAX + 1];
	int absfuzz[ABS_MAX + 1];
	int absflat[ABS_MAX + 1];

	int (*open)(struct input_dev *dev);
	void (*close)(struct input_dev *dev);
	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
	int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
	int (*erase_effect)(struct input_dev *dev, int effect_id);

	struct input_handle *handle;
	struct input_dev *next;
};
</div>

<strong>定義按鍵</strong>

我們可以設定struct input_dev裡的evbit欄位，來定義所要接受的輸入類型，目前共有8種輸入類型如下：

* EV_KEY：Keys and buttons（按鍵與按鈕）。
* EV_REL：Relative axes（相對座標）。
* EV_ABS：Absolute axes（絕對座標）。
* EV_MSC：Misc events（其它事件）。
* EV_LED：LEDs。
* EV_SND：Sounds（聲音輸入）。
* EV_REP：Autorepeat values（自動重覆數值）。
* EV_FF：Force feedback事件。

以下是一個範例，我們指定dev可接受EV_KEY事件：

dev.evbit[0] = BIT(EV_KEY);

evbit是一個陣列，每個元素可以索引一種輸入類型。每種輸入類型均可指定特定的輸入資料，例如：TAB鍵。指定方式是使用set_bit()或BIT巨集來設定每種輸入類型的陣列。以下是各輸入類型的欄位名稱：

* keybit[NBITS(KEY_MAX)]：Keys and buttons（按鍵與按鈕）。
* relbit[NBITS(REL_MAX)]：Relative axes（相對座標）。
* absbit[NBITS(ABS_MAX)]：Absolute axes（絕對座標）。
* mscbit[NBITS(MSC_MAX)]：Misc events（其它事件）。
* ledbit[NBITS(LED_MAX)]：LEDs。
* sndbit[NBITS(SND_MAX)]：Sounds（聲音輸入）。
* ffbit[NBITS(FF_MAX)]：Force feedback事件。

以下是使用set_bit()的範例：

* set_bit(KEY_UP,    dev.keybit);
* set_bit(KEY_LEFT,  dev.keybit);

或是使用BIT巨集也可以：

* keybit[0] = BIT(KEYUP) | BIT(KEY_LEFT);

input.h裡做位元運算的3個巨集如下：

* NBITS(x)：計算要幾個陣列元素，才夠紀錄第x個位元。
* BIT(x)：傳回單獨第x個位元為1時所代表的數值，例如：x=0時為0x1，x=1時為0x2，x=2時為0x4。
* LONG(x)：第x個位元是屬於第幾個陣列元素（即索引值）。

我們先設定驅動程式能接受EV_KEY事件，然後指定EV_KEY事件的特定輸入值為KEY_UP與KEY_LEFT按鍵。不同事件的輸入資料定義，請參考input.h檔，例如以下是EV_KEY事件的按鍵1~按鍵9定義：

/*
 * Keys and buttons
 */

#define KEY_1			2
#define KEY_2			3
#define KEY_3			4
#define KEY_4			5
#define KEY_5			6
#define KEY_6			7
#define KEY_7			8
#define KEY_8			9
#define KEY_9			10

設定好struct input_dev後再呼叫input_register_device()註冊至上層。當”input device”註冊至kernel後，當該input device被開啟時，便呼叫input device的open method；當input device關閉時，便呼叫input device的close method。
實作input device的open method時，若成功應傳回0，失敗的話則傳回任意的非零值；close method則不須傳回值。

<strong>Report 按鍵</strong>

使用者的按鍵往上層回報所使用的API如下：

* input_report_key(struct input_dev *dev, unsigned int code, int value);
* input_report_rel(struct input_dev *dev, unsigned int code, int value);
* input_report_abs(struct input_dev *dev, unsigned int code, int value);
* input_report_key與input_report_rel其實都是使用input_event()的巨集，input_event()的函數原型與參數說明如下：
* void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);

* @dev：指向input device的指標。
* @type：輸入類型（EV_KEY、EV_ABS等）。
* @code：輸入按鍵（例如EV_KEY的KEY_1）。
* @value：按鍵值。

<strong>Input Handler</strong>

任何的按鍵輸入，都應呼叫input_event()來將input event送到input.c，再由input.c分派事件（遶送）到每一個”input handler”。有註冊input handler的驅動程式，都能讀取輸入資料。
]]></description>
         <link>https://www.jollen.org/blog/2009/04/linux_input_device_apis.html</link>
         <guid>https://www.jollen.org/blog/2009/04/linux_input_device_apis.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Wed, 08 Apr 2009 12:18:51 +0800</pubDate>
      </item>
            <item>
         <title>CTimes 矽導論壇：行動通訊產業 - 看中國崛起與市場機會</title>
         <description>（原文刊載於零組件雜誌2009年3月份）
 
近來讀到幾則報導二零零九年移動通信大會的新聞，提到中國在手機產業中，營造了一股崛起的氣氛，有些人更直接了當提出中國手機產業崛起的看法。從產品角度來看，中國在這次大會的產品展示，幾乎涵蓋了所有產品線，可想見中國在手機產業的研發能量頗為驚人。以中興通訊來說，其自主研發的手機產品線，己經涵蓋了各種作業系統，以及三端手機（中階、高階與低階），顯見其產品線的佈局頗為完整。 

中國的華為也成功進入全球幾家主要的電信營運商，同時目前也是部份地區的主要供應商之一，可見中國手機產業也具備了電信營運商模式的能力。開放式手機平臺對中國在國際業務的拓業，也會有正向的幫助。二零零八年全球GSM手機出貨排名，華為以24.4%的佔有率排名第二，己經超過 Nokia。華為在移動通信大會上也展示了使用 Android 平臺的手機，因此在結合網路服務與應用客製化方面，華為做足了準備，有機會透過 Android 手機拓業國際市場。

中國的禹華通信近期也推出了 Android 手機的參考設計，這個參考設計採用 Marvell PXA-310 平臺，這是除了 Qualcomm 外的另一個 Android 手機參考平臺。禹華通信的 Android 手機平臺也有客戶採用了。現在，中國許多手機廠商陸續備齊了完整的 Android 設計平臺，只要 Android 應用程式的研發能量能到位，或是能善加利用 Android 開發者社群的資源的利用，未來中國手機製造商（ODM）在 Android 客製化手機的市場，會有一定的競爭力。

至於中國本地的 Android 手機市場機會在哪裡，以下就過去與中國業者的往來，整理一些資訊與大家分享。

目前，2G 的 Android 手機（GPRS/EDGE）在中國市場部份，短期還是會有不錯的市場需求，特別與業者合作的客製化手機部份，特別被看好。支援 Android 的 2G 手機參考平臺也相對完善，例如上述提及，禹華通信推出的參考設計，就是屬於 GPRS/EDGE 的規格。Openmoko 的 GTA02（Neo FreeRunner）本身也是 2G 規格，目前也有多家加值應用開發商（Value-Added Reseller）在詢問採用的可行性，當然軟體部份考慮的是 Android 平臺，而非 Openmoko 本身的軟體。

在 3G 手機部份，許多人認為 3G 在幾年內勢必成為市場主流，屆時 2G 手機將會進一步降價，因此 2G 手機本身的利潤空間也會比較壓縮。Android 手機在 3G 的市場，則是需要比較多的資源整合，以及可行的商業模式，這是 Android 手機研發業者的主要挑戰。

針對 Android 平臺的  3G 手機，中國本土的市場機會之一是 TD-SCDMA 系統。原因是，TD-SCDMA 雖是 4 大 3G 標準之一，但起步較晚，成熟度也較低。以這個角度來看，TD-SCDMA 系統的 Android 3G 手機，仍有待研發資源的投入，因此有一個切入的機會點。以市場角度來看，中國最大電信商中國移動也投入不少研發資金在 TD-SCDMA 的手機研發；TD-SCDMA 目前的主要問題之一就是終端裝置，由此來看，終端裝置開發商會有許多取得中國移動支持的機會。</description>
         <link>https://www.jollen.org/blog/2009/04/android_phone_china_market.html</link>
         <guid>https://www.jollen.org/blog/2009/04/android_phone_china_market.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Wed, 01 Apr 2009 17:25:50 +0800</pubDate>
      </item>
            <item>
         <title>CTimes 矽導論壇：Android 手機 - 邁向心理效應的轉折點</title>
         <description>不確定因素消散 G2延伸氣勢 趨勢即將確立（原文刊載於零組件雜誌2009年2月份）

最近一則有關Android的最新消息是，T-Mobile 即將在五月份開始銷售 G2 手機，對 Android 平臺來說，這是一個具實質意義的里程碑，代表者 Android 手機的氣勢與話題將再延續。從現在看過去，T-Mobile 的 G1 手機帶來的意義，它並不只是全球第一支 Google 手機，更成功扮演了開路者（帶路者）的角色。

回顧過去，一開始，大家都在謠傳 Google 即將推出自己的手機，並進軍行動通訊產業，但後來 Google 在一場正式的記者會上宣佈成立 OHA 並釋出 Android 平臺後，大家才又恍然大悟，原來 Google 推的一個開放平臺，而不是 Google 手機。針對開放開放平臺來說，開發者社群期待的是一個開放源始碼（open source）的平臺，因此一開始非完全開放源始碼的 Android 便開始受到一些質疑。Google 的反應夠快，在去年（二零零八年）的6月2日便發表了一則聲明，宣稱「Android 將會 100% 開放源碼」，果然，在10月22日，Google 便正式公開了 Android 的完整原始碼。

Google 的策略相當靈活。Android 雖然是一個開放源碼的平臺，但是卻採取有別於傳統 Linux 手機平臺的授權策略。簡單來說，以 Apache 2.0 授權授出的 Android 平臺，允許電信商（carrier）與 OEM 保留原始碼，也就是 Google 不能保證這些廠商也會公開自己的 Android 版本原始碼，對應用程式來說也相當同，「不保證應用程式開發商也會公佈其原始程式碼」。這樣的授權策略無疑是比過去的 GPL 授權更加「商業友善」。

分食 Android 商機似乎成為一個全球運動，在 Android 平臺上開發應用程式，以及電信服務的客制化，成為一個很好的商業模式。從過去的 Android Developer Challenge 大賽作品來分析，Google 與 OHA 從 1788 個參賽者，挑選出 50 個決選者，而大部份的決選作品，都是基於 GPS 與 Google Maps 開發應用程式，其次則是 social networking。顯見大家的看法有很高的共同點。獨立軟體開發商（ISV）也開始推出 Android 平臺的軟體，或是軟體開發套件（SDK），例如一家名叫 Skyhook Wireless 便推出了移植到 Android 上的定位軟體開發套件（geo-positioning）。從這些現象觀察到，過去開發者所專注的平臺（middleware）技術開發，現在己經被 Android 轉移到應用程式（application）與服務整合開發上了。

從近來的報導或是分析報告來看，各界對 Android 手機的商機都是看好的，這與大家過去對 Android 抱持許多不確定性的看法，己經有了很大的不同。做為領頭羊的角色的 G1 手機功不可沒，G1 為大家做了很好的心理建設。因為它的成功，讓大家對 Android 手機充滿信心，也看到了許多商業機會。G2 若能再交出漂亮成績單，所產生的心理效應想必相當驚人。
</description>
         <link>https://www.jollen.org/blog/2009/03/android_phone_boost_business.html</link>
         <guid>https://www.jollen.org/blog/2009/03/android_phone_boost_business.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Mon, 30 Mar 2009 23:51:13 +0800</pubDate>
      </item>
            <item>
         <title>CTimes 矽導論壇：Android元年 vs 山寨機氣象變化元年</title>
         <description>誰能掌握山寨機的新商業元素，誰就能逐鹿中原。（原文刊載於零組件雜誌2009年1月份）

Android與山寨機的關係，是一個特別另人感興趣的話題。最近實地與北京Androidin社群核心成員交換許多想法，有些觀點很值得台灣硬體廠商參考，筆者就一些討論，整理出部份觀察心得與大家交流。

Android在中國地區投下一顆能量彈，這可能是一顆破壞力驚人的炸彈，不過，最可能的地方是它具備「定向精確爆破」的能力。山寨機的成功，讓不管是本地，或是外來的廠商，都打著「山寨模式」的主意。山寨模式在大陸地區出現快三年的時間，在中國本地的銷售數字為一億台，這還不包括「外銷」到海外的數量。這段期間，中國幾家山寨機廠商，也在短短時間內，一躍成為前幾大的本土品牌手機商。

山寨商的腦筋動得快，現在更把主意打到Android上頭。根據山寨商的說法，Android在中國地區會是一個很好的機會，也就是「智能型Android山寨機」將是下一個山寨風潮。與Android社群合作開發Android系統，是山寨商近來特別感興趣的項目。

根據Androidin（中國本地最大Android社群）的說法，已經有幾家山寨商表示對Android智能手機的興趣，同時也希望能與中國本土Android社群合作，開發不同市場需求的軟體，並針對特定市場做銷售。這也正式開放手機平臺最大的優勢，根據客戶需求彈性並快速客製化軟體的「市場定向爆破」能力。

Android社群在大陸地區越活躍，相對的「Android山寨機」就更有機會。過去由晶片廠商提供完整山寨機軟硬體解決方案的模式，將會逐漸轉變為由社群提供Android山寨機軟硬體解決方案。軟體方面，就不需要多說了，以Androidin社群為例，其社群研發能量，以及社群的人員成份，都非常有實力。以硬體來說，社群需要的是開放式的硬體平臺，因為社群人員需要在此平臺上優化Linux核心，才能提出效能穩定的解決方案。

這樣的變化與市場機會，對台灣廠商來說是很好的轉型機會。若是山寨商接受了Android社群提出的解決方案，那麼品牌硬體就會是一個發展方向，因為在操作系統與軟體可以由使用者任意更新（更換）的情況之下，一個受社群、山寨商以及使用者信賴的手機裝置，就會具有相當的潛力。

在研發方面，硬體廠商確實需要即早建立Linux核心與驅動程式的研發能力。大陸地區有許多具備Linux核心開發能力的社群以及開發者。山寨機廠商的強項是在銷售以及通路，因此研發的工作則是落在晶片供應商，或是Android社群上，這就是大家所知道的「山寨機模式」。

與傳統山寨機相比，Android山寨機需要一些不同的元素，誰能掌握這些元素，誰就能逐鹿中原。

（作者為Androidin台灣地區連絡人，連絡信箱：jollen@androidin.com）</description>
         <link>https://www.jollen.org/blog/2009/02/android_china_business_opportunities.html</link>
         <guid>https://www.jollen.org/blog/2009/02/android_china_business_opportunities.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">我的觀點</category>
        
        
         <pubDate>Thu, 26 Feb 2009 21:28:02 +0800</pubDate>
      </item>
            <item>
         <title>自行編譯 Neo FreeRunner 的 kernel</title>
         <description><![CDATA[怎麼編譯 Openmoko 的 kernel for Neo FreeRunner 呢？請依照以下步驟進行操作。

<strong>1. 取得 Neo FreeRunner 的 kernel 原始碼</strong>

Openmoko 專案的所有原始碼都存放於 git.openmoko.org，到 [<a href="http://git.openmoko.org/?p=kernel.git;a=summary">Openmoko 的 kernel 原始碼目錄樹</a>] 底下，可以看到裡頭有完整的 kernel 原始碼，以及開發中的分支。首先執行以下指令，將所有的 kernel 原始碼取出：

$ git clone git://git.openmoko.org/git/kernel.git linux-2.6

所有的原始碼都被放置於 linux-2.6/ 目錄下。

<strong>2. 取出 Andy 的 branch</strong>

Andy 是 Openmoko 的 kernel 開發者，我們想要使用的是開發中的版本，就要取出 Andy 的分支。Openmoko 的 kernel 開發者，會隨時將新程式碼放置於開發中的版本。Openmoko 的 kernel 也會與 mainline 的 kernel 做合併（patch merge）的動作。取出 Andy branch 的指令如下：

$ cd linux-2.6
$ git checkout origin/andy-tracking

<strong>3. 取得 GTA02 的 kernel 設定檔</strong>

Openmoko 提供 GTA01/GTA02/GTA03 的 kernel 設定檔，只要將 GTA02（Neo FreeRunner）的設定檔取出使用即可，不需要再自行設定 kernel 選項：

$ cp arch/arm/configs/gta02_moredrivers_defconfig .config

<strong>4. 取得 Openmoko 的 toolchain</strong>

要編譯 kernel 就需要 cross toolchain，Openmoko 提供一份預先建立好的 ARM9 toolchain，請由 [<a href="http://downloads.openmoko.org/developer/toolchains/" target="_blank">這裡</a>] 下載。請下載 20080916 的版本，例如：openmoko-i686-20080916-arm-linux-gnueabi-toolchain.tar.bz2。

Toolchain 的安裝方式是先切換到根目錄（'/'），再解壓縮：

$ cd /
$ sudo tar jxf &lt;your-path&gt;/openmoko-i686-20080916-arm-linux-gnueabi-toolchain.tar.bz2

解壓後，可以在 /usr/local/openmoko 目錄下找到 toolchain。

<strong>5. 下載 build-kernel.sh/build-image.sh/mkimage</strong>

到 [<a href="http://people.openmoko.org/jollen/openmoko-kernel/">這裡</a>] 下載二個 script 以及 mkimage 工具，並放置於 kernel 原始碼目錄下。別忘了變更屬性為可執行：

$ chmod a+x build-*.sh

另外，將 mkimage 變更屬性後，搬移到系統標準路徑下：

$ chmod a+x mkimage
$ sudo mv mkimage /usr/sbin

Neo FreeRunner 使用 U-Boot 開機程式，所以必須使用 mkimage 工具將 kernel image 包裝成 U-Boot 格式。此工具的原始碼於 U-Boot 原始碼目錄裡可取得。

<strong>6. 開始編譯 kernel</strong>

先執行 build-kernel.sh 編譯 kernel：

$ ./build-kernel.sh

編譯成功後，再執行 build-image.sh 以產生最後的 image 檔：

$ ./build-image.sh

完成後，可在 kernel 原始碼目錄下找到 'uImage-GTA02.bin' 檔案。uImage-GTA02.bin 就是支援 Neo FreeRunner 的 kernel image 檔，將此檔案以 dfu-util 燒錄到手機裡即可。

以上過程若有任何問題，可以到 [<a href="http://openmoko-tw.net">Openmoko 正體中文站</a>] 詢問。]]></description>
         <link>https://www.jollen.org/blog/2009/02/compile_neo_freerunner_kernel.html</link>
         <guid>https://www.jollen.org/blog/2009/02/compile_neo_freerunner_kernel.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Fri, 20 Feb 2009 14:02:51 +0800</pubDate>
      </item>
            <item>
         <title>Fyp：LXDE 與 FreeRunner</title>
         <description><![CDATA[過去 Debian 的使用者可以將 [<a href="http://wiki.openmoko.org/wiki/Debian">Debian for FreeRunner</a>] 安裝在 FreeRunner 上，並安裝 LXDE 享用輕量級的桌面環境。現在有一個基於 Debian for FreeRunner 的新 distribution 己經將 LXDE 正式整合進來了。

源自台灣，現在已是知名的開放源碼專案 [<a href="http://lxde.org">LXDE</a>] 現在已經被 Openmoko 的社群移植到 Neo FreeRunner 了。這個以 LXDE 桌面環境，以及 ［<a href="http://wiki.openmoko.org/wiki/Zhone">Zhone</a>] 為基礎的新 distribution 稱為 [<a href="http://wiki.openmoko.org/wiki/Fyp">Fyp</a>]，以下是 Fyp 的實機畫面。

<img src="http://wiki.openmoko.org/images/4/4e/Fyp3.png" />
(圖片來源：Openmoko Wiki）

Zhone 全名是 Zen Phone，也就是「禪之手機程式」。Zhone 是一個簡單的撥號與簡訊收發程式，Zhone 基於 [<a href="http://www.freesmartphone.org">FSO</a>] 的應用程式框架，全部採用 Python 撰寫。一個手機的撥號軟體，也可以做得這麼簡約有內涵，或許正是「禪之手機程式」所要表達的意境。

不過根據 [<a href="http://bbs.androidin.com/viewthread.php?tid=3485&extra=page%3D1">大陸網友的實測</a>]，Fyp 在 FreeRunner 上比較難以操作，原因是使用手指觸控，比較難以操作傳統的桌面環境與應用程式：「除了zhone这个大大的界面外，其他的程序用手指简直无法操作」。因為 FreeRunner 的觸控螢幕只有 2.8 吋，所以應該幾乎很難以手指進行操作；但從這個使用者的角度來看，客製化桌面環境的應用，在未來的消費性裝置就是一個重要的技術了。目前市面上有許多小筆電（Netbook）也引進了客製化桌面的技術，讓使用者能更方便操作系統。]]></description>
         <link>https://www.jollen.org/blog/2009/02/fyp_lxde_freerunner.html</link>
         <guid>https://www.jollen.org/blog/2009/02/fyp_lxde_freerunner.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Thu, 19 Feb 2009 12:17:38 +0800</pubDate>
      </item>
            <item>
         <title>二零零九年十大 Linux 與 open source 發展預測、Openmoko 新武器</title>
         <description><![CDATA[幾天前，一則新聞「2009 年 Linux 與 open source 十大預測」指出，Android 將會大放異采。報導原文可見 [<a href="http://www.e-linux.it/news_detail.php?id=7731">10 predictions for Linux and open source in 2009</a>]，另外，這裡也有一份簡體中文版的報導 [<a href="http://tech.sina.com.cn/s/2009-02-05/08112794801.shtml">2009年Linux和开源软件的10大预测</a>]。

十大事件預測的第一名是 Android，報導推測指出，2009 年將是 Android 重要的一年。文中也提到 Openmoko 的 GTA02（Neo FreeRunner）也會採用 Android 系統。對於 FreeRunner 採用 Android 系統的話題，網路上有許多討論，不過簡單來說，移植 Android 到 FreeRunner 上是社群進行的一個專案，而將 FreeRunner 搭載 Android 進行市場推廣與銷售，則是行銷端的一個想法。至於未來是否會讓 GTA02 搭載 Android 並推廣到其他市場，則是一個「還不確定」的問題。

此外，Openmoko 仍舊持續在開發自已的 software stack。在 Om2008.8 之後，將起而代之的新版本是 [<a href="http://www.FreeSmartphone.Org">FreeSmartphone.Org</a>] 以及 [<a href="http://www.paroli-project.org/">Paroli</a>]，這是一個基於 [<a href="http://www.enlightenment.org/">Enlightenment</a>] 與 Python 技術的行動通訊平臺，簡單來說，FSO 與 Paroli 將 Python 帶進行動通訊裝置，讓開發者能只用 Python 進行手機軟體發展；新的 FSO/Paroli 底層系統則是採用 Enlightenment 技術。

在十大事件預測報導中的第八名就是 Enlightenment，報導指出「Enlightenment 桌面將會推出穩定版 E17」，可見 Enlightenment 也會是 2009 年受矚目的一個 open source 專案。]]></description>
         <link>https://www.jollen.org/blog/2009/02/predictions_linux_opensource_2009_openmoko.html</link>
         <guid>https://www.jollen.org/blog/2009/02/predictions_linux_opensource_2009_openmoko.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 05 Feb 2009 15:41:15 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#11: AndroidManifest.xml 的用途是什麼？</title>
         <description><![CDATA[AndroidManifest.xml 是一個用來描述 Android 應用程式「整體資訊」的設定檔。簡單來說，這是一個「自我介紹」檔，我們可以向 Android 系統「介紹」我們的 Android 應用程式，以便讓 Android 系統完整地了解我們的應用程式資訊。

在 [<a href="/blog/2009/01/jollen-android-programming-9.html">教學, #9</a>] 中，我們提及：「在這裡修改 AndroidManifest.xml 的目的是為了『在我們的 Android 應用程式裡加入一個 Service 類別』，這樣才有辦法啟動 Service...」這個工作的目的是為了向 Android 系統做二項自我介紹。說明如下。

1. 應用程式「實作了一個 MokoService 類別」

<pre>    &lt;application android:icon="@drawable/icon" android:label="@string/app_name"&gt;
    ...
        &lt;service android:name=".MokoService"&gt;
        ...
        &lt;/service&gt;
    ...
    &lt;/application&gt;</pre>

在 application 標籤裡加入 ‘service’ 標籤，告訴 Android 系統我們的應用程式有一個叫做「MokoService」的類別。「android:name」屬性用來指定 Service 的類別名稱，別忘了在 AndroidManifest.xml 裡，類別名稱都是以「.」（小數點）開始。

2. MokoService 類別可處理「com.moko.hello.START_MUSIC」意圖

<pre>       &lt;service android:name=".MokoService"&gt;
        	&lt;intent-filter&gt;
        		&lt;action android:name="com.moko.hello.START_MUSIC" /&gt;
        		&lt;category android:name="android.intent.category.DEFAULT" /&gt;
        	&lt;/intent-filter&gt;
        &lt;/service&gt;</pre>

在 service 標籤裡加入 ‘intent-filter’ 標籤，告訴 Android 系統我們的應用程式可「濾出」哪一個「Intent」。在前面的教學裡，我們把 Intent 暫時解釋為 Event（事件）；因此，這裡的「自我介紹」用意是為了告訴 Android 系統，我們可接受的事件名稱為何。

我們只要在 intent-filter 標籤裡加入 ‘action’ 標籤，並指定 action 標籤的 android:name 屬性即可。Intent 的命名規則為「xxx.yyy.NAME」的路徑命名法。

當 Android 收到由 Activity 發出的 Intent 後，便去找尋可處理 com.moko.hello.START_MUSIC 的類別，然後載入並啟動此類別。

最後，在 ’intent-filter’ 裡加入 ‘category’ 標籤，用來定義 com.moko.hello.START_MUSIC 的分類，在這裡指定為預設類別 「android.intent.category.DEFAULT」，這是一個 Android 定義的常數。完整的 Service 類別「自我介紹」標籤與屬性，可參考 Android SDK 的說明。]]></description>
         <link>https://www.jollen.org/blog/2009/01/jollen-android-programming-11.html</link>
         <guid>https://www.jollen.org/blog/2009/01/jollen-android-programming-11.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 19 Jan 2009 22:39:13 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#10: 如何檢查 Service 是否已啟動？使用 Android 除錯器</title>
         <description><![CDATA[Activity 是一個有 UI 的類別，Service 則是一個沒有 UI 的類別。要知道 Activity 是否啟動，只要看看手機是否出現畫面即可；要知道 Service 是否有啟動，最容易的方式就是透過「除錯」的方式。以下我們實際以一個完整專案方式來對 Android 應用程式做除錯。

<strong>建立 MokoService 類別</strong>

點擊 Eclipse 的 File -> New -> Class 項目，利用 Eclipse 的自動新增功能，在先前的 HelloMoko 專案裡建立 MokoService 類別，如圖1。欄位「Superclass」應填入 android.app.Service。

<img alt="create_class_eclipse.png" src="http://www.jollen.org/blog/2009/01/19/create_class_eclipse.png" width="525" height="663" />
圖1: 建立 MokoService 類別

<strong>修改 MokoService 實作</strong>

在新增的 MokoService 類別裡，加入 onStart() 與 onDestory() 實作，如圖2。onStart() 的實作如下：

<blockquote><pre>	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
	}</pre></blockquote>

因為 onStart() 是一個負載（override）實作，因此要呼叫 superclass 的 onStart() 方法。接著，將滑鼠移到 MokoService 類別裡的第 17 行（super.onStart），然後點擊 Run -> Toggle Breakpoint 在程式碼第 17 行的地方建立一個中斷點。

<img alt="debug_service_1.png" src="http://www.jollen.org/blog/2009/01/19/debug_service_1.png" width="1280" height="727" />
圖2: onStart() 與 onDestory() 實作與設定中斷點

除了 MokoService 類別外，我們還要修改 AndroidManifest.xml 並在 Activity 裡啟動 MokoService 類別，請參考 [<a href="/blog/2009/01/jollen-android-programming-9.html">教學, #9</a>] 的說明。

<strong>啟動除錯器</strong>

點擊 Run -> Debug Configurations 執行專案，並啟動除錯器。當 Android 應用程式成功安裝到 target device 並執行時， 會出現一個詢問對話框，選 Yes 即可，Eclipse 會將環境切換至除錯模式，如圖3。

<img alt="debug_service_2.png" src="http://www.jollen.org/blog/2009/01/19/debug_service_2.png" width="516" height="211" />
圖3: 是否要切換到除錯模式？

接著可以在除錯模式下看到 Android 應用程式停在先前所設定的中斷點（breakpoint），這表示 MokoService 類別已被 Android 系統載入並執行了，如圖4。

<img alt="debug_service_3.png" src="http://www.jollen.org/blog/2009/01/19/debug_service_3.png" width="1280" height="727" />
圖4: 程式在中斷點暫停]]></description>
         <link>https://www.jollen.org/blog/2009/01/jollen-android-programming-10.html</link>
         <guid>https://www.jollen.org/blog/2009/01/jollen-android-programming-10.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 19 Jan 2009 10:45:13 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#9: 啟動 Service - startService()</title>
         <description><![CDATA[<p>上一個課程裡，我們實作了一個 Service 的類別稱為 MokoService，現在我們想要在 Activity 裡載入並啟動 MokoService 類別，讓它可以在背景執行，請依以下步驟完成這個任務。。</p>

<strong>修改 AndroidManifest.xml</strong>

<p>在 Package Explorer 視窗裡找到目前 Android 專案的資訊描述檔，檔名是 AndroidManifest.xml。這是一個用來描述 Android 應用程式「整體資訊」的檔案，每個 Android 應用程式專案都會有一個。在這裡修改 Androidmanifest.xml 的目的是為了「在我們的 Android 應用程式裡加入一個 Service 類別」，這樣才有辦法啟動 Service。修改後的內容如下，紅色的部份是新增的描述：。</p>

<blockquote><pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.moko.hello"
      android:versionCode="1"
      android:versionName="1.0.0"&gt;
    &lt;application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true"&gt;
        &lt;activity android:name=".HelloMoko"
                  android:label="@string/app_name"&gt;
            &lt;intent-filter&gt;
                &lt;action android:name="android.intent.action.MAIN" /&gt;
                &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
            &lt;/intent-filter&gt;
        &lt;/activity&gt;
<font color="#ff0000">        &lt;service android:name=".MokoService"&gt;
            &lt;intent-filter&gt;
                &lt;action android:name="com.moko.hello.START_MUSIC" /&gt;
                &lt;category android:name="android.intent.category.DEFAULT" /&gt;
            &lt;/intent-filter&gt;
        &lt;/service&gt;</font>
    &lt;/application&gt;
&lt;/manifest&gt;</pre></blockquote>

<p>這是什麼意思呢？我們留待後續再做說明。接著只需要再加上一行程式碼，就能啟動 MokoService 類別了。</p>

<strong>啟動 Service - startService()</strong>

<p>回到 HelloM 類別，加入一行程式碼：</p>

<blockquote><pre>public class HelloMoko extends Activity {
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState); 
       
       setContentView(R.layout.main);
       
       <font color="#ff0000">startService(new Intent ("com.moko.hello.START_MUSIC"));</font>
   }
}</pre></blockquote>

<p>Activity 類別裡有一個 method 叫做 startService：</p>

<blockquote>startService(Intent service)</blockquote>

<p>呼叫 startService() 即可啟動一個 Service 類別，只是，startService() 的參數是一個「Intent」的型別，並不是所要啟動的類別名稱。「Intent」是一個很像「Event」的類別，後續我們再做比較精確的說明，在這裡，我們不如把 Intent 當成是 Event（事件）。</p>

<p>當程式送出 com.moko.hello.START_MUSIC 事件給 Android 時，Android 便去尋找能處理此事件的類別，然後啟動它。在這裡，能處理 com.moko.hello.START_MUSIC 事件的類別就是 MokoService，這個關係就是透過 AndroidManifest.xml 的設定實現的。</p>]]></description>
         <link>https://www.jollen.org/blog/2009/01/jollen-android-programming-9.html</link>
         <guid>https://www.jollen.org/blog/2009/01/jollen-android-programming-9.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 12 Jan 2009 16:02:33 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#8: 沒有 UI 的 Service</title>
         <description><![CDATA[<p>到目前為止，我們都著重在 Activity 以及 UI 的介紹，在 Android 應用程式裡，有一種沒有 UI 的類別（android.app.Service），稱之為 Service。簡單來說，Service 是一個 background process（背景程序），透過背景程序，我們可以實作一些不需要 UI 的功能，例如：在背景撥放音樂。</p>

<p>以下是利用 Eclipse 環境自動產生的類別 'MokoService'：</p>

<blockquote><pre>import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MokoService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

}</pre></blockquote>

<p>MokoService 類別繼承自 android.app.Service，幾個有關 Service 的重要觀念如下：</p>

<p>1. Service 物件以 separated process 的方式執行，這表示 Service 與 UI（Activity）並不在同一個 process 裡執行，而是個自在不同的 process 執行。</p>

<p>2. Android 應用程式是在 Activity 裡啟動與停止 Service。</p>

<p>3. 覆載（override）onStart() 方法（method）在 Service 被啟動時，執行我們想要的背景功能。</p>

<p>4. 覆載 onDestroy() 方法在 Service 被停止時，停止執行中的背景功能。</p>

<p>以下是一個加入 onStart 與 onDestroy 的  MokoService 實作：</p>

<blockquote><pre>import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MokoService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public void onStart(Intent intent, int startId) {
		
	}
	
	@Override
	public void onDestroy() {
		
	}
}</pre></blockquote>

目前，我們了解 Activity 與 Service 的觀念了，接下來，要怎麼在 Activity 裡啟動 Service 呢？]]></description>
         <link>https://www.jollen.org/blog/2009/01/jollen-android-programming-8.html</link>
         <guid>https://www.jollen.org/blog/2009/01/jollen-android-programming-8.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 12 Jan 2009 15:15:49 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#7: 如何讓文字並排顯示 - TableLayout</title>
         <description><![CDATA[<p><a href="http://code.google.com/intl/zh-TW/android/reference/android/widget/TableLayout.html" target="_blank">android.widget.TableLayout</a> 是一個「排版」的類別，假設現在我們想要做出如圖1的文字排版效果，那麼使用 TableLayout 就是標準的做法。傳統寫程式排版的做法不是非常的方便，所以我們將採用 XML layout 方式來實作。</p>

<img alt="TableLayout_1.png" src="http://www.jollen.org/blog/2009/01/08/TableLayout_1.png" width="900" height="752" />
<p>圖1: 文字並排顯示</p>

<strong>建立新專案: HelloLayout</strong>

<p>建立新的專案「HelloLayout」，並撰寫程式碼如下：</p>

<pre>package com.moko.layout;

import com.moko.layout.R;

import android.app.Activity;
import android.os.Bundle;

public class HelloLayout extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}</pre>

<p>由於我們採用 XML layout 的做法，所以主程式不需要做什麼修改。本範例的重點應該是在 main.xml 檔案的編寫。</p>

<strong>設計 TableLayout: 編寫 main.xml</strong>

<p>新的 Android 專案創建時，預設是使用 LinearLayout（線性排版）來安排 UI。現在，我們將使用 TableLayout 來取代 LinearLayout，以「表格」方式來安排 UI。TableLayout 讓我們可以將畫面切割成一張表格，如果我們可以設計一個二欄式（2 columns）的表格，就可以做出如圖1的顯示效果了。</p>

<p>以下是 main.xml 的內容：</p>

<pre>&lt;?xml version="1.0" encoding="utf-8"?>

&lt;TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"&gt;

	&lt;TableRow&gt;
		&lt;TextView  
		    android:layout_width="fill_parent" 
		    android:layout_height="wrap_content" 
		    android:text="www.androidin.com"
		    android:padding="3dip"
		    android:autoLink="web" /&gt;
		&lt;TextView  
		    android:layout_width="fill_parent" 
		    android:layout_height="wrap_content" 
		    android:text="www.google.com"
		    android:autoLink="web" /&gt;
	&lt;/TableRow>

&lt;/TableLayout&gt;</pre>

<p><a href="http://code.google.com/intl/zh-TW/android/reference/android/widget/TableRow.html" target="_blank">android.widget.TableRow</a> 是配合 TableRow 使用的一個類別，當我們在 TableLayout 裡安排一個 TableRow 時，在 TableRow「裡頭的所有 View」就會被安排在同一列（row）裡。以本範例來說，在第一列（row）裡有二個 TextView，所以這二行文字顯示時，就會呈現如圖1的效果。</p>

<p>為了避免文字擠在一起，因此我們在 TextView 裡加上了 padding 的屬性，如下：</p>

		    android:padding="3dip"

<p>第一個 TextView 的 padding 為 3dip，表示與旁邊的 View 必須空 3 個「間格」，這樣二個 TextView 才不會擠在一起。</p>



]]></description>
         <link>https://www.jollen.org/blog/2009/01/jollen-android-programming-7.html</link>
         <guid>https://www.jollen.org/blog/2009/01/jollen-android-programming-7.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Thu, 08 Jan 2009 16:51:57 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#6: WebView 體驗與 findViewByID</title>
         <description><![CDATA[<p>如果要繼續體驗 View 的樂趣，那麼「WebView」這個 View 無疑是最佳人選。android.webkit.WebView 是使用「WebKit」技術的 View，主要的用途是「顯示網頁」。使用 WebView，我們可以在 Android 應用程式裡顯示自已的 HTML 文件，或是線上的網頁。</p>

<p>接下來請依照以下步驟，建立我們的第二個 Android 應用程式「Hello Web」。</p>


<strong>建立新專案: HelloWeb</strong>

<p>建立一個新的 Android 專案，如圖1。</p>

<img alt="hello_web_1.png" src="http://www.jollen.org/blog/2009/01/05/hello_web_1.png" width="525" height="508" /><br />
圖1: 建立 Hello Web 專案

<p>並且撰寫 HelloWeb.java 程式如下：</p>

<pre>package com.moko.web;

import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;

import com.moko.web.R;

public class HelloWeb extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
<strong>        final String mimetype = "text/html";
        final String encoding = "utf-8";
        
        WebView wv;
        
        wv = (WebView) findViewById(R.id.wv);
        wv.loadData("&lt;img src=\"http://www.google.com.tw/intl/en_com/images/logo_plain.png\" /&gt;", mimetype, encoding);</strong>
    }
}</pre>

<p>上述程式碼採用 XML layout 方式來安排 UI，因此接下來的工作就是編輯 XML layout 檔案。</p>


<strong>規劃 UI: main.xml</strong>

<p>編輯 main.xml 來規劃「Hello Web」的 UI 如下：</p>

<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;

&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    &gt;
<strong>&lt;WebView 
	android:layout_width="fill_parent" 
	android:layout_height="wrap_content" 
	android:id="@+id/wv" 
	/&gt;</strong>
&lt;/LinearLayout&gt;</pre>

<p>在這裡我們定義了「WebView」標籤，並且指定「WebView」的 ID 為「wv」。透過指定「ID」屬性給 View 的方式，便能「讓 Android 應用程式在執行時期（run-time）找到指定的 View 物件」。</p>


<strong>使用 View 的 ID 屬性: findViewByID</strong>

<p>怎麼在執行時期，找到「XML layout」安排好的 View 呢？看到 HelloWeb.java 的程式片斷如下：</p>

<pre>        final String mimetype = "text/html";
        final String encoding = "utf-8";
        
        WebView wv;
        
        <strong>wv = (WebView) findViewById(R.id.wv);</strong>
        wv.loadData("&lt;img src=\"http://www.google.com.tw/intl/en_com/images/logo_plain.png\" /&gt;", mimetype, encoding);</pre>

<p>呼叫 findViewByID() 方法，即可在執行時期「動態取得 View 物件」。當我們在 main.xml 裡加入 WebView 標籤，「存檔」後，R.java 資源索引檔也會跟著更新，我們可透過「R.id.wv」來索引到 WebView 物件。以下是 R.java 的內容：</p>

<pre>package com.moko.web;

public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int icon=0x7f020000;
    }
<strong>    public static final class id {
        public static final int wv=0x7f050000;
    }</strong>
    public static final class layout {
        public static final int main=0x7f030000;
    }
    public static final class string {
        public static final int app_name=0x7f040001;
        public static final int hello=0x7f040000;
    }
}</pre>

<p>取得 WebView 物件後，呼叫 WebView 的 loadData() 方法，將 HTML 內容載入到 WebView 物件裡，並顯示在 Activity 上。loadData() 的參數如下：</p>

<ul><li>第一個參數：HTML 內容</li>
<li>第二個參數：MimeType 類型，指定為 text/html，即 HTML 類型文件</li>
<li>第三個參數：文字編碼方法，指定為 utf-8（Unicode）</li></ul>

<p>Hello Web 範例程式所載入的 HTML 文件是一個 &lt;IMG&gt; 標籤，因此我們在視窗上所看到的內容就是一張圖檔，如下圖（使用真正的 Google Phone 做測試）。</p>

<img alt="hello_web_2.png" src="http://www.jollen.org/blog/2009/01/05/hello_web_2.png" width="308" height="466" /><br />
圖: Hello Web 執行結果。]]></description>
         <link>https://www.jollen.org/blog/2009/01/jollen-android-programming-6.html</link>
         <guid>https://www.jollen.org/blog/2009/01/jollen-android-programming-6.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 05 Jan 2009 23:08:43 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#5: 使用 View 的 XML 屬性</title>
         <description><![CDATA[上一篇文章介紹了 XML-based layout 後，發現這是一個很方便，而且有用的 UI 安排方式。以圖1為例，我們現在想要做出一個應用程式，讓文字的超鍊結（hyperlink）可以被使用者點選，並自動呼叫瀏覽器連到該網站，這樣的應用程式該如何撰寫呢？請依以下步驟修改程式碼。

<img alt="xml_layout_attributes_1.png" src="http://www.jollen.org/blog/2009/01/04/xml_layout_attributes_1.png" width="900" height="752" />
圖1：如何設計一個可點擊 URL link 的應用程式？

<strong>View 的 XML 屬性</strong>

每一個 View 都有許多屬性，我們可以利用 XML 來描述每一個 View 的屬性，進而達到控制物件的效果。以 TextView 為例，有一個「android:autoLink」屬性可以控制「是否要自動將網址轉換為可點擊的 URL 文字」。

要怎麼知道每一個 View 都哪些屬性呢？這個時候就要祭出 Android SDK 的 documentation 了。以 TextView 為例，透過以下的說明，可以了解 TextView 有哪些屬性，以及該屬性的用途：

<a href="http://code.google.com/intl/zh-TW/android/reference/android/widget/TextView.html#attr_android:autoLink">http://code.google.com/intl/zh-TW/android/reference/android/widget/TextView.html#attr_android:autoLink</a>

原來，只需要透過「autoLink」屬性，並將此屬性設定為「web」即可做出我們想要的功能。

<strong>修改  main.xml</strong>

將 main.xml 修改如下：

<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"    
    &gt;
&lt;TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
<strong>    android:text="Jollen's Blog - http://www.jollen.org/blog"
    android:autoLink="web"</strong>
    /&gt;
&lt;/LinearLayout&gt;</pre>

我們為 TextView 物件新增一個「android:autoLink」的屬性，並將此屬性設定為「web」，以後只要「text」屬性裡出現 URL，TextView 就會自動將 URL「文字」轉換成可點擊的 link。

程式執行時，只要點擊 link，就會自動啟動瀏覽器，並連接該網址，如下圖。

<img alt="xml_layout_attributes_2.png" src="http://www.jollen.org/blog/2009/01/04/xml_layout_attributes_2.png" width="900" height="752" />]]></description>
         <link>https://www.jollen.org/blog/2009/01/jollen-android-programming-5.html</link>
         <guid>https://www.jollen.org/blog/2009/01/jollen-android-programming-5.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 04 Jan 2009 22:49:52 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#4: 使用 XML 安排 UI</title>
         <description><![CDATA[<p>繼上一篇文章介紹了 View 的觀念後，接下來就要了解一下如何「安排」Android 應用程式的 layout。</p>

<p>Android 應用程式的 layout（UI 佈局）除了直接撰寫程式碼的方式外，也能使用 XML 檔案來做描述（XML-based Layout）。在 Android Development Kit 的「Package Explorer」視窗，點選「res -> layout」選擇 main.xml，可以看到 “Hello Moko” 應用程式的 XML layout 檔案，如圖1。</p>

<img alt="xml_layout_1.png" src="http://www.jollen.org/blog/2009/01/04/xml_layout_1.png" width="1194" height="729" />
圖1："Hello Moko" 的 XML layout 檔

<p>以下這段 XML 用來描述「TextView」物件的 layout：</p>

<pre>&lt;TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    /&gt;</pre>

以上述的例子來看，我們可以設定以下幾個 tag 來定義 TextView 的屬性：

- 'android:layout_width' - View 的寬度
- 'android:layout_height' - View 的高度
- 'android:text' - TextView 所要顯示的文字

到上一篇文章為止所撰寫的「Hello Moko」是以程式碼編寫的方式來安排 UI，要怎麼將 Hello Moko 改以 XML 做為 UI 的安排方式呢？請依照以下步驟進行程式調整。

<strong>R.java: 資源索引檔</strong>

在 Package Explorer 點選「src -> com.moko.hello -> R.java」，如圖2。

<img alt="xml_layout_2.png" src="http://www.jollen.org/blog/2009/01/04/xml_layout_2.png" width="1194" height="729" />
圖2："Hello Moko" 的 R.java

R.java 是由 Android Development Kit 所自動產生的資源索引檔（resource index），「R」是一個類別，這是 Android 應用程式資源的索引類別。「R.layout」類別則是 UI 佈局的索引類別，R.layout 類別裡的「main」成員就是 Android 應用程式的「主佈局索引」。

<strong>修改 Hello Moko 程式碼</strong>

R.java 根據 main.xml 自動產生，「並不是由程式設計師手動編寫」，請勿修改此檔案。接下來，只要把先前的程式（HelloMoko.java）修改成下列寫法即可：

<pre>public class HelloMoko extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
       <strong> setContentView(R.layout.main);</strong>
	}
}</pre>

<p>當 Activity 被建立後，我們呼叫 setContentView() 方法，將主要的 UI（R.layout.main）顯示在視窗上。R.layout.main 索引到一個 TextView 物件，此物件定義於 main.xml。將 R.layout.main 顯示在 Activity 視窗上，請記得修改程式碼，才能使用 XML-based layout。

<p>我們順帶將 TextView 物件的 text 屬性修改如下：</p>

<pre>&lt;TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Hello TextView"
    /&gt;</pre>

<img alt="xml_layout_3.png" src="http://www.jollen.org/blog/2009/01/04/xml_layout_3.png" width="1194" height="729" />

程式執行結果如下（使用 Android 模擬器）。

<img alt="xml_layout_4.png" src="http://www.jollen.org/blog/2009/01/04/xml_layout_4.png" width="900" height="752" />]]></description>
         <link>https://www.jollen.org/blog/2009/01/jollen-android-programming-4.html</link>
         <guid>https://www.jollen.org/blog/2009/01/jollen-android-programming-4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Sun, 04 Jan 2009 22:22:04 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#3: 第一個 Android 專案</title>
         <description><![CDATA[初步了解如何撰寫第一個 Android 應用程式後，接著就可以實際來建立我們的第一個 Android 專案了。Android Development Kit（ADT）使用 Eclipse 整合式開發環境，根據 Android SDK 裡的文件說明，先安裝 Eclipse 以及 ADT，然後建立一個新專案（File -> New -> Project），並選擇「Android Project」，如圖1。

<img alt="new_project_1.png" src="http://www.jollen.org/blog/2008/12/29/new_project_1.png" width="525" height="500" />
圖1：建立 Android Project

接著輸入專案屬性：

1. Package name：Java 套件名稱，在這裡我們將應用程式的套件命名為 com.moko.hello。
2. Activity name：輸入應用程式的 Activity 類別名稱，建立一個繼承 Activity 的新類別。
3. Application name：輸入應用程式名稱，即應用程式標題。

<img alt="new_project_2.png" src="http://www.jollen.org/blog/2008/12/29/new_project_2.png" width="525" height="508" />
圖2：輸入專案屬性

建立新專案後，在主程式 HelloMoko.java 撰寫第一個 Android 應用程式如圖3。第一個 Android 應用程式在 Activity 上顯示一個 View。

<img alt="new_project_3.png" src="http://www.jollen.org/blog/2008/12/29/new_project_3.png" width="1194" height="729" />
圖3：第一個 Android 應用程式

設計好的應用程式可以使用 Android 模擬器來執行。Android Development Kit 也將 Android 實體手機整合在開發環境裡，因此，我們實際以 T-Mobile G1 手機（全球第一支 Google phone）來做測試。

選取 Run -> Run Configurations 後，可以看到圖4的設定選單。在「Project:」欄位選取「Browse...」選取我們的 Android 專案 - HelloMoko，然後點選「Apply」套用設定。

<img alt="new_project_4.png" src="http://www.jollen.org/blog/2008/12/29/new_project_4.png" width="800" height="640" />
圖4：設定 Run Configurations

接著點選「Target」標籤，將「Device Target Selection Mode」設定為「Manual」，然後點選「Run」執行 Android 專案。在執行專案前，請先將手機連接到電腦。

<img alt="new_project_5.png" src="http://www.jollen.org/blog/2008/12/29/new_project_5.png" width="800" height="640" />
圖5：設定 Target

最後，可以看到圖6的畫面，Android Development Kit 會自動偵測 Android 手機。選取手機後按「OK」，Android Development Kit 就會自動編譯專案，並將應用程式打包成 apk 套件後下載到手機上執行。

除了 G1 手機外，Openmoko 的 Neo FreeRunner 也能執行 Android 系統，Android Development Kit 也能偵測到 Neo FreeRunner 並自動將套件下載到 Neo FreeRunner 上安裝執行。

<img alt="new_project_6.png" src="http://www.jollen.org/blog/2008/12/29/new_project_6.png" width="500" height="300" />
圖6：選擇 Android 手機

在 Eclipse 的訊息窗裡，可以看到 apk 套件的下載、安裝以及執行過程的完整訊息：

<pre>[2008-12-29 16:51:29 - HelloMoko] ------------------------------
[2008-12-29 16:51:29 - HelloMoko] Android Launch!
[2008-12-29 16:51:29 - HelloMoko] adb is running normally.
[2008-12-29 16:51:29 - HelloMoko] Launching: com.moko.hello.HelloMoko
[2008-12-29 16:51:32 - HelloMoko] Uploading HelloMoko.apk onto device 'HT843GZ46367'
[2008-12-29 16:51:32 - HelloMoko] Installing HelloMoko.apk...
[2008-12-29 16:51:35 - HelloMoko] Success!
[2008-12-29 16:51:35 - HelloMoko] Starting activity com.moko.hello.HelloMoko on device 
[2008-12-29 16:51:36 - HelloMoko] ActivityManager: Starting: Intent { comp={com.moko.hello/com.moko.hello.HelloMoko} }</pre>

<strong>延伸閱讀</strong>

* 2008.11.21: <a href="http://www.jollen.org/blog/2008/11/hello_moko_free_runner_android.html">安裝 Android 應用程式（apk）至 Neo FreeRunner</a>]]></description>
         <link>https://www.jollen.org/blog/2008/12/jollen-android-programming-3.html</link>
         <guid>https://www.jollen.org/blog/2008/12/jollen-android-programming-3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 29 Dec 2008 20:13:25 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Android 教學,#2: “Hello Moko” - Activity 與 View 的關係</title>
         <description><![CDATA[<p>上一則文章介紹了 Activity 與 View 的觀念，若能再理解 Activity 與 View 的關係，就不難了解 Android 應用程式的整個模式了。請看以下的範例程式：</p>

<pre>package com.moko.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloMoko extends Activity {
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       TextView tv = new TextView(this);
       tv.setText("Hello Moko");
       setContentView(tv);
   }
}</pre>

<p>這是在 Android SDK 文件裡的一段範例程式，類別 HelloAndroid 繼自 Activity。下圖是Activity的生命週期（lifecycle）。在「Jollen 的 Android 教學,#1」裡提到 Activity 負責建立視窗，根據 Activity lifecycle，當視窗建立時，onCreate 事件被觸發，所以我們在 onCreate 裡建立 View。</p>

<img alt="activity_lifecycle.png" src="http://www.jollen.org/blog/2008/12/29/activity_lifecycle.png" width="538" height="668" />

<p>TextView 是 Android 的其中一個 View，故名思義，這是一個顯示文字的 View。最後，呼叫 Activity 的 method 'setContentView' 來將 UI 顯示於視窗上。</p>
]]></description>
         <link>https://www.jollen.org/blog/2008/12/jollen-android-programming-2.html</link>
         <guid>https://www.jollen.org/blog/2008/12/jollen-android-programming-2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 29 Dec 2008 20:04:21 +0800</pubDate>
      </item>
            <item>
         <title>Jollen  的Android 教學,#1: Android 應用程式模式</title>
         <description>Android 應用程式的模式（application model），可由以下幾個觀念講起。在真正進入 Android 程式設計前，必須先了解以下幾個名詞觀念。

1. Android package（.apk）

Android 應用程式套件，包含應用程式本身，以及相關的資源檔案。將 apk 套件下載到 Android 手機後，即可安裝至手機上。Android Development Kit 可自動將 apk 套件下載至模擬器或實體手機。

2. task

Task 就是「應用程式」本身，也就是 Android 手機上的圖示，使用者可點擊圖示啟動 task。從開發者的角度來看，task就是一個或多個 activities。

3. process

Process 在作業系統的定義上，指的是「執行中的程式」，在 Android 的應用程式模式中，代表的是低階的執行程式，也就是系統層（kernel）的部份。一個 apk 套件裡的所有程式，都是在一個 process 裡執行。

4. 什麼是 Activity（android.app.Activity）

簡單來說，這昰一個與使用者互動的物件。一個 Activity 類別（class）負責建立視窗（window），我們可以透過 View 類別將UI放置在視窗上。

當 Activity 被啟動（active）或執行（running）時，就是在 foreground（前景）模式，在 foreground 模式的 Activity 會被顯示在螢幕上。

當執行中的 Activity 部份畫面被其他 Activity 蓋掉時，該 Activity 便被暫停（paused），被暫停的 Activity 在系統記億體不足時，便會被清除（kill）。只被蓋掉部份畫面，或是變成透明狀況的 Activity 不會停止，只會進入暫停狀態。

當執行中的 Activity 全部畫面都被其他 Activity 取代時，該 Activity 便被停止（stopped），當系統需要記憶體時，停止中的 Activity 會先被系統清除。

當系統需要清除 Activity 時，系統會要求 Activity 自行結束，或是直接殺掉 Activity 的 process。

5. 什麼是 View（android.app.View）

簡單來說，android.app.View 類別就是手機的 UI。View 負責繪製UI與處理事件（event），Android 利用 View 打造出所謂的 Widgets（元件），利用 Widget 可打造出互動式的使用者介面（interactive GUI）。

Android 應用程式的 UI 從程式碼的角度來看，就是一顆「view tree」，程式設計師可以利用直接撰寫程式碼，或是透過「XML layout」檔的方式，來安排應用程式的 view tree。

另外，還有一個特殊的 View 類別，稱為 ViewGroup（android.view.ViewGrup）。ViewGroup 是一種特別的 View，可以用來「裝載」其他的 View，對 ViewGroup 而言，這些被包含起來的 View 為 Children。

6. Process Types

在一般情況下，Android 應用程式都有一個自已的 Linux process。

在 Android 系統裡，process 的生命週期並不是直接由 Android 應用程式本身來決定，而是由系統來決定，也因此，Android 應用程式的開發者必須正確地撰寫程式碼，並使用 Android 的元件（component），否則系統不管應用程式是否仍在執行，仍會將應用程式的 process 清除（kill）掉。

Android 的 process 有五種類型：foreground process、visible process、service process、background process 與 empty process。</description>
         <link>https://www.jollen.org/blog/2008/12/jollen-android-programming-1.html</link>
         <guid>https://www.jollen.org/blog/2008/12/jollen-android-programming-1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Android OS</category>
        
        
         <pubDate>Mon, 29 Dec 2008 19:58:24 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的 scheduling 觀念, #1:</title>
         <description><![CDATA[<p>本週進行「Linux Device Drivers: Jollen 的 10 堂課」教育訓練，發現有些同學對於作業系統的「排程（scheduling）」觀念仍有些疑問，因此，在課程中以三段小程式來說明「什麼是排程」，以協助同學建立紮實的排程觀念。</p>

<p>以下是一段正常的 open system call 實作：</p>

<pre>int card_open(struct inode *inode, struct file *filp)
{
	return 0;
}</pre>

<p>當 process 開啟 device file 後，因為 kernel-space 的 open system call 實作會直接 return，因此 CPU 會切換至前景（foreground）、 process 繼續往下執行。</p>

<p>第二段是不正確的 open system call 實作：</p>

<pre>int card_open(struct inode *inode, struct file *filp)
{
	while (1);
	return 0;
}</pre>

<p>當 process 開啟 device file 後，因為 CPU 切換至後景（background），但是在 OS 裡頭的 open system call 實作是一個無窮迴圈，OS 並沒有做 system call 的返回，因此 CPU 並不會切換到前景，所以，電腦感覺起來呈現「當機」狀態。</p>

<p>因為 CPU 的時間無法切換到前景，因此任何的 process 都無法執行，電腦當然沒有任何反應。這裡，可沒有所謂的「time slice」與「context switch」的觀念與動作。請同學思考：作業系統是對 process 做排程。</p>

<p>最後一段程式，我們加入了呼叫排程器的動作：</p>

<pre>int card_open(struct inode *inode, struct file *filp)
{
	while (1)
		schedule();
	return 0;
}</pre>

<p>schedule() 是排程器主函數，等於是主動呼叫 scheduler 做 re-scheduling。在這個情況下，雖然 OS 仍然沒有做 system call 返回，但因為我們以「手排」的方式做排程，因此 CPU 會換切到前景，前景程式就會被分配時間執行，因此，電腦仍然有反應。</p>

<p>唯一要注意的地方是，因為這一個 open system call 並沒有 return，所以原來的 process 並不會繼續往下執行。利用三段簡單的程式碼，對作業系統的排程觀念做了一次討論，對於建立「什麼是 scheduling」的觀念，是很有幫助的。</p>]]></description>
         <link>https://www.jollen.org/blog/2008/12/kernel-scheduling-concept-1.html</link>
         <guid>https://www.jollen.org/blog/2008/12/kernel-scheduling-concept-1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 14 Dec 2008 11:58:18 +0800</pubDate>
      </item>
            <item>
         <title>安裝 Android 應用程式（apk）至 Neo FreeRunner</title>
         <description><![CDATA[首先，依照 Android 文件上的說明 [<a href="http://code.google.com/android/intro/installing.html">先安裝 SDK</a>]，再 [<a href="http://code.google.com/android/intro/hello-android.html">撰寫 Hello, Android!</a>] 應用程式後，打包成 apk 格式；本文使用的 Android SDK 搭配的 Eclipse 版本是 3.4（Ganymede）。接著，再照 [<a href="http://code.google.com/android/documentation.html">Android Documentation</a>] 的說明撰寫一個 Android 應用程式，再將程式編譯後打包成 HelloMoko.apk 檔案。

當然，必須將 FreeRunner 更新為 Android 系統，更新方式可至 Openmoko 中文 wiki 下載投影片：<a href="http://wiki.openmoko.org/wiki/Main_Page/zh_tw">http://wiki.openmoko.org/wiki/Main_Page/zh_tw</a>。請依以下步驟將 HelloMoko.apk 安裝至 FreeRunner 手機上。以下的實驗環境為 Ubuntu 8.04.1。

1. 連接 FreeRunner 與 PC

將 FreeRunner 以 USB 連接 PC，再將 FreeRunner 手機開機至 Android 環境。請注意，依照 Openmoko wiki 上的說明，若在開機後再連接 PC，可能會有問題。可利用 lsusb 指令檢查 FreeRunner 是否順利連到 PC 上：

<blockquote><pre>$ lsusb
Bus 007 Device 002: ID 04b4:1724 Cypress Semiconductor Corp. 
Bus 007 Device 001: ID 0000:0000  
Bus 003 Device 001: ID 0000:0000  
Bus 006 Device 001: ID 0000:0000  
<font color="#ff0000">Bus 005 Device 034: ID 1457:5117 </font>
Bus 005 Device 001: ID 0000:0000  
Bus 004 Device 001: ID 0000:0000  
Bus 004 Device 004: ID 04d9:0499 Holtek Semiconductor, Inc. 
Bus 002 Device 001: ID 0000:0000  
Bus 001 Device 001: ID 0000:0000</pre></blockquote>

2. 殺掉 adb server

執行 adb 時，adb-server 會自動啟動。因此，若是先前曾利用 Eclipse 啟動過 Android 模擬器來測試 HelloMoko 的話，adb-server 己經在背景執行了。啟動 adb server 後再連接 FreeRunner，可能會讓 adb server 找不到 FreeRunner，因此，最可靠的做法是：先檢查系統是否有 adb server，將執行中的 adb server kill 掉後，再重新啟動 adb server。

<blockquote><pre>$ ps ax|grep adb
20092 ?        S+     0:00 grep adb
21032 ?        Sl     0:00 adb fork-server server
$ sudo kill -9 21032</pre></blockquote>

adb 是 Android SDK 所提供的工具，可於 Android SDK 的 tools/ 目錄下取得。

3. 設定 PC 端 IP

接下來設定 PC 端的 IP 位址為 192.168.0.x，例如：

<blockquote><pre>$ sudo ifconfig <font color="#ff0000">usb0 192.168.0.200</font></pre></blockquote>

FreeRunner 的預設 IP 為 192.168.0.202，可以 ping 此位址測試是否能正常連線。

4. 啟動 adb server

設定 ADBHOST 環境變數：

<blockquote><pre>$ export ADBHOST=192.168.0.202</pre></blockquote>

ADBHOST 的值為 FreeRunner 的 IP 位址。再執行 adb 啟動 adb server，adb server 會自動偵測 Android 手機：

<blockquote><pre>$ adb devices
* daemon not running. starting it now *
* daemon started successfully *
List of devices attached 
<font color="#ff0000">emulator-5554   device</font></pre></blockquote>

在 'List of devices attached' 項目可以看到系統自動偵測到的 Android 手機。

5. 安裝 HelloMoko.apk

使用 adb 將 HelloMoko.apk 安裝到 FreeRunner / Android 手機：

<blockquote><pre>$ adb install HelloMoko.apk</pre></blockquote>

安裝完成後，重新啟動 FreeRunner 即可在應用程式選單裡看到 HelloMoko。
]]></description>
         <link>https://www.jollen.org/blog/2008/11/hello_moko_free_runner_android.html</link>
         <guid>https://www.jollen.org/blog/2008/11/hello_moko_free_runner_android.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Fri, 21 Nov 2008 13:54:30 +0800</pubDate>
      </item>
            <item>
         <title>Neo FreeRunner: 3 軸加速度感測器程式實作</title>
         <description><![CDATA[<p>Neo FreeRunner 搭載二個「3 軸加速度感測器」，這是一個很有趣的硬體，他可 以偵測手機的移動狀態，配合應用程式，可以實作出有趣的小玩具。以 iPhone 為 例，它有一個很人性化的功能，當手機 90 度轉向時，視窗也會跟著 90 度轉換， 這樣的功能就是透過「加速度感測器」晶片來完成的。</p>

<p>Neo FreeRunner 有二個 3 軸加速度感測器，當手機移動時，可以利用程式讀取每 一個軸的加速度值。讀取加速度感測器的方法是透過 Linux 的 input layer：</p>

<pre>    /dev/input/event{2,3}</pre>

<p>Process 透過這二個裝置檔讀取 input layer 的資料，kernel 會回傳一筆加速度感測器的資料給 process，該筆資料的 data type 為 struct input_event，即 kernel 的 input event 資料結構。程式的做法是，每次讀取一筆加速度感測器資料，再判斷資料的類型，以及各軸的加速度值。</p>

<p>程式寫法如下：</p>

<pre>    #include &lt;linux/input.h&gt;

    int main(void)
    {

        struct input_event motion;

        fd = open("/dev/input/event3", S_IRUSR);

        while (1) {
            read(fd, &motion, sizeof(motion));
        }
    }</pre>

<p>struct input_event 資料結構的定義如下：</p>

<pre>    struct input_event {
        struct timeval time;
        __u16 type;
        __u16 code;
        __s32 value;
    };</pre>


<p>讀出的資料如下：</p>

<ul>
<li>motion.time: 讀取到該筆資料的時間</li>
<li>motion.type: 值為 2 時，表示該筆資料為加速度值</li>
<li>motion.code: 表示讀取的加速度方向，0 = x 軸、1 = y 軸、2 = z 軸</li>
<li>motion.value: 該軸的加速度值，正負號代表「正負方向」</li>
</ul>

<p>本範例讀取 FreeRunner 上的第二個 3 軸加速度感測器，x/y/z 軸所代表的方向如下圖。</p>

<img src="http://wiki.openmoko.org/images/c/c6/Accelerometer_orientation2.png" />
(Source: Openmoko Wiki)

<p>如果想要讀取 x 軸的加速度：</p>

<pre>    int x;

    /* 資料有效且為 x 軸 */
    if (motion.type == 2 && motion.code == 0) {
        x = motion.value;
    }</pre>

<p>若 x 為負值，表示手機往左邊的方向移動。再舉一個例子，讀取 y 軸的加速度：</p>

<pre>    int y;

    /* 資料有效且為 y 軸 */
    if (motion.type == 2 && motion.code == 1) {
        y = motion.value;
    }</pre>


<p>若 y 軸的加速度為正值，表示手機往下移動。更多有關 FreeRunner 加速度感測器（accelerometer）的資訊，可參閱 Openmoko wiki [<a href="http://wiki.openmoko.org/wiki/Accelerometer_data_retrieval">Accelerometer data retrieval</a>]。</p>]]></description>
         <link>https://www.jollen.org/blog/2008/11/programming_freerunner_accelerometer.html</link>
         <guid>https://www.jollen.org/blog/2008/11/programming_freerunner_accelerometer.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Mon, 10 Nov 2008 12:29:37 +0800</pubDate>
      </item>
            <item>
         <title>FreeRunner 搭載 Android 實機測試</title>
         <description><![CDATA[看了這麼久的 Android 消息，今天終於可以實際將 Neo FreeRunner 更新成 Android 系統，進行實機測了。將 FreeRunner 變成 Android 手機的做法，可以在 [<a href="http://wiki.openmoko.org/wiki/Main_Page/zh_tw">Openmoko Wiki</a>] 下載教學簡報。以下是實機測試畫面。

將 FreeRunner 重新燒錄 Android 的 kernel 以及 rootfs 後，直接開機。大約過了1分鐘後，看到了 Android 的開機畫面，開機時間還可接受，但似乎目前的版本並不是相當穩定，仍有開機失敗的機率。

<img src="http://people.openmoko.org/jollen/freerunner-android/freerunner-android-1.png" />

開機後，來到鎖定畫面，必須按一下 FreeRunner 的按鍵，才能進到主選單。

<img src="http://people.openmoko.org/jollen/freerunner-android/freerunner-android-2.png" />

主選單是典型的 Android 畫面，當然是採用觸控方式操作。整個 FreeRunner + Android 給我的第一印像還算不錯，觸控操作挺順手的，不會有「卡卡」的感覺。

<img src="http://people.openmoko.org/jollen/freerunner-android/freerunner-android-3.png" />

來到 Android 的應用程式選單，裡頭有許多 Android 的應用程式。

<img src="http://people.openmoko.org/jollen/freerunner-android/freerunner-android-4.png" />

這是 Android 的撥號器（dailer）程式。雖然沒有很仔細測試整體系統，不過第一時間給我的感覺很不錯、也很有新鮮感。 ;-)

<img src="http://people.openmoko.org/jollen/freerunner-android/freerunner-android-5.png" />]]></description>
         <link>https://www.jollen.org/blog/2008/11/freerunner_android_snapshot.html</link>
         <guid>https://www.jollen.org/blog/2008/11/freerunner_android_snapshot.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 05 Nov 2008 14:45:34 +0800</pubDate>
      </item>
            <item>
         <title>Android 已經能在 FreeRunner 上執行</title>
         <description><![CDATA[自從 Google 正式公開 Android 原始碼後，Openmoko 社群便開始努力將 Android 移植到 FreeRunner 手機上，期間不斷有小報導指出 Openmoko 將推出 Android 手機。不過，正確的說法應該是「Openmoko 將 Android 移植到 FreeRunner 上」，而不是另外打造一支新的手機。

前二天，連「Openmoko 投靠 Android 聯盟」的消息都出現了（其中一則報導還附上 FreeRunner 執行 Android 的照片）：

* <a href="http://financenews.sina.com/sinacn/304-000-106-109/2008-11-02/0908935461.html">Openmoko來投 Android聯盟再添一員</a>
* <a href="http://financenews.sina.com/sinacn/304-000-106-109/2008-11-03/1421936946.html">本月有望推出 Android家族第2款手機</a>

投靠 OHA 聯盟？這還是未經證實的小報導，不過，FreeRunner 能執行 Android 已經是可以官方證實的消息了。Openmoko 的確「已經將 Android 移植到 FreeRunner」，而且也對外釋出，已經可以由網路上下載 Android 的 image 了。我們來看一下 Openmoko 在這裡做了什麼事情：

1. 主要的移植開發者為 Sean McNeil

2. 修改了 Binutils 2.18 以順利編譯 Android 原始碼

3. gcc 4.2.4 加入 gcc41-java-arm4.patch 以順利編譯 Android 原始碼

4. 執行 Android 的 kernel 版本是 2.6.26

這項工作，其實過程都是很透明的，因為 Sean McNeil 陸續都有將大部份的 patch 都丟到 mailing list 上，再加上社群的討論以及網路上的移植工作，「FreeRunner 能執行 Android」已經是預料中的事情了。

針對開發者或教育研究單位來說，Openmoko 所維護的 distribution 將會是一個「super set」，也就是「不同的 Linux distribution 或開放手機軟體，都能在 FreeRunner 硬體裝置上執行」。如同前一篇日記所說的，FreeRunner 能執行 Android 的話，不但可以吸引 Android 的開發者參與 Openmoko 社群的活動，對投入 Android 平臺的教學研究機構來說，也能將成果實際放到 FreeRunner 手機上執行測試。

<strong>延伸閱讀</strong>

* 2008.10.31: <a href="http://www.jollen.org/blog/2008/10/openmoko_freerunner_google_android.html">Openmoko FreeRunner 與 Google Android: 現況與想法</a>
* 2008.10.27: <a href="http://www.jollen.org/blog/2008/10/porting_android_to_free_runner.html">移植 Android 到 Neo1973 與 Neo FreeRunner</a>]]></description>
         <link>https://www.jollen.org/blog/2008/11/android_running_on_freerunner.html</link>
         <guid>https://www.jollen.org/blog/2008/11/android_running_on_freerunner.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 05 Nov 2008 09:05:47 +0800</pubDate>
      </item>
            <item>
         <title>Openmoko FreeRunner 與 Google Android: 現況與想法</title>
         <description><![CDATA[自從 Android 公開原始碼後，Openmoko 的社群便開始積極進行移植的工作。最近在網路上也開始看到許多這方面的報導：

* <a href="http://phandroid.com/2008/10/29/rumor-openmoko-android-phone-in-november/">Rumor: OpenMoko Android Phone In November?</a>
* <a href="http://www.ubergizmo.com/15/archives/2008/10/openmoko_jumps_on_android_bandwagon.html">OpenMoko Jumps On Android Bandwagon</a>
* <a href="http://chinese.engadget.com/2008/10/30/openmoko-at-work-on-android-handset/">Openmoko將在11月推出Android手機？</a>
* <a href="http://news.cnblogs.com/n/43307/">开源手机OpenMoko转投Android门下</a>
* <a href="http://gphone.tgbus.com/news/mtnews/200810/167494.shtml">预计Openmoko将在11月推出Android手机？</a>

這些報導看起來比較像是「謠言」（rumor），有些報導甚致還放了圖片，不過，有圖不一定有真相，明眼人一看就知道這些圖都是「後製」出來的。但是，Openmoko 社群正在移植 Android 至 FreeRunner 也是一個事實。從目前 Openmoko 社群的移植工作來看，主要進行的項目有：

1. 修改 ARMv5 指令集。由於 FreeRunner 採用的 s3c2442 處理器是 ARMv4 指令集，因此必須修改低階的 assembly code。

2. 硬體支援的部份。讓 Android middleware 能在 FreeRunner 的 Linux kernel 上執行。

3. 修改 Android 的 build system。讓 Android 的 build system 能製作 jffs2 格式的 image file，這也是 FreeRunner 採用的 image file 格式。

這些工作看起來都有很不錯的進展，社群開發者也已經將部份成果 patches 提交到 [<a href="http://source.android.com/submit-patches">Android Open Source Project</a>]。Android 開放式手機平臺成功吸引全球目光，但對社群開發者來說，仍然缺少能支援 Android 的「開放手機硬體」，雖然市面已經推出 Android 手機，但仍不是「開放式」硬體，對 open source developer 來說，少了那麼一點樂趣。

如果 FreeRunner 真的能執行 Android 的話，想必一定可以吸引對 Android 有興趣的開發者進入 Openmoko 社群，對 Openmoko 社群的成長也會有所幫助。FreeRunner 是一個完全開放式的手機硬體，因此對 application 開發者、middleware 開發者以及 kernel 開發者來說，都是一個充滿許多樂趣的平臺。

目前台灣也有許多大學實驗室投入 Android 平臺的研究，但不足的地方在於「硬體裝置」的取得。當 FreeRunner 能真正支援 Android 時，對這些研究人員來說，想必也是一個好消息，因為可以把 Android 的研究成果，實際放到真正的手機裝置上執行與測試。]]></description>
         <link>https://www.jollen.org/blog/2008/10/openmoko_freerunner_google_android.html</link>
         <guid>https://www.jollen.org/blog/2008/10/openmoko_freerunner_google_android.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Fri, 31 Oct 2008 12:54:26 +0800</pubDate>
      </item>
            <item>
         <title>移植 Android 到 Neo1973 與 Neo FreeRunner</title>
         <description><![CDATA[Android 於 10 月 21 日正式公開原始碼 [<a href="http://source.android.com/posts/opensource">Android is now available as open source</a>]，Openmoko 的開發者社群第一時間也出現許多討論 [<a href="http://lists.openmoko.org/pipermail/community/2008-October/034025.html">Android open sourced</a>]。

很快地，「移植 Android 到 Neo1973 與 Neo FreeRunner」的工作也開始了，目前這項移植工作還是「underway」進行中；不過，社群開發者仍紀錄了最新的移植狀況在 Openmoko wiki 上 [<a href="http://wiki.openmoko.org/wiki/Android">Android</a>]。

相關資源

* 2008-08-25: <a href="http://openmoko-tw.net/phpbb/viewtopic.php?f=10&t=9&p=51#p51">Andorid是否可以porting到FreeRunner</a>]]></description>
         <link>https://www.jollen.org/blog/2008/10/porting_android_to_free_runner.html</link>
         <guid>https://www.jollen.org/blog/2008/10/porting_android_to_free_runner.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Mon, 27 Oct 2008 16:23:54 +0800</pubDate>
      </item>
            <item>
         <title>「Openmoko Linux 2008 開放手機新體驗」簡報上線</title>
         <description><![CDATA[今天受邀至台北科技大學資訊工程系演講，講題是「Openmoko Linux 2008 開放手機新體驗」，正好能接續昨天的日記「<a href="http://www.jollen.org/blog/2008/10/programming_neo_freerunner.html">Neo FreeRunner 應用程式開發概念圖</a>」，在簡報的最後，附上了一小部份的講義「如何在Om2008.8 撰寫 EFL 應用程式並編譯」供同學們參考。

下載簡報 [<a href="http://people.openmoko.org/jollen/Om-programming-freerunner-ntut-1016.pdf">Openmoko Linux 2008 開放手機新體驗</a>]。Openmoko 近期有許多新消息，有興趣的同學可以至 [<a href="http://wiki.openmoko.org/wiki/Main_Page/zh_tw">Openmoko 中文 Wiki</a>] 查詢。

<strong>延伸閱讀</strong>

2008.10.15: <a href="http://www.jollen.org/blog/2008/10/programming_neo_freerunner.html">Neo FreeRunner 應用程式開發概念圖</a>]]></description>
         <link>https://www.jollen.org/blog/2008/10/programming_neo_freerunner_efl.html</link>
         <guid>https://www.jollen.org/blog/2008/10/programming_neo_freerunner_efl.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Thu, 16 Oct 2008 23:47:36 +0800</pubDate>
      </item>
            <item>
         <title>Neo FreeRunner 應用程式開發概念圖</title>
         <description><![CDATA[Openmoko 的 distribution 現在有許多不同的版本，一大堆名詞總讓人一開始就一頭霧水，像是：Om2007.2、Om2008.8、FDOM、ASU等等。剛開始接觸 Openmoko 以及 Neo FreeRunner 的同學，經常要花一些時間認真讀 Openmoko wiki 才能了解通盤概念。

許多同學只是想要在 FreeRunner 上寫寫簡單的小程式，一開始遇到這些從沒聽過的名詞，花很多時間，有時還是搞不清楚「怎麼在 FreeRunner 上寫小程式」，總是會有一些挫折感。因此，在這裡整理一份簡單的「Neo FreeRunner 應用程式開發概念圖」，幫助同學建立整體概念。

怎麼在 Neo FreeRunner 上寫一個簡單的小程式呢？請看下圖的說明。

<img alt="programming_freerunner.png" src="http://www.jollen.org/blog/2008/10/15/programming_freerunner.png" />

Om2007.2 與 Om2008.8 都是 Openmoko 的官方版本（official distribution），Om2007.2 與 Om2008.8 除了有 Openmoko 自已的開發成果外，也使用了許多 FOSS（Free and Open Source Software）的套件，因此，需要將 Openmoko 的成果與這些 FOSS 套件整理成一個完整的散佈套件，以提供 Openmoko 手機使用，這就是「Openmoko Linux」或「Openmoko Linux distribution」。這樣的概念就像是 Ubuntu Desktop 是一個給個人電腦使用的 Linux distribution，Fedora 也是一個 Linux distribution。

Om2007.2 是 Openmoko 官方的第二個釋出版本，middleware 的主要元件為 libgsmd 與 GTK+。Openmoko 提供 Om2007.2 的 pre-built toolchain 以及一個 sample（放置於 pre-built toolchain 的 openmoko-sample2 目錄）讓開發者可以很方便地撰寫程式，並將編譯好的程式打包成 opkg 後安裝到手機。

Om2008.8 是 Openmoko 官方的第三個釋出版本，同時也是很重要的一個版本，Om2008.8 主要的變更是使用了 Qtopia 的成果，將 Qtopia 移植到 x11 環境，並與其 GTK+ 的環境做整合。此外，也加入了 EFL（Enlightenment）的 UI 環境，並加入了一個可動態安裝套件的 Installer 程式，以及 Locations GPS 應用程式。在視窗管理方面，採用 EFL 的 Illume；同時也有較佳的 theme 支援，也加入了 Qtopia keyboard。Om2008.8 可同時執行 Qtopia/EFL/GTK+/x11 應用程式，並透過 D-Bus 技術做整合，可以說是一個創新的手機軟體平臺。

在 Om2008.8 平臺上，除了能使用 GTK+ 的 pre-built toolchain 來寫程式外，Openmoko 也提供了新版本的 pre-built toolchain。如果您想使用 EFL 來寫程式，可以安裝新版本的 pre-built toolchain，並透過 opkg-target 來安裝 EFL 的開發環境（libevas-dev 與 libetk-dev）至 pre-built toolchain，即可使用 C 來撰寫 EFL 程式。同樣地，我們可以使用上述的 openmoko-sample2 來編譯 EFL 程式，並打包成 opkg 後安裝至手機。

另外一個方式則是使用 Python 來撰寫 EFL 應用程式，直接將 Python script 上傳到 FreeRunner 即可執行，當然也可以打包成 opkg 後再安裝。

過去曾使用 Qtopia 撰寫程式的同學，也能在 Om2008.8 上執行 Qtopia 應用程式；不過，如果是 Qt 應用程式的話，雖然 Om2008.8 裡有包含 Qt library，但比較不建議使用。]]></description>
         <link>https://www.jollen.org/blog/2008/10/programming_neo_freerunner.html</link>
         <guid>https://www.jollen.org/blog/2008/10/programming_neo_freerunner.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 15 Oct 2008 14:41:33 +0800</pubDate>
      </item>
            <item>
         <title>停止軟體專利世界日（Stop Software Patents World Day）</title>
         <description><![CDATA[今天是 [<a href="http://www.fsf.org/">Free Software Foundation</a>（自由軟體基金會）] 的 [<a href="http://stopsoftwarepatents.org/">Stop Software Patents, World Day</a>]，所以也來 [<a href="http://stopsoftwarepatents.org/banners">貼貼 banner</a>] 嚮應一下。

<a href="http://stopsoftwarepatents.org/"><img src="http://stopsoftwarepatents.wdfiles.com/local--files/banners/webbanner_standard.png" border="0"></a>

今年五月份，自由軟體基金會的創辦人 Richard Stallman 來台演講時，發表了一場「The Danger of Software Patent」的演說。Richard Stallman 以一張圖說明了為何軟體專利是「無限的危害」，至今仍令人印像深刻。ZDNet Taiwan 上的一則報導 [<a href="http://www.zdnet.com.tw/news/software/0,2000085678,20129320,00.htm">自由軟體之父：軟體專利有害無益</a>] 便引述了 Richard Stallman 的一段談話：

<blockquote>「專利權本身的目的是為鼓勵人們將新創概念公佈出來，但現行的專利體系卻會造成反效果，」</blockquote>

因為認同這個觀念，因此嚮應一下「停止軟體專利世界日」。

<strong>延伸閱讀</strong>

* 2008.05.22: <a href="http://www.jollen.org/blog/2008/05/richard_stallman_speech_nthu.html">Richard Stallman 台灣行第四天紀錄: 5/15 演講實紀</a>
* 2008.05.21: <a href="http://www.jollen.org/blog/2008/05/richard_stallman_day4.html">Richard Stallman 台灣行第四天紀錄: 5/15 新竹清大行側寫</a>
* 2008.05.21: <a href="http://www.jollen.org/blog/2008/05/richard_stallman_departure.html">Richard Stallman 結束訪台行程: 5/19 離台</a>
* 2008.05.16: <a href="http://www.jollen.org/blog/2008/05/richard_stallman.html">Richard Stallman 台灣行第三天：下課後</a>
* 2008.05.14: <a href="http://www.jollen.org/blog/2008/05/richard_stallman_day3_tku.html">Richard Stallman 台灣行第三天：演講「The Danger of Software Patent」</a>
* 2008.05.12: <a href="http://www.jollen.org/blog/2008/05/richard_stallman_day1.html">Richard Stallman 台灣行第一天</a>
* 2008.05.03: <a href="http://www.jollen.org/blog/2008/05/richard_stallman_speech_taiwan.html">自由軟體基金會創辦人 Richard Stallman 來台演講</a>]]></description>
         <link>https://www.jollen.org/blog/2008/09/stop_software_patent_world_day.html</link>
         <guid>https://www.jollen.org/blog/2008/09/stop_software_patent_world_day.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 25 Sep 2008 23:08:32 +0800</pubDate>
      </item>
            <item>
         <title>演講「加入 kernel 除蟲大隊：簡介 kernel debug 工具」簡報上線</title>
         <description><![CDATA[近期受邀發表了一場與「kernel debug」有關的演講，進行時間大約是70分鐘，在此提供簡報電子檔[<a href="http://tw.jollen.org/slides/kernel-debug-intro-20080914.pdf">加入 kernel 除蟲大隊</a>]供有興趣的朋友下載。本次演講的目的是對 kernel debug 主題做「開場」，再加上時間有限，因此並沒有對 kernel debug 的技術做太深入的討論，反而是透過整理的方式，對幾個常見的 kernel debug 工具與方法做總覽。

演講最後展示了使用 gdb+qemu 來進行 kernel source-level debug 的方法。「加入 kernel 除蟲大隊」需要具備什麼樣的基本技能呢？

首先，主要的工具「GNU Debugger」當然是必學的主題，不過因為這次的主題是 kernel debug，因此只約略介紹 user-space 下幾個重要的除錯觀念：

<strong>Breakpoint, single step, inspect variables</strong>

如何設定中斷點，進行單步執行，並在程式 run-time 時期檢視變數內容。

<strong>Segmentation fault</strong>

程式被訊號（signal）被中止時，怎麼找到不正常終止的程式碼。一個典型的 segmentation fault 是發生在程式存取非法的記憶體位置時。

<strong>Core dump</strong>

程式不正常中止時，會產生 core dump 檔案。透過 core dump 檔與原始的 ELF image 來比對出程式發生不正常中斷的行號。

<strong>Multi-threaded, client-server, GUI</strong>

多執行緒的除錯、client-server 架構與 GUI 系統的除錯也是重要的除錯技能。以多執行緒為例，我們可以透過「attach」的方式讓 gdb attach 到執行中的子程序進行除錯；又以 GUI 系統為例，我們有時會想要追踪一個由 kernel 發出到 UI 端的事件（event)，或是追踪 IPC 的訊息內容。針對這部份來說，有時透過除錯來觀察「系統行為」會比程式邏輯的除錯來得重要，當然除錯上也會稍微複雜一些。

針對 kernel debug 的部份來說，初學者應該以「建立正確使用除錯工具的觀念」與「建立合適的除錯環境」為主軸。另外，對於 oops（kernel panic）訊息的分析，則是核心除錯的重頭大戲，只是這裡所涉及的背景知識會多一些，在投影片裡列出 oops 訊息分析所應了解的幾個主題。不過，不管是 user-space 除錯，或是 kernel-space 除錯，仍必須建立基本的系統程式觀念，像是：

* symbol table
* process virtual address space
* kernel virtual address space
* system call
* exceptions
* stack, heap

本次演講，也介紹了一些關於核心除錯的重要觀念，幾個未列在投影片的部份，在這裡做個簡單整理。

<strong>開機是一個循序式過程</strong>

Kernel 的開機過程，是一個傳統的「循序式（sequence）」執行過程，如同我們展示以 gdb + qemu 進行除錯一樣，可以檢視並了解 kernel 的開機流程，以及開機失敗時的程式碼位置。

<strong>開機完成後是狀態切換</strong>

當 kernel 開機完成後，便會啟動一個外部程式，稱為 init process，從這個時間開始，user-space 程式便得以執行。Kernel 此時並不是一個循序式執行（sequential function calls）的狀態。

當不同的 process 執行時，因為呼叫的 system call 不同，因此 kernel 的狀態也不同。再舉一例，以驅動程式來說，在開機過程中，kernel module 只做載入的動作；但是，驅動程式所實作的 system call，如：read、write、ioctl，則是必須透過 user program 執行 system call 才會被叫用。以後面的例子來說，這就不會只是一個單的 kernel debug，或者說「並不是只對 kernel 做除錯」。

<strong>Remote Debug</strong>

以 remote debug 的方式進行除錯，remote 端（target 端）所載入的 kernel 是編譯完成後，放置於 arch/arm/boot（以 ARM 為例）目錄下的 kernel image，這是 target 端所使用的 kernel image（run-time）。

Local 端（除錯本機端）也要載入 kernel image，此時載入 kernel image 的目的是為了讀取 symbol table 以及其它 ELF 節區資訊，這裡所載入的 kernel image 為 ‘vmlinux’，即編譯完成後放置於 kernel 根目錄下的原始 ELF image file。由此可知，本機端的 ELF image 必須包含完整的除錯資訊，讓 kernel image 包含除錯資訊的做法是在編譯核心時，將幾個除錯選項打開，這部份在簡報中有做說明。

最後，大家可以參考 jserv 兄過去所發表過有關gdb的演講簡報 [<a href="http://blog.linux.org.tw/~jserv/archives/002043.html">「快快樂樂學 GNU Debugger (gdb) Part I + II」簡報上線</a>]。]]></description>
         <link>https://www.jollen.org/blog/2008/09/kernel_debug_intro.html</link>
         <guid>https://www.jollen.org/blog/2008/09/kernel_debug_intro.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 14 Sep 2008 20:00:11 +0800</pubDate>
      </item>
            <item>
         <title>UI 設計新體驗 Python-etk</title>
         <description><![CDATA[<p>最近在整理一些有關使用者介面設計的資料，希望可以提份一份簡單有趣的UI設計教學投影片，對象是學生，目的是讓同學可以「無恐懼玩UI」。</p>

<p>現今能做UI的技術很多，其中相對比較容易的方式就是以script language來打造UI，而當前最受注意的方式就是以Python快速寫UI。當然，網路上還有許多不同的UI技術，像是能讓「designer」真正自由發揮、免除寫code的SVG browser，也是一個值得注意的技術。</p>

<p>今天要介紹的主角是python-etk，故名思義，這是Python的ETK模組。ETK是一個不錯的東西，全名是Enlightenment Tool Kit，它的功能定位就像是GTK+的角色。GTK+是大名鼎鼎的圖形元件（widget）庫，由GTK+專案所沿伸出來的Glib也是一個使用廣泛的「強化版C程式庫」。ETK有沒有像Glib這樣的東西？有的，叫做Ecore。</p>

<p>Enlightenment（簡稱E）是一個知名且古老的window manager。[<a href="http://www.enlightenment.org/">Enlightenment</a>]包含許多程式庫以及工具，這些程式庫與工具總稱為EFL（Enlightenment Foundation Libraries）。簡單來看，可以畫出EFL的架構如下。</p>

<img alt="efl.png" src="http://www.jollen.org/blog/2008/09/04/efl.png" width="438" height="263" />

<p>EVAS是一個「畫布」程式庫。Ecore的角色如同GTK+的產物Glib，不過並不完全等於。EDJE則是一個「layout engine」。最上面的ETK就是我們的主角，Enlightenment的圖形元件庫。</p>

<p>透過python-etk來實作ETK應用程式，可以帶給我們一些愉快的經驗。要怎麼寫一個向世界問好的入門級python-etk程式呢？以下是一個範例（hello_world.py）：</p>

<blockquote><pre>#!/usr/bin/python

import etk

class MyButton(etk.Button):
	def _size_request(self):
	    return (100, 200)

btn = MyButton(label = "Click Me")
btn.on_clicked(lambda x: etk.main_quit())

# Main
w = etk.Window(title="Hello World", size_request=(150, 150), child=btn)
w.show_all()

def quit(obj):
    etk.main_quit()
w.on_destroyed(quit)

etk.main()</pre></blockquote>

<p>目前有一些知名的Linux手機專案已經開始使用python-etk，例如Openmoko以及Nokia的maemo專案。後續將再配合一個範例來說明hello_world.py程式，及其執行結果。</p>


]]></description>
         <link>https://www.jollen.org/blog/2008/09/introduction_python-etk.html</link>
         <guid>https://www.jollen.org/blog/2008/09/introduction_python-etk.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Thu, 04 Sep 2008 23:00:18 +0800</pubDate>
      </item>
            <item>
         <title>Intel 收購 Opened Hand</title>
         <description><![CDATA[一年多前 [<a href="http://www.jollen.org/blog/2007/07/_intelbased_mobile_linux.html">Mobile Linux initiative 成立</a>]，Mobile Linux initiative（或稱為 Moblin community）採用 Intel Atom 平臺，並專注於 Mobile Internet Devices（MIDs）、Netbooks 與其它 embedded devices 的開發。而這個以 Intel Atom 為主的社群，有了重大的變化。

根據 PC World 的一則消息指出：[<a href="http://www.pcworld.com/businesscenter/article/150520/intel_buys_british_linux_developer_opened_hand.html">Intel Buys British Linux Developer Opened Hand</a>]。是的，Intel 買下了知名的軟體開發公司 [<a href="http://o-hand.com/">OpenedHand</a>]；同一時間，Opened Hand 也在網站上發佈這則消息（引述自 Opened Hand 網站）：

<blockquote>We are pleased to announce that OpenedHand Ltd has been acquired by Intel Corporation. The OpenedHand team will join the Intel Open Source Technology Center and will focus on the development of the Moblin Software Platform, the optimized software stack for Intel Atom processors.

Intel will continue supporting open source projects currently led by OpenedHand staff such as Clutter and Matchbox projects, and in most cases, will accelerate these projects as they become an integral part of Moblin.</blockquote>

Opened Hand 在被 Intel 收購後，將加入 Intel Open Source Technology Center，並且將專注在 Moblin 軟體平臺的發展。Opened Hand 是 GNOME 最重要的 contributor，Opened Hand 帶領的幾個知名專案像是 Clutter 與 Matchbox 等，也將會繼續發展，這些專案在加入 Moblin 後有助於加快其開發速度。

Opened Hand 過去開發過許多知專案，幾個代表性專案有 Nokia Internet Tablets（N770）、OLPC、Vernier，以及 Openmoko。]]></description>
         <link>https://www.jollen.org/blog/2008/09/intel_buy_opened_hand.html</link>
         <guid>https://www.jollen.org/blog/2008/09/intel_buy_opened_hand.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Tue, 02 Sep 2008 10:19:31 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] fork 多個小孩</title>
         <description><![CDATA[今天進行「GNU Toolchains & Embedded Linux Programming」課程，在講解 fork 時，有同學問到「能不能 fork 多個 child process」，當然是可以的。後續又有同學問到，「能不能 fork 孫子」，當然也是可以的，只不過，在 child process 裡再 fork child process 並不是很主要的做法，只在一些特殊情況，例如要避免 zombie process 產生時，才會用上。

所以，我們只講解如何 fork 多個 child process 的做法。我們舉了一段 code 當做範例，這段 code 寫得很單純，可以說明 fork 的一些觀念。在這裡，大家必須了解的是「fork 之後」的行為是什麼：

1. parent process 的執行流程
2. child process 的執行流程

接著將以下的程式（<em>n_fork.c</em>）實際上機執行，並觀察執行結果。

<img alt="n-fork.jpg" src="http://www.jollen.org/blog/2008/08/23/n-fork.jpg" width="606" height="345" />

執行後出現以下結果：

<pre>$ gcc -o n_fork n_fork.c
$ ./n_fork &
This is the child process. PID: 8450
This is the child process. PID: 8451
This is the child process. PID: 8452
Parent process, PID: 8449
Child process ID: 8450 8451 8452</pre>

我們用 <em>pstree</em> 指令來觀察執行之後的系統狀態：

<img alt="nfork-pstree.jpg" src="http://www.jollen.org/blog/2008/08/23/nfork-pstree.jpg" width="388" height="105" />

後續我們會結合一個 Web server 的練習，來展示 fork 多個 child process 的應用。]]></description>
         <link>https://www.jollen.org/blog/2008/08/fork_n_child.html</link>
         <guid>https://www.jollen.org/blog/2008/08/fork_n_child.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sat, 23 Aug 2008 15:48:08 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] Symbol Table、objdump 與 ELF 綜合小考</title>
         <description><![CDATA[上週進行「GNU Toolchains & Embedded Linux Programming」課程時，最後出了一道考題給同學。題目如下。

<img alt="helo.jpg" src="http://www.jollen.org/blog/2008/08/23/helo.jpg" width="280" height="328" />

請說明上述程式執行後，為什麼會出現以下結果。請將原理描述清楚。

<pre>$ gcc -o helo helo.c
$ ./helo
0x80495c0
now x = 10
now x = 100</pre>

這是一道綜合性的考題，考了很多東西。完全沒有 toolchains 觀念前，同學是一頭霧水，也沒有什麼方向。但是在二天的課程後，從同學繳回的測試卷來看，大家的觀念都已經很健全了。在這裡將題目也提供給大家思考。這是一道不算難的考題，主要考的是「觀念」，並透過「工具的操作」來驗證這些觀念。

觀念的建立絕對是教育訓練最重要的一個環節，也是講師的主要任務，而透過工具的交互操作，來強化課堂觀念，是一個不錯的方法，可以幫助同學記憶。]]></description>
         <link>https://www.jollen.org/blog/2008/08/symbol_table_elf_data_start.html</link>
         <guid>https://www.jollen.org/blog/2008/08/symbol_table_elf_data_start.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sat, 23 Aug 2008 10:47:36 +0800</pubDate>
      </item>
            <item>
         <title>Openmoko 釋出 ASU</title>
         <description><![CDATA[2008 年 8 月 8 日是北京奧運的開幕日，這一天，也是 Openmoko 推出第三代最新一代手機平臺「Om 2008.8」的日子。Om 2008.8 也稱為 ASU，ASU 是一個專案代號，全名是 April/August Software Update。

ASU 可同時支援 EFL、Qtopia 以及 GTK+ 應用程式，同時也包含一個安裝程式（Installer），可讓使用者自由安裝手機應用軟體，這是過去在校園巡迴課程向同學所介紹的 Openmoko 新的概念，如今終於正式現身了。

ASU 比較另人期待的地方是加入了 EFL 以及 Qtopia，過去 Om 2007.2 是基於 GTK+ 的手機平臺，現在的 Om 2008.8 則是「跳脫」GTK+ 的重要里程碑。ASU 裡有二個值得一提的軟體成果，首先是 Illume。[<a href="http://wiki.openmoko.org/wiki/Illume">Illume</a>] 是一個能支援 GTK+ 的 window manager，這是 [<a href="www.enlightenment.org">Enlightenment</a>] 裡的一個模組，Illume 讓 enlightenment 的 UI 能在手機上有更好的表現。另外一個重要的軟體成果則是 ASU 能在 X11 環境上執行 Qtopia 的應用程式與服務，並使用上述提及的 Illume 做為 window manager。Illume 能同時支援多種不同的圖形介面程式庫。

<a href="http://wiki.openmoko.org/images/b/bb/OpenmokoFramework08.png" target="_blank"><img src="http://wiki.openmoko.org/images/b/bb/OpenmokoFramework08.png"  border="0" width="680" /></a>
圖：Openmoko 新的 Software Architecture，能同時支援 ELF（enlightenment）、Qtopia（on X11）以及 GTK+ 應用程式。（圖片來源：wiki.openmoko.org）

Neo FreeRunner/ASU 是 Openmoko 至今最重要的產品，雖然軟體與硬體仍然不足以讓 Neo FreeRunner/ASU 成為日常生活用的手機，但 Openmoko 社群對 ASU 仍大多是抱持正面的看法。其他軟體方面的問題，像是最近在社群裡被抱怨的開機時間（boot time）問題，或是已知的其他問題，像是 GSM modem、電池、WiFI driver 等，都是目前努力的工作項目，未來也會有所改善。

從 Om 2007.1 到 Om 2007.2，再由 Om 2007.2 到 ASU/Om 2008.8，接下來 Openmoko 即將推出的好菜為 FSO（FreeSmartphone.Org）。FSO 會是再次令人驚奇的新里程碑。]]></description>
         <link>https://www.jollen.org/blog/2008/08/asu_om20088_release.html</link>
         <guid>https://www.jollen.org/blog/2008/08/asu_om20088_release.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Tue, 12 Aug 2008 23:38:04 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的 I/O, #4: 什麼是 Blocking I/O</title>
         <description><![CDATA[在先前的專欄中，我們為大家介紹了「I/O」以及「interrupt handling」，接下來我們要將這二個部份合在一起，並討論幾<!--this article is copyright by www.jollen.org-->個相當重要的觀念以及機制。首先，我們回到最早在介紹 Linux 驅動程式架構的部份，我們介紹到了 system call 以及 file operations 的觀念；接續 I/O 的部份，我們又提到 read/write system call。到這裡，我們就要融合貫通先前所介紹的重要觀念。請大家先將先前的專欄讀熟，再接續本系列專欄。

在 Linux 驅動程式的<!--this article is copyright by www.jollen.org-->整個框架中，最重要，而且必須一開始就先了解的主題有二個：

1. Blocking I/O 的觀念。
2. Wait queue 以及 event-driven（event-polling）的觀念。

雖然這裡分成二個小主題，不過<!--this article is copyright by www.jollen.org-->其實這是同樣的一個主題。這裡有很多值得提出討論的觀念，首先針對 Blocking I/O 的觀念進行深度探討。

<strong>什麼是 blocking I/O？</strong>

當 user process 透過 read/write system call 讀取<!--this article is copyright by www.jollen.org-->硬體資料時，會有哪些情況出現？請注意，這裡講的是「user process」，並不是驅動程式的 system call 實作（driver function、fops->read 或 fops->write）。以 user process 呼叫 read() 來讀取硬體資料的案例來說，當然免不了就是以下的情況：

1. 驅動程式能順利由硬<!--this article is copyright by www.jollen.org-->體拿到資料，並丟到 user-space 給「該」process。
2. 驅動程<!--this article is copyright by www.jollen.org-->式目前無法由硬體拿到資料。怎麼辦？

現在沒辦法馬上由硬體拿到資<!--this article is copyright by www.jollen.org-->料，怎麼辦呢？當然要等待了。驅動程式必須設法等到可以由硬體拿到資料為止，再將資料丟給該 process。試想，現在有一個 process 想要讀取硬體資料，第一次的 read() 很順利地馬上就拿到資料了，可是第二次的 read() 因為硬體的關係，驅動程式暫時無法由硬體取得資料，於是 process 的第二次 read() 並不會立刻結束。

Process 這個時候「停在」第二次的 read() 呼叫，但其實此時，系統的執行位置是來到驅動程式的 read driver function（fops->read），而 fops->read 在待候硬體資料。「Blocking」就是「停住」的意思，所以第<!--this article is copyright by www.jollen.org-->二次的 read() 動作就變成是 blocking I/O。請注意：

1. 這裡一定是討論 user process 是否會停在 read() 或 write()。Blocking I/O 一定要由 user process 的角度討論才會是正確的觀念。
2. 第二次<!--this article is copyright by www.jollen.org-->的 read 是 blocking I/O（blocking operaton），這是「驅動程式的 fops->read 實作」所導致的結果。所以，user process 的 read/write 會不會停住，完全是看 fops->read 與 fops->write 的實作。
3. 我們在這裡只討論驅動程式的 read/write 實作，也就是 user process 存取 device file。

這就是所謂的 blocking I/O。由驅動程式<!--this article is copyright by www.jollen.org-->的角度來看，可以總結如下：

1. 當驅動程式想讓 user process 在呼叫 read() 函數時，都保證能取得資料，此時驅動程式便要實作「當目前尚<!--this article is copyright by www.jollen.org-->無資料可回傳給 user  process 時，便讓 user process 停留等待」的機制（可能是驅動程式尚無法由裝置取得資料）。

2. w當驅動程式想讓 user process 在呼叫 write() 函數時，都保證能寫<!--this article is copyright by www.jollen.org-->入資料，此時驅動程式便要實作「當目前無法寫入資料至裝置時，便讓 user process 停留等待」的機制（可能是裝置尚未 ready）。

讓 user process 停止的方式，涉及排程的觀念，這部份在下一篇日記會接著說明。

<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%"><tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr></table><table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr></table><table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td width="100%"><ul><li>2006.12.22:&nbsp;<a href="http://www.jollen.org/blog/2006/12/inux_device_driver_io_1.html">Linux 驅動程式的 I/O, #1: 基本概念</a></li><li>2006.12.20: <a href="http://www.jollen.org/blog/2006/12/linux_device_driver_io2.html">Linux 驅動程式的 I/O, #2: I/O 存取相關函數</a></li><li>2006.12.20: <a href="http://www.jollen.org/blog/2006/12/linux_device_driver_io_3.html">Linux 驅動程式的 I/O, #3: kernel-space 與 user-space 的「I/O」</a></li></ul></td></tr></table>
                          ]]></description>
         <link>https://www.jollen.org/blog/2008/08/linux_device_driver_blocking_io.html</link>
         <guid>https://www.jollen.org/blog/2008/08/linux_device_driver_blocking_io.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Mon, 04 Aug 2008 08:13:33 +0800</pubDate>
      </item>
            <item>
         <title>Openmoko 跨出英勇的一步</title>
         <description><![CDATA[自從 Openmoko 在7月4日正式發佈新版的 Neo FreeRunner 後，便不斷受到許多西方媒體的矚目與報導，各種聲音也出現在 Openmoko 社群，以及每一個人的網誌上。許多媒體都給予相當正面的評價與報導，對 Openmoko 團隊來說，這絕對是一大鼓舞。當然，好聲音不少，壞聲音也有。像是，Boing Boing 便刊登了一篇對 Openmoko 相當正面的報導：

<blockquote><a href="http://gadgets.boingboing.net/2008/07/04/openmoko-opensource.html">Openmoko open-source cell phone beats Android to the punch</a></blockquote>

不久前，Internetling 也刊登了一則有關Openmoko 的評論，標題是「<a href="http://www.internetling.com/2008/07/22/3-reasons-why-everyone-will-buy-the-iphone-and-not-openmoko/">3 Reasons Why Everyone will buy one iPhone and not OpenMoko</a>」。一開始的開場白實在犀厲：

<blockquote>But unfortunately, people probably wont buy it...</blockquote>

作者並不是在批評或是「唱衰」Openmoko，反而是以比較中性的來度來看待Openmoko 的努力與成果，我認為這是一篇很有價值的文章，同時也能讓我們能以更多元的角度來思考 Openmoko 的未來。這些不同的聲音與看法，反而是 Openmoko 更應該要正視與關心的。

作者由 marketing、software 以及 users 的角度來評論。首先，以 marketing 的角度來看，Apple 有一個優秀的行銷團隊，但 Openmoko 只有社群，雖然我們都喜歡Openmoko，可是沒有什麼驅動力讓一般大眾購買 Openmoko 手機，大家會買的是iPhone。Openmoko 只能透過口耳相傳（word of mouth）的模式進行。

接著，由 software 的角度來看，FreeRunner 比起之前的 Neo1973 好很多了，但仍然「unreliable」。「Openmoko 並不是 Nokia，他們名號不大。」作者提到。「要有大變革，不要賣一個無法和 iPhone 相比的產品。」

最後是由 users 的角度來看，Linux 很令人敬畏，「但有誰告訴過他外婆，這個長相可疑的手機，比 iPhone 還好？」iPhone 做到了完全創新，也有好看的介面。「這些潛在的信眾（Linux 愛好者）並不信任你並且也不在乎。」這是一場辛苦的戰爭，但Linux 最終會立足於 hand-held 市場。「我們需要的是時間，只是現在時機不對。」但作者最後還是給予 Openmoko 團隊正面的評價：

<blockquote>but let us still cheer the OpenMoko team on for making a brave step forward. </blockquote>

從作者的口吻來看，他似乎希望大家等待 Linux 手機時代的到來。「why wait for the Year of the Linux Phone…」。

或許，大家也發現了，Neo FreeRunner 現階段還是定位給開發者使用的手機，並不符合與該作者的論述出發點。但是，不管作者是否了解 Openmoko 的商業模式，也不管這些論點是否符合 Openmoko 的 roadmap，出現在網路上的看法，就是外界對 Openmoko 的看法。因此，如果這些觀點與 Openmoko 的想法不符，那麼對 Openmoko 團隊來說，更重要的工作就不是做手機，而是「更正確地傳達理念」。

英勇的第一步已經邁出，希望 Openmoko 往當初的理想與目標繼續邁進。加油。]]></description>
         <link>https://www.jollen.org/blog/2008/07/openmoko_brave_step.html</link>
         <guid>https://www.jollen.org/blog/2008/07/openmoko_brave_step.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Sat, 26 Jul 2008 18:54:09 +0800</pubDate>
      </item>
            <item>
         <title>可以開機就好：談作業系統的基礎訓練</title>
         <description><![CDATA[最近在和朋友在討論 Embedded Linux 課程的規劃事宜，希望可以歸納過去所收集的學員意見，以及就助教所提出的課堂問題，對現有課程做調整以及精進。二個星期前，和 thinker 以及 dennis 在聊課程時，興起了一個念頭，我們想要規劃一門「作業系統」的課程。

過去的教育訓練發現，有些學員對於作業系統的背景知識不足，也有些學員對於 Linux kernel 的原理很有興趣，更有些學員對如何寫一個 OS 感興趣，但，由於沒有系統化的文件提供這方面的資訊，因此，讓大家只能由片斷的文件（googling）自己拼湊相關知識，不但沒有效率，而且也經常徒勞無功。

我們想要做的「作業系統」課程，可不是把「恐龍書」搬出來教一教就行了，而是希望走「實務」路線，因此，thinker 提出了一個想法：教大家做一個「只能開機」的 OS。我跟 dennis 都覺得這是一個很不錯的構想，透過「建構式」教學，讓大家從無到有自己寫一個作業系統，這個作業系統也不需要很完整，只要能做到「可以開機」就好了。

昨天晚上，大家再次聚會，再討論了這個「boot only」的 OS。「從無到有自己寫」是一個不太可行的做法，畢竟訓練時數有限，況且「有現成的 Linux kernel」可以用，因此，「從 Linux kernel 剪貼程式碼來做一個新的 OS」是一個最具體可行的做法，也是大家的共識。

透過由 Linux kernel「copy-and-paste」程式碼，拼裝出一個 OS，是一件有意義的事情。雖然是一個拼裝的 OS，但是要讓它可以動，就要了解處理器架構，以及整個開機流程，而且也要知道「要讓一個 OS 可以開機，至少要實作什麼單元。」這是一件有趣的事，大家興趣都來了，一陣技術討論後，很快地，在不到一個小時內，我們就把主題都抓出來了。初步的構想如下。

要知道 Linux kernel 的開機流程，就要有一個學習環境，我們一致認為，透過 Qemu 模擬器與 gdb 進行 source-level debug 會是一個很不錯的做法，而且也可以在拼湊的同時，透過 Qemu 來測試與除錯；最後再將拼裝好的 OS 實際放到開發板上做測試。我們自己的 JK2410 開發板，同時也提供 JK2410 模擬器，所以可以繼續延用我們的「<a href="http://wiki.jk2410.org">Jollen-Kit! 嵌入式系統專用學習平臺</a>」。

有了學習環境後，就可以開始討論「最小型的作業系統」需要實作什麼功能。理論來說，實作出 clock、IRQ handler、virtual memory 以及 context-switch 就可以讓 OS 開機，並且提供「最最最」簡單的功能，比如「Hello, World!」。要能成功 copy-and-paste 出可放到 ARM9 開發板開機的 OS，除了要對 Linux kernel 的 BSP 本身很熟悉外，也要對這些技術的實作細節掌握得很好，所以，用 Linux kernel 來從無到有打造一個自己的 OS 可說是一舉三得的方法：

1. 可以了解 Linux 的 BSP（board-support package）做法，以及開機流程。
2. 可以了解 ARM architecture。
3. 可以學習最根本的 OS 理論與技術。

目前，我們已經完成課程的規劃，不久後也會完成講義的初步規劃。這是一個頗有意義的主題，而且也是一個很好的學習途徑，希望對這個主題有興趣的朋友，可以提供意見或想法給我們。

]]></description>
         <link>https://www.jollen.org/blog/2008/07/linux-kernel-from-scratch.html</link>
         <guid>https://www.jollen.org/blog/2008/07/linux-kernel-from-scratch.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 24 Jul 2008 15:50:52 +0800</pubDate>
      </item>
            <item>
         <title>Process State 與 Wait Queue</title>
         <description><![CDATA[本週要繼續進行 Linux Device Driver 的教育訓練，正好上週有同學問到「process 狀態」的問題，原因是我們在講解範例時，提到 O'Reilly 的 Linux Device Driver 書上以「half sleep」來說明「手動變更 current 狀態」的做法，以及為何不直接呼叫 sleep_on() API 的原因。

當時只做了相當簡要的回答，同學也對 OS 書上講到的 process state 有些不了解。本週打算做比較深入的討論，結果發現我的 Linux Kernel 專欄休眠許久，「Process State」這個章節居然還沒有整理上來，因此在這裡補上這個部份，以免上課要講解時無料可用。

Process 的狀態（state）紀錄在 process descriptor 的 <em>state</em> 欄位。如果您對 process、fork（process creation）以及 preemptive 不熟悉的話，建議您先從頭依序閱讀「<a href="http://www.jollen.org/Linux_Kernel/">Linux Kernel 專欄</a>」裡的文章，才能對以下的 process state 轉換圖產生一點感覺。

<img alt="process_states.png" src="http://www.jollen.org/blog/2008/07/18/process_states.png" width="645" height="285" />

Linux 系統透過 fork system call 建立新的 process，當 process 被生出來後，他的狀態就是「執行中狀態」，也就是 TASK_RUNNING。不過 process 產生後，並不是馬上就能執行，必須被排程器挑選後做 context switch 才是「正在執行中」的 process。我們以 <em>current</em> 代表「正在執行中」的那一個 process，前面所謂的執行中狀態指的是被放在 task queue / run queue（OS 叫 ready queue）中的 process。當 <em>current </em>正在執行時，很可能被其他 priority 更高的 process 搶奪執行權，這時 <em>current</em> 就會被放回 ready queue 裡，這是因為 Linux 是一個可搶先（preemptive）排程的作業系統。

好了，今天 <em>current</em> 要存取硬體，這時如果硬體裝置出現問題，無法讀寫資料，此時「Linux 驅動程式必須把<em> current </em>放到 wait queue 裡睡覺（等待）」。

Wait queue 就是用來讓 <em>current</em> 睡覺的 kernel API。

Process 被放到 wait queue 時的狀態為 TASK_INTERRUPTIBLE 或是 TASK_UNINTERRUPTIBLE。這個時候因為我們的 process 在睡覺了（被放到 wait queue），所以 scheduler 就會再由 ready queue 裡挑一個 process 來執行。

Wait queue 裡的 process 怎麼辦？

睡著的 process 必須被叫醒（wake up），這個動作一般是在 interrupt handler 裡做的。當 process 被叫起來後，狀態再度切換成 TASK_RUNNING 了，於是，又得到被 scheduler 挑選執行的權力了。

Process state 的定義可以在 include/linux/sched.h 裡找到（以下適用 2.6.24 版本以前）：

<blockquote>#define TASK_RUNNING            0<br />
#define TASK_INTERRUPTIBLE      1<br />
#define TASK_UNINTERRUPTIBLE    2</blockquote>

Kernel 也提供了一個 API 可用來設定 <em>current</em> 的狀態：

<blockquote>set_current_state(state_value) </blockquote>

在驅動程式裡手動變更 process state 的目的是為了將 process 放到 wait queue 並達到 critical section 的效果。]]></description>
         <link>https://www.jollen.org/blog/2008/07/process_state_wait_queue.html</link>
         <guid>https://www.jollen.org/blog/2008/07/process_state_wait_queue.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 17 Jul 2008 23:52:48 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] 入門 ARM9 平臺 Linux 驅動程式的基本功</title>
         <description>本週進行「Linux Device Drivers」訓練課程，開始帶領學員在 JK2410 開發板上實際撰寫硬體控制的程式碼。前一階段課程費了許多功夫解釋整個架構和觀念，這部份是與硬體無關的主題，主要針對 Linux 作業系統本身的實作與機制做觀念解說，例如：scheduling（為什麼要使用 wait queue 做 I/O 排程、以及使用時機）、critical section 等等。

我們花了一點時間幫同學建立基本概念，主要都是基本功的訓練，整理如下：

1. JK2410 開發板的操作：如何下載 kernel 與 rootfs 到開發板。

2. Kernel 編譯與設定：toolchain 的取得與安裝、Linux kernel 原始碼的取得、如何設定 kernel、如何包裝成 u-boot 格式。

3. 如何將 cdata 移植到 kernel source tree：透過 Config.in（2.4 kernel）與 Makefile 的修改，將過去辛苦從零寫起的 cdata 驅動程式移植到 kernel 原始碼目錄裡。目前我們的做法是，將 cdata 範例與 kernel 編譯成一個 image 檔，理由是，現階段暫時不去碰 root filesystem。

4. 在 JK2410 開發板上實際把玩 cdata 範例，過去我們都是在 PC 端操作，這次終於能將 cdata 移植到 ARM9 開發板上，並實際操作了。

我們在編譯 kernel 的過程中，也機會教育同學如何思考並解決一些編譯時的錯誤：

5. 如何選擇正確的驅動程式，例如：JK2410 的 serial driver。

6. linking 階段的錯誤（undefined reference）以及解決問題的思考邏輯。

7. compiling 階段的錯誤，以及解決問題的思考邏輯。

這些都是課程由 host 端進入到 target 端（ARM9）時，同學必須要具備的基本功夫，大家可將以上 7 點做為基本能力檢核的 check list。</description>
         <link>https://www.jollen.org/blog/2008/07/goto_arm_linux_device_drivers.html</link>
         <guid>https://www.jollen.org/blog/2008/07/goto_arm_linux_device_drivers.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sun, 13 Jul 2008 14:27:10 +0800</pubDate>
      </item>
            <item>
         <title>千呼萬喚 Neo FreeRunner 正式上市</title>
         <description><![CDATA[千呼萬喚始出來，第一個開放式的行動通訊平臺 Openmoko 今天正式展開第二代手機產品 Neo FreeRunner 的線上銷售。Openmoko 的官方新聞稿已經發佈在這裡了 [<a href="http://www.openmoko.com/press/Openmoko_20080702.pdf">Openmoko Declares Independence for the Mobile Phone</a>]，特別選在獨立紀念日開放 Neo FreeRunner 訂購，正意味著 Openmoko 將在手機市場裡「獨立」走出自己的路，Openmoko 在行動通訊界做了一個革新，這個革新代表的是手機終於獲得真正的自由。

<img alt="freerunner_onsale.PNG" src="http://www.jollen.org/blog/2008/07/04/freerunner_onsale.PNG" width="554" height="255" />

與之前銷售 Neo1973 不同的地方是，Openmoko 這次除了透過線上直銷外，在印度、德國、法國與英國的朋友也能向當地代理商購買 Neo FreeRunner。

距離上次 Openmoko 推出第一代產品 Neo1973 已經過了好長一段時間了，這些日子裡，行動通訊產業發生了許多大事，像是開放手機平臺（如 Android）概念的興起，以及觸控螢幕手機（如 iPhone）的流行，都讓大家有一種耳目一新的感覺。

Openmoko 呢？

這段時間，Openmoko 除了面臨外在的挑戰外，內部也有很大的調整與改變，但不管怎麼樣，我們相信結果是好的，社群開發者也更積極參與 Openmoko 平臺的開發，而且我們也看到了 Openmoko 更進一步將 Neo 手機的機構設計以 CC 授權公開了。許多革新的做法，不斷讓大家看到這個開源手機專案的獨特之處。

Openmoko 還有一個與過去不同的地方。現在的 Openmoko 特別著重學校教育，在 Openmoko 新版的網站上可以看到有一個 [<a href="http://www.openmoko.com/opportunities-universities.html">University</a>] 的頁面，Openmoko 特別關心學校方面的研究計畫，不管是軟體或是設計，都能向 Openmoko 公司或是 Openmoko 社群取得一些幫助。過去 Openmoko 在台灣也與多所大學有所接觸，許多老師與同學對於使用 Neo 手機來製作專題都表達高度興趣，目前也有一些小成績，下學期希望能夠和大家分享這些同學的研究成果。

]]></description>
         <link>https://www.jollen.org/blog/2008/07/neo_freerunner_onsale.html</link>
         <guid>https://www.jollen.org/blog/2008/07/neo_freerunner_onsale.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Fri, 04 Jul 2008 11:42:17 +0800</pubDate>
      </item>
            <item>
         <title>Google 手機計畫的腳步慢下來了</title>
         <description><![CDATA[在 The Wall Street Journal 上的一篇文章指出「Google 手機計畫的腳步慢下來了」，有興趣的朋友可參考原文 [<a href="http://online.wsj.com/article/SB121418837707895947.html">Google's Mobile-Handset Plans Are Slowed</a>]。原因是 carrier 仍與 Android 平臺奮戰中。以下是一些重點掃描，還有些許自已的想法。

Wireless carriers 要的是可以支援自家網路服務的行動裝置，而不是銷售支援其他網路服務的手機，或是自已的網路服務只是該手機的「附加功能」。由此文章也可以看出，這樣的需求，讓 wireless carriers 也開始要求手機製造商製造「branded phones」。即使像 Samsung 這樣的手機製造大廠，也面臨 carrier 要求掛品牌的問題，這方面 Samsung 並沒有什麼回應。

手機品牌廠面臨的一個問題是，當使用者需要的是能提供網路服務的手機時，勢必要和 carrier 建立良好的合作關係。當 carrier 提出的規格，是要求掛自家品牌時，像 Samsung 這樣的手機廠就會面臨一些壓力。從另一個角度來看，手機製造商（handset makers）有了另一個不錯的機會。

提供客製化的應用程式與 UI，以支援不同的網路服務，這是 carrier 使用 Android 平臺的原因。他們希望能發展客製化的 Android 應用程式，以推廣自已的網路服務，並透過服務來收費。但是在客製化 Android 軟體時，不但自已糟遇到一些問題，連手機製造商也面臨一些技術難題。

有些手機製造商花出比原先預期更多的時間，在整合與測試 Android 平臺。同時，依照 carrier 所提出的規格進行 UI 客製化時，也花費更多的時間。這是造成 Android 手機延誤上市的原因之一。

Google 必須召集更多的硬體、服務與軟體供應商來支援 Android 平臺。Google 必須解決他的 3rd party 目前所面臨的一些技術問題，這些都造成 Android 軟體發展上的沈重負擔。

為 wireless carrier 製造手機裝置是開放手機平臺公司的另外一個選擇，carrier 想要自有品牌的手機，並且想要使用 Android 平臺發展客製化的軟體平臺與 UI，以提供自有品牌的服務，而不是提供一支只有 Android 原有功能的手機。Carrier 正在思考如何提供使用者一個易於存取自有網路服務的行動裝置，看起來開放手機平臺的確有很好的機會。

 ]]></description>
         <link>https://www.jollen.org/blog/2008/06/android_slowed_carrier.html</link>
         <guid>https://www.jollen.org/blog/2008/06/android_slowed_carrier.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Tue, 24 Jun 2008 11:11:08 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] 從 kernel-space 讀取 user-space 的字串</title>
         <description><![CDATA[User application 使用 write() 函數將字串寫到裝置檔，所以在 driver 裡頭，就要實作 write system call。當字串的傳遞是透過 write system call 寫至 kernel-space 時，driver 就要使用 copy_from_user() 來讀取 user-space 的字串。以下是一個簡單的 write driver function 實作參考，此實作提供由 kernel-space 讀取 kernel-space 字串的方法，當然這裡頭包含諸多隱含在程式裡的重要關念，例如：

1. user-space page 是 valid 或 invalid。
2. 讓不同 device file 擁有私有資料結構的做法。
3. 同一個 process 在重覆進入 write driver function 時的同步問題尚未考慮，簡單來說就是同步問題還沒考慮到。

程式片段如下：

<pre>ssize_t card_write(struct file *filp, const char *buff, 
		size_t count, loff_t *offp)
{
	struct cdata_s *cdata = (struct cdata_s *)filp->private_data;
	char *str = cdata->buffer;
	int idx = cdata->idx;
	int i;
 
	if (count == 0 || count > 64) 
		goto fail1;
 
	/* get data from user-space */
	for (i = 0; i < count; i++) {
		if (idx >= 64) {
			printk(KERN_ALERT "cdata: buffer full.\n");
			goto fail2;
		}
		if (copy_from_user(str+idx, buff+i, 1))
			goto fail2;
		idx++;
	}
 
fail1:
	cdata->idx = idx;
	return 0;
fail2:
	cdata->idx = idx;
	return -EFAULT;
}</pre>

課堂中提到，這是一個 nonblocking write 的實作，後續可加入一些機制來處理 buffer full 的狀況。]]></description>
         <link>https://www.jollen.org/blog/2008/06/write_driver_function.html</link>
         <guid>https://www.jollen.org/blog/2008/06/write_driver_function.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sun, 22 Jun 2008 11:47:40 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] 呼叫 kmalloc(GFP_KERNEL) 的函數要可以重覆進入</title>
         <description><![CDATA[使用 kmalloc() 時，要特別注重的是「可重覆進入」的觀念。kmalloc() 的第二個參數稱為 allocation flag，用來控制 kmalloc() 的行為，當此參數有指定 GFP_KERNEL 旗標時，kmalloc() 就是一個 blocking function。

使用 GFP_KERNEL 旗標來配置記憶體時，為什麼會有可重覆進入的議題呢？主要的關鍵在於，當 kmalloc(..., GFP_KERNEL) 無法配置記憶體時，便會做「等待」的動作，這個等待的動作是對「current process」做重排程，並等候記憶體空間。

以 open driver function 來看，通常我們會在 open driver function 裡做記憶體的配置，當記憶體目前無法取得時，open driver function 便會停止（等待），因此不會完成這一次的函數呼叫（沒有 return），此時，同一個 open driver function 會不會再被「重覆」呼叫執行呢？當然會。因為，可能會有另一個 process 去開啟 major number 相同的裝置檔，因此，同一個 open driver function 又被呼叫了，但是前一次的呼叫卻還在等待。

當函數第二次被呼叫時，第一次的呼叫還沒有結束執行，所以，函數「重覆」進來執行了。

延伸閱讀

2008.04.20: <a href="http://www.jollen.org/blog/2008/04/private_data_and_reentrant_function.html">[教育訓練紀錄] 關於驅動程式的 private data 與可重覆進入函數</a>]]></description>
         <link>https://www.jollen.org/blog/2008/06/kmalloc_reentrant.html</link>
         <guid>https://www.jollen.org/blog/2008/06/kmalloc_reentrant.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sun, 22 Jun 2008 10:10:46 +0800</pubDate>
      </item>
            <item>
         <title>Open Source in Mobile 2008 今年更盛大了</title>
         <description><![CDATA[[<a href="http://www.osimworld.com">OSiM World（Open Source in Mobile 2008）</a>] 將於九月17-18日於德國 Berlin 舉行，將有超過 100 位來自 Open Source Mobile 生態系統（ecosystem）的不同產業界重量級講者，為大家帶來各種不同的講題。OSiM 可說是全世界最大且影響力最強的 Open Source Mobile 研討會，有超過 42 個國家的與會者以及不同產業的領域者將出席此會議，今年的 OSiM 可說是 Open Source Mobile 的領導級活動。

倒底有哪些重量級人物將發表演說，查了一下 [<a href="http://www.osimworld.com/newt/l/handsetsvision/osim08/speakers.html">Speakers</a>] 果然不是蓋的，像是：

* Ari Jaaksi - Nokia / Open Source Operations 的 Director
* hristy Wyatt - Motorola / Software Platform & Ecosystem 的 Vice President
* Morgan Gillis - LiMo Foundation 的 Executive Director
* Benoit Schillings - Trolltech 的 Chief Technical Officer

當然，Openmoko 也沒有缺席此項盛會。Openmoko 的 founder Sean Moss-Pultz 也會出席發表演說。又看了一下 [<a href="http://www.osimworld.com/newt/l/handsetsvision/osim08/agenda_d1.html">Agenda Day 1</a>] 以及 [<a href="http://www.osimworld.com/newt/l/handsetsvision/osim08/agenda_d2.html">Agenda Day 2</a>] 二天的議程，「果然犀利」，其中有一個議程是我特別感興趣的：

<blockquote>Raising Operator Confidence in Open Source, Guido Arnone, Director,Terminals Technology Vodafone

    * What is the benefit of Open Source for Operators?
    * What reservations are there about Open Source?
    * Understanding what the Operator wants to hear
    * Are Operators comfortable outside the walled garden?</blockquote>

由「operator」的角度來看「Open Source」絕對是一件有意思的事。
]]></description>
         <link>https://www.jollen.org/blog/2008/06/osim_2008.html</link>
         <guid>https://www.jollen.org/blog/2008/06/osim_2008.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Sat, 21 Jun 2008 23:14:31 +0800</pubDate>
      </item>
            <item>
         <title>Google 說 Android 將會 100% 開放源碼</title>
         <description><![CDATA[過去大家經常在討論「Android 不是 100% 開放源碼」，但 Google 目前已做了正式的解釋，Google 表示「<a href="http://www.searchenginejournal.com/google-says-android-will-be-100-open-source/7012/">the core Android platform will be 100% open source</a>」這又將掀起大家對 Android 的另一波討論。

報導表示，在與多位 Google 員工確認後知道「everything will be opened」，並且所有核心部份也都將採用 Apache software license (ASLv2)，非 core Android 部份的授權則不一定採用 ASLv2。Android 平臺是基於 Embedded Linux 系統，在 Embedded Linux 平臺上，大部份 FOSS 軟體原本就採用 GPLv2 的授權，這個部份當然不會有所變動。與 Eclipse 相關的軟體，例如 ADT (Android Development Tools plug-in) 採用的是 Eclipse Public License (EPL)。

雖然 core Android platform 將會 100% open source，但要了解的重要觀念是：

<blockquote>The core Android system will be open source, but there’s no guarantee that carrier,s OEMs and application developers will keep their applications open source.</blockquote>

開放手機平臺的演化又再進一步了。]]></description>
         <link>https://www.jollen.org/blog/2008/06/google_android_full_opensource.html</link>
         <guid>https://www.jollen.org/blog/2008/06/google_android_full_opensource.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Thu, 05 Jun 2008 08:48:08 +0800</pubDate>
      </item>
            <item>
         <title>Linux WiMAX Driver 實作現況分析</title>
         <description><![CDATA[前陣子接受 DigiTimes 的「手持行動裝置開發關鍵軟體技術發展」研討會邀請，當時就在思考要以什麼主題為主。一些題目大概都是老生常談了，而且又不想以介紹性的方式進行。想了又想，發現最近最當紅的主題非 WiMAX 莫屬，WiMAX 也是今年 Computex 展的主題，因此決定以 Intel 的 [<a href="http://linuxwimax.org">Linux WiMAX development project</a>] 專案做為討論標的。5 月 29 日這一天的演講就以「Linux WiMAX Driver 實作現況分析」定題了。

linuxmax.org 是 Intel 所支持的一個專案計畫，此計畫目前已釋出第一個 WiMAX device driver 以及 WiMAX stack。目前在 linuxwimax.org 上已能找到 Intel WiMAX Connection 2400m 的驅動程式，以及一個 WiMAX stack 驅動程式（subsystem）。2400m 是一個符合 mobile WiMAX 標準的 WiMAX chipset，mobile WiMAX 是行動 WiMAX 的一個標準（802.16e），主要給行動裝置使用。

當天的演講投影片可由此下載 [<a href="http://tw.jollen.org/slides/introduction_wimax_driver.pdf">introduction_wimax_driver.pdf</a>]。雖然定題為「Linux WiMAX Driver 實作現況分析」，不過若以 device driver 的角度來看，其實會變得比較像是在講 USB 與 network device 的 subsystem。若是以整個架構來看，WiMAX driver 在分層設計這裡已經有很不錯的實作，包含以下二個部份：

1. 透過 netlink layer 做為 user-to-kernel 的介面，在 application 端也有 API 的實作，可透過 libnl 來操作 WiMAX 的設定。
2. 針對 device driver 提供分層架構設計：<em>struct wimax_dev</em> 以及 <em>wimax_dev_add()</em>。

另外，為了解 WiMAX driver 與 kernel-space 的緊密性關性，我們透過了 2400m 的驅動程式來分析其架構關係，以及 I/O 處理方法。初步了解，WiMAX device driver 仍是透過 transport layer 來做處理，WiMAX stack 目前只提供 netlink layer 給下層的裝置驅動程式。其餘部份大略整理於投影片中，請指教。]]></description>
         <link>https://www.jollen.org/blog/2008/06/linux_wimax_driver.html</link>
         <guid>https://www.jollen.org/blog/2008/06/linux_wimax_driver.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 01 Jun 2008 23:20:11 +0800</pubDate>
      </item>
            <item>
         <title>Richard Stallman 台灣行第四天紀錄: 5/15 演講實紀</title>
         <description><![CDATA[這次的演講地點是清華大學的大禮堂，這是一個又大又舒適的場地，大家都能坐在舒服的軟椅上聽演講。我們在入口處設置了「義賣處」，專賣由 Free Software Foundation 遠渡重洋寄來的一些小玩意兒，我們也依照 Richard 的意思，在現場義賣他自己的書。

<img alt="rms_day4_5.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_5.jpg" width="640" height="427" />

<img alt="rms_day4_6.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_6.jpg" width="640" height="427" />

今天的主題是自由軟體運動，以及 GNU/Linux operating system，這場演講其實我並沒有很專心在聽講（*汗*），因為一直不斷地在會場穿梭。不過，有許多這天來聽演講的朋友，都在自已的 blog 上做了很不錯的紀錄，他們的紀錄會比較有參考價值：

* 2008-05-21: <a href="http://twcu.blogspot.com/2008/05/definition-1-free-software-2-4.html">E-Mate News: A talk from Richard Stallman</a>
* 2008-05-19: <a href="http://hialan.blogspot.com/2008/05/515-richard-stallman.html">Hialan's Blog: 5/15 Richard Stallman 清大演講心得</a>
* 2008-05-17: <a href="http://magicdesign.blogspot.com/2008/05/blog-post_17.html">魔法設計的藝術: 自由軟體之父理查史托曼的演講(清華場)</a>
* 2008:05-16: <a href="http://ryan403.blogspot.com/2008/05/richard-stallman.html">Ryan Chung's Blog: Richard Stallman 清華演講心得分享</a>
* 2008-05-15: <a href="http://www.wretch.cc/blog/ranma/12494671">踏出阿宅 走向質男   Freedom is ..the GNU</a>

這一場的聽眾人數我們沒有精確統計過，但是比起前一天在淡江大學，人數明顯多很多。超過一千個座位的大禮堂，坐了將近七成，而且互動與反應都比前一天還熱烈。當我的同事帶領 Richard 上講臺時，我還聽到旁邊有個小妹講「哇！好可愛喔」，她該不會是覺得老爹圓滾滾的肚子很可愛吧。

<img alt="rms_day4_7.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_7.jpg" width="640" height="427" />

<img alt="rms_day4_8.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_8.jpg" width="640" height="427" />

講臺上除了 Richard Stallman 外，最引人注意的大概就是台上一字排開的「綠茶」吧！沒辦法，大師真的超愛這種綠茶的，只記得第一天帶 Richard 回 Openmoko 公寓時買了幾瓶給他，從此他就愛上這種綠茶了。

<img alt="rms_day4_9.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_9.jpg" width="640" height="427" />

<img alt="rms_day4_10.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_10.jpg" width="640" height="427" />

Richard Stallman 準備許多不同的演講主題，在清華大學這場，我們特別選了與自由軟體運動相關的主題，因為可以看到 Richard 知名的「變裝秀」。Richard Stallman 都會在演講後舉行「祈福」儀式。這就是 Richard Stallman 知名的另一個身份 [<a href="http://www.stallman.org/saint.html">Saint IGNUcius</a>]，Saint IGNUcius 是 Emacs 教會聖徒的意思。Emacs 是 Richard Stallman 學生時代所開發的一個知名文字編輯軟體，至今 Emacs 已經成為一種生活與一種信仰（a way of life and a religion）。變身為 Saint IGNUcius 的 Richard 對著大家說「I bless your computer, my child!」（我保佑你們的電腦，我的子民！），想加入這個教會的人，只需要默唸這個懺悔詞三次即可「There is no system but GNU, and Linux is one of its kernels.」。Richard 還有知名的「Free Software Song」，可惜這次沒有機會聽到，他都隨身攜帶笛子，一有機會就會吹起自由軟體之歌，這次我也只能看到這支笛子，沒有機會聽到 Richard 親自表演 free software song。

<img alt="rms_day4_11.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_11.jpg" width="640" height="427" />

<img alt="rms_day4_12.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_12.jpg" width="640" height="427" />

最後，Richard 來了個拍賣會。比起前一天在淡江大學，這次的拍賣會熱烈多了，Richard 親自帶來的原版書，以及 GNU 玩偶，都以高價拍出。

<img alt="rms_day4_13.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_13.jpg" width="640" height="427" />

<img alt="rms_day4_14.jpg" src="http://www.jollen.org/blog/2008/05/22/rms_day4_14.jpg" width="640" height="427" />

散場後，很多人都帶著在場外買的書給 Richard 簽名。他也在一塊活動宣傳的布條上簽名，協助活動的另外二個主辦單位「清華大學資工系」與「清華大學電通中心」也和 Richard 大合照，為這次大師訪台畫下美好句點。

當天標到 GNU 玩偶的 CNET 記者馬冶國先生也做了活動紀錄 [<a href="http://www.zdnet.com.tw/news/pix/0,2000085677,20129415,00.htm">圖片：Richard Stallman在清華</a>]。能參與舉辦這個難得的活動，並近身觀察自由軟體之父，對所有工作人員來講，都是一個難忘的回憶！]]></description>
         <link>https://www.jollen.org/blog/2008/05/richard_stallman_speech_nthu.html</link>
         <guid>https://www.jollen.org/blog/2008/05/richard_stallman_speech_nthu.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 22 May 2008 11:40:24 +0800</pubDate>
      </item>
            <item>
         <title>Richard Stallman 台灣行第四天紀錄: 5/15 新竹清大行側寫</title>
         <description><![CDATA[5 月 15 日的行程是帶 Richard Stallman 到新竹清華大學，這一天大師所要發表的演講主題是「The Free Software Movement and the GNU/Linux Operating System」。與 Richard 接觸幾天下來，發現他其實是一個很喜歡「看世界」的老爹，正好，我偏愛人物側寫，因此再度來和大家爆料。

<img alt="rms_day4_1.jpg" src="http://www.jollen.org/blog/2008/05/21/rms_day4_1.jpg" width="640" height="427" />

Richard Stallman 喜歡什麼東西？幾天下來，我們知道大師喜歡「民俗傳統音樂」、「中國菜」、「中國茶」、「山」，以及「火車」。因此這一天，我和一位商周的記者特別帶 Richard 搭台灣高鐵。Richard 喜歡靠窗的位子，不過他很有風度地讓座給他的女朋友 Dora。

<img alt="rms_day4_2.jpg" src="http://www.jollen.org/blog/2008/05/21/rms_day4_2.jpg" width="640" height="427" />

來到高鐵站後，Richard 和 Dora 都覺得高鐵站真是太漂亮了，又大又寬倘，我們照了幾張照片，就往「玻璃工藝博物館」出發了。沒錯！我們是從高鐵站直接開跋到玻璃工藝博物館，為什麼我們會跑到這裡來呢？因為，大師不知道是怎麼知道新竹有這個地方的，他在前一天就特別交待我們，一定要帶他來這裡！

<img alt="rms_day4_3.jpg" src="http://www.jollen.org/blog/2008/05/21/rms_day4_3.jpg" width="640" height="427" />

參觀完玻璃工藝博物館後，我們終於可以來到真正的目的地「清華大學」了。清華大學資工系的幾位教授，在清大裡的一間咖啡店設席款待大師。Richard 非常熱愛閱讀，我們一帶他進到咖啡店後，他就突然眼睛一亮，因為店裡滿滿地都是原文書。他問道「這裡是書店嗎」，正巧遇到咖啡店的老闆，他為大師解答了這個問題，原來，這裡以前是一家書店。

<img alt="rms_day4_4.jpg" src="http://www.jollen.org/blog/2008/05/21/rms_day4_4.jpg" width="640" height="427" />

大師突然興緻一來，逛起書店了。他和咖啡店老闆似乎相當聊得來，聊書、聊音樂，興致大開，還在店裡的牆上留下簽名。聊天簽名還不夠，Richard 還挑了幾本書想要買回去，老闆也很夠意思，打折賣出！用餐後，即將展開今晚的演講。離去前，Richard 還問道「你們店開到什麼時候」，他還想要回來挑書呢！幾天下來，看到了大師非常不一樣的另一面，真是一個意外的收獲。]]></description>
         <link>https://www.jollen.org/blog/2008/05/richard_stallman_day4.html</link>
         <guid>https://www.jollen.org/blog/2008/05/richard_stallman_day4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 21 May 2008 20:54:14 +0800</pubDate>
      </item>
            <item>
         <title>Richard Stallman 結束訪台行程: 5/19 離台</title>
         <description><![CDATA[Richard Stallman 於前天（5/19）離台，前往香港，正式結束這次的訪台行程。相隔三年，Richard 再度來台，對台灣還是記憶深刻呢。上個週末，Openmoko 同事帶著大師品嘗台灣的知名餐廳「鼎泰豐」，以及別具特色的茶料理「喫茶趣」；後者是大師欽點，他在三年前到台灣時曾經品嘗過，這次來台灣特別點名要我們帶他去呢！

這天早上大約八點來到 Openmoko 公寓找大師，大師給了我一堆貼紙以及胸針，要我分給有需要的朋友。Richard 搭乘港龍航空到香港，接下來的行程如下：

* 2008-05-24: 上海復旦大學 - Free Software in Ethics and in Practice
* 2008-05-28: 西安交通大學 - Free Software in Ethics and in Practice
* 2008-05-30: 北京清華大學 - Free Software in Ethics and in Practice
* 2008-05-31: 北京清華大學 - The Danger of Software Patents

需要更詳細資訊的朋友可查詢 Free Software Foundation 的 [<a href="http://www.fsf.org/events/rms-speeches.html">活動公告</a>]。Richard 本次訪台共發表了二場公開演講，後續會補上其他行程及活動紀錄，我們也會進行演講的逐字稿工作，以及照片分享。希望能將自由軟體大師這次的訪台紀錄與大家完整分享。]]></description>
         <link>https://www.jollen.org/blog/2008/05/richard_stallman_departure.html</link>
         <guid>https://www.jollen.org/blog/2008/05/richard_stallman_departure.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 21 May 2008 16:43:20 +0800</pubDate>
      </item>
            <item>
         <title>Richard Stallman 台灣行第三天：下課後</title>
         <description><![CDATA[大師第三天除了到淡江大學演講外，下午還有私人行程，現在就由我來為大家大爆料，瞧瞧大師的另一面！這天，結束公開演講行程後，一行人加大師一共五個人，驅車來到淡水老街裡的一家海鮮餐廳，準備替 Richard 安排一頓豐富的海鮮大餐。

<img alt="rms_day3_06.jpg" src="http://www.jollen.org/blog/2008/05/16/rms_day3_06.jpg" width="640" height="427" />

不說您不知道，Richard 是個美食愛好者，而且特愛中國菜，這次來到台灣，我們帶他品嘗了好幾家各具特色的餐廳，遇到他喜歡的菜時，還會眯著眼睛細細品味，一副陶醉樣呢。Richard 今天出門特別帶了一包從義大利帶來的麵包條，午餐時，他把麵包條拿了出來，然後分給大家吃。嗯！這麵包條的味道還真不錯，酥酥脆脆，還有點甜甜的，不過，大師可不是想把麵包條拿來配飯，他把麵包條當筷子用！據大師說，這樣在吃完飯後，可以直接把筷子吃掉！

<img alt="rms_day3_07.jpg" src="http://www.jollen.org/blog/2008/05/16/rms_day3_07.jpg" width="640" height="427" />

午餐後，我們帶 Richard 來到關渡的華碩總公司，他今天有一個拜訪 EeePC 的行程。大師跟 EeePC 的幾位工程師，討論了一些技術議題，其中最重要的就是有關「Free BIOS」的討論。大師的工作配備是一台 OLPC（One Laptop Per Child）再加上一個外接鍵盤，為什麼他要用 OLPC 呢？「因為 OLPC 用的是 Free BIOS」，Richard 說。

<img alt="rms_day3_08.jpg" src="http://www.jollen.org/blog/2008/05/16/rms_day3_08.jpg" width="640" height="427" />

當然，自由軟體之父來到此寶地，最重要的工作莫過於推廣自由軟體的理念。「希望 EeePC 能協助推廣自由軟體，並告訴大家，EeePC 用的是 GNU/Linux 系統，而不是 Linux 系統」，Richard Stallman 說。現場 EeePC 的朋友也表達高度的誠意，以及協助推廣自由軟體的意願，「或許我們可以先將 Web 上的內容做修改」（將 Linux 改成 GNU/Linux），EeePC 的朋友說。

<img alt="rms_day3_09.jpg" src="http://www.jollen.org/blog/2008/05/17/rms_day3_09.jpg" width="640" height="427" />

<img alt="rms_day3_10.jpg" src="http://www.jollen.org/blog/2008/05/17/rms_day3_10.jpg" width="640" height="427" />

Richard 此行挺有收獲的，除了得到友善的回應外，也拿到一台免費的 EeePC。我想，Richard 的第一個念頭應該是把 EeePC 的 BIOS 換成 Free BIOS 吧！

<img alt="rms_day3_11.jpg" src="http://www.jollen.org/blog/2008/05/17/rms_day3_11.jpg" width="640" height="427" />

<img alt="rms_day3_12.jpg" src="http://www.jollen.org/blog/2008/05/17/rms_day3_12.jpg" width="640" height="427" />

貼有「Free Software Foundation」以及「GNU/Linux Inside」貼紙的 EeePC。]]></description>
         <link>https://www.jollen.org/blog/2008/05/richard_stallman.html</link>
         <guid>https://www.jollen.org/blog/2008/05/richard_stallman.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Fri, 16 May 2008 22:23:52 +0800</pubDate>
      </item>
            <item>
         <title>Richard Stallman 台灣行第三天：演講「The Danger of Software Patent」</title>
         <description><![CDATA[大師今天的行程是到台北的淡江大學發表公開演說，我的同事 tick 今天充當地陪，到 Openmoko 公寓帶 Richard 到淡江大學。今天的場地大約有200個座位，因為反應熱烈，連講座的樓梯也都快坐滿人了！

今天的講題是「The Danger of Software Patent」，Richard 主要在談論軟體專利是如何危害「創意與想法」的進步，他並且在黑板上畫了一個圖，來講解軟體專利的危險，以及與藥品或其它工程專利的不同。這是一個很有想法的見解，過去我只知道自由軟體基金會是非常「反專利」的，但從未深入了解其原因，今天大師親自到場為大家解釋「為什麼軟體專利不合理」的想法，非常有收獲，因為讓我了解到其實 Richard Stallman 並不是在「反專利體系」，而是強調「軟體專利」的不合理性。

<img alt="rms_day3_01.jpg" src="http://www.jollen.org/blog/2008/05/15/rms_day3_01.jpg" width="640" height="427" />

今天在會場遇到了ZDNet Taiwan的馬培治記者，他也寫了一篇相關的報導 [<a href="http://www.zdnet.com.tw/news/software/0,2000085678,20129320,00.htm">自由軟體基金會創辦人：軟體專利有害無益</a>] 簡單紀錄了 Richard Stallman 今天的演說主軸，是一篇很有參考價值的報導。

<img alt="rms_day3_02.jpg" src="http://www.jollen.org/blog/2008/05/15/rms_day3_02.jpg" width="640" height="427" />

Richard 提到「軟體是一個很大的設計專案、需要數以千計的想法（idea）」，他又說道「一個功能就需要由許多的想法所構成（one feature, lots of ideas）」，所以，如果所有的想法都被專利所禁箇，對使用者（也就是我們）其實是一種傷害，我們（也就是使用者）應該站出來悍衛軟體的「自由」不被商業利益所危害。若軟體無法自由修改或變更，使用者也就失去這樣享受「軟體無限創意」的自由了。

<img alt="rms_day3_03.jpg" src="http://www.jollen.org/blog/2008/05/15/rms_day3_03.jpg" width="640" height="427" />

「The Danger of Software Patent」軟體專利的危害，在於讓我們無法將「各種不同的想法加以組合」。因為，若想法被專利所限制，人類（使用者）將無法享受軟體多樣化的自由。「想法若透過專利來加以限制，時間一久，將會造成無限的危害」，Richard Stallman 說。

<img alt="rms_day3_04.jpg" src="http://www.jollen.org/blog/2008/05/15/rms_day3_04.jpg" width="640" height="427" />

大師將於15日晚間於清華大學發表第二場公開演說，這是大師訪台的最後一場演講，想要一睹大師風采的朋友，可要好好把握機會了！活動訊息請參閱：[<a href="http://wiki.openmoko.org/wiki/Richard_Stallman/zh_tw">http://wiki.openmoko.org/wiki/Richard_Stallman/zh_tw</a>]。

<strong>延伸閱讀</strong>

2008.05.12: <a href="http://www.jollen.org/blog/2008/05/richard_stallman_day1.html">Richard Stallman 台灣行第一天</a>
2008.05.03: <a href="http://www.jollen.org/blog/2008/05/richard_stallman_speech_taiwan.html">自由軟體基金會創辦人 Richard Stallman 來台演講</a>]]></description>
         <link>https://www.jollen.org/blog/2008/05/richard_stallman_day3_tku.html</link>
         <guid>https://www.jollen.org/blog/2008/05/richard_stallman_day3_tku.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 14 May 2008 23:01:05 +0800</pubDate>
      </item>
            <item>
         <title>Richard Stallman 台灣行第一天</title>
         <description><![CDATA[自由軟體基金會創辦人 Richard Stallman 今天下午抵達台灣。這次 Richard Stallman 來台，有一位美麗的小姐 Dora 隨行。到機場接機時，一眼就認出站在路邊等候的大師。大鬍子是大師的特色，非常地容易辨認。

<img alt="rms_dinner.JPG" src="http://www.jollen.org/blog/2008/05/13/rms_dinner.JPG" width="613" height="246" />

一行人先將 Richard 接到 Openmoko apartment，沒錯！這是「Openmoko 公寓」，是專門「招待」外國工程師的「行館」，Openmoko 公寓非常靠近 Taipei 101。來的路上，Richard 在高速公路上看到 Taipei 101 時，發出了讚嘆的聲音。Richard 的女友 Dora 小姐，對於 101 的外觀則是感到興趣，她覺得 101 大樓長的真是奇怪呢！

大師是一位非常依賴電子郵件的人，他所有的工作幾乎都是透過電子郵件完成的。在 Richard 來台前，我們也都是完全依靠電子郵件和 Richard 討論行程，以及確認每一個細節。在電子郵件往來過程發現，大師就是大師，對每一個細節都很注重以及重視，這可不是台灣人講的「龜毛」，而是對於工作的認真態度，以及對理念的執著。Richard 在「自由軟體運動」的道路上，一路走來，始終如一。

Richard Stallman 對於自由軟體運動理念相當執著，因此可能有人會認為他是一個不好相處的人，但是今天和大師相處一天下來，我覺得，大師並沒有大師的感覺。不要誤會我的意思了，我指的是，Richard 是一個沒有大師架子的「老爹」，也就沒有那種難以接近，或是言語交談時的壓迫感。除了有些地方，大師有他的「堅持」外，其他事情都很容易和他溝通。但其實，Richard 所堅持的，也只是在表達他的想法，希望能讓我們都能聽聽他的觀念。能親自聽到大師述說他的觀念，這真是一個難得的經驗。

大家可能也都聽過，Richard Stallman 是相當喜歡蝴蝶的。下午我們陪 Richard Stallman 回到 Openmoko 公寓時，大廳裡恰巧有一隻蝴蝶，不斷地衝撞著落地窗。Richard 看到了，他走向前去等到蝴蝶停止，然後很仔細很細心地，捏住蝴蝶的翅膀，將牠放到大門外的樹上。

我說 Richard Stallman 其實是一個「可愛」的老爹。晚上 Openmoko 大批人馬，來到內湖的伍角船板，和大師共進晚餐。Richard 一開始有點嚴肅，我想應該是跟大家都還不熟的關係，不過接下來跟大家可就有說有笑的了，甚致還會開些小玩笑！晚餐時，當然免不了要向大師請益「自由軟體」的一些問題，有同事問到「patent」的議題，Richard 也很願意向大家說明。

第一次見到「傳說中」的自由軟體之父，內心有一點點感動，也有敬佩。因為 Openmoko 公寓是電梯大廈，電梯有保全，需要門禁卡才能操作，沒想到大師說「住戶有進出電梯的『自由』，我了解這是為了安全需要，但讓大家失去了這個自由。」

晚餐後，大師向大家說「謝謝」，離去前，也向大家道別「happy hacking」，很有禮貌。Richard 在回 Openmoko 公寓的路上「再次」問了我「do you like the music?」，老爹分享他帶來的音樂給我們，今天一整天都聽著老爹帶來的音樂。我估計他問了我音樂好不好聽至少有 5 次吧！但說真的，我還蠻喜歡 Richard 帶來的音樂。這音樂非常有民俗風，是傳統音樂，聽起來非常舒服。我計畫向老爹要他的 CD 當紀念品，希望我能成功！

Richard Stallman 將在 5 月 14 日和 5 月 15 日發表公開演說，想要一睹大師風采的朋友，趕快來看這裡：[<a href="http://wiki.openmoko.org/wiki/Richard_Stallman/zh_tw">http://wiki.openmoko.org/wiki/Richard_Stallman/zh_tw</a>]。

<strong>延伸閱讀</strong>

2008.05.03: <a href="http://www.jollen.org/blog/2008/05/richard_stallman_speech_taiwan.html">自由軟體基金會創辦人 Richard Stallman 來台演講</a>]]></description>
         <link>https://www.jollen.org/blog/2008/05/richard_stallman_day1.html</link>
         <guid>https://www.jollen.org/blog/2008/05/richard_stallman_day1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Mon, 12 May 2008 23:09:36 +0800</pubDate>
      </item>
            <item>
         <title>自由軟體基金會創辦人 Richard Stallman 來台演講</title>
         <description><![CDATA[學生時代就相當敬佩的自由軟體精神領袖 Richard Stallman 要來台灣了。Richard Stallman 在就讀哈佛大學時，於麻省理工人工智能實驗室發展 Emacs 軟體，也就是在這個時期，他體驗到駭客文化的可貴與精神，從此成為悍衛自由軟體的鬥士。Sam Williams 也寫了一本「自由軟體的聖戰」[1]，內容在描述 Richard Stallman 的自由軟體運動。

以下引述 Openmoko 的新聞稿：

自由軟體基金會創辦人、同時也是知名軟體 GNU Compiler Collection (GCC) 與 GNU Debugger (GDB) 的原始作者與開發者 Richard Stallman 將於 5 月 12 日來台並發表演說。Richard 於 1984 年發動 GNU operating system 發展計畫，並在 1985 年成立 Free Software Foundation（自由軟體基金會），接著在 1989 年寫出第一個 GPL （GNU General Public License）條款。GPL 至今已是最重要的自由軟體授權條款，至今有超過 60% 的自由軟體都是採取 GPL 授權規範。因應商業化需要，GPLv3 在經過長時間的討論後，也於 2007 年 6 月正式釋出，並受到產業界高度重視與討論。

睽違三年，Richard Stallman 再度來台，將在台北與新竹各發表一場公開演說。Richard 提到「希望能讓大家了解 GNU operating system，以及自由軟體（free software）的真正意義。」除了發表與自由軟體相關之演說外，Richard Stallman 也會和現場聽眾進行公開討論，這是一個向 Richard 當面請益的好機會。

Richard 本次來台預計將發表與「自由軟體運動」以及「軟體專利威脅」有關之演說。所有活動都是免費參加，Richard 同時也非常想聽到來自各界對自由軟體的聲音。詳細活動資訊請參閱 Openmoko Wiki 活動頁面。

    * 活動頁面：<a href="http://wiki.openmoko.org/wiki/Richard_Stallman/zh_tw ">http://wiki.openmoko.org/wiki/Richard_Stallman/zh_tw 
</a>
<strong>延伸閱讀</strong>

2008.04.30: <a href="http://www.jollen.org/blog/2008/04/foss_gplv3_business_embedded_systems.html">嵌入式系統廠商不能不懂的自由軟體授權 GPLv3</a>

[1] Free as in Freedom: Richard Stallman's Crusade for Free Software, http://www.faifzilla.org/]]></description>
         <link>https://www.jollen.org/blog/2008/05/richard_stallman_speech_taiwan.html</link>
         <guid>https://www.jollen.org/blog/2008/05/richard_stallman_speech_taiwan.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Sat, 03 May 2008 11:55:48 +0800</pubDate>
      </item>
            <item>
         <title>嵌入式系統廠商不能不懂的自由軟體授權 GPLv3</title>
         <description><![CDATA[Richard Stallman[1] 是 Free Software Foundation[2]（自由軟體基金會）的創始人。FSF 成立於 1985 年，致力於爭取電腦使用者的軟體使用自由。Richard 也在 1989 年寫出了第一個 GPL （GNU General Public License）[3] 條款，並在 1991 年 6 月份時釋出 GPLv2（GPL version 2）。

GPL 是現今最重要的 Free and Open Source Software（FOSS）授權條款，至今有超過 60% 的自由軟體都是採取 GPL 授權規範。因應時勢需要，GPLv3 在經過長時間的討論後，也於 2007 年 6 月正式釋出。由於過去的 FOSS（Free and Open Source Software）是採用 GPLv2 授權，因此在 GPLv3 釋出後，大家最關心的便是 GPLv2 與 GPLv3 的差異。由於 GPLv2 與 GPLv3 是不相容的，而且目前資訊工業也大量採用 FOSS 解決方案，再加上嵌入式系統（Embedded System）的應用所帶出的「firmware 與 hardware 不可分割」議題，因此不管是軟體開發商或是嵌入式系統廠商，無不積極針對相關的法律議題進行了解，以釐清 GPL 在商業運用方面議題。

中研院「自由軟體鑄造場」在今年三月份舉辦「自由軟體法律研討會：嵌入式應用專題」[4]，當天有許多台灣的科技大廠以及政府單位參與，可見大家對自由軟體法律問題的重視。對商業運作而言，特別是嵌入式系統廠商來說，GPLv2 與 GPLv3 的議題會是最重要的部份。了解 GPL 授權規範，以及釐清 v2 與 v3 的差異，將是當前最重要的功課。

Richard Stallman 也親自寫了一份「Why Upgrade to GPL Version 3」文件 [5]，向大家說明 GPLv2 與 GPLv3 的主要差異。大致整理 GPLv2 與 GPLv3 的主要差異如下：

* GPLv2 與 GPLv3 是不相容的，沒有法律上的方式將 GPLv2 的程式碼與 GPLv3 的程式碼組合成單一程式。

* GPLv2 與 GPLv3 都是「copyleft」的授權，在自已的程式裡引用使用此授權的程式碼，則自已的程式同樣要引用相同的授權。

* 只在我們需要連結（link）、合併（merge）或組合（combine）二個不同授權的桯式成為一個單一程式時，才會引發授權不相容的議題。但是，GPLv3 的程式與 GPLv2 的程式在一個作業系統裡各自執行時，就不會有什麼問題。例如，TeX 與 Apache 授權都是與 GPLv2 不相容的授權，但我們仍可以在同一個系統裡執行這些程式。因為這些程式都是獨立的程式。又如，如果 Bash 與 GCC 都改採 GPLv3 授權，但 Linux 仍然採用 GPLv2 授權時，這也是沒有衝突的。

* 解決「tivoization」問題。有些裝置以硬體的方式限制使用者，讓使用者無法在該裝置上執行經過修改的軟體。像是 DRM（數位內容管理 - Digital Restrictions Management），如 DVD 撥放器，就會限制 DVD 的撥放。但 GPLv3 並不是想禁止 DRM 的使用，而是確保使用者能有修改軟體的自由，例如：加入一個功能到軟體裡。

* 試圖解決軟體專利問題。但目前仍然無法有效地單獨以 GPLv3 解決此問題。

* GPLv3 與 Apache 授權相容。

原文法律條文有點艱澀難懂，這裡有一份社群協作的「GPLv3 中文翻譯」: <a href="http://wiki.debian.org.hk/w/GPLv3">http://wiki.debian.org.hk/w/GPLv3</a>， 或許可以提供一些幫助。Richard Stallman 去年接受 Linux Link Tech Show 的訪談，也親自說明 GPLv3 的觀念，值得一聽。

許多嵌入式裝置，如：smartphone、router、media player 等，都廣泛使用 FOSS 做為解決方案。因此，當我們不斷討論自由軟體與開源軟體在商業化的應用時，GPL 授權這個最重要的「許可證」以及相關法律問題，也不能忽略。

<strong>延伸閱讀</strong>

2006.11.12: <a href="http://www.jollen.org/blog/2006/11/linux_link_tech_show_gplv3.html">Linux Link TEch Show 的訪談：理查史都曼談 GPLv3</a>

[1] Richard Stallman, http://en.wikipedia.org/wiki/Richard_Stallman
[2] Free Software Foundation, http://www.fsf.org/
[3] GPL, http://en.wikipedia.org/wiki/GNU_General_Public_License
[4] 自由軟體鑄造場『自由軟體法律研討會：嵌入式應用專題』, http://www.openfoundry.org/component/option,com_docman/Itemid,112/gid,230/task,cat_view/
[5] Richard Stallman, Why Upgrade to GPL Version 3, http://gplv3.fsf.org/rms-why.html]]></description>
         <link>https://www.jollen.org/blog/2008/04/foss_gplv3_business_embedded_systems.html</link>
         <guid>https://www.jollen.org/blog/2008/04/foss_gplv3_business_embedded_systems.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 30 Apr 2008 15:14:53 +0800</pubDate>
      </item>
            <item>
         <title>Qt 4.4 在 Neo1973 與 HTC Touch Cruise 上展示 iPhone-Like 介面</title>
         <description><![CDATA[前一篇日記「<a href="http://www.jollen.org/blog/2008/04/iphone_reconsider_embedded_design.html">iPhone 改變工程師設計嵌入式裝置的思惟</a>」提到 iPhone 在 UI 方面的卓越表現。稍早前，[<a href="http://labs.trolltech.com/blogs/">Trolltech Labs</a>] 發表一項新的實驗項目：新的 Qt 4.4 已經可以在 Windows Mobile 以及 Embedded Linux 二個平臺上執行了。

<img alt="neo1973_iphone_ui.png" src="http://www.jollen.org/blog/2008/04/24/neo1973_iphone_ui.png" width="629" height="426" />
(圖片來源：http://dist.trolltech.com/video/wince/qtembedded44video.html)

Trolltech Labs 提供一段 demo 影片，Windows Mobile 平臺使用 HTC Touch Cruise 手機，Embedded Linux 平臺則是使用 Openmoko 的 Neo1973 手機。不過，最引人注目的不是「Qt Everywhere」的表現。新的 Qt 在 UI 方面最令人驚豔的是它的「iPhone like graphics」。

我們都知道，未來的智慧型手機開發方法論，會是以使用者為導向的一個體系，包含如何讓應用程式之間更緊密地結合（coherent）以及如何提昇使用性（usability），因此 UI 將會是決定這個部份的關鍵。Nokia 在收購 Trolltech 公司後，在 UI 這一段看來已經有一些不錯的成果了。

]]></description>
         <link>https://www.jollen.org/blog/2008/04/qt_iphone_like_graphics.html</link>
         <guid>https://www.jollen.org/blog/2008/04/qt_iphone_like_graphics.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Thu, 24 Apr 2008 14:00:31 +0800</pubDate>
      </item>
            <item>
         <title>iPhone 改變工程師設計嵌入式裝置的思惟</title>
         <description><![CDATA[嵌入式系統發展的標準化平臺正在加速進行。嵌入式裝置的確和桌上型系統（desktop）很不一樣，iPhone 的成功展示了以使用者為中心（user-centered）的設計模式，Patrick Mannion 稱 iPhone 是一種軟體設計的工藝（feat）。<em>(*1)</em>

去年（2007年）十月份於 San Francisco 所舉辦的「Mobile 2.0」研討會上，討論了「Mobile 2.0」（例如：開發式手機平臺）的三大重要課題：user experience、usability 與 design。user-centered 設計模式即是一種收集使用者經驗，並透過使用者經驗工程，設計使用性（usability）更佳的操作介面（UI）。UI 的設計是使用性的重要一環，iPhone 的 UI 設計已經不用再多說了，使用性要佳，裝置必須更聰明（smarter）。應用程式之間是否能緊密地整合，是決定使用性良劣的另外一個重要的因素，「緊密整合」稱之為 coherence 而不是 integration。

Coherence 才能讓裝置更聰明，而不是 integration。

一般的嵌入式裝置都有多層的應用程式架構（layers），也有很多功能層，將許多不同的程式庫、軟體元件等整合在一起，稱之為「integration」，並不是 coherence。甚致，目前的嵌入式裝置雖然有複雜的多分層設計，但之中完全沒有緊密性（coherence）可言。

「iPhone 是一項偉大的創舉與成功，它全部都是軟體。它是一個開放標準（open-standard）的平臺、很可靠，並且有很好的 user interface。」<em>(*1)</em>

iPhone 是一個「以使用者為中心的設計典範」並且強力展示了「嵌入式軟體的設計工藝」。要把軟體設計得較複雜，很簡單！但要把軟體設計簡單化，就不容易了！這就是 iPhone 軟體工藝技術的表現。在莫耳定律的影響下，科技業無不卯足全力提升技術，並加速創新流程，但「Apple 則是很滿意他的慢步化表現」<em>（*2)</em>。

[1] iPhone nudging embedded design toward standard,  http://www.eetimes.com/showArticle.jhtml?articleID=207400109
[2] iPhone impacts CE design, http://www.macworld.co.uk/ipod-itunes/news/index.cfm?newsid=21035&pagtype=allchandate]]></description>
         <link>https://www.jollen.org/blog/2008/04/iphone_reconsider_embedded_design.html</link>
         <guid>https://www.jollen.org/blog/2008/04/iphone_reconsider_embedded_design.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Tue, 22 Apr 2008 23:45:59 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] 關於驅動程式的 private data 與可重覆進入函數</title>
         <description><![CDATA[今天進行 Linux 驅動程式的教育訓練課程，課堂中提到「多個 process (<em>/dev/debug[0..n]</em>) 同時 invoke 同一個驅動程式 (fops) 的架構觀念與程式設計」，我們也做了一個課堂練習。這是 Linux 驅動程式架構上，很重要的一個觀念。

<img alt="Private Data and Reentrant Function" src="http://www.jollen.org/blog/2008/04/20/private_data.png" width="512" height="371" />

如圖，當 P 與 Q 二個 process 同時在系統裡執行時，因為開啟的裝置檔不同，因此 kernel（VFS switch）會分別為二個裝置檔建立一個 <em>struct file</em> 的資料結構空間。因為二個裝置<!--Copyright (C) 2008 www.jollen.org. All rights reserved.-->檔的 major number 相同，因此如果 P/Q 同時（或非同時）執行 write system call 時，都會引用（invoke）到同一份程式碼（即圖上的 <em>xxx_write</em>）。

VFS Switch 在 callback <em>xxx_write</em> 時，便會將「正確的」<em>struct file</em> 結構傳給a write driver function，即圖中的 <em>filp</em> 指標。如此一來，<em>xxx_write</em> 函數<!--Copyright (C) 2008 www.jollen.org. All rights reserved.-->便能「重覆進入」：computation code 一份，但有二份獨立的 data （memory space）。

<em>struct file</em> 裡設計了一個稱為 <em>private_data</em> 的欄位，用來指向「私有資料」，即<!--Copyright (C) 2008 www.jollen.org. All rights reserved.-->保存個別 user process 狀態的資料結構。這是在 Linux 驅動程式中很常見的程式結構，當然也是一個「不可不懂」的觀念。`

<strong>延伸閱讀</strong>

* 2006.09.20: <a href="http://www.jollen.org/blog/2006/09/_reentrant_code_program.html">一篇有關 Reentrant Code Program (可重覆進入程式碼) 的文章</a>


]]></description>
         <link>https://www.jollen.org/blog/2008/04/private_data_and_reentrant_function.html</link>
         <guid>https://www.jollen.org/blog/2008/04/private_data_and_reentrant_function.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sun, 20 Apr 2008 17:09:20 +0800</pubDate>
      </item>
            <item>
         <title>開放手機：談中國市場的機會</title>
         <description><![CDATA[開放手機的新機會在中國。過去在日記「<a href="http://www.jollen.org/blog/2008/03/open_mobile_phone_linux.html">開放手機：Linux Mobile Phone</a>」與「<a href="http://www.jollen.org/blog/2008/03/eastern_world_open_source_culture.html">開放手機：談東方開源</a>」中提到：「拿掉開放手機這件事不說，中國手機市場，不管是低價或高階手機，都已經被國際大廠佔據，很難從中再找到發揮的空間。」、「中國白牌與黑牌手機的特殊市場，更把中國手機的市場空間壓縮得更小」。

這幾天，恰巧也有新聞指出：「大陸主流手機  台品牌廠全面撤退  難敵國際大廠  紛轉戰智慧型手機」。中國手機市場龐大，相當迷人，但進去走一遭卻發現這個市場的困難度。中國人口眾多，「一人買我一顆包子就夠了」形容中國市場「到處是錢潮」，可是手機廠在這裡卻履踢鐵板。解決策略是什麼？

以下就市場機會與開放平臺二個方向，發表一些個人想法。

<strong>市場機會</strong>

「透過開放平臺的Linux手機，在更低價手機端（高度一致性的軟硬體平臺），或是不同市場需求的高階手機市場，會是一個很好的機會。」幾天前在台大晶片中心所舉辦的「開放式手機平台論壇」中，主持人陳良基主任也提到「透過開放式手機進入中國的中低階手機市場，會是一個很好的機會」。

由於中國的手機市場已經是國際大廠的天下了，所以採取弱者策略應是中小型手機廠可參考的做法。怎麼樣的廠商叫中小型手機廠？如果不是在中國的這幾個國際大廠，當然就屬中小型廠商。幾百萬支的量，在中國也不算是大規模。以Openmoko來說，這本來就是一家小規模的手機公司，放到中國，只能以「微型企業」自居，所以行銷策略的目的並不是在搶市佔率，而是建立一個屬於自已的小天地。

高階中階低階手機，在這裡都會有機會。不過，「不同市場需求」的高階手機，會是比較容易的一個方向。所謂的不同市場需求的高階手機，就是「差異化的smartphone」。
<strong>
開放平臺</strong>

從事開放手機的工作，最好可以將「積極尋求open-source社群」協助列為主要的經營策略之一。透過open-source社群、集結眾人智慧、快速累積成果與經驗、建立使用者驗體管道、收集使用者經驗等，都是「開放手機平臺」的新革命。

開放手機是一門「使用者生產」的藝術， 如果只是將Android當作是一個「快速的」、「降低研發成本的」、「免費的」、「現成的」手機平臺，就會忽略掉開放平臺最重要的資源與最強大的武器，就會有一點點可惜囉。正因為社群是「協作」模式，所以有時會感覺到「生產過程缺乏組織與條理」，這個部份，肯定違背專業經理人的思考原則。社群發展原本就是比較「抽象」的藝術， 跟科技的「具象」思考不太一樣。有些時候，就要調整自已的想法，或是建構新的思惟體系，才能適應。畢竟，看似沒有條理的一些行動，最後都可能連結成一股強大的力量。

開放平臺的open-source ecosystem，能協助產品差異化的進行，並加速創新流程。不管是外來廠商，或是中國本土的手機廠，開放平臺都是一個很好的新機會。]]></description>
         <link>https://www.jollen.org/blog/2008/04/open_mobile_in_china_new_chance.html</link>
         <guid>https://www.jollen.org/blog/2008/04/open_mobile_in_china_new_chance.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Sat, 12 Apr 2008 13:36:13 +0800</pubDate>
      </item>
            <item>
         <title>「開放式手機平台論壇」會後手札</title>
         <description><![CDATA[臺灣大學系統晶片中心（System-on-Chip Center, National Taiwan University）今天舉辦「<a href="http://soc.ee.ntu.edu.tw/soc/download/forum_080408.jpg">開放式手機平臺論壇</a>」研討活動，此活動邀請產研界的多位先進，以談話方式針對開放式手機平臺進行看法分享與意見交換。

去年自從Google公開發表「Android」平臺與OHA聯盟現身後，開放手機平臺的概念開始被大量討論，並且受到極大的重視。今天的活動當然也是將主軸放在Android平臺上，由會議中的討論，很明顯地感受到各界對於開放手機概念的重視，許多人對於Android也有高度的期許，希望新概念的出現，能產生新的機會與商機。

已經感冒超過二個星期了，惱人的咳嗽久久不能停止，連講話都有點上氣不接下氣，不過還是很認真地在會場聆聽多位先進的看法。順手寫上一點手札，整理下來和大家分享。最後也加上一點個人看法，請參考指正。

「工研院資通所 林寶樹所長」提到，Android可以幫助廠商減少在軟體方面的投資。而台灣廠商面對未來的開放式手機新挑戰，將會是手機「外觀」與「界面」的議題。此外，開放手機平臺將能整合WiMAX並產生新的服務。林所長也提到，在open source community & project這裡，尚未找到成功的開放手機案例。

「台大資訊系 林風教授」是很資深的行動網路應用研究者，實務上也有很完整的經驗，林教授提到，在他過去的研究經驗中看到，「security」會是開放手機平臺一個重要的議題。「凌陽 林文昌副總」以技術角度分享了一些有趣的看法，林副總認為，從IC design house的角度來看，太多的open source軟體對他們來說也是一種負擔，porting的工作以及來自於客戶端的要求，經常成為沈重的負擔。

「資策會網多所 何寶中所長」表示，「Google的企圖是建立新的手機產業鍊」，何所長也針對台灣完整的產業鍊，提出一份「台灣版OHA對照表」，試著從台灣本土的廠商組織出「台灣版的OHA」，這是個有趣而且值得深思的看法。

「聯發科技 張志偉特助」以比較結論的方式發表看法，同時也大略介紹了聯發科技在手機的佈局，聯發科技在3G與smartphone上也會有些著墨，例如WiMAX phone以及DVB-H的roadmap。

前陣子，與Openmoko的CEO ‘Sean’討論了一些關於Linux手機的研發成本議題。開放手機採用Linux作業系統核心，不管是Openmoko或是Android都是採用2.6的Linux核心，但大家對於「Linux手機」卻有一種「美麗的誤會」，這是建立在「Linux是免費的」（正確來講是自由的、不是免費的）刻板映像上。但使用「開放」的手機平臺，是為了尋找新的機會，更明白來說，是為了建立或尋找新的商業模式（business model）。

「台大系統晶片中心 陳良基主任」一開始也提到「open source需要Q.C.」的技術，才能導入產品化。原因是，大多數的open source軟體都是玩家為了證明一些觀念所實作的程式碼，要將這麼多的open source project成果整合成一個平臺，在此平臺上開發應用程式，原本就不是一件簡單的事情，若要再將這個open source平臺整合到「裝置」上（產品），則需要更多的know-how與工程技術，才能讓軟硬體整合無間。

這些都是使用「Linux」來開發手機，需要付出的成本。

所以說，使用Linux並不是為了降低成本，事實上無形的工程成本是相當龐大的。過去，Openmoko也針對這個部份做了計算，若要將open source的成果整合成開放式手機平臺，並且生產手機，要付出的工程費用，單位是「億」。

使用Linux並不能讓你的研發成本降低。簡單來說，就像在玩樂高積木，你用積木堆出一台酷炫的機器人，但卻希望明天他就變成會飛的無敵鐵金鋼。玩具終究還是玩具，我們可以用樂高堆出「概念」，但要做出一台無敵鐵金鋼，這又是另當別論。

「嵌入式產業聯盟 盧功勳」會長也不約而同提到「Q.C.」是open source模式的重要一環，Q.C.是產品化的技術，也是軟硬體整合技術的領域。

Android的出現是一件相當好的事情，許多廠商能在這裡找到許多新商機，並且也能激發更多的創新服務。Google台灣工程研究所的簡立峰所長，是今天的神祕嘉賓，簡所長今天也以「非官方」身份提到，他認同「台灣是Android最佳的合作伙伴」這件事情。Android能帶出許多新的機會，這是與會先進都認同的想法。

「Works Systems」的CEO ‘Tom’就是由「服務商」的角度來分享開放手機平臺所帶來的新機會，例如透過Android開發’location-based’的服務。

會議上，Openmoko也分享了一些看法。開放手機除了「一個技術平臺」外，相對重要的元素還有「使用者經驗（user experience）」、「使用性（usability）」與「設計（design）」。使用性就是UI的設計，開放手機與smartphone最重要也是新的挑戰就是UI的設計，資策會網多所的何所長就提到「使用Android平臺必須還有一些差異化」才能找到新機會，「差異化」可由UI做起。

設計（design）這個環節在這次會議中，比較沒有被討論到，不過這卻是開放手機另一個重要的元素。工研院資通所林所長就明白提到，手機「外觀」會是開放手機一個重要的項目，也是廠商的新挑戰。這裡所指的設計，就是ID（工業設計）。Openmoko很久就體認到設計的重要性，因此做出了「開放設計」的大膽嘗試，在今年的二月份，Openmoko將其手機的機構設計原稿（CAD）以創用CC授權公開給社群使用，結果效果出奇地好，很快地就收到來自於社群的回饋。

開放手機是一個新觀念，也是一個新挑戰，不妨以新的腦袋來思考，除了「長知識」外，也增進工作樂趣。
<strong>
延伸閱讀</strong>

2008.02.14: <a href="http://www.jollen.org/blog/2008/02/openmoko_perspective_to_android.html">OpenMoko 對 Android 的「官方」看法</a>
2007.12.02: <a href="http://www.jollen.org/blog/2007/12/android_apache_license_not_gpl.html">Google Android 採用 Apache License: 為什麼不是 GPL？</a>
2007.11.09: <a href="http://www.jollen.org/blog/2007/11/android_gphone.html">Android 與 Gphone 觀察</a>
2007.11.08: <a href="http://www.jollen.org/blog/2007/11/abc_news_openmoko.html">ABC News 報導 OpenMoko</a>]]></description>
         <link>https://www.jollen.org/blog/2008/04/open_mobile_forum_notes.html</link>
         <guid>https://www.jollen.org/blog/2008/04/open_mobile_forum_notes.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Tue, 08 Apr 2008 23:56:21 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] 交叉編譯（cross compile）thttpd</title>
         <description><![CDATA[本週進行 root filesystem 相關的教育訓練，今天給的課堂練習是 thttpd 的交叉編譯（cross compile）。thttpd 採用標準的 GNU autoconf 來產生 Makefile，因此，交叉編譯 thttpd 的方式是蠻簡單的。配合課堂提供的 cross toolchain（gcc 3.4.1），我們先定義以下有關 cross toolchain 路徑檔檔名的 Makefile 變數：

<blockquote><pre>TOOL_TOP = /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu
CC = $(TOOL_TOP)/bin/arm-9tdmi-linux-gnu-gcc
AR = $(TOOL_TOP)/bin/arm-9tdmi-linux-gnu-ar
LD = $(TOOL_TOP)/bin/arm-9tdmi-linux-gnu-ld
AS = $(TOOL_TOP)/bin/arm-9tdmi-linux-gnu-as
STRIP = $(TOOL_TOP)/bin/arm-9tdmi-linux-gnu-strip
RANLIB = $(TOOL_TOP)/bin/arm-9tdmi-linux-gnu-ranlib
TARGET_ARCH = arm-linux</pre></blockquote>

接著再定義與 thttpd 的 'CFLAGS' 參數：

<blockquote><pre>THTTPD_CFLAGS = -Os -Wall -mtune=arm9tdmi -march=armv4 \
                -fomit-frame-pointer -fsigned-char -fPIC</pre></blockquote>

我們希望能有一個比較彈性以及系統化的做法，因此採用 Makefile 的系統來實作，不考慮編寫 script 的方式。接著，再定義一個 target，用來設定 thttpd 的 autoconf：

<blockquote><pre>configured-thttpd:
        (export CC=$(CC); \
        export AR="$(AR)"; \
        export AS=$(AS); \
        export LD=$(LD); \
        export STRIP=$(STRIP); \
        export RANLIB=$(RANLIB); \
        export CFLAGS="$(THTTPD_CFLAGS)"; \
        ./configure \
                --prefix=/ \
                --host=$(TARGET_ARCH) );</pre></blockquote>

我們想要用 'install' 指令來安裝 thttpd 執行檔到 root filesystem，因此 '--prefix' 參數的定義對我們講並不重要。Makefile 有良好的 dependencies 系統，所以我再撰寫一段 rule 如下：

<blockquote><pre>thttpd: configured-thttpd
        make</pre></blockquote>

接著，將以上的 Makefile 內容存檔，例如存成 thttpd.cross，再將這個檔案放到 thttpd 原始碼根目錄下，執行 make 並引用 thttpd.cross 的 'thttpd' target：

<blockquote>$ make -f thttpd.cross thttpd</blockquote>

一轉眼，我們得到 thttpd 的執行檔了。再將此 thttpd 執行檔加到 root filesystem 裡即可，別忘了，thttpd 需要幾個 shared library，把他們也加到 root filesystem 裡！還有，thttpd 會去讀取 user database（/etc/passwd 等），所以也要把 NSS 的 files 程式庫（libnss_files.so）加到 root filesystem 裡！

利用 Makefile 系統取代直接敲命令（或編寫 script）的麻煩做法，讓整個過程看起來簡單又清楚！]]></description>
         <link>https://www.jollen.org/blog/2008/03/cross_compile_thttpd.html</link>
         <guid>https://www.jollen.org/blog/2008/03/cross_compile_thttpd.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sun, 30 Mar 2008 10:54:26 +0800</pubDate>
      </item>
            <item>
         <title>開放手機：Linux Mobile Phone</title>
         <description><![CDATA[中國手機市場如此迷人，又該如何切入呢？開放手機平臺便是一個絕佳的機會。拿掉開放手機這件事不說，中國手機市場，不管是低價或高階手機，都已經被國際大廠佔據，很難從中再找到發揮的空間。此外，中國白牌與黑牌手機的特殊市場，更把中國手機的市場空間壓縮得更小，在中國，超過5000萬台的黑手機，消滅掉了一些中小型的手機廠的生存空間。

因此，透過開放平臺的Linux手機，在更低價手機端（高度一致性的軟硬體平臺），或是不同市場需求的高階手機市場，會是一個很好的機會。自從去年十一月份， Google 正式公開 Android 計畫後，「開放手機平臺（Open Mobile Platform）」的概念開始受到重視。幾個月下來，隨著媒體的報導，讓開放手機平臺概念的大量且持續的曝光，越來越多人在網路上討論這樣的概念，而真 正的引爆點則是 Android 原型機的現身。今年的 Mobile World Congress 展上出現了 Android 的原型機。

<strong>Linux手機的技術議題</strong>

Linux 作業系統在開放手機平臺佔有舉足輕重的角色，Android 的系統層使用 Linux 2.6 作業系統核心，OpenMoko 平臺也是採用 Linux 2.6 作業系統核心，另外一個重要的開放手機平臺 GMAE 也是基於 Linux 作業系統。

Linux kernel在技術端有幾個主要的議題，在北京的 Linux Developer Symposium上被提出討論。由於官方的Linux kernel更新速度相當頻繁，因此造成不同版本間的一些相容性問題。此外， Linux kernel的社群對Linux kernel的貢獻量已經到了一個很可怕的地步，因此還延伸出另外一個問題。許多patch的檢視與提交（commit）需要很長的時間，造成許多非官方的Linux kernel到處流竄。其它問題，包含在會議上幾個重要的Linux kernel開發者討論到的即時性與電源管理問題，以及本土化支援等。

<strong>Linux 開放手機的前景</strong>

開放手機的議題由Google的Android帶起全球性的熱烈討論，根據ABI Research的預測數據指出，在2012年以前（2007-2012），Linux手機將以每年超過75%的複合成長率成長，到2012年時，Linux手機在智慧型手機市場將佔有31%的市佔率，即大約3.31億支的Linux智慧型手機被賣出。

Linux手機未來也會受到微軟系統的正面挑戰，其中包括微軟指出，Linux侵犯了大約235項微軟的專利，這些都是未來開放手機的潛在挑戰。

《待續》]]></description>
         <link>https://www.jollen.org/blog/2008/03/open_mobile_phone_linux.html</link>
         <guid>https://www.jollen.org/blog/2008/03/open_mobile_phone_linux.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Sat, 22 Mar 2008 21:12:08 +0800</pubDate>
      </item>
            <item>
         <title>開放手機：談東方開源</title>
         <description>今年的2月19到20日，中國開源軟件推廣聯盟（COPU, China OSS Promotion Union）與Linux基金會（the Linux Foundation）在北京共同舉辦「Linux Developer Symposium（Linux開發者研討會）」。全球三大手機聯盟LiPS、LiMO與OhA都到場發表演說。LiPS在會中提到「中國目前已經是全球最大的手機市場了。」顯見未來中國在手機產業，不管是消費者端、技術端或是規格標準面，都扮演重要的角色。

在手機市場策略方面，鎖定中國市場會是很好的做法，但是若想要由龐大的中國市場分享利益（market share），恐怕並不是一件簡單的事情。

去年全球手機出貨量大約11億支，其中有5.5億支是被賣到中國，佔了將近一半的數量，這還不包括「無法統計」的部份。中國市場由於受「在地文化」的影響很深，因此外來的手機廠商比較難以切入中國市場。中國的開源軟件風氣也很興盛，但與西方的開源文化確有很大的不同。

中國的開源軟件文化主要是由國家單位以及軟件公司推動，再加上本地文化的影響，造就出一個中國自已的特殊開放源碼文化。中國很重視「本地化」這件事情，所謂的本地化，只做「中文化」是不夠的，心須是中國本土「製作」的才是本地化軟件。

許多公司想要運用開放源碼策略進軍中國，但總是吃閉門𡙡。根本原因在於「開放源碼這件事情在中國已經自成體系」，直接拿西方那套策略套用在中國是行不通的。西方的開源是由社群（community）驅動，由社群裡發展出商業模式；但在中國或是台灣，開源是直接拿取開源軟件進行商業用途，直接透過企業或聯盟驅動。

《待續》</description>
         <link>https://www.jollen.org/blog/2008/03/eastern_world_open_source_culture.html</link>
         <guid>https://www.jollen.org/blog/2008/03/eastern_world_open_source_culture.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Thu, 20 Mar 2008 11:34:07 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] nonblocking wait: try lock</title>
         <description><![CDATA[今天在進行 GNU Toolchains 與 Embedded Linux Programming 教育訓練課程時，提及以 shared memory 實作 IPC 時的同步問題。針對 unrelated process 的同步存取控制，一種古老的做法「locking」能簡單地應用在此同步問題上。

當寫入端做出 locking（如：lock file）時，讀取端便要等待 locking 被解除，因此這是一個 blocking wait 的架構。不過，若將「wait for unlucking」改成「try lock」，便能在中間的空閒時間「做點事情」，程式也不會晾著沒事做。

<img alt="lock_trylock.png" src="http://www.jollen.org/blog/2008/03/15/lock_trylock.png" width="423" height="266" />

一種簡單的程式架構，以「try lock」來做同步控制，讓程式閒著也要想辦法幹點活兒。另一個類似的觀念為 pthread semaphore 的 <em>sem_trywait()</em>。

<strong>延伸閱讀</strong>

2007.01.16: <a href="http://www.jollen.org/blog/2007/01/shared_memory_race_condition.html">Shared Memory 的 Race Condition</a>

]]></description>
         <link>https://www.jollen.org/blog/2008/03/nonblocking_wait_try_lock.html</link>
         <guid>https://www.jollen.org/blog/2008/03/nonblocking_wait_try_lock.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sat, 15 Mar 2008 18:13:17 +0800</pubDate>
      </item>
            <item>
         <title>Openmoko 開放 Neo 手機工業設計</title>
         <description><![CDATA[過去，手機的工業設計（Industrial Design）都是封閉的，設計原稿走不出深宮大院，設計師拿不到設計原稿，一般人也很難一探手機工業設計的原始樣貌。不過，現在事情不一樣了。在 open source 手機軟體平臺深耕許久的 Openmoko 今天正式發佈一則新聞「<a href="http://www.businesswire.com/portal/site/home/?newsLang=en&viewID=news_view_popup&epi-content=NEWS_VIEW_POPUP_TYPE&beanStrID=reportcenterndm&newsId=20080304005158">Openmoko Unlocks Neo Mobile Phone Industrial Design</a>」，Openmoko 以 ShareAlike Creative Commons （創用CC）授權開放 Neo 手機的工業設計原稿，讓設計師可以自由修改 Neo 工業設計。

創用CC不是一件新鮮事，但是將產品的工業設計原稿以創用CC授權對外公開，還是史上頭一遭。這次所公開的工業設計是 Neo1973 的設計，並提供 CAD 檔供下載 [<a href="http://downloads.openmoko.org/CAD/">http://downloads.openmoko.org/CAD/</a>]。

不過早在此新聞稿發佈的幾個禮拜前，Openmoko 早就已經將 CAD 檔公開在首頁上（openmoko.com），社群上的人也很快得到這個消息並下載 CAD 檔，其中也有大學教授，將 Neo 的 CAD 應用在實務教學上。在這則新聞稿發佈的幾天前，一位大學教授  Guillermo 也將他所設計的 Neo 概念機回饋給 Openmoko；Guillermo 教授也提到：

<blockquote>"I am amazed at the depth of your commitment to open design. This must be the first time in history that a company has opened its intellectual property to this extent. Openmoko's revolutionary posting of the CAD files gives a whole new generation of Industrial Design students incredible insight into how it's done as well as an opportunity to contribute with new concepts."</blockquote>

「這是人類歷史上第一次有公司將他們寶貴的智財開放」。]]></description>
         <link>https://www.jollen.org/blog/2008/03/openmoko_unlocks_neo_mobile_phone_industrial_design.html</link>
         <guid>https://www.jollen.org/blog/2008/03/openmoko_unlocks_neo_mobile_phone_industrial_design.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 05 Mar 2008 11:47:41 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的中斷處理, #3: Bottom Half 的觀念</title>
         <description><![CDATA[為了能寫出很棒的 interrupt handle，Linux 採用一種稱為 bottom half 的觀念來實作 interrupt handler。

<img alt="bottom_half.PNG" src="http://www.jollen.org/blog/2008/03/03/bottom_half.PNG" width="595" height="143" />

Linux 將完整的 interrupt handler 切成2個部份（half）：top half 與 bottom half。Top half 是在呼叫 <em>request_irq()</em> 時所指定的 interrupt handler 函數，bottom half 則是由 top half 所排程（scheduling），真正負責回應中斷的 task。

一般來說，top half 的基本實作原則如下：

1. 儲存裝置相關資料，這個部份會涉及「中斷不同步」的議題，在這裡先不做解釋。
2. 將 bottom half 排程後結束執行。

Top half 是真正接受中斷請求的 task，因此應避免執行過久。由 top half 的實作原則可以看出，top half 真正要做的工作其實只有排程 bottom half，因此執行的速度將會非常快。Top half 與 bottom half 的最大差別為，bottom half 在執行時，interrupt 是開啟的，因此 CPU 仍然可以接受中斷請求。

由此也可以看出另外一個 bottom half 機制的特點：當 bottom half 尚未結束執行時，top half 仍然可以處理中斷請求。另外，bottom half 就是 interrupt handler，因此「也視為」在 interrupt mode 下執行。]]></description>
         <link>https://www.jollen.org/blog/2008/03/interrupt_handling_bottom_half.html</link>
         <guid>https://www.jollen.org/blog/2008/03/interrupt_handling_bottom_half.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Mon, 03 Mar 2008 23:36:14 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的中斷處理, #2: 深入淺出中斷模式</title>
         <description><![CDATA[Interrupt handler 的工作是負責處理裝置的中斷請求，並將結果回報（feedback）給裝置。一般而言，裝置產生中斷時，都是與資料讀寫有關的請求。Interrupt handler 便要根據中斷的特性，來判斷此中斷是要請求驅動程式將資料寫入至裝置，或是由裝置讀取資料。

Linux 驅動程式的 interrupt handler 實作原則如下：

1. 在 interrupt handler 裡，叫醒真正負責此中斷的 task 後立即結束執行。
2. Interrupt handler 應儘量執行最少的程式碼。
3. Interrupt handler 若執行過久，會造成中斷的關閉時間過長，因此可能會遺失緊接著產生的中斷請求。
4. 若 interrupt handler 裡有過長的計算動作或執行過久的程式碼，則應使用 tasklet 或 task queue 將該段程式碼做排程，留待其它時間再執行。如此便可避免interrupt handler執行時間過久。

此外，在中斷模式下做同步控制時，還要考慮是否會佔用過久 CPU 時間的問題。為什麼中斷模式下寫 code 需要注意這個議題呢？請看以下的說明。

<strong>Counting Semaphore</strong>

在作業系統教科書中，提及二種 semaphore 的做法：

1. blocking counting semaphore，即 Linux 的 down/up 版本。
2. spinlock counting semaphore，即 Linux 的 spinlock。

Spinlock counting semaphore 屬於傳統作法，spinlock 讓 waiting 的動作（P operation）以 busy-loop 方式實作，因此會佔用 CPU 資源；blocking counting semaphore 則是將 waiting 的動作以 blocking（sleeping） 的方式來進行。

在Linux kernel 裡，blocking counting semaphore 採用 wait queue 來實作，即以 sleep 方式做P operation。Wait queue 機制對驅動程式來說，原始用意為實作 process（user-space）的 blocking read 與 blocking write，這樣的 behavior 機制經常出現在實作 process 讀取裝置資料的場合；當 process 進行 blocking I/O 讀寫（synchronous I/O）時，由於 kernel-space 是以 sleeping 方式進行等待，因此能把 CPU 交給其它人使用，並將 process 排程到 I/O queue（waiting queue），因此不獨佔或浪費 CPU 時間。

在中斷模式下，因為不能使用 sleeping 版本的 semaphore，所以必須使用 spinlock，在此情況下，便會遭遇到 interrupt handler 佔用 CPU 時間太久的問題。]]></description>
         <link>https://www.jollen.org/blog/2008/03/interrupt_handling_semaphore.html</link>
         <guid>https://www.jollen.org/blog/2008/03/interrupt_handling_semaphore.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 02 Mar 2008 22:09:11 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的中斷處理, #1: request_irq 基本觀念</title>
         <description><![CDATA[在Linux device driver 中，名為 “interrupt handler” 的 routine 負責處理（回應）實體的硬體中斷。當裝置中斷被觸發時，interrupt handler 便會執行，而 interrupt handler 就工作便是回應該中斷的請求（request）。

Interrupt handler 執行於 interrupt mode，並無 process context 資訊，因此，在 interrupt mode 下執行的執行碼需注意以下 3 點：

1. 由於沒有 process context 的關係，因此無法存取 user space。
2. 無法存取 <em>current</em> 巨集。<em>current</em> 巨集是一個指向自己的 kernel symbol。
3. 不能呼叫 scheduler 做排程，也不能做 sleeping waiting。由於 down/up 的 semaphore API 是 sleeping waiting 的版本，因此在 interrupt mode 必須改用 spinlock，spinlock 是以 busy waiting 的方式做 wait operation。

Linux device driver 安裝 interrupt handler 的方式是呼叫 <em>request_irq()</em> 函數，透過此函數來佔用 IRQ，並且安裝interrupt handler。

<strong>Request IRQ</strong>

request_irq() 用法如下：

<pre>int request_irq( unsigned int   irq,
                 void           (*handler)(int, void *, struct pt_regs *),
                 unsigned long  irqflags,
                 const char    *devname,
                 void          *dev_id);</pre>

irqflags 參數說明：

1.	SA_INTERRUPT
2.	SA_SHIRQ
3.	SA_SAMPLE_RANDOM

在 Linux 驅動程式的 framework 中，呼叫 <em>request_irq()</em> 安裝 interrupt handler 的位置為：

1.	<em>init_module()</em>
2.	<em>fops->open</em>

若是「共用中斷」（shared IRQ），只能在<em> fops->open</em> 裡呼叫 <em>request_irq()</em>。在 <em>fops->open</em> 裡請求 IRQ ，相對的也要在 <em>fops->release</em> 裡呼叫 <em>free_irq()</em> 將佔用的 IRQ 釋放。
]]></description>
         <link>https://www.jollen.org/blog/2008/03/interrupt_handling_1.html</link>
         <guid>https://www.jollen.org/blog/2008/03/interrupt_handling_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 01 Mar 2008 23:58:26 +0800</pubDate>
      </item>
            <item>
         <title>[科技資訊] 日本嵌入式系統技術協會（JASA）介紹</title>
         <description><![CDATA[嵌入式系統是目前熱門的科技之一，各地也都有相關的產業協會。日本是嵌入式技術的大國，特別是日本的機器人技術更是全球知名的項目。JASA（Japan Embedded Systems Technology Association ）是日本的嵌入式技術協會，可參考 JASA 官方網站的介紹 [<a href="http://www.jasa.or.jp/top/intro/information.html">JASA Introduction</a>]。由 JASA 的介紹發現，JASA 提供一個稱為「ETEC」（Embedded Technology Engineer Certification）的認證制度，以及一個年度的機器人大賽，可見日本在嵌入式技術產業上，有相當良好的組織運作。以下是 JASA 簡介的日中對照。

<strong>JASA 介紹</strong>

* 原文引用自 JASA 網站 [<a href="http://www.jasa.or.jp/top/intro/information.html">http://www.jasa.or.jp/top/intro/information.html</a>]。
* 日文翻譯及中文編修由 ViYi 提供。

JASAは、組込み業界の基盤を作るべく、以下のような事業を行なっています。
会員、業界の方々に、事業参加への門戸を広く開放していますので、是非、一緒に活動して下さるようお誘いします。

<blockquote>JASA乃是日本社團法人嵌入式系統技術協會(Japan Embedded Systems Technology Association )的英文簡稱 。JASA成立於1986年8月7日，總部設在東京都中央區，分布則有札幌、東京、名古屋、金沢、大阪、福岡。目前會員數包括正會員: 148家企業 以及贊助會員:35家企業。</blockquote>

事業案內

<blockquote>JASA主要的事業內容</blockquote>

<strong>1. 日本最大の組込み技術展示会 ET（Embedded Technology）ショー</strong>

毎年、秋（11月）に開催する、日本の組込み業界最大の展示会ETショーを主催しています。パシフィコ横浜にて開催し、毎年多くの来場者を集めています。
ET2007は、11月14～16日で開催し、盛況のうちに幕を閉じました。

<blockquote>日本最大的嵌入式技術展示會 ET SHOW (Embedded Technology）

每年11月在橫濱Pacafico Yokohama 舉行的日本業界嵌入式最大的展示會是由JASA所主辦的。而ET2007 也在2007年11月14 - 16日舉行完畢。</blockquote>

<strong>2. 西日本で唯一の組込み技術展示会 ETWest</strong>

2006年からマイドームおおさかにて、西日本地区では初めての組込み技術展示会を開催しています。当初の予想以上の出展社数や来場者数を得て、西日本地区での組込みシステム技術に対する関心度の高さが実証されました。
次回のETWest2008は会場をインテックス大阪に移し、6月5日（木）・6日（金）の開催となります。是非、ご来場ください。

<blockquote>西日本地區唯一的嵌入式技術展示會 ETWest

西日本地區首屆以及第二屆的嵌入式系統技術展示會均於2006年和2007年在大阪Mydome Osaka會場舉行。不論是參展廠商及觀展人數均大幅超出大會預期，也顯示了西日本地區對於嵌入式系統技術的高度關心與注目。今年(2008年)西日本嵌入式系統技術展示會將移到大阪Intex Osaka會場舉行。展出日期為2008/6/5(Thu) - 6/6(Fri)。</blockquote>

<strong>3. 組込み技術者試験制度「ETEC」(Embedded Technology Engineer Certification)</strong>

「質の高い教育と技術範囲の標準化指標を提供し、業界全体の活性化を図るべき」との方向性に基づいてJASAが実施する試験です。クラス２（エントリレベル）とクラス１（ミドルレベル）から構成され、受験者にはJASAから点数の証明書が発行されます。
<blockquote>
嵌入式技術工程師認證制度「ETEC」(Embedded Technology Engineer Certification)

JASA亦同時提供嵌入式技術工程師認證制度「ETEC」。這個認證制度主要目的在於「提供高教育的品質與標準化的技術範圍指標，同時希能促進嵌入式系統在業界的活躍度」。認證分為兩個等級，Class 2 初階  與 Class 1 中階，參加認證考試者將可得到JASA發行的證書。</blockquote>

<strong>4. ETソフトウェアデザインロボットコンテスト（ETロボコン）</strong>

組 込みソフトウェア分野における技術教育をテーマに、レゴブロックの車体で決められたコースを自律走行する競技です。同一のハードウェア（車体）のもと、 UML等で分析・設計したソフトウェアの技術を競うコンテストです。夏に行われる競技会のほか、ET会期中に展示会場内でチャンピオンシップ大会が開催さ れています
<blockquote>

ET軟體設計機器人比賽 (ET Robo )

ET軟體設計機器人比賽是以嵌入式軟體領域的技術教育為主題，且以樂高積木為車體，在既定走道上自動競速的一種比賽。並使用同一硬體(車體)，來進行UML等分析以及軟體設計的比賽。除了在夏季舉行的競技賽之外，在ET會期期間也會在展示會場內舉辦冠軍大賽。</blockquote>

<strong>5. 海外の組込み団体・会社との提携</strong>

JASA では、海外の組込み業界との連携・協力関係を推進するため、タイ、インドなどの海外団体を招聘したり、現地の会社を視察したり、積極的に海外交流を図っています。2006年11月15日には、台北市コンピュータ協会（TCA）とMOU協定を締結しました。海外への進出を考えておられる業界の方は、是非、 JASAにご相談下さい。
<blockquote>

與海外的嵌入式相關團體・企業之結盟提攜

JASA為促進提升與海外嵌入式業界的合作關係，除了邀請泰國，印度等海外嵌入式系統團體，前往當地廠商視察之外，也積極推動海外交流，因此在2006年11月15日JASA也與台北市電腦協會(TCA)締結MOU協定。</blockquote>

<strong>6. 組込み教育関連</strong>

組込み技術者育成のための教育を、IPA（SEC）の組込み技術者のスキルスタンダード（ETSS）にそって行なうための枠組み作り、スキル認定のための実証実験などを行なう準備を進めています。業界の有識者を集めてWGを構成し、検討を行なっています。

<blockquote>嵌入式系統技術教育相關

JASA也以嵌入式技術者的 Skill Standard(ETSS)為評量基準，建立培育嵌入式系統技術者(工程師)的基礎架構，並逐步進行Skill認定的實證實驗等相關準備階段。同時並網羅業界相關人士組成工作小組(WG)進行相關研究。</blockquote>

<strong>7. 組込み技術者教育</strong>

組込み技術者養成のための技術教育、新人教育を年に数回行なっています。企業内教育を補完するために、ご活用下さい。

<blockquote>嵌入式技術者教育養成

JASA提供嵌入式技術的專門技術教育以及新人入門教育。期能在企業內訓之外，發揮補強機能。</blockquote>


]]></description>
         <link>https://www.jollen.org/blog/2008/02/jasa_introduction.html</link>
         <guid>https://www.jollen.org/blog/2008/02/jasa_introduction.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 21 Feb 2008 16:45:20 +0800</pubDate>
      </item>
            <item>
         <title>開放手機平臺（Open Mobile Platform）的革命</title>
         <description><![CDATA[自從去年十一月份，Google 正式公開 Android 計畫後，「開放手機平臺（Open Mobile Platform）」的概念開始受到重視。幾個月下來，隨著媒體的報導，讓開放手機平臺概念的大量且持續的曝光，越來越多人在網路上討論這樣的概念，而真正的引爆點則是 Android 原型機的現身。今年的 Mobile World Congress 展上出現了 Android 的原型機，這裡有一些 Android 原型機的照片 [<a href="http://www.engadget.com/2008/02/11/google-attacks-android-at-mobile-world-congress/">Google attacks: Android at Mobile World Congress</a>]，如果您希望了解更多有關 Android 原型機的資訊，這篇也是入口文。

「開放手機平臺」（或精確來說「開放式智慧型手機作業系統與平臺」）在二零零八年正式引爆。[<a href="http://code.google.com/android/">Android</a>] 是 Google 所提出的開放手機軟體平臺，根據 [Open Handset Alliance]（OHA）的報導，Android 提供 mobile device 完整的軟體環境，包含：作業系統（Linux）、中介軟體（middleware）與主要的 mobile applications。Open Handset Alliance 是由 Google 登高一呼所成立的行動裝置開放平臺聯盟，聯盟成員包含電信業者、手機廠造商、手機軟體公司、半導體公司等，共計三十四個會員。

Linux 作業系統在開放手機平臺佔有舉足輕重的角色，Android 的系統層使用 Linux 2.6 作業系統核心，OpenMoko 平臺也是採用 Linux 2.6 作業系統核心，另外一個重要的開放手機平臺 GMAE 也是基於 Linux 作業系統。這次不只一家廠商在 Mobile World Congress 上展示 Android 原型機，其中有一款是採用 TI OMAP 3430 處理器（執行速度 500 MHz），這款原型機平臺，即將在二個月內對開發者（developer）販售，這是一個令人興奮的好消息！

另外還有二個知名的商業導向開放手機平臺，分別是 OpenMoko 與 Trolltech Qtopia。Trolltech 公司日前被 Nokia 以1.5 億美金的代價收購，Trolltech 是一家知名的跨平臺 GUI 軟體製造商，同時也在 Linux 手機領域中享有盛名。Trolltech 的 Qtopia 產品是專門針對行動與嵌入式裝置所開發的行動裝置平臺，由於 Trolltech 採取雙授權的商業模式，因此開發者也能取得以 GPLv2 授權的 Qtopia 原始碼。

開放手機的概念出現後，宣告「Mobile 2.0」時代的來臨。去年（二零零七年）十月十五日於 San Francisco 所舉行的「 Mobile 2.0」研討上，討論了幾個重要的 Mobile 2.0 革命性新觀念：

1. 使用者參與開發（developing）與設計（design）
2. 使用者經驗（user experience）的回饋
3. 資料（data）傳輸
4. 相異於過去的商業模式

此外，Mobile 2.0 與「網路服務（web service）」的整合也將會是相當重要的項目。早在二年前（二零零六年），Nokia 就公佈一種稱為 WidSets 的手機元件平臺，「<a href="http://www.widsets.com">WidSets</a>」是一種手機資料傳輸的新技術，透過 Widsets 上的元件，我們能將 web 上的  email、blog、RSS、video 等內容傳送到手機上，並在手機上閱讀。目前 Widsets 網站上提供超過 2000 種不同的元件，並且支持超過 300 支不同型號手機。

Android 平臺於二月十三日釋出 RC14，這次的釋出版本裡包含最新的 Linux kernel（linux 2.6.23）以及 Android Emulator 原始碼，其它 Android 平臺的原始碼還包含：Android 開發工具原始碼（Eclipse plugin）以及 Webkit 原始碼，程式可由 [<a href="http://code.google.com/p/android/downloads/list">Android - Google Code</a>] 下載。]]></description>
         <link>https://www.jollen.org/blog/2008/02/open_smartphone_mobile_platform.html</link>
         <guid>https://www.jollen.org/blog/2008/02/open_smartphone_mobile_platform.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Tue, 19 Feb 2008 23:14:00 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 對 Android 的「官方」看法</title>
         <description><![CDATA[OpenMoko 的 project lead 'Sean Moss-Pultz' 今天接受專訪時，正式提出他對開放手機以及對其他競爭對手的看法。其中，關於 Android 的出現對同樣也是開放平臺的 OpenMoko 有何影響，以及對 OpenMoko 會有什麼衝擊，Sean 今天也都提出他的看法。

首先，對於開放手機這件事情來說，Sean 認為這是不相衝突的二件事情，其觀念在於：一、OpenMoko 本身是一家做「產品」的公司；Google 的 Android 是提供「平臺」的方案。二、OpenMoko 想要做的是 100% 開放源碼的手機平臺，並透過開放平臺建立可獲利的商業模式；但 OHA 旨在發掘商業機會，並不是專注在製作一個 100% 開放源碼的手機軟體平臺。總合來說，Sean 提到「OpenMoko 與 Android 是二個商業模式、二個不相干的東西」。

此外，專訪過程也問到一個根本的問題「OpenMoko 與 Android 軟體的比較與差異」，這是一個技術面的問題，早在一月二十二日，由 Wolfgang Spraul（OpenMoko 工程部門副總）對內部所發出的一封 email 提到「So we are trying to position OpenMoko as a superset of Android. Android is just a GUI toolkit.」，意即「就技術面而言，我們可以讓 Android 在 OpenMoko 平臺上執行」，這是 OpenMoko 對 Android 的官方看法。Wolfgang 在他的 email 提到，這項看法並不是 OpenMoko 的內部機密。今天透過專訪由 Sean 本人親口提出這項看法，因此終於可以正式公諸於世。

從技術面來看，Sean 也提到「OpenMoko 是一個平臺，由底層 kernel 到上層 application 都可以被修改」，但「Android 只是一個 GUI toolkit」，因此，若由這個角度來看，Sean 也說「OpenMoko 與 Android 是沒有衝突的二個技術平臺」。

<strong>延伸閱讀</strong>

* 2008.01.09: <a href="http://www.jollen.org/blog/2008/01/first_android_phone.html">First Android Phone？</a>
* 2007.12.02: <a href="http://www.jollen.org/blog/2007/12/android_apache_license_not_gpl.html">Google Android 採用 Apache License: 為什麼不是 GPL？</a>
* 2007.11.09: <a href="http://www.jollen.org/blog/2007/11/android_gphone.html">Android 與 Gphone 觀察</a>]]></description>
         <link>https://www.jollen.org/blog/2008/02/openmoko_perspective_to_android.html</link>
         <guid>https://www.jollen.org/blog/2008/02/openmoko_perspective_to_android.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Thu, 14 Feb 2008 17:09:22 +0800</pubDate>
      </item>
            <item>
         <title>MontaVista Mobilinux 5.0 入圍 EDN 年度創新獎</title>
         <description><![CDATA[MontaVista 的 Mobilinux 5.0 入圍 [<a href="http://www.edn.com">EDN（Electronics Design, Strategy, News）</a>] 舉辦的 [<a href="http://www.edn.com/info/CA6522717.html?industryid=48661">第 18 屆創新獎（Innovation Award）</a>]。第 18 屆 EDN 創新獎共分為 20 個類別，Mobilinux 入圍的是軟體類，除了 Mobilinux 外，另外三個軟體類的入圍名單如下：

 * Graphics library (Microchip Technology)
 * LabView 8.5 (National Instruments)
 * Robotics Studio (Microsoft)

MontaVista 指出，目前有 90% 的 Linux smartphone 都是採用 MontaVista Linux Professional Edition。最後的年度創新獎得主將在四月十四日公佈。此外，MontaVista 也將在 2/11~2/14 於西班牙所舉辦的「<a href="http://www.mobileworldcongress.com/homepage.htm">GSMA Mobile World Congress</a>」展覽上參與展示。

GSMA Mobile World Congress 是全球行動通訊業的年度大拜拜，這是全球最大的行動產業展覽，現場將會集各大手機製造商、電信業者、以及內容供應商等等，各路人馬都會在這裡展示最新的產品以及概念。這是 CES 後另一個重要的科技展，GSMA Mobile World Congress 滿足單點購足的需求，走一趟就能網羅全球行動通訊產業的重要資訊，是一場不可錯過的展覽。

MontaVista 的產品能入圍 EDN 年度創新獎，並參與全球最大行動通訊業的展覽，足以表示 MontaVista 在 Linux smartphone 領域上的代表地位，同時，我們也看見開放源碼的 Linux 手機軟體走入了行動與電信業的生態系裡。從 MontaVista 的活動軌跡來看， Mobilinux 走的方向是「正規戰」，也就是由「聯盟」、「標準」以及「供應商」的角度前進；與 OpenMoko 比較的話，OpenMoko 本身並不是從這三個角度切入，而是由社群以及個人用戶的角度切入，與正規戰相較，OpenMoko 比較像是「街頭賣藝」的模式。

<strong>延伸閱讀</strong>

2006.11.16: <a href="http://www.jollen.org/blog/2006/11/montavista_dev_rock_5_linux_id.html">MontaVista 推出 Dev Rock 5 嵌入式 Linux 開發工具之《殺手級 IDE 快快出現！》</a>]]></description>
         <link>https://www.jollen.org/blog/2008/02/montavista_edn_innovation_award.html</link>
         <guid>https://www.jollen.org/blog/2008/02/montavista_edn_innovation_award.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 07 Feb 2008 13:47:58 +0800</pubDate>
      </item>
            <item>
         <title>Nokia 收購 Trolltech</title>
         <description><![CDATA[今天來自 linuxdevices.com 上的一則消息 [<a href="http://linuxdevices.com/news/NS8066582994.html">Nokia to acquire Trolltech for $150 million</a>] 表示，Nokia 將以 1.5 億美金的代價收購 [<a href="http://trolltech.com/">Trolltech</a>] 公司。Trolltech 是一家知名的跨平臺 GUI 軟體製造商，同時也在 Linux 手機領域中享有盛名。

Symbian 是一家獨立的手機軟體公司，Nokia 擁有將近五成（47.9%）的持股，Nokia 過去大多以 [<a href="http://www.symbian.com">symbian</a>] 作業系統來製造手機，近年來也有少量使用 Linux 的產品問世。Trolltech 的 Qtopia 產品是專門針對行動與嵌入式裝置所開發的 application platform 與 UI，Qtopia 能夠支援 Linux 與 Windows CE 平臺，並且能讓製造廠完整客製化 UI。

Nokia 在收購 Trolltech 後，將會持續開發 Trolltech 的產品線，並且也會持續保持授權的商業模式。原有的手機製造商，以及有意願採用 Qtopia 的新客戶，都能向 Trolltech 取得商業授權。此外，可以預料的是，Qt 以及 Qtopia 也將能支援 Symbian 平臺，未來搭載 Symbian 作業系統的智慧型手機，都會有更棒的 UI。

Nokia 在行動裝置軟體以及桌面應用軟體採取跨平臺的策略，在收購 Trolltech 後，將可以加速此項策略的推行；同時，可以預期 Nokia 將會製造更多 Linux 平臺的手機。過去 Nokia 在 Linux 平臺的努力有目共睹，[<a href="http://maemo.org/">maemo</a>] 專案以 Linux 以及 GTK+ 打造的 maemo SDK 已成為知名的 Linux 開放源碼專案，maemo 的開發者社群也相當熱絡，Nokia 的 [<a href="http://www.nseries.com/products/n800/#l=products,n800">N800</a>] 就是採用 maemo 平臺所開發的產品 。]]></description>
         <link>https://www.jollen.org/blog/2008/01/nokia_acquire_trolltech.html</link>
         <guid>https://www.jollen.org/blog/2008/01/nokia_acquire_trolltech.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Tue, 29 Jan 2008 20:16:57 +0800</pubDate>
      </item>
            <item>
         <title>libusb 簡介與第一個範例</title>
         <description><![CDATA[<p>[<a href="http://libusb.sourceforge.net/">libusb</a>] 是一個 user-space 的 USB 程式庫，在 embedded linux 應用實作上，我們會使用 libusb 實作一個 host  端的應用程式，並透過 USB 介面存取或控制 target device。</p>

<strong>找到所有 USB bus 與 USB device</strong>

<p>要撰寫一支 user-space 的 USB device 控制程式，最首要的工作就是找到自己的 USB 裝置。這個工作主要透過以下 4 個函數來進行：</p>

<ul>
<li>usb_init: 初始化 libusb</li>
<li>usb_find_busses: 尋找系統裡的所有 USB bus</li>
<li>usb_find_devices: 尋找所有的 USB bus 上的所有 USB device</li>
<li>usb_get_busses: 傳回找到的 USB bus</li>
</ul>

<p>程式一開始必須先呼叫 usb_init 將 libusb 做初始化，這是 libusb 程式第一個要呼叫的函數。接著依序呼叫 usb_find_busses 與 usb_find_devices 找到所有的 USB bus 與 USB device。</p>

<p>最後，呼叫 usb_get_busses 取得所有找到的 USB bus。程式如下：</p>

<pre>
    usb_init();
    usb_find_busses();
    usb_find_devices();

    struct usb_bus *busses;
    busses = usb_get_busses();
</pre>

<p>*busses 指向一個串列資料結構（struct usb_bus），訪問這個串列即可找到所有的 USB 裝置。</p>

<strong>偵測 USB 裝置</strong>

<p>接下來，我們要在所有的 USB bus 上偵測系統是否有我們的 USB device。以下是這段程式的框架：</p>

<pre>
    struct usb_bus *bus;
    for (bus = busses; bus; bus = bus->next) {
	    struct usb_device *dev;

	    for (dev = bus->devices; dev; dev = dev->next) {
               struct usb_device_descriptor *desc;

               desc = &(dev->descriptor);
               printf("Vendor/Product ID: %04x:%04x\n", desc->idVendor, desc->idProduct);
	    }
    }
</pre>

<p>這段演算法可以將系統上所有 USB device 的 vendor id 與 product id 印出來。假如，我們想找到特定的 USB 裝置，這時就可以設計一個這樣的函數（假設我們想要找的 USB 裝置是 0xffff/0x0001）：</p>

<pre>
struct usb_device *usbio_probe()
{
    struct usb_bus *busses, *bus;
    int c, i, a;

    usb_init();
    usb_find_busses();
    usb_find_devices();

    busses = usb_get_busses();

    for (bus = busses; bus; bus = bus->next) {
	struct usb_device *dev;

	for (dev = bus->devices; dev; dev = dev->next) {
	    struct usb_device_descriptor *desc;

	    desc = &(dev->descriptor);
	    printf("Vendor/Product ID: %04x:%04x\n", desc->idVendor,
		   desc->idProduct);
	    if ((desc->idVendor == 0xffff) && (desc->idProduct == 0x0001)) {
		return dev;
	    }
	}
    }

    return NULL;
}
</pre>

<strong>開始操作 USB 裝置</strong>

<p>找到我們的 USB device 後，接著就可以對它進行操作（讀/寫）。如同操作檔案般，我們要先對 USB device 做開啟的動作，方式是呼叫 usb_open 函數：</p>

<blockquote>usb_dev_handle *usb_open(struct *usb_device dev);</blockquote>

<p>開啟成功後，會傳回此裝置的 <strong>handle</strong>。</p>

<strong>第一個 libusb 程式</strong>

<p>以下提供學習 libusb 的第一個範例程式：</p>

<pre>
/*
 * findusb.c - A libusb sample program.
 *
 *  Authored by Jollen Chen <jollen@jollen.org>
 *
 *  Copyright (C) 2008 www.jollen.org
 *  Copyright (C) 2008 Jollen's Consulting, Inc.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Public License as published by
 *  the Free Software Foundation; version 2 of the license.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser Public License for more details.
 *
 */

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;usb.h&gt;
#include &lt;libusb.h&gt;

void usbio_main(struct usb_device *dev)
{
    usb_dev_handle *dev_handle;

    dev_handle = usb_open(dev);

    if (dev_handle == NULL) {
	printf("USB IO open failed.\n");
	return;
    }

    usb_close(dev_handle);
}

struct usb_device *usbio_probe()
{
    struct usb_bus *busses, *bus;
    int c, i, a;

    usb_init();
    usb_find_busses();
    usb_find_devices();

    busses = usb_get_busses();

    for (bus = busses; bus; bus = bus->next) {
	struct usb_device *dev;

	for (dev = bus->devices; dev; dev = dev->next) {
	    struct usb_device_descriptor *desc;

	    desc = &(dev->descriptor);
	    printf("Vendor/Product ID: %04x:%04x\n", desc->idVendor,
		   desc->idProduct);
	    if ((desc->idVendor == 0xffff) && (desc->idProduct == 0x0001)) {
		return dev;
	    }
	}
    }

    return NULL;
}

int main()
{
    struct usb_device *dev;
    struct usb_device_descriptor *desc;

    dev = usbio_probe();
    desc = &(dev->descriptor);

    if (dev == NULL) {
	printf("USB IO Card not found.\n");
	return -1;
    }

    printf("SUB IO Card found.\n");
    printf("Vendor/Product ID: %04x:%04x\n", desc->idVendor,
	   desc->idProduct);

    usbio_main(dev);
}
</pre>

<p>檔案可由 [<a href="http://tw.jollen.org/findusb/">http://tw.jollen.org/findusb/</a>] 下載。這裡提供一個 Makefile 檔案；編譯時，請記得安裝 libusb 並與 libusb 做連結。</p>

<strong>網路資源</strong>

<p>* libusb API 文件. <a href="http://libusb.sourceforge.net/doc/">http://libusb.sourceforge.net/doc/</a></p>]]></description>
         <link>https://www.jollen.org/blog/2008/01/libusb_hello_world.html</link>
         <guid>https://www.jollen.org/blog/2008/01/libusb_hello_world.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Fri, 25 Jan 2008 15:49:43 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 近況更新：Neo FreeRunner、Job Positions 與 Education</title>
         <description><![CDATA[自從先前分享了 OpenMoko 的 OpenLab 活動後，己經很久沒有再跟大家更新相關消息了。在這裡一次將 OpenMoko 的近況做更新。

<strong>Neo FreeRunner</strong>

OpenMoko 在今年的 [<a href="http://www.cesweb.org/">CES 2008</a>]（美國消費性電子大展）上，正式揭露新一代產品 Neo FreeRunner。新的 Neo FreeRunner 的對象是終端消費者，並且在硬體端加入了 WiFi、motion sensor 以及 3D 處理器。Neo FreeRunner 的軟體也是基於 OpenMoko，不過以現在的軟體狀況來看，要面對真正的消費者，還需要一些時間。

<strong>OpenMoko Open Job Positions</strong>

在臺灣的朋友有福了，OpenMoko 正式提供工作機會，詳情可參考 [<a href="http://wiki.openmoko.org/wiki/OpenMoko_Visits_and_Hiring_Day/zh_tw">OpenMoko Visits and Hiring Day</a>]。這是 OpenMoko 首次提供公開的工作機會，過去 OpenMoko 的開發者都是在 open source 社群裡找來的高手，或是透過朋友介紹。

<strong>OpenMoko Education</strong>

OpenMoko 因為開放源碼的特性，因此也很適合應用在教學或研究用途。OpenMoko 將在下學期支持幾個學校的研究計畫，並提供學生實習機會，讓學生也能在學校進行「手機軟體」的研究計畫。

<strong>延伸閱讀 </strong>

2007.11.21: <a href="http://www.jollen.org/blog/2007/11/openmoko_openlab_intro_v1.html">OpenMoko 專案介紹與 OpenLab</a>]]></description>
         <link>https://www.jollen.org/blog/2008/01/openmoko_fresh_news_2008.html</link>
         <guid>https://www.jollen.org/blog/2008/01/openmoko_fresh_news_2008.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Mon, 21 Jan 2008 11:48:55 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的 Semaphore 觀念小談</title>
         <description><![CDATA[這二天在進行 Linux 驅動程式的訓練，課程裡談論到 semaphore 可以用來宣告 critical section。在作業系統所述敘的 critical section 觀念中提及，critical section 具備互斥性（mutual exclusive）與單一性（atomic）。對單一性來講，我們必須確保在 critical section 裡不會發生任何的排程（scheduling）動作，wait queue 的使用就是一個例子。Linux kernel 提供的 wait queue 可以讓驅動程式在進行 I/O polling 時，以睡覺（sleep）方式進行，以讓出系統時間；若以 busy-loop 方式進行，是不正確的做法。

所以，sleep 的動作就不能寫在 critical section 裡面，必須先做一個釋放的動作，再呼叫 wait queue 的 API 做睡覺的動作，醒來後再進入 critical section。但是，事情並沒有這麼單純，若是驅動程式的架構設計，能支援多個 minor device，就必須考慮可重覆進入的問題，這個時候，semaphore 的信號燈（semaphore variable）就要放在私有空間裡，Linux 驅動程式用 <em>struct file</em> 裡的 <em>private_data</em> 欄位來實作。對 wait queue 來講，我們也是將 wait queue 變數擺放在私有空間裡。

從互斥性的角度來講，semaphore 的使用可能是要用來保護資料，以避免資源衝突，因此將程式碼互斥。只是，kernel 所提供的睡覺 API，在真正重做排程前，還有對傳入的參數做存取，而這個參數就是擺在私有空間裡的 wait queue，於是便可能仍有同步（race condition）的問題。
]]></description>
         <link>https://www.jollen.org/blog/2008/01/linux_device_driver_semaphore_discussion.html</link>
         <guid>https://www.jollen.org/blog/2008/01/linux_device_driver_semaphore_discussion.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 20 Jan 2008 10:53:22 +0800</pubDate>
      </item>
            <item>
         <title>下週二的嵌入式系統大拜拜：DTF 2008 Embedded World</title>
         <description><![CDATA[下週二即將舉行的 DTF 2008 Embedded World 是嵌入式系統年度的大拜拜，當天的議程相當精采，還沒報名的朋友要趕快把握機會喔！全程都是免費參加，有興趣的朋友可參考 [<a href="http://edu.jollen.org/2008/01/dtf_2008_embedded_world.html">詳細的活動說明</a>]。

當天的議程之一是「嵌入式系統發展機會－我們能做什麼?」，這是由教育部嵌入式軟體聯盟召集人金仲達教授所帶來的演說。[<a href="http://esw.cs.nthu.edu.tw/">嵌入式軟體聯盟</a>] 多年來致力於規劃嵌入式系統課程，在校園培育許多嵌入式軟體的人才。金教授的演說我想會由教育層面切入，這是一個了解目前嵌入式系統在校園紮根狀況的很好機會。

x86 架構近來在嵌入式系統應用佔用非常重要的角色，Track I 的議題針對 x86 嵌入式解決方案做介紹，也是很不錯的議題規劃。Graphics solution 一直是嵌入式系統應用的關鍵技術，許多應用都需要 graphics 的解決方案。

Track II 的議題偏向 SOC 的設計，我自己對「Natural Evolution in the embedded world」這個議程相當感興趣，這是由 ARM 公司行銷部門的經理 Tom Wang 所帶來的演說，ARM 一直是嵌入式 SOC 的領導者，這場演說必定可以吸收到許多新觀念。

Track III 的議題則是著重在嵌入式應用與服務，觸控面板在嵌入式的應用佔用重要地位，圖形化設計也是我自己很感興趣的主題。]]></description>
         <link>https://www.jollen.org/blog/2008/01/go_to_dtf_2008.html</link>
         <guid>https://www.jollen.org/blog/2008/01/go_to_dtf_2008.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 17 Jan 2008 13:29:22 +0800</pubDate>
      </item>
            <item>
         <title>First Android Phone？</title>
         <description><![CDATA[來自台灣的 [<a href="http://www.wneweb.com.tw/chinese/index.htm">啟碁科技（WNC）</a>] 在 CES 上展示一支 GSM/VoWiFi 的雙模手機，並且是採用 Linux 作業系統核心，同時，也有小道消息指出，Google Android 平臺也會移植到該手機，並且可能的時間是在今年三月，若消息屬於，WNC 將會是第一個發佈 Android 相容手機的廠商。新聞全文 [<a href="http://www.linuxdevices.com/news/NS6697265909.html">First Android phone?</a>]。

同一時間，另一個也是開放手機平臺的 OpenMoko 也公佈 Dash Express 的消息，以及 Neo FreeRunner（GTA02）的產品規格。單純以「開放手機系統」來講的話，雖然這二個被廣為注目的開放手機產品有機會同時現身，但嚴格來講，Google Android 平臺似乎己經佔了上風，因為目前尚沒有其他廠商能夠利用 OpenMoko 平臺來生產 end-user 手機，但 Android 己經快做到了。

<strong>延伸閱讀</strong>

2007.11.09: <a href="http://www.jollen.org/blog/2007/11/android_gphone.html">Android 與 Gphone 觀察</a>
2007.12.02: <a href="http://www.jollen.org/blog/2007/12/android_apache_license_not_gpl.html">Google Android 採用 Apache License: 為什麼不是 GPL？</a>]]></description>
         <link>https://www.jollen.org/blog/2008/01/first_android_phone.html</link>
         <guid>https://www.jollen.org/blog/2008/01/first_android_phone.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Wed, 09 Jan 2008 12:14:25 +0800</pubDate>
      </item>
            <item>
         <title>1/8 tossug 分享活動：用中文寫 Python / 周蠎</title>
         <description><![CDATA[今天 [<a href="http://tossug.org">tossug</a>] 的分享主題是 [<a href="http://tossug.org/pipermail/hojia/2008-January/000162.html">使用 Python 與周蟒</a>]，周蠎（zhpy）是一個可以執行中文 Python 程式的軟體，周蠎讓我們可以用中文來寫 Python 程式，真是一個有趣的主題。zhpy 的下載位址是：

<blockquote><a href="http://code.google.com/p/zhpy/">http://code.google.com/p/zhpy/</a></blockquote>

現場 gasolin 展示了一個用 Python 設計的文字遊戲編輯引擎，叫做 [<a href="http://www.renpy.org/wiki/renpy/Home_Page">Ren'Py</a>]，這是一個可以製作類似角色扮演遊戲的編輯器。

最後 gasolin 也提到，Python 是去年（2007）成長速度最快的程式語言，也是目前相當受到重視的程式語言。網路上常見的「懶人包」就是利用 Python 撰寫的工具製作的，最近 Google 也在招募 Python 高手，顯見 Python 真的是一個重要的程式語言。]]></description>
         <link>https://www.jollen.org/blog/2008/01/zhpy_python.html</link>
         <guid>https://www.jollen.org/blog/2008/01/zhpy_python.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Tue, 08 Jan 2008 21:13:46 +0800</pubDate>
      </item>
            <item>
         <title>2008 開工了！</title>
         <description><![CDATA[新的 2008 年，「Jollen's Blog」會把重點放在「Linux kernel」與「Linux device driver」，並且也會將一些時間放在 Embedded Linux 教育訓練的課程規劃上；期待您的意見與指教。

<img src="http://tw.jollen.org/photos/2008_101_fireworks/2008_101_fireworks_01.jpg" />

<img src="http://tw.jollen.org/photos/2008_101_fireworks/2008_101_fireworks_02.jpg" />

<img src="http://tw.jollen.org/photos/2008_101_fireworks/2008_101_fireworks_03.jpg" />

<img src="http://tw.jollen.org/photos/2008_101_fireworks/2008_101_fireworks_04.jpg" />

<img src="http://tw.jollen.org/photos/2008_101_fireworks/2008_101_fireworks_05.jpg" />

<strong>延伸閱讀</strong>

2007.01.01: <a href="http://www.jollen.org/blog/2007/01/2007_new_year.html">2007 開工了！</a>]]></description>
         <link>https://www.jollen.org/blog/2008/01/2008_101_fireworks.html</link>
         <guid>https://www.jollen.org/blog/2008/01/2008_101_fireworks.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Tue, 01 Jan 2008 03:51:20 +0800</pubDate>
      </item>
            <item>
         <title>qemu + Linux kernel 模擬與除錯環境實習</title>
         <description><![CDATA[Qemu 是一個功能強大的「processor emulator」，qemu system emulator 還能模擬開發板的週邊。此外，qemu 還包含一個 gdb server 的實作，配合 gdb client 能組合出一個很棒的 kernel & device driver「source-level debug」環境。

Jollen-Kit! Pro. 是由 jollen.org 所推出的 ARM9 開發板，主要用途是拿來做 Embedded Linux 的教育訓練。在前一陣子的 Linux Device Driver 訓練課程中，特別規劃了一個時段的「qemu + Linux kernel 模擬與除錯環境實習」的操作課程，此課程所採用的 qemu 能模擬我們的 Jollen-Kit! Pro. 開發板，同時也介紹如何設定 breakpoint 以進行 kernel debug。

在此提供實習簡報電子檔 [<a href="http://tw.jollen.org/slides/qemu_jk2410_cgdb.pdf">qemu_jk2410_cgdb</a>] 供下載。由於這是帶領操作的課程，簡報內容可能帶不到一些細節，但主要的內容大多能帶出，還請見諒。本月即將於台北開班的 [<a href="http://www.jollen.org/consulting/training.html">Linux Device Driver 課程</a>]，也會介紹 qemu 除錯環境的安裝與操作指導。

若台北教室時段許可，或許可以提供免費的 seminar 課程。

<strong>延伸閱讀</strong>

2007.04.19: <a href="http://www.jollen.org/blog/2007/04/qemu_hw_emulation_how.html">Qemu 模擬週邊的兩三事</a>
2007.04.18: <a href="http://www.jollen.org/blog/2007/04/cpustate_qemu_gdbserver.html">再聊 CPUState、qemu 的 gdbserver</a>
2007.04.11: <a href="http://www.jollen.org/blog/2007/04/qemu_cpustate_object.html">小聊 qemu 的 CPUState</a>
2007.04.08: <a href="http://www.jollen.org/blog/2007/04/qemu_neo1973_openmoko_jk2410.html">qemu-neo1973 / openmoko-emulator / jk2410-emulator</a>
2006.09.28: <a href="http://www.jollen.org/blog/2006/09/qemu.html">QEMU 虛擬機器</a>
]]></description>
         <link>https://www.jollen.org/blog/2007/12/qemu_linux_kernel_debug.html</link>
         <guid>https://www.jollen.org/blog/2007/12/qemu_linux_kernel_debug.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 31 Dec 2007 15:41:48 +0800</pubDate>
      </item>
            <item>
         <title>新的 Linux Wireless Stack 現身</title>
         <description><![CDATA[Linux 2.6.22 有一個重要的更新，就是改進了過去對於 wireless 支持的不足。一家叫做 [<a href="http://www.devicescape.com/pub/">Devicespace</a>] 的公司，為 open source 做了一項重要的貢獻，他們將一份新的 wireless stack 實作提交給 kernel，並正式收錄於 Linux 2.6.22。詳情可參考 kernelnewbies.org 上的說明 [<a href="http://kernelnewbies.org/Linux_2_6_22#head-1498b990e997cc0e95dbfa9047e7ebe8d84847cc">New Wireless stack</a>]。

Linux 在 wireless stack 上的功能並不是很充份，在 Devicespace 貢獻 kernel 更好的全新 wireless stack 實作後，對 Linux 在無線網路上的支援與應用，將是一個重要的進展。由 Devicespace 所提交的新一代 wireless stack 包含的實作有（引述 kernelnewbies.org 原文）：

<blockquote>This wireless stack has many features, like a complete software MAC implementation, WEP, WPA, a "link-layer" bridging module, hostapd, QoS support to prioritize things like VoIP, 802.11g support, and full debug capabilities.</blockquote>

此外，另一個重要的改進則是：

<blockquote>Another feature of this stack is a completely new user interface. </blockquote>

這裡所指的「全新 user interface」指的是新的 user-space interface 實作。過去的 wireless stack 是採取 ioctl-based interface，新的 user-space interface 實作則是 netlink-based，並且能與舊有的 ioctl-based interface 相容。

另外，kernelnewbies.org 上也提到：

<blockquote>The disadvantage is the lack of drivers using this stack: the drivers that have been in the tree for a long time do not support this stack, and will need to be ported.</blockquote>

目前長久存在於 "tree"（kernel tree，kernel 原始碼目錄樹）裡的驅動程式並不支援新的 stack，必須要做 porting 的工作。不過，kernelnewbies.org 也提到，這個工作從技術角度來講並不困難，而且以現在的 kernel community 來說，這些工作不久的將來就會完成。
]]></description>
         <link>https://www.jollen.org/blog/2007/12/new_linux_wireless_stack.html</link>
         <guid>https://www.jollen.org/blog/2007/12/new_linux_wireless_stack.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 29 Dec 2007 23:14:22 +0800</pubDate>
      </item>
            <item>
         <title>Linux 2.6.22 新增 display class</title>
         <description><![CDATA[Linux 2.6.22 於 2007 年 7 月 8 日正式釋出，這個版本的 kernel 有一個令我感興趣的新功能。Linux 2.6.22 新增一個 class driver，稱為「display class」。顧名思義，這個新的 class driver 是用來管理與 "display" 有關 device driver。以下引用自 [<a href="http://lxr.linux.no/linux+v2.6.22/include/linux/display.h">include/linux/display.h</a>]：

<blockquote><pre>
28 struct display_device;
29
30 /* This structure defines all the properties of a Display. */
31 struct display_driver {
32         int  (*set_contrast)(struct display_device *, unsigned int);
33         int  (*get_contrast)(struct display_device *);
34         void (*suspend)(struct display_device *, pm_message_t state);
35         void (*resume)(struct display_device *);
36         int  (*probe)(struct display_device *, void *);
37         int  (*remove)(struct display_device *);
38         int  max_contrast;
39 };
40
41 struct display_device {
42         struct module *owner;                   /* Owner module */
43         struct display_driver *driver;
44         struct device *parent;                  /* This is the parent */
45         struct device *dev;                     /* This is this display device */
46         struct mutex lock;
47         void *priv_data;
48         char type[16];
49         char *name;
50         int idx;
51 };
52
53 extern struct display_device *display_device_register(struct display_driver *driver,
54                                         struct device *dev, void *devdata);
55 extern void display_device_unregister(struct display_device *dev);
</pre></blockquote>

在 Kconfig 選單中，可以找到一個 "Display device support" 的項目。根據 display class 的規劃，這個新的驅動程式界面除了支援 LCD 外，也會支援 CRT、TVout 以及其它的顯示界面。

2007 年下半年度因為工作特別忙碌，或者說，工作項目比較繁雜，無法將時間有效利用，便堆積了許多未讀的資料。正好利用 2007 結束前的幾個放假日，將這半年來累積的資料一次消化完畢，並將一些紀錄和大家分享。

<strong>延伸閱讀</strong>

2007.08.29: <a href="http://www.jollen.org/blog/2007/08/linux_frame_buffer_lecture.html">Linux frame buffer 驅動程式開發簡報下載</a>]]></description>
         <link>https://www.jollen.org/blog/2007/12/linux_display_class.html</link>
         <guid>https://www.jollen.org/blog/2007/12/linux_display_class.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 29 Dec 2007 20:22:07 +0800</pubDate>
      </item>
            <item>
         <title>簡報下載：Linux 驅動程式的 read/write 觀念解析</title>
         <description><![CDATA[12/19（三） 下午於 [<a href="http://www.openmoko.org">OpenMoko</a>] 的 [<a href="http://wiki.openmoko.org/wiki/OpenLab_2nd_Event/zh_tw">OpenLab</a>] 發表了一場演說，時間是一個小時。這是一場有關 Linux 驅動程式的演講訓練活動，本次演講主要在解析 Linux 驅動程式的 read/write 程式碼框架（framework）與觀念解析。大綱如下：

* read/write system call
* vfs switch
* user-space vs. kernel-space: I/O data
* short discussion on wait queue: blocking I/O
* cdata example

時間只有一個小時，因此在規劃簡報時，決定採取範例導向的方式做介紹。當天以一個 cdata 的範例做主軸，直接透過程式碼來做講解。在此提供簡報電子檔供參考 [<a href="http://tw.jollen.org/slides/linux-device-drivers-read-write.pdf">linux-device-drivers-read-write</a>]。]]></description>
         <link>https://www.jollen.org/blog/2007/12/linux_device_driver_read_write.html</link>
         <guid>https://www.jollen.org/blog/2007/12/linux_device_driver_read_write.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 21 Dec 2007 16:15:25 +0800</pubDate>
      </item>
            <item>
         <title>Google Android 採用 Apache License: 為什麼不是 GPL？</title>
         <description><![CDATA[Google 讓基於 Android 技術平臺的「應用程式」採用 [<a href="http://www.apache.org/licenses/LICENSE-2.0.html">Apache License, Version 2</a>] 的授權條款。許多 Free & Open Source Software（FOSS）採取 GPL 的授權條款，目前 OpenMoko 的架構，也都是選用基於 GPL 授權條款的 FOSS。

Android 與 OpenMoko 一個是開放手機平臺，另一個則是開放源碼手機平臺；前幾日對二個平臺做了差異比較，並且製作了一份 [<a href="http://www.jollen.org/blog/2007/11/openmoko_vs_android.html">OpenMoko vs. Android 主要差異比較表</a>]，可透過此表約略了解二個手機平臺之間的不同。

為什麼 Android 的應用程式要採取 Apache License 的授權方式？由 [<a href="http://www.apache.org">Apache Software Foundation</a>] 所發展的軟體，也都改採 Apache License 授權的故事來看，我想 Android 平臺的應用程式選擇 Apache License 的授權條款，也是很合理的。這要由 Apache License 與 GPL 之間的二個大差異講起。

第一、GPL 要求程式碼在重新散佈或修改時，必須提供原始程式碼（source code），並且也要基於 GPL 條款進行授權。因此，我們無法由原始授權者取得 GPL 軟體後，就無法再做「再授權」的動作。Apache License 則不然。如果軟體是基於 Apache License 授權，我們在重新散佈或進行程式碼修改時，就不會被要求必須要提供原始程式碼；同時，Apache License 授權也允許他人對軟體做「再授權」的動作。

第二、若是程式包含專利技術，使用 Apache License 時，表示授權者願意將程式的專利技術授權出來（Patent License）。但是，GPL 授權則不允許專利授權，因此在某些情況下，使用 GPL 授權的軟體可能會有侵害專利權的問題。

另外，Apache License 也對商標（trademarks）做保護，「不」允許他人使用原始授權人的 trademark、product name 與 service mark，除非是基於「合理」用途，並且附上 Apache License 的 NOTICE 聲明檔案。在 Android 的 SDK 裡，隨處可見 NOTICE 聲明檔。

<strong>Android vs. OpenMoko</strong>

Android 的授權方式，以及 OHA 的成立，代表著 Google 對未來手機市場的策略將會是一個建構舞台者的角色。Android 的出現對 OpenMoko 是否構成威脅，我倒覺得不必將 Android 當成是 OpenMoko 的假想敵，因為OpenMoko 未來潛在的競爭對手，恐怕不是 Android，而是使用者。走好自己路，是目前比較穩健的做法。

對開放或開源的手機平臺來說，最大的受益者是開發者以及使用者。就現況而言，顯然開發者對 Android 還沒有產生太大的興趣，OpenMoko 在開發者這個族群做得還算不錯。不過，OpenMoko 要能吸引真正使用者，還有努力的空間，所以 OpenMoko 真正要搞定的應該是使用者，倒不必太在意 Android 對他的影響。

至少就開發者族群來講，Android 還在起步階段；Google 現階段也很努力地在經營開發者族群。開發者的確是開放手機業者的兵家必爭之地。當然，最後還是必須在使用者族群裡分出高下。

總結來看，目前 Android 還沒有一個真正的商業模式。Terrence Russell 在他的 blog [<a href="http://blog.wired.com/business/2007/11/google-android-.html">Google: Android Has No Built-in Business Model</a>] 提到，Android 的主要開發者之一 Richard Miner 告訴他："For Android, there is no built-in business model"。至少就我目前的觀察來說，我是相信這個說法的。

也別忘記，Google 總是喜歡做實驗，任何新的 Google 服務都要在 Lab 裡育成，也會被掛上 Lab 的 logo。Android  是 Google 的新服務，或許現在他還是個「Android Lab」，只是沒有被掛上 Lab 字眼。Google 正在對開放手機平臺做大規模的實驗。]]></description>
         <link>https://www.jollen.org/blog/2007/12/android_apache_license_not_gpl.html</link>
         <guid>https://www.jollen.org/blog/2007/12/android_apache_license_not_gpl.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Sun, 02 Dec 2007 19:43:10 +0800</pubDate>
      </item>
            <item>
         <title>register_netdev 的入門實例: bonding.c</title>
         <description><![CDATA[<p>今天在「Linux 驅動程式: 進階」的訓練課程中提到，找到正碓的入門實例，比起什麼都來得重要。</p>

<p>有時，老舊的程式碼，更容易幫助初學者掌握基本的架構與 API 用法。在 Linux 驅動程式的課程裡，我推薦同學研究一個早期的網路介面驅動程式，叫做 bonding.c。bonding 是一個用來合併網路卡的驅動程式，而早期的 bonding 驅動程式並沒有太多額外的 algorithm 實作，因此我建議同學，拿出 kernel 2.4.14 裡的 bonding.c 來做實例研究。由於這個版本的 bonding 驅動程式非常赤祼祼地呈現 register_netdev、struct net_device 與封包傳送的介面實作，因此肯定比許多最新的網路卡驅動程式更適合初學者。</p>

<p>kernel 2.4.14 的 bonding.c 可以在 2.6.x 的 kernel 上使用，美中不足的是，使用古老的 bonding driver 無法將 eth 介面合併進來，但至少可以配合 ifconfig 指令來做動態的操作與觀察，例如了解 'ifconfig bond0 down' 與 struct net_device::close 的實作。</p>

<p>如果要實際把玩 bonding 的功能，只要直接使用 kernel 2.6.x 本身的 bonding driver 即可。另外，kernel 2.4.14 的 bonding.c 必須修改一行 code，才能在 kernel 2.6.x 的環境下順利載入。以下提供此修改的 patch：</p>

<pre>
--- bonding.c.orig      2007-12-02 21:15:03.000000000 +0800
+++ bonding.c   2007-12-02 22:07:44.000000000 +0800
@@ -275,12 +275,11 @@
        /* Find a name for this unit */
        int err;
 
-       dev_bond.init = bond_init;
-
        err = dev_alloc_name(&dev_bond,"bond%d");
        if (err<0)
                return err;
 
+       bond_init(&dev_bond);
        SET_MODULE_OWNER(&dev_bond);
        if (register_netdev(&dev_bond) != 0)
                return -EIO;
</pre>

]]></description>
         <link>https://www.jollen.org/blog/2007/12/register_netdev_study.html</link>
         <guid>https://www.jollen.org/blog/2007/12/register_netdev_study.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 02 Dec 2007 14:30:59 +0800</pubDate>
      </item>
            <item>
         <title>ＯpenMoko OpenLab 的第二次官方活動將於12/19（三）舉行</title>
         <description><![CDATA[OpenMoko 贊助成立的 OpenLab 將會在 12/19 舉辦第二次的訓練活動，以下引用自 [<a href="http://wiki.openmoko.org/wiki/OpenLab">OpenLab</a>] 的 [<a href="http://wiki.openmoko.org/wiki/OpenLab_2nd_Event/zh_tw">公關稿</a>]：

<blockquote>為了與台灣的 open source 社群互動並回饋社群，OpenMoko 特別贊助成立了 OpenLab。OpenMoko 會不定時舉辦官方的 OpenLab 活動，第二次 OpenLab 活動將於 12/19 (三) 舉辦，本次活動除了延續上次的 Linux 驅動程式訓練外，更特別邀請了中研院自由軟體鑄造場法律組的林誠夏先生來為大家介紹 GPL 的法律觀念。

另外，這次的活動也特別針對 Apache 授權的觀念進行隨興討論，現場由主持人針對 Apache 授權做介紹，並與現場朋友做討論與收集意見回饋。Google 的 Android 手機平臺採用的是 Apache 授權，與 OpenMoko 的 GPL/LGPL 授權不同，我們希望能針對這二者的差異做比較，並透過腦力激盪的方式，討論二個平臺未來的發展與商業模式。

OpenLab 訓練活動的主題是 Linux 驅動程式架構的 I/O 模型，我們將延伸第一次的訓練內容，針對 device file 的 read/write 做介紹。活動即日起開放報名，訓練活動同樣採取限額方式。OpenLab 入口網頁：http://wiki.openmoko.org/wiki/OpenLab/zh_tw，報名網址：http://wiki.openmoko.org/wiki/OpenLab_2nd_Event/zh_tw。 </blockquote>

延伸閱讀：

* 2007.11.28: <a href="http://www.jollen.org/blog/2007/11/openmoko_vs_android.html">OpenMoko vs. Android 主要差異比較表</a>
* 2007.11.21: <a href="http://www.jollen.org/blog/2007/11/openmoko_openlab_intro_v1.html">OpenMoko 專案介紹與 OpenLab</a>
* 2007.11.09: <a href="http://www.jollen.org/blog/2007/11/android_gphone.html">Android 與 Gphone 觀察</a>]]></description>
         <link>https://www.jollen.org/blog/2007/11/openlab_2nd_event.html</link>
         <guid>https://www.jollen.org/blog/2007/11/openlab_2nd_event.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Fri, 30 Nov 2007 15:43:05 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko vs. Android 主要差異比較表</title>
         <description><![CDATA[今天製作了一份 OpenMoko 與 Android 的主要差異比較表，提供給大家做參考。

<img src="http://people.openmoko.org/jollen/slides/openmoko_android_cmp.jpg" width="640" />

下載大圖 [<a href="http://people.openmoko.org/jollen/slides/openmoko_android_cmp.jpg">http://people.openmoko.org/jollen/slides/openmoko_android_cmp.jpg</a>]。]]></description>
         <link>https://www.jollen.org/blog/2007/11/openmoko_vs_android.html</link>
         <guid>https://www.jollen.org/blog/2007/11/openmoko_vs_android.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 28 Nov 2007 19:20:40 +0800</pubDate>
      </item>
            <item>
         <title>OpenLab 場地實景</title>
         <description><![CDATA[[<a href="http://www.openmoko.org">OpenMoko</a>] 的 [<a href="http://wiki.openmoko.org/wiki/OpenLab">OpenLab</a>] 位於 OpenMoko 總部旁 ，進門後，映入眼廉的是一對橘色的鋼柱，這個顏色正是 OpenMoko CIS 的顏色。這對鋼柱，是大樓避震結構的設計之一（避震器），原本藏在一片門板後門，幾個月前 OpenMoko 在裝修新辦公室時，才把這片門板拆除。

<img src="http://people.openmoko.org/jollen/photos/openlab/openlab_1.jpg" />

<img src="http://people.openmoko.org/jollen/photos/openlab/openlab_2.jpg" />

從你所看到的這道門進門去，便是 OpenLab，從 OpenLab 角落往外看去，右邊是電腦區，左邊您所看到的投影畫面則是 OpenMoko 的 IRC channel。坐在 OpenLab 裡看社群朋友在聊天，還蠻有趣的！

<img src="http://people.openmoko.org/jollen/photos/openlab/openlab_3.jpg" />

一進到 OpenLab，就可以看到一整面綠色的牆，這邊是 OpenLab 的沙發區。

<img src="http://people.openmoko.org/jollen/photos/openlab/openlab_4.jpg" />

目前 OpenLab 提供 3 台電腦，裡頭目前存放一些講義電子檔。現在大家似乎都還蠻喜歡在 OpenLab 的沙發區聊天開會呢！

<img src="http://people.openmoko.org/jollen/photos/openlab/openlab_5.jpg" />

OpenLab 的隔壁，就是 training center，目前由 [<a href="http://www.jollen.org">Jollen's Consulting</a>] 團隊負責提供訓練課程。

<img src="http://people.openmoko.org/jollen/photos/openlab/openlab_6.jpg" />

歡迎大家有空來 OpenLab 坐坐 ;-)

<img src="http://people.openmoko.org/jollen/photos/openlab/openlab_openmoko.jpg" />]]></description>
         <link>https://www.jollen.org/blog/2007/11/openlab_photos.html</link>
         <guid>https://www.jollen.org/blog/2007/11/openlab_photos.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Thu, 22 Nov 2007 11:42:27 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 專案介紹與 OpenLab</title>
         <description><![CDATA[在 11/17 (Sat.) 的 OpenLab 開幕活動中，我們為大家準備了一份正式的「OpenMoko 公司與 OpenMoko 專案介紹」文件，將過去到現在的 OpenMoko 專案做一個主要的重點瀏覽。OpenMoko 也於十月二十三日正式獨立為 OpenMoko, Inc.（我本墨客股份有限公司）。在此提供此專欄的電子檔供各位朋友下載 [<a href="http://people.openmoko.org/jollen/slides/openmoko_openlab_intro_v1.pdf">openmoko_openlab_intro_v1.pdf</a>]。

延伸閱讀：

* 2007.10.02: <a href="http://www.jollen.org/blog/2007/10/introduction_openmoko_project.html">「Introduction to OpenMoko Project」簡報</a>
* 2007.11.21: <a href="http://www.jollen.org/blog/2007/11/openmoko_openlab_opening.html">OpenMoko OpenLab 開幕活動紀錄（上）</a>]]></description>
         <link>https://www.jollen.org/blog/2007/11/openmoko_openlab_intro_v1.html</link>
         <guid>https://www.jollen.org/blog/2007/11/openmoko_openlab_intro_v1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 21 Nov 2007 19:38:21 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko OpenLab 開幕活動紀錄（上）</title>
         <description><![CDATA[上週六在大眾總部大樓舉辦 OpenMoko 的 OpenLab 開幕活動，活動的形式是以 OpenLab free training 配合 OpenLab 隨性討論的方式進行。當天來了 35 位朋友，我們的大家長簡明仁董事長以及 OpenMoko 的 leader Sean Moss-Pultz 也都有到現場。

第一場 OpenLab training 是由 OpenMoko coreteam 的 [<a href="http://blog.linux.org.tw/~jserv/">jserv</a>] 為大家分享 [<a href="http://blog.linux.org.tw/~jserv/archives/001938.html">LINUX 驅動程式架構入門</a>]，jserv 兄在百忙之中還能接受 OpenLab 的邀請，實在是大家的福氣啊！第二場 training 則是由  Dennis 兄為大家介紹最基本的 qemu-neo1973 安裝與 OpenMoko 環境的執行，這是第一次想要在 PC 上執行 OpenMoko 平臺的第一課。

會中也有許多朋友到 OpenLab 體驗 Neo1973 實體手機，大家也在現場討論了許多話題，只是小弟我因為要服務全場，未能參與這些活動，倒是自覺有點可惜；有些地方服務也未盡完善，只好請各位朋友多多包涵了，會後我們也做了一些檢討，會在爾後的活動做改善。

當天的活動講義，也已經更新到 OpenLab 的電腦裡了，需要講義電子檔的朋友，可依當天的「OpenLab 入場說明」至現場取用。開幕會後，OpenLab 也正式運轉，目前可直接在 OpenMoko 的 Wiki 上取得 OpenLab 的官方資訊，網址是 [<a href="http://wiki.openmoko.org/wiki/OpenLab">http://wiki.openmoko.org/wiki/OpenLab</a>]。

DigiTimes 與蘋果日報都有相關報導：

* <a href="http://www.digitimes.com.tw/n/article.asp?id=0000072911_B8M11RL8U70UDGF2BY10X">搶先宏達電一步  大眾集團旗下OpenMoko推出首款Linux智慧型手機  本週末將在大眾總部設立OpenLab</a>
* <a href="http://1-apple.com.tw/apple/index.cfm?Fuseaction=Article&Sec_ID=8&ShowDate=20071119&NewsType=twapple&Loc=TP&Art_ID=4009959">軟硬兼施 大眾研發行動裝置系統</a>

延伸閱讀：

*<a href="http://www.jollen.org/blog/2007/11/android_gphone.html">Android 與 Gphone 觀察</a>
* <a href="http://www.jollen.org/blog/2007/10/introduction_openmoko_project.html">「Introduction to OpenMoko Project」簡報</a>]]></description>
         <link>https://www.jollen.org/blog/2007/11/openmoko_openlab_opening.html</link>
         <guid>https://www.jollen.org/blog/2007/11/openmoko_openlab_opening.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 21 Nov 2007 01:29:32 +0800</pubDate>
      </item>
            <item>
         <title>Android 與 Gphone 觀察</title>
         <description><![CDATA[自從本週一 Gphone 的消息正式公開後，Gphone 的新聞幾乎攻佔各大媒體的科技版版面，這個星期，被 Google 收購並被 Gphone 採用的開放源碼 Linux 手機平臺 [<a href="http://www.openhandsetalliance.com/android_overview.html">Android</a>] 也受到大量關注。

Android 是 Gphone 所使用的軟體平臺，根據 [<a href="http://www.openhandsetalliance.com/">Open Handset Alliance</a>]（OHA）的報導，Android 提供 mobile device 完整的軟體環境，包含：作業系統（Linux）、中介軟體（middleware）與主要的 mobile applications。週一的一場公開視訊會議宣佈了 Gphone 的正式消息，Engadget 提供了這場視訊會議的紀錄稿：

<a href="http://www.engadget.com/2007/11/05/live-coverage-of-googles-android-gphone-mobile-os-announcement/">http://www.engadget.com/2007/11/05/live-coverage-of-googles-android-gphone-mobile-os-announcement/</a>

以下節錄幾個重要的談話內容：

1. On Google's side we have a two part strategy... the part we want to talk about today is creating a whole new mobile experience for users, and we're going to do that with Android.

對 Google 來說，他們的 mobile phone 策略有二個，其中一個是打算建立一個全新的使用者行動體驗，Google 打算以 Android 來做這件事情。

2. Google along with 33 other companies are announcing Android, the first truly integrated mobile operating system.

Google 與其它 33 家廠商共同發佈 Android，這是第一個真正的整合式行動作業系統。

3. T-Mobile 將在 2008 年於歐洲與美國發表第一支採用 Android 的手機。

4. HTC 是 OHA 的發起會員之一，並計畫於 2008 年中釋出第一個 Android 軟體。

5. Qualcomm 也是 OHA 的會員之一，他們將會與 T-Mobile 合作。

6. 來自中國移動研究中心（China Mobile Research Institute）的 Bill Wang 提到，中國移動（China Mobile）很興奮能與 Google 合作。

7. 既然 Android 出自 Google，當然在 Web service 方面的支援應該也會很強健，並且也應該強調網路搜尋廣告。在談話中，我們也發現這二段文字：

<blockquote>Rubin: "One of the interesting things about Google's business is the flexibility and relevance... part of this Android solution is a very robust HTML web browser... contrary to a lot of the speculation out there, you won't see a completely ad-driven cellphone based on this platform."</blockquote>

<blockquote>Eric: "The fundamental problem with most phones today is they don't have full-power browsers. We've been taking our mobile services and use specialized engineering to get them on other devices...</blockquote>

8. Android 有著令人驚奇的 UI。

<strong>結論</strong>

Google 打造的是平臺，並建立 relationships，藉由這些 relationships（即 OHA）來協同打造數以千計的【Gphone】。Google 不自己打造 Gphone，這是 Google 於 mobile phone 的運作策略；Google 希望大家都能加入 OHA。

這場 conference call，主要在發佈 Android 與此平臺的策略，並不是 Gphone 的產品發表會。Android 以 Linux 為基礎，並且是完全 open source 的平臺。

最後，談話中也提及，Android 在所有的 data networks 上都能執行，並且也能在即將公開拍賣的 700MHz 頻段上執行。

總結是，這是一場 Gphone 的開發者平臺（developer platform）發表會。]]></description>
         <link>https://www.jollen.org/blog/2007/11/android_gphone.html</link>
         <guid>https://www.jollen.org/blog/2007/11/android_gphone.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Fri, 09 Nov 2007 15:35:44 +0800</pubDate>
      </item>
            <item>
         <title>ABC News 報導 OpenMoko</title>
         <description><![CDATA[大家都知道，OpenMoko 是一個完全 open source 的專案，在 9/23 日的 ABC News 裡，也特別報導了 OpenMoko 專案，並強調 OpenMoko 的開放特性。報導全文與影片，可由以下網址瀏覽：

<a href="http://abclocal.go.com/kgo/story?section=drive_to_discover&id=5671823&ft=exLg">http://abclocal.go.com/kgo/story?section=drive_to_discover&id=5671823&ft=exLg</a>

我的同事 Chelsea 特別製作了逐字稿，在此特別感謝，並將全文與大家分享。

You've seen the iphone by now, but maybe you have a better idea. Members of Home Brew mobile phone club from Menlo Park are designing and making their own phones. How do they do it? Richard Hart takes us to the club where the FOSS are dialed in on building a better phone from scratch.
你們現在已經看到 iPhone 了，但你可能有更好的想法。Home Brew 行動電話俱樂部裡來自 Menlo Park 的成員正在設計，並行銷他們自己的電話。他們是如何辦到的？Richard Hart 帶領我們到俱樂部，這裡 FOSS 正在重新建立一個更好的電話。

The one on the left is the iPhone, the other is a MyPhone, a do-it-yourself, my size, my color, my features mobile device.
左邊這一個是 iPhone，另一個是 MyPhone，一個可以 DIY 自己的大小、自己的顏色、自己的特色的行動電話。

It is a product of the Home Brew Mobile Phone Club, in the tradition of legendary Home Brew Computer Club, members have day job, but volunteer their skills in software, hardware and design. A company called Gumstix donates circuitry. But why?
這是 Home Brew 行動電話俱樂部的產品，Home Brew Computer Club 裡一般來說，裡頭的成員都有自己的平常工作，但自願付出他們的技能在軟體、硬體與設計上。一家叫做 Gumstix 的公司捐獻電路。為什麼這麼做呢？

I am still waiting for Motorola or Palm or someone to build the exact phone that I want, that has all the features that I want. And a lot of people here found themselves in that situation.
我仍然在等待 Motorola 或 Palm 或有人來建造我真正想要的那個電話，有著所有我想要的特點。這裡有許多人也發現他們自己也處於這樣的情況。

They aren't alone, so-called open-source projects like this are springing up around the world. The most successful so far is this: the Neo 1973 designed to be plugged into a computer and completely customized with the software tool called OpenMoko.
他們並不孤單，被稱為 open source 的專案正在全球興起。目前為止最成功的就是這個：Neo1973 被設計成可以接到電腦，並可透過稱為 OpenMoko 的軟體工具來完全客製化。

Let's say that you wanted to change the way the touch screen operates. Fine. Let's say you wanted to program the GPS in the phone. Fine. And what if you wanted an accelerometer, a kind of motion detector? That's also fine.
我們說，你想要改變觸控螢幕的操作方案。沒問題。我們說，你想要程式化電話裡的 GPS。沒問題。如果你想要一個 accelerometer，一種動作偵測器。也沒問題。

The point of the open source phone movement is: if you can imagine it, you should be able to program it.
開放源碼電話運動的觀點是：如果你可以想像它，你就能程式化它。

In fact, we shouldn't even limit it to programmers. We have people who have joined the project who are, for instances, musicians or graphic designers. And they're helping with things like icons and ring tones.
事實上，我們甚致不應對程式設計者限制它。我們己經有參與這個專案的人了，例如有音樂家或繪圖設計家。他們正在協助像是 icon 或是鈴聲的工作。

So what we would like to see is the completely innovative, creative, fantastic (almost) application that we just can't even imagine now. And that is the benefit to the consumers.
所以，我們想要看到的是完全創新、有創意、新潮的應用程式，即使我們現在還無法想像。而且這對消費者也是好處。

Any consumer product is many months away. But a developer version can be purchased online now, and anyone can download the Openmoko software absolutely free. Even the Home Brew Club is turning to OpenMoko to help turn their Frankenphone into Cinderella.
任何對消費者的產品還要再幾個月。但開發者版本現在己經可以由線上購買了，而且任何人都可以免費下載 OpenMoko 軟體。即便是 HomeBrew Club 也轉向 OpenMoko，來幫助將他們的 Frankenphone 轉換為 Cinderalla.

With one ring tone that rules them all! Richard Hart from ABC

在 11/17 所舉辦的 OpenMoko OpenLab 開幕活動中，我們也會介紹如何下載並安裝 OpenMoko 軟體。有興趣的朋友可參考 [<a href="http://www.jollen.org/blog/2007/11/coscup_2007_openmoko.html">COSCUP 2007 演講簡報下載</a>] 日記，目前開放線上報名。]]></description>
         <link>https://www.jollen.org/blog/2007/11/abc_news_openmoko.html</link>
         <guid>https://www.jollen.org/blog/2007/11/abc_news_openmoko.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Thu, 08 Nov 2007 22:38:44 +0800</pubDate>
      </item>
            <item>
         <title>COSCUP 2007 演講簡報下載　</title>
         <description><![CDATA[上週日參加 [<a href="http://coscup.org/2007/">COSCUP 2007</a>]（開源人年會），與現場朋友分享了「OpenMoko 好好玩！」的議題。OpenMoko 是一個 100% 開放源碼的 Linux 手機軟體平臺，這場分享演講主要是以推廣者（promoter）的角度來切入，介紹了 OpenMoko 的技術與非技術主題。在此提供簡報電子檔 [<a href="http://people.openmoko.org/jollen/slides/COSCUP_2007_OpenMoko_Funny.pdf">COSCUP_2007_OpenMoko_Funny.pdf</a>] 供下載。

另外，演講過程中，撥放了 OpenMoko 社群為 OpenMoko 所製作的創意小廣告，這些廣告可在 YouTube 網站上，以「openmoko」關鍵字做搜尋即可取得。此外，會上提到的「OpenLab 開幕活動」，亦可在 OpenMoko Community Wiki 上取得相關資訊以及報名方式，網址為 [<a href="http://wiki.openmoko.org/wiki/OpenMoko_OpenLab_Opening/zh_tw">http://wiki.openmoko.org/wiki/OpenMoko_OpenLab_Opening/zh_tw</a>]。

]]></description>
         <link>https://www.jollen.org/blog/2007/11/coscup_2007_openmoko.html</link>
         <guid>https://www.jollen.org/blog/2007/11/coscup_2007_openmoko.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 07 Nov 2007 22:48:10 +0800</pubDate>
      </item>
            <item>
         <title>「Introduction to OpenMoko Project」簡報</title>
         <description><![CDATA[昨天到某公司給了一份「Introduction to OpenMoko Project」的簡報，內容主要是針對 OpenMoko 的「技術面」有哪些「組成（components）」做一些簡單的摘要報告，目的是幫助第一次看到 OpenMoko project 的工程師，能在 5 分鐘之內掌握一些重點。簡報電子檔 [<a href="http://www.jollen.org/blog/2007/10/02/OpenMoko_Project_Intro_v0.2.pdf">OpenMoko_Project_Intro_v0.2.pdf</a>]。

<strong>OpenMoko Framework</strong>

OpenMoko 專案最主要是提供了一個「UI Framework」，稱為 [<a href="http://wiki.openmoko.org/wiki/OpenMokoFramework">OpenMoko framework</a>]，現階段的 OpenMoko framework 提供以下四個程式庫：

* libmokocore：核心程序庫，提供 OpenMoko 應用程序間的通訊。
* libmokonet：通訊程序庫，提供 GSM、GPS、Bluetooth 等功能。
* libmokopim：PIM（Personal Information Management）程序庫，提供個人資料管理功能。
* libmokoui：UI（user interface）程序庫，提供 OpenMoko 主要的界面設計，包含：containers、widgets 與 dialogs。

libmoko 使用到許多現有的 FOSS 成果，其中最主要的 library 包含：

* glibc
* D-Bus
* libX11、kdrive7、matchbox
* GTK+2
* Pango
* Freetype
* libxml2

第一個公開釋出的 OpenMoko application framework 為「OpenMoko 2007」，目前最新版本則是「<a href="http://wiki.openmoko.org/wiki/OpenMoko2007.2">OpenMoko 2007.2</a>」。]]></description>
         <link>https://www.jollen.org/blog/2007/10/introduction_openmoko_project.html</link>
         <guid>https://www.jollen.org/blog/2007/10/introduction_openmoko_project.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Tue, 02 Oct 2007 18:56:29 +0800</pubDate>
      </item>
            <item>
         <title>Software Freedom Day 2007 北京：OpenMoko Booth</title>
         <description><![CDATA[OpenMoko 的 booth 並沒有別人家的精彩，可不是我們不用心，因為漂洋過海來到北京，行季箱能裝能塞的東西，也就這麼多了！好吧，還有一個小原因是因為這次準備活動也略顯匆忙，不過，該有的東西可是一樣都沒有少呢。

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/19.jpg" />

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/18.jpg" />

身為「銀牌贊助」商（沒看到嗎？沒關係我來放大一點。），OpenMoko 當然不能讓大家失望，除了提供 lucky draw 的大獎外，現場可是也準備了一份「Community Press CD」送給大家呢，裡頭有一些 OpenMoko 的「寫真影片」、OpenMoko 簡報 PDF 檔還有當天的 DM 電子檔。

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/20.jpg" />

我覺得 OpenMoko 的 poster 還挺酷的，所以也放了一份 PDF 檔到 Community Press CD 裡送給大家。

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/12.jpg" />

某個程度上來說，OpenMoko 的 booth 並不算華麗，但是重頭戲「實機展示」可是不能少的。我們擺了 3 台 Neo1973 在現場，Neo 們可是忙碌得很呢，因為大家從未停止把玩他們！

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/6.jpg" />

《待續》]]></description>
         <link>https://www.jollen.org/blog/2007/09/sfd_2007_openmoko_booth.html</link>
         <guid>https://www.jollen.org/blog/2007/09/sfd_2007_openmoko_booth.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Sun, 23 Sep 2007 17:38:18 +0800</pubDate>
      </item>
            <item>
         <title>Software Freedom Day 2007 北京：Booth 剪影</title>
         <description><![CDATA[主辦單位這次準備了 400 多個印有《北京軟件自由日2007》的小提袋在現場發放，但是因為與會人數遠超過預期，所以有許多朋友沒機會能拿到這個具有紀念意義的小提袋。當然囉，我也沒能這麼幸運能拿到這個小提袋。啊！好想要一個帶回來收藏啊，原本到會場時，看到地上整排的小提袋，還以為能有機會拿到一個的。

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/2.jpg" />

現場有實體展示活動的廠商有 15 家，趁著人潮尚未湧入，拍了幾個「招牌」起來，大家應該都可以看得出來這是哪一家公司。

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/3.jpg" />

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/17.jpg"  />

 <img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/4.jpg" />

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/9.jpg" />

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/13.jpg" />

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/7.jpg" />

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/16.jpg" />

另外，主辦單位的用心，從小細節就能看得出來。不但為我們準備了很棒的展示桌，連「桌布」也都很有特色，上面印了企鵝的 logo 以及「我們用開源軟件，你呢？」的標語。我覺得這句標語真在是酷弊了，所以也拍了照。

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/10.jpg" />

<img src="http://people.openmoko.org/jollen/photos/software_freedom_day_2007/booth/Images/11.jpg" />

《待續》]]></description>
         <link>https://www.jollen.org/blog/2007/09/software_freedom_day_2007_booth_photo.html</link>
         <guid>https://www.jollen.org/blog/2007/09/software_freedom_day_2007_booth_photo.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Sun, 23 Sep 2007 16:58:46 +0800</pubDate>
      </item>
            <item>
         <title>Software Freedom Day 2007 北京：會後報告</title>
         <description><![CDATA[<strong>活動概況</strong>

這次到北京參與「軟件自由日」活動，相當另人印象深刻。根據「官方」的非正式統計，當天與會者大約有 700 人左右，同時有 15 個參展廠商，以及 10 名的演講者，真是一個熱鬧非凡活動，也是一個很成功的開源活動。

這次的北京軟件自由日是由 Beijing Linux User Group 所策劃主辦，由於這是一個「自由參加」的活動，並不需要事先報名，因此事前並不知道確實的與會人數，主辦單位只能根據以往的經驗來推估可能的人數。我們由主辦單位得到的可能人數是 300~500 位左右。

當天我跟另外一位同事 Chelsea 大約 11 點左右到達會場，活動是在下午 1 點才開始。OpenMoko 是這次的活動贊助商，所以主辦單位提供了一個 booth 讓我們使用，當然，現場一定要實際展示一下 Neo1973 實機！活動的 booth 區是設置在清華大學的 FIT（信息大樓）大樓的 1F，這是一個不錯的空間，相當寬敞而且明亮，演講的會議廳則是在 2F；演講廳的座位雖然不少，但是明顯的前來聽講的朋友多更多，沒有座位坐的朋友，就索性直接坐在走道上，因此整個演講廳都是滿滿的人。

OpenMoko 的場次是在 3:50PM，因為要照顧攤位，所以前面幾場演講都沒有聽到。我們在 2:30PM 左右才進到會議廳，離活動正式開始還不到二個小時，我們準備的 100 份「Community Press CD」與 300 份 DM 居然都已經快發放完畢了，還不到 3 點居然就「收攤」了。

「軟件自由日」當然是要提倡「自由軟件」的精神，這次的講題都是由「社群」與「開源」的角度切入。前來和大家分享講題的有Google 的蘇哲先生、紅旗中文 2000 的胡才勇總經理、蘭州大學的周慶國老師、Red Hat 的餘洋、Mozilla 的米嘉、MySQL AB 的 Colin Charles、互動維客的潘海東執行官、康盛創想的戴志康執行官與BlenderCN 的黃海先生。

演講會後的高潮活動是公開抽出由 OpenMoko 所贊助的 Neo1973 手機以及 10 件「Free Your Phone」T-Shirt。最後的大獎 Neo1973 手機由一位北京清華大學的博士生所獲得，正巧他也是開源運動的愛好者，同時也參與過二個開源專案，希望他拿到這個大獎後，也能對 OpenMoko 的軟體專案有所貢獻！

《待續》]]></description>
         <link>https://www.jollen.org/blog/2007/09/software_freedom_day_report_1.html</link>
         <guid>https://www.jollen.org/blog/2007/09/software_freedom_day_report_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Fri, 21 Sep 2007 23:20:47 +0800</pubDate>
      </item>
            <item>
         <title>Software Freedom Day, Beijing：OpenMoko 演講簡報下載</title>
         <description><![CDATA[這是 [<a href="http://sfd.beijinglug.org/">Software Freedom Day</a>] 第二次於中國舉辦，第二屆的軟體自由日由 Beijing Linux User Group 所籌辦，OpenMoko 本次有幸受邀與會，並發表 20 分鐘的演說。這次活動是在北京清華大學的 FIT 大樓舉辦，因為 OpenMoko 是 sponsor 之一，所以也分配到一個 booth，現場 OpenMoko 展示了 Neo1973 實機，大家都很支持「開源手機」這個想法，現場得到的反應也都很好。

這次活動 OpenMoko 所準備的題目是「由 OpenMoko 社區看開源文化」，演講內容分為二個階段：

1. 前 10 分鐘為大家介紹 OpenMoko 專案、Free Your Phone 理念以及 Neo1973。
2. 後 10 分鐘由 OpenMoko 社群裡的活動（例如：mailing-list）或現象來闡釋一些開放源碼的文化。

這裡先提供簡報電子檔 [<a href="http://www.jollen.org/blog/2007/09/17/OpenSourceCulture_OpenMoko_Community.pdf">OpenSourceCulture_OpenMoko_Community.pdf</a>] 供各位朋友參考，有機會再與大家分享這次的活動心得。
]]></description>
         <link>https://www.jollen.org/blog/2007/09/software_freedom_day_2007_openmoko.html</link>
         <guid>https://www.jollen.org/blog/2007/09/software_freedom_day_2007_openmoko.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Mon, 17 Sep 2007 17:43:25 +0800</pubDate>
      </item>
            <item>
         <title>CIC 演講「Embedded Linux 開發環境」簡報下載</title>
         <description><![CDATA[上週五受邀至 CIC（晶片系統設計中心）與大家分享「Embedded Linux 開發環境」的題目。本次講題主要分為二個階段，第一階段介紹主要的開發工具，例如：gcc、gdb 等，除了整理必要的幾個工具外，也針對部份工具介紹了重要的使用觀念，以及分享過去的實作經驗。

第二階段則是列出幾個目前主要的 Embedded Linux 發展環境，以及常用的系統分析工具。本次講題針對的對象是 Linux 開發工具初學者，在此提供簡報電子檔 [<a href="http://www.jollen.org/blog/2007/09/17/Embedded_Linux_Toolchains_Intro_v0.1.pdf">Embedded_Linux_Toolchains_Intro_v0.1.pdf</a>] 供 CIC 以及有興趣的朋友參考。]]></description>
         <link>https://www.jollen.org/blog/2007/09/cic_embedded_linux_tools_presentation.html</link>
         <guid>https://www.jollen.org/blog/2007/09/cic_embedded_linux_tools_presentation.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 17 Sep 2007 12:26:55 +0800</pubDate>
      </item>
            <item>
         <title>LinuxWorld China 2007 會後感想</title>
         <description><![CDATA[[<a href="http://linux.chinaunix.net/linuxworld/">LinuxWorld China 2007</a>] 第一天主要是關於 FOSS 以及 community 的講題，主辦單位所邀請的佳賓都是中國地區領導級的開放源碼軟體公司，例如：Novell、IBM、Microsoft、中科紅旗、紅旗中文2000、MySQL AB等等。在上午的 keynote 演說中，多位講者都是以「開源社區」以及「社群參與」的角度切入，分析開放源碼與商業行為之間的關係，其中令人印象最深刻的講場是「中科紅旗軟件」以及「紅旗中文2000」的演說。為什麼對這二場演講的內容印象最深刻呢？原因有二：

1. 「<a href="http://www.redflag-linux.com/">北京中科紅旗</a>」以及「<a href="http://www.ch2000.com.cn/">北京紅旗中文貳仟</a>」都是中國本土的開放源碼軟體公司。
2. 北京開放源碼公司的活動非常熱絡。

二天的活動整個下來，得到許多了寶貴的訊息。就我個人來說，對 FOSS 有了更「整體」性的結論，就以台灣和中國地區的開放源碼軟體運動的現況來說，整體而言，可以給出以下幾個看法，供大家參考：

1. 北京當地的「開源軟件」公司數量遠多於台灣。如果要以嚴格的標準來審核的話，台灣的開放源碼軟體公司數量可能比想像中更少。

2. 關於開放源碼的產業推動，台灣的經濟部也有相關的組織在運作，不過目前尚未看到比較好的成效。在中國地區，一些指標性的開放源碼大公司（例如北京中科紅旗）都能協助推動開放源碼的運動。從比較實際的角度來看，台灣在推動開放源碼方面，組織的建構完成了，例如：委員會的成立、教育聯盟的成立等，但缺乏指標性的推動活動。另外，「open source」最重要的是他的「文化」教育，而不是「使用 open source software」的教育。

另外，當天也聽到 Microsoft（yes）所發表的 open source 簡報，在「<a href="http://www.microsoft.com/opensource/default.mspx">Open Source at Microsoft</a>」網站可找到許多資訊。開放源碼運動推行至今，已經不再只是單純的「公開原始碼」的活動了，而是逐漸轉變成軟體或服務公司的重要 business model。

]]></description>
         <link>https://www.jollen.org/blog/2007/09/linuxworld_china_2007.html</link>
         <guid>https://www.jollen.org/blog/2007/09/linuxworld_china_2007.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Fri, 07 Sep 2007 22:53:56 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 第一次在北京：會後感想</title>
         <description><![CDATA[先前的日記提到 [<a href="http://www.jollen.org/blog/2007/07/openmoko_in_beijinq.html">OpenMoko 於北京的首次公開演講</a>] 的活動，這是 OpenMoko 第一次在大陸地區有公開的演講。回台灣後，一直想要紀錄分享這次的活動感想，不過由於這陣子太忙碌了，一直到今天才能有空與大家分享這次的活動感想。

當天 Beijing Linux User Group 與會的朋友，出忽我意料之外的，有超過一半都是外國朋友，北京當地的社群朋友比率反而比較低。Beijing LUG 的朋友，給我的印像是很親切熱情的，對於開放源碼的社群參與都展現十足的活力。例如，大家都很熱心發表對 OpenMoko 專案的看法與意見，並且也都希望能參與 OpenMoko 專案，或是給予一些幫助。

Sean 給了一份大約 40 分鐘的 'Introduction to OpenMoko' 簡報。簡報大約是在晚上的 7 點 30 分開始，所以大約在 8 點 10 分左右就結束演說了。不說您不相信，由於 Beijing LUG 的朋友真的太熱情了，OpenMoko 和會場的朋友一直聊天並交換意見，直到晚上 11 點才「正式」結束這次的活動。當然囉，「非正式」的活動（續攤）仍持續著，至於我們一行人，由於有點累了，只好和大家道別了。

本次聚會活動是在北京俄羅斯大使館附近的「白夜餐廳」舉行，Beijing LUG 的聚會看起來應該是有免費啤酒的喔！因為幾乎人手一杯大號啤酒，整個會場就是一個大家庭的感覺。

這次 OpenMoko 主要是針對概念與市場部份做介紹，現場反應非常不錯，talking time 的互動也很熱絡。大家最關心的問題無非是「怎麼買 Neo」！當然，OpenMoko 這次大老遠到北京跟大家交流，自然也不會讓社群的朋友失望，只要是當天有到場的朋友，都可以直接向社群負責人登記團購。

社群的文化是很令人感動的，因為每一個社群的朋友都很關心 OpenMoko 專案的發展，不但提出許多意見，也主動表達願意協助的意願。有多位朋友向 OpenMoko 提出許多寶貴的意見，例如，一位在 Moto*la 公司服務的社群朋友，就向 OpenMoko 提出建言，與我們分享他在中國手機市場行銷的經驗。Beijing LUG 的協調人，也表示願意協助 OpenMoko 與中國 Linux 社群進行交流活動。

這種分享、貢獻、互助與交流的文化，是 open source community 最難能可貴的。]]></description>
         <link>https://www.jollen.org/blog/2007/09/openmoko_in_beijing_lug.html</link>
         <guid>https://www.jollen.org/blog/2007/09/openmoko_in_beijing_lug.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Sat, 01 Sep 2007 14:57:34 +0800</pubDate>
      </item>
            <item>
         <title>Linux frame buffer 驅動程式開發簡報下載</title>
         <description><![CDATA[今天有幸受邀發表了一場演講，和大家分享「Linux frame buffer 驅動程式開發」的議題。時間並不長（１個小時），因此只能針對整體架構與部份重點做介紹。簡報檔案下載 [<a href="http://www.jollen.org/blog/2007/08/29/Linux_FB_Driver_Intro_v0.2.pdf">Linux_FB_Driver_Intro_v0.2.pdf</a>]。

本次演講分為二個階段進行，首先是背景知識的部份，針對 Linux 2.6 的 driver model 做總覽；第二部份則是本次的講題，以 s3c2410fb 的驅動程式為例，介紹了 kernel subsystem 與底層（low-level）驅動程式的整個關係。

同時，也在演說中提到 register 與 callback 的觀念。由於 subsystem 的實作是分層架構，C 的實作採用 in-direct function call，所以不能依照傳統結構化程式的方式，單純以 function call 的流程來 trace 驅動程式。
]]></description>
         <link>https://www.jollen.org/blog/2007/08/linux_frame_buffer_lecture.html</link>
         <guid>https://www.jollen.org/blog/2007/08/linux_frame_buffer_lecture.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Wed, 29 Aug 2007 19:12:06 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 第二代 UI 出爐！</title>
         <description><![CDATA[OpenMoko 的第一代 UI 雖然做得太「PC」，不過這段時間也得到許多社群的回饋、意見、抱怨 ;-)

前幾天，OpenMoko 的第二代 UI 終於出現在 svn.openmoko.org 上了。第二代 UI 的最大變革是將整個操作方式，改以「finger-based」的方式重新實作，感謝 [<a href="http://blog.linux.org.tw/~jserv/">jserv</a>] 很努力的在幫大家 build 新的 OM，終於在昨天看到這傳說中的新 UI 了！

Will 也在他的 YouTube 頻道放了二個小短片：

<a href="http://www.youtube.com/watch?v=6s8i5uVU5AQ">http://www.youtube.com/watch?v=6s8i5uVU5AQ</a>
<a href="http://www.youtube.com/watch?v=njfeP0g22JY">http://www.youtube.com/watch?v=njfeP0g22JY</a>

OpenMoko 2007.2 也把「摜性托放」的功能加入了。]]></description>
         <link>https://www.jollen.org/blog/2007/08/openmoko_om2007_2.html</link>
         <guid>https://www.jollen.org/blog/2007/08/openmoko_om2007_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 01 Aug 2007 10:14:28 +0800</pubDate>
      </item>
            <item>
         <title>何謂 Monolithic Process？</title>
         <description><![CDATA[今天討論到 IPC 的程式設計議題時，順帶提及「monolithic process」的觀念，簡單紀錄如下。

傳統的應用程式架構方式，是把不同的「功能」以結構化程式設計觀念，寫成不同的函數。每當我需要使用某個功能時，便以「函數呼叫」的方使來叫用，當該函數結束工作並返回（return）後，主程式才能繼續「polling」下一個所要執行的功能。

這種架構應用程式的程式寫法，會讓整個應用程式是以「單一執行緒」的方式存在，此種「整合應用程式」的做法，便稱為「monolithic process」。例如，Linux kernel 本身也是屬於這種架構。

但是，在應用程式越來越複雜與多元化的今天，我們無法再使用這種方便來整合「不同的應用程式套件」，因為可能會變動到相當大量的程式碼。因此，便導入「應用程式框架」（application framework）的觀念來解決此一問題。

延伸閱讀：

* <a href="http://www.jollen.org/blog/2007/05/talking_d-bus.html">開放原碼架構設計：D-BUS 觀念小談</a>]]></description>
         <link>https://www.jollen.org/blog/2007/07/about_monolithic_process.html</link>
         <guid>https://www.jollen.org/blog/2007/07/about_monolithic_process.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Sun, 29 Jul 2007 13:59:25 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 於北京的首次公開演講</title>
         <description><![CDATA[OpenMoko 將在八月份的 Beijing Linux User Group 定期聚會後的一週，於北京親自向 Linux 社群的朋友介紹 Neo1973 手機並且現場回答相關問題。這是 OpenMoko 第一次在中國大陸的公開演講，時間是 8 月 21 日的晚上 7 點。

官方網站的公告請參考：

<a href="http://www.beijinglug.org/en/index.php?option=com_content&task=view&id=141&Itemid=1">http://www.beijinglug.org/en/index.php?option=com_content&task=view&id=141&Itemid=1</a>

這次的活動，將會由 OpenMoko 的 leader 'Sean' 親自至現場與社群朋友分享 OpenMoko 的理念並展示 Neo1973 實體手機。]]></description>
         <link>https://www.jollen.org/blog/2007/07/openmoko_in_beijinq.html</link>
         <guid>https://www.jollen.org/blog/2007/07/openmoko_in_beijinq.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Fri, 27 Jul 2007 19:38:00 +0800</pubDate>
      </item>
            <item>
         <title>Mobile Linux Initiative：專注 Intel 平臺的 Mobile Linux 開發社群</title>
         <description><![CDATA[一個以 Intel-based 平臺為主的 mobile Linux 開發機構日前公開了。這是一個專注於 Linux kernel、UI framework、browser、multimedia framework 以及 embedded linux image creation tools 為主的開發「社群」。可參閱 LinuxDevices.com 上的報導全文：

<a href="http://www.linuxdevices.com/news/NS9302797289.html">http://www,.linuxdevices.com/news/NS9302797289.html</a>

Mobile Linux initiative 的官方網站：

<a href="http://www.moblin.org/">http://www.moblin.org/</a>

Mobile Linux initiative 所提供的社群開發資源包含：documentation、mailing lists、bugzilla 以及 IRC 頻道。

Mobile Linux initiative 的 UI framework 方面採用 Hildon application framework，這也是一個以 GTK+ 為基礎的 application framework。]]></description>
         <link>https://www.jollen.org/blog/2007/07/_intelbased_mobile_linux.html</link>
         <guid>https://www.jollen.org/blog/2007/07/_intelbased_mobile_linux.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Wed, 25 Jul 2007 18:52:37 +0800</pubDate>
      </item>
            <item>
         <title>人類使用手機的習慣正在慢慢改變</title>
         <description><![CDATA[繼個人電腦後，下一波將會是行動電話的競爭，但目前的行動電話都是封閉且受限的。當行動電話軟體不再被硬體裝置受限時，硬體裝置的重要性將會消失，因為軟體開發者可以儘情地在同一個裝置上客製化或修改軟體，而不需要購買新的手機硬體。

OpenMoko就是一個這樣的平臺，Neo1973是第一支採用OpenMoko系統的手機。OpenMoko是全世界第一個100%開放的手機系統平臺（platform）。OpenMoko的CEO ‘Sean’於FIC（大眾電腦，臺灣）內部培育這項計畫，並於二零零六年十一月正式對外公開推出。

OpenMoko除了致力於完全免費且開放的手機平臺軟體外，也致力於對外宣導一個新的手機使用概念。Sean於第一場OpenMoko的公開演講中提到此概念，即一個呼應「Mobile 2.0」的理念，這也是近來開始被提出討論與分析的一個新的手機生態觀念。

以最簡單的句子來代表OpenMoko的概念，那就是「Free Your Phone」。

<strong>一個 Mobile 2.0 的概念正在形成</strong>

Mobile 1.0 的代表性產品「手機」改變了這個世界，並且也改變了人類的生活習慣。Mobile 1.0 強調的是手機的「通訊」（communications）能力，我們需要一個能隨時快速撥號的裝置，並且能與遠在千里之外的朋友即時談話；因此，在這個時代，feature phone 的概念被人類完全接受。

何謂 feature phone？由使用者習慣來說，feature phone 具備簡單的操作界面，並且是功能導向式的操作，沒有太多複雜的選單，同時，手機上也都有實體的撥號按鍵，可以讓我們很容易撥打「電話」。

Mobile 2.0 的改變是什麼？有一些線索，可以說明人類使用「手機」的習慣正在改變；也就是，我們的「手機」使用習慣正慢慢轉變為「Mobile 2.0」。另外一個使用手機的觀念轉變是「為什麼我不能客製化或自由修改我的手機」？

「為什麼我不能客製化或自由修改我的手機」Free Your Phone

每當我需要一個特定的新功能時（new feature），就必須重買一支新的手機，雖然我們可能已經習慣於這樣的手機使用習慣，但是由技術面的角度來說，這是多麼令人感到不解的事情。假如，能有一個開放的平臺，能讓我們隨心所慾客製化手機軟體，或是能自由地更換佈景主題，這不是一件令人愉快的事情嗎。

這就是未來的手機新生態觀念，即以使用者（end-user）為導向，由使用者客製化與設計的手機新使用習慣。Mobile 1.0是一個以廠商為中心，使用者「被迫」接受由廠商所提出的所有概念與產品的手機生態。

<strong>通訊習慣的改變</strong>

Mobile 1.0強調的是「打電話」的通訊功能，這也是我們唯一所使用的手機通訊功能，因此，在手機裝置上加上實體按鍵是聰明的做法，因為可以為我們帶來撥號的便利性。

喜愛旅遊的朋友，GPS 功能的PDA或手機可以說是不可或缺的重要裝備；具備GPS功能的手機，已經不再是強調打電話的功能了，而是更便利的GPS導航功能。導航是GPS手機重要的「通訊」功能，所以我們想要的是解析度夠好，且螢幕夠大的手機；我們並不會想要在傳統2.2”吋（或更小）的螢幕上看地圖，所以，何不把實體按鍵移除，並換上更大更好的螢幕呢。

除了GPS外，還有一些通訊方式正在改變我們使用手機的習慣：

-WiFi：使用手機上網，看新聞、天氣，或接收電子郵件。
-Bluetooth：透過籃芽與手機週邊連接。
-3G / 3.5G：提供更高速的資料傳輸系統，可在手機上接收多媒體資訊。
-Mesh networking：手機與手機之間可以經由WLAN直接點對點通訊，並透過附近的手機將資料「繞送」（漫遊）到目的手機。

OpenMoko利用Linux為做主要的作業系統平臺，底層系統可以整合現有的free software，再加上OpenMoko平臺，提供了一個完整的「mobile communication」手機平臺。

<strong>內容瀏覽取代電話撥打</strong>

以瀏覽內容為主，撥打電話為輔，可說是人類在Mobile 2.0時代最重要的使用習慣改變。以Apple的iPhone來說，或許許多人使用iPhone手機的目的可能並不是在於撥打電話的功能，而是將手機當作聽音樂、看電影的裝置，或是上網查查地圖，看看YouTube。]]></description>
         <link>https://www.jollen.org/blog/2007/07/mobile_for_end_users.html</link>
         <guid>https://www.jollen.org/blog/2007/07/mobile_for_end_users.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Open Mobile Platform</category>
        
        
         <pubDate>Wed, 25 Jul 2007 12:50:05 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 高層專訪</title>
         <description>有別於傳統封閉生態系統的手機產業，OpenMoko完全是以新的觀念在做手機。以下是OpenMoko的高層專訪（譯文）。在 Youtube 網站上找到一則今年一月份的 OpenMoko 專訪影片，受訪對象是 OpenMoko 的 leader &apos;Sean&apos;，影片網址：http://www.youtube.com/watch?v=jRvtAAXTIlg

以下節錄專訪重點，以及中文翻譯。感謝 Chelsea 及 Will 的幫忙 ;-)

So, OpenMoko is the first integrated software stack for mobile phones that starts with the kernel and goes all the way to the application and it’s completely open source, completed with GPL base. It&apos;s the first time that  application developers will be able to develop an application, royalty free, on a mobile communication platform.
OpenMoko是第一個針對行動電話的整合軟體架構，此架構採用Linux kernel，並且所有的應用程式也是基於Linux平臺來執行；而且，這是完全開放源碼，完全基於GPL版權的。這是第一個開發者可以在行動通訊平臺上發展免權利金的應用程式。

How we differ from other Linux phones?
跟其他Linux行動電話有什麼不同？

There are Motorola Linux phones out there now. They take the Linux kernel and they sort of just cramp this into a small device. The top layer is the middleware and the GUI in the application, that are really important for the whole ecosystem to be built, are not open in the spirit of Linux.
這邊也有Motorola的Linux行動電話。他們採用Linux kernel，並且只是這種將Linux放置於一個小型裝置。應用程式中，最上層是middleware與GUI，這對整個我們在建立的ecosystem來說是真的很重要的，但還沒有以Linux的精神公開出來。

Why it needs to be open?
為什麼需要開放？

Let me give you an example. Right now we’re talking, and one of the things the phone should be able to do is turn the microphone on and sense ambient noises to be able to determine whether or not there is a conversation in this room. So, if there was, you could be able to turn the ring-tone to silent. This is not possible if your phone was closed, only if it was open.
讓我舉一個例子，就我們現在提到的，手機應用要具備的事項之一就是開啟麥克風，並且感受週遭的聲音，能確認房間裡面是否有對話。所以，如果有的話，你就可以將手機的鈴聲調整安靜一點。如果你的手機是封閉系統，這就辦不到了，只有手機是開放的才有辦法。

What are the benefits?
好處是什麼呢？

And this is sort of the most unexpected success of this company. There is a lot of big name companies who are interested in this device who want to put their software application on top of it. For example, the Italian firefighting team wants to use this device to fight forest fires. In my opinion, the killer application is not so much an application, it’s called the application manager and this allows you to connect to two different feeds. Think of RSS for applications. One is a commercial feed, one is a community feed. The community is sort of the free for all. The commercial feed, these are applications, also free software, also open source software, that has been tested, ported and optimized by us. So if you are an end-user, you have access to thousands of applications that have been certified and tested, that won’t cause problems to your phone, you can just use them.
這就是這家公司的最不被預期能成功的特性。有許多大公司，都對我們的手機有興趣，他們想要放他們自己的軟體進來。例如，義大利的消防隊想要使用這個裝置來對抗森林大火。從我的觀點來說，殺手級應用程式並不是就只是應用程式而己，一個叫做application manager的東西，允許你連到二個不同的新聞供稿來源（feed）。試想RSS的應用程式。一個是商業性的，一個是社群提供的。社群的特性就是什麼都是免費的。商業性質的供稿來源，這些都是應用程式，是自由軟體，也是開放源碼軟體，這些軟體都被我們測試過、移植以及最佳化。因此，如果你是一個end-user，你會用到數以千計的應用程式，這些都是被認證與測試的，因此不會對你的電話造成傷害，你可以放心使用他們。</description>
         <link>https://www.jollen.org/blog/2007/07/openmoko_sean_interview.html</link>
         <guid>https://www.jollen.org/blog/2007/07/openmoko_sean_interview.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 25 Jul 2007 11:31:08 +0800</pubDate>
      </item>
            <item>
         <title>Neo1973 ads from The Community</title>
         <description><![CDATA[這就是社群有趣以及可愛的地方，在 OpenMoko 的 community mailing-list 上出現一則 post，krikstone 替 OpenMoko 做了一些網路廣告，並張貼在 YouTube 上：

<blockquote>Good and bad, here are some ads for openmoko and the neo1973 I did.  Sorry for the bad quality on some but there aren't many videos or pictures of the neo1973 besides the wiki.  I stayed with the "free your phone," aspect since advertising linux to the public is not going to work. I can make better ones if someone can get me high res photos and video (720x480 and above).

Playlist:
http://www.youtube.com/view_play_list?p=472DE700A3CC70A4

Individual:
http://www.youtube.com/watch?v=DCQ7dmGuAU8
http://www.youtube.com/watch?v=tQPjfUqp-dk
http://www.youtube.com/watch?v=4qP-K1HOMHk
http://www.youtube.com/watch?v=S--2HeQqjq4
http://www.youtube.com/watch?v=dpwxzEopg60
http://www.youtube.com/watch?v=EuG2hYiO9AU
http://www.youtube.com/watch?v=lGjY7tigdkA
http://www.youtube.com/watch?v=YR4ezMgRlWo
http://www.youtube.com/watch?v=OZC3mjRW5Tg
http://www.youtube.com/watch?v=GxsVFG7jHI8
http://www.youtube.com/watch?v=62kLhNngE20</blockquote>

果然夠有創意！]]></description>
         <link>https://www.jollen.org/blog/2007/07/neo1973_ads_from_the_community.html</link>
         <guid>https://www.jollen.org/blog/2007/07/neo1973_ads_from_the_community.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Thu, 05 Jul 2007 15:42:08 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 的 New Oceans</title>
         <description><![CDATA[關心 OpenMoko 計畫的朋友，應該都在上週收到 announce@lists.openmoko.org 的一消息了：

<blockquote> [openmoko-announce] New Oceans </blockquote>

信件提到，core team 歷經過幾個月的努力，將於將 OpenMoko 由一個小小的計畫團隊，經營成為一個大的 open mobile platfom（community）；OpenMoko 的 leader Sean 也在 email 裡透露 FIC 對 OpenMoko 成長的幫助，以及長輩的支持。

現在我們再回頭來檢視當初 OpenMoko 第一次現身時，與現在的模樣，你會發現 OpenMoko 確實已經不可同日而語，OpenMoko 之所以能宣佈已經成為一個「open mobile platform」，一定是有其道理的，當然這需要在參與的過程中做觀察，並深入剖析，才能了解一些奧妙；畢竟所有的人、事、物都是會進步的（或退步），所以我們也應該要更正面看待這個 project。

在這段 OpenMoko 的成長過程中，最難能可貴的並不是技術上的突破，或是產品的量產，而是許多來自於「社群」上朋友的鼓勵與幫助，這才是最令人感動的。另外，在這段時間，還有最令人敬配的二位重要幕後推手，就是 FIC 的 CEO 與 Chairman。二位長輩的支持與開放的心胸，無疑是 OpenMoko 計畫能順利進行的重要關鍵因素。引述 Sean 在 email 裡的一段文字：

<blockquote>The people inside FIC are amazingly open-minded. Our CEO and Chairman 
are the two greatest supporters inside this company. Earlier this month 
they did something courageous and support of the communities commitment. </blockquote>

Open source & community 計畫，需要的就是長輩們「open-minded」的支持與鼓勵，或許長輩們並不能很清楚地知道 OpenMoko 團隊的需求或是想法，但是二位重要的長輩，以如此開放的心胸，給予任何 OpenMoko 需要的資源，才是讓這個計畫順利進行的關鍵。畢竟，OpenMoko 計畫是無法以過去的經驗來分析的，更無法以過去的方法來執行，有時太多的羈絆，反而不利於專案的進行。

一個 open source 計畫要能成功，核心團隊必須要能掌握所有可能的資源，不可否認的是，FIC 與二位長輩對這個計畫的支持，絕對是 OpenMkoo 計畫未來繼續成長的關鍵。

Sean 在 email 裡除了提到 FIC 對 OpenMoko 的支持外，依照原先 OpenMoko 的計畫，也公開了開放銷售的時程了，詳情可參考 openmoko 的 mailing-list。另外，如果您對 OpenMoko 計畫有興趣，並且有高度的參與意願，無論是商業合作、學術交流、加入研發團隊、合作提案等等，也可以使用我的 email 與我連繫：jollen (at) jollen (dot) org。]]></description>
         <link>https://www.jollen.org/blog/2007/07/openmoko_new_oceans.html</link>
         <guid>https://www.jollen.org/blog/2007/07/openmoko_new_oceans.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Mon, 02 Jul 2007 12:59:32 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 的 UI application 分為三大類</title>
         <description>OpenMoko 將手機應用程式以 UI 為導向，並依程式的控制方式，將 application 區分為以下三大類：

* finger application
* stylus application
* panel application

Finger application 是以手指觸控為導向的應用程式，比如撥號界面。Stylus application 是以筆尖觸控為主的應用程式，例如最典型的 stylus application 為手寫辨試界面。Panel application 則是「嵌」在狀況欄與程式列的應用程式，例如電池計量圖示。

過往的 X 環境（lagecy X）並不適合 mobile device 直接使用，特別是對 mobile phone 而言。由於 UI &amp; user experiments 是手機的重頭戲與特色賣點（unique selling point），所以唯有重新打造自己的 UI &amp; application framework，並重寫介面，才能展現一台手機的概念與特色。這就是為什麼許多 Linux mobile phone 的 framework 並不是直接採取 porting &amp; integrating 現有的 xorg-server 的原因。

採用 Linux 做為手機作業系統的另外一個好處是，system-level 有非常多的 FOSS 專案與工具，並且與 UI 的整合也越來越容易。OpenMoko 的 core 也將 D-Bus 做「封裝」，讓這一段的程式設計變得更輕鬆。</description>
         <link>https://www.jollen.org/blog/2007/06/openmoko_ui_application.html</link>
         <guid>https://www.jollen.org/blog/2007/06/openmoko_ui_application.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 25 Jun 2007 23:05:27 +0800</pubDate>
      </item>
            <item>
         <title>關於 Mobile Device 的美工圖</title>
         <description>在為數眾多的 smart phone 產品中，都採用 QVGA（240x320）規格的 TFT-LCD panel，而這些 panel 都是 6x6x6 的 RGB 格式。因此，許多專為 mobile device 所發展的 rendering/text library 都會建議以此格式（6x6x6 RGB）來進行圖形介面與 art work 的設計。

比較另人好奇的地方是，6x6x6 的 RGB 格式要怎麼與 Linux kernel 的 framebuffer 驅動程式做對應。對 linux framebuffer 驅動程式有經驗的朋友都曉得，Linux framebuffer 驅動程式的 pixel format 為 4/8/16/24/32 BPP，可是我們的顯示 panel 是 18 BPP（6x6x6）的格式，要怎麼對應呢？

這是一個觀念問題。在數像處理的理論中，6x6x6 與 8x8x8 的 RGB 色彩「理論」上可視為相同，因為對人眼來說，少掉 1 或 2 個「bit plane」是對原來的圖像沒有影響的；不過，實際應用上仍會有一些「色差」的問題，所以需要一些方法來做「色差校對」。上週有一位朋友問到這個問題，利用端午佳節 google 了一些文獻，得到幾個簡單的方法，有機會再與大家分享。

由此可知，18 BPP 的 pixel format 在 Linux framebuffer 驅動程式中，可視為 8x8x8 即 24 BPP 的方式來處理。考量到 pixel 在記憶體的排列方式，以 s3c2410 為例，由於 1 pixel 佔用 1 word（4 bytes）的空間，所以用 0888 即 32 BPP 的方式來處理才方便。

對美工軟體來說，雖然我們的 panel 只能顯示 18 BPP 的顏色，但是對 art work 來說，只需要以傳統的 true color（0888 RGB）方式來繪圖即可，並不需要做特殊處理，也不需要特殊的繪圖軟體。而且，在大多數的例子中，也不需要考慮色差校對的問題。</description>
         <link>https://www.jollen.org/blog/2007/06/artwork_mobile_device.html</link>
         <guid>https://www.jollen.org/blog/2007/06/artwork_mobile_device.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 19 Jun 2007 23:37:52 +0800</pubDate>
      </item>
            <item>
         <title>KDrive 與 Embedded Linux</title>
         <description><![CDATA[<p>KDrive 也被叫做 TinyX，故名思意，這是一個小型的 X server 實作，主要是針對 embedded Linux 的應用所實作的版本。</p>
<p>有別於 xorg-server 裡的其他 X server，KDrive 並不基於以往的 XFree86 程式碼，而是由 
<a title="Keith Packard" href="http://en.wikipedia.org/wiki/Keith_Packard">Keith 
Packard</a> 所實作的新版本；目前，KDrive 已被應用在許多 embedded system 的專案中。例如，<a href="http://www.openmoko.org">OpenMoko</a> 
的 framework 就是使用 KDrive。</p>
<p>KDrive 另外一個特色是，他並不是像典型的 X server 是在動態時期去載入一些設定，而是在編譯時間就做好參數的設定。</p>
<p>Xorg-server 從 7.1 開始，已經將 KDrive 整合進來，只要下載 xorg-server 7.1 或更新版本的原始碼，裡頭就包含了 
KDrive。以 xorg-server 7.2 來說，要設定 X server 為 KDrive 的話，只要將 KDrive 參數打開，並將其他的 X 
server 關閉即可。例如：</p>
<blockquote>
	<p>&nbsp;./configure \<br>
	--host=arm-linux \<br>
	--disable-composite \<br>
	<font color="#000080"><b>--enable-kdrive</b></font> \<br>
	--disable-dga \<br>
	--disable-dri \<br>
	--disable-xinerama \<br>
	--disable-xf86misc \<br>
	--disable-xf86vidmode \<br>
	--disable-xorg \<br>
	--disable-xorgcfg \<br>
	--disable-xkb \<br>
	--disable-xnest \<br>
	--disable-xvfb \<br>
	--disable-xevie \<br>
	--disable-xwin \<br>
	--disable-xprint \<br>
	--disable-xtrap \<br>
	--disable-dmx \<br>
	--disable-w100 \<br>
	--disable-xsdl \<br>
	--disable-xephyr \<br>
	--with-default-font-path=built-ins \</p>
</blockquote>
<p>目前，KDrive 在 embedded Linux 的應用中，底層部份大多是透過 native framebuffer（fbdev、/dev/fb）來做圖形的顯示。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/06/kdrive_embedded_linux.html</link>
         <guid>https://www.jollen.org/blog/2007/06/kdrive_embedded_linux.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Fri, 08 Jun 2007 12:38:14 +0800</pubDate>
      </item>
            <item>
         <title>Ubuntu Mobile and Embedded 計畫近況</title>
         <description><![CDATA[<p>前陣子所注意的「<a href="https://blueprints.launchpad.net/~ubuntu-mobile">Ubuntu 
Mobile and Embedded</a>」計畫，最近又有了新發展。在前次的日記中提及 Ubuntu Mobile and Embedded 是針對 
MID（Mobile Innteret Devices）應用所發起的計畫，並且 Uuntu Mobile and Embedded 也會與 Intel 合作推出 
Nokia N800 web tablet 產品線。</p>
<p>今天在 Linuxdevices.com 上再度看到 UME（Ubuntu Mobile and Embedded）的新消息了：「<a href="http://linuxdevices.com/news/NS7778150149.html">Ubuntu's 
mobile and embedded project advances</a>」。關於此 project 的最新情況，引述一段報導原文如下：</p>
<blockquote>
	<p><font face="Arial,Helvetica" size="3">UME is working to port Ubuntu to 
	lpia, starting with an lpia bootloader and a port of the Hildon application 
	framework, best known for its use in the Nokia 770 and N800 Internet 
	tablets. </font></p>
</blockquote>
<p><a href="http://www.intel.com/technology/systems/lpia/">LPIA</a>（Low Power on 
Intel Architecture）是 Intel 最新的省電技術專案，UME 正在將 Ubuntu 移植到此架構上。UME 專注的目標是：</p>
<blockquote>
	<p><font face="Arial,Helvetica" size="3">UME goals apparently include 
	outstanding multimedia playback and web browsing, along wtih fast boot and 
	resume times, and small memory and disk footprints. </font></p>
</blockquote>
<p>其它更詳細的資訊可閱讀 <a href="https://wiki.ubuntu.com/MobileAndEmbedded">UME's Wiki</a>。這真是忙碌的一週，大部份時間都在做協調的工作，一些技術研究也停了好幾天了，正好利用一個早上的空擋來讀書，整理技術資料。</p>
<p>在今年的 Computex Taipei 上，看到了非常多 MID 
概念的產品，共同的特色當然就是他們都具備輕巧的工業設計，這是「入市」（進入市場競爭行列）的門票；此外，單就技術面來說，另一張門票應該會是開機時間、suspend與電池的技術掌握能力。</p>
<p>延伸閱讀</p>
<li class="archive-list-item">2007.05.11:
<a href="http://www.jollen.org/blog/2007/05/ubuntu_mobile_and_embedded_next.html">
Ubuntu Mobile and Embedded 後續報導</a></li>
<li class="archive-list-item">2007.05.08:
<a href="http://www.jollen.org/blog/2007/05/ubuntu_mobile_and_embedded.html">
Ubuntu Mobile and Embedded</a></li>]]></description>
         <link>https://www.jollen.org/blog/2007/06/ubuntu_mobile_embedded_advances.html</link>
         <guid>https://www.jollen.org/blog/2007/06/ubuntu_mobile_embedded_advances.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Fri, 08 Jun 2007 10:34:40 +0800</pubDate>
      </item>
            <item>
         <title>TurboLinux 的 PMP ？！</title>
         <description><![CDATA[<p>最近大家都在談 UMD（Ultra Mobile Device），今天在閱讀 LinuxDevices.com 新聞時突然瞄到「PMP」這個字，原來是「<a href="http://linuxdevices.com/news/NS6478809029.html">PMP 
doubles as personal Linux desktop</a>」。奇怪，PMP 不是已經是過時的 marketing 名詞了嗎？索性點過去看清楚：</p>
<blockquote>
	<p>Asian Linux distributor TurboLinux today announced the &quot;global launch&quot; of 
	its combination PMP (portable media player) and Linux boot device. </p>
</blockquote>
<p>這個 TurboLinux 的 PMP（稱為 Wizpy）其實和以往傳統的 PMP 
產品，概念上有點差異，這告訴我們一件事情，就是以前老師有教過的，產品的定位與名詞一定要突顯「差異」，否則人家一看是 PMP 
連標題都草草帶過，何況是要讀者移動滑鼠在「more」上點一下。</p>
<p>TurboLinux 的 PMP 有「boot device」的功能，引述一段報導的原文如下：</p>
<blockquote>
	<p><font face="Arial,Helvetica" size="3">When plugged into a PC, the &quot;Wizpy&quot; 
	player launches users into their own localized, personalized Linux-based 
	operating system environment.</font></p>
</blockquote>
<p>這其實有點像「USB 開機隨身碟」。但是，只把 Linux 做到 USB 隨身碟，再用 BIOS 的 USB 開機功能啟動 
Linux，似乎無法彰顯「device」的價值。Wizpy 現在也加入了 portable media player 的功能，馬上就能突顯 device 
的價值。</p>
<p>Wizpy 的報價大約是一萬塊台幣左右，姑且不論這個價格是否夠吸引人，單就產品的感覺來說，花一萬塊買「可開機的 USB 
身碟」是很不合理的事情，但是如果是花錢買「PMP、可開機」的 USB 裝置，掏錢的感覺就整個不一樣了。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/06/turbolinux_pmp.html</link>
         <guid>https://www.jollen.org/blog/2007/06/turbolinux_pmp.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Tue, 05 Jun 2007 23:29:31 +0800</pubDate>
      </item>
            <item>
         <title>週三的 stack-based coroutine 範例</title>
         <description><![CDATA[<p>週三至工研院進行教育訓練，其中提到了「Linux pthread」的議題，並透過作業系統的觀念（sys_fork），簡單分析了一下為何 Linux pthread 是「bad」。在講解過程中，展示了一個 video surveillance 的應用程式，說明如何改用 event-driven / event-loop / feedback scheduling 的方式來取代傳統的 multi-threaded 架構。</p>

<p>此外，也提到 co-routine（協同式多工）的概念，建議可先行閱讀 [<a href="http://blog.linux.org.tw/~jserv/">jserv</a>] 兄的「<a href="http://blog.linux.org.tw/~jserv/archives/001848.html">使用 coroutine 實做 user-level thread</a>」。</p>

<p>只以程式語言的技巧，來實作「多工」是一種很有用的做法，課程中，撰寫了一個「stack-based coroutine」的簡單實例，程式碼如下：</p>

<pre>
#include &lt;stdio.h>
#include &lt;pthread.h>

int count = 0;
int state = 0;

#define CHECK_POINT      \
if (count > 3) { \
   count = 0; \
   state = 1; \
   return; \
}

int foo(void)
{
   static unsigned int i;

   switch (state) {
        case 0: goto L0;
        case 1: goto L1;
   }
L0: /* start of function */
   i = 0;

   while (1) {
      i++;
      CHECK_POINT;
L1:
      printf("i = %d\n", i);
      if (i == 30) break;
      sleep(1);
   }

   printf("---------- foo exits ------------\n");
   state = 0;
   return 0;
}

void *counter(void *p)
{
   while (1) {
      count++;
      sleep(1);
   }
}

int main(void)
{
   pthread_t tid;
   pthread_create(&tid, NULL, &counter, NULL);

   count = 0;
   state = 0;

   while (1) {
      foo();
      printf("Enter event looping ...\n");
   };
}
</pre>

<p>Stack-based coroutine 是一種基於「function call」的「交錯執行」做法，最大的用途是，我們可以在「event handler」裡埋入「check point」，以達到「協同式」多工的效果。</p>

以下是執行結果：

<pre>
$ ./co
i = 1
i = 2
i = 3
i = 4
Enter event looping ...
i = 5
i = 6
i = 7
i = 8
Enter event looping ...
i = 9
i = 10
...
Enter event looping ...
i = 29
i = 30
---------- foo exits ------------
Enter event looping ...
i = 1
i = 2
i = 3
Enter event looping ...
i = 4
</pre>

非常有趣 ;-)]]></description>
         <link>https://www.jollen.org/blog/2007/05/stack_based_coroutine.html</link>
         <guid>https://www.jollen.org/blog/2007/05/stack_based_coroutine.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 30 May 2007 23:37:25 +0800</pubDate>
      </item>
            <item>
         <title> 開放原碼架構設計：Glib 小談 (1)</title>
         <description><![CDATA[<p><b>前言</b></p>
<p>就程式碼內部的組織來說，以往我們經常重新定義一些基本的東西，並透過這些 're-defidition' 的巨集或函數（亦稱為 wrapper function）來撰寫程式。有時，也能透過重新定義的方式，將框架（framework）的實作隱藏起來。這種技巧，大量被應用在許多軟體專案中。</p>
<p><b>Glib 小談</b></p>
<p>Glib 是 GTK+ 計畫下的「副產品」，Glib 原本<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->主要是給 
GTK+ 與 GNOME 使用，不過，現在也有越來越多的應用程式開始使用 Glib 來開發軟體。Glib 就本質來看，他提供一套重新定義過的 data 
type、event loop 框架、thread 框架、以及物件（object）系統。</p>
<p>Glib 以一種「宏觀」的方式，提供 C 語言一套高度可攜性、簡單<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->易學且通用的工具庫（toolkit）。由於 Glib 實在太優異了，除了 GTK+ 與 
GNOME 本身外，也有非常多的 Free / Open Source Software 也開始採用 Glib 來開發軟體。</p>
<p>Glib 主要提供的內容，以及使用 Glib 來撰寫程式<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->時需要具備的核心觀念，簡單說明如下。 </p>
<p><b>Data Type</b> </p>
<p>Glib 提供一組簡單易用且可攜性佳的 data type 定義。例如，我們想要擺放身高的資料，那麼傳統的 ANSI C 會以 unsigned int 
來宣告變數；但是，改用 Gli<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->b 來做的話，就要改用 guint 這個由 glib 提供的 data type 來宣告變數。 </p>
<p>以 guint 來說，Glib 的內部<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->實作為： </p>
<blockquote>
	<pre>typedef unsigned int	guint;</pre>
</blockquote>
<p>雖然 Glib 仍以 ANSI C 的 data type 來定義 guint，但提供了高度可攜的 data type。 </p>
<p><b>Event Loop</b> </p>
<p>以前，如果您要規劃一套<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->以事件驅動（event-driven）為主的應用程式框架（application framework），在 event loop 
的設計上常會遭遇到許多困難；我想，對 Linux system programming 有經驗的朋友，也一定曾遇過「如何與硬體事件整合」的麻煩題目。 </p>
<p>Event loop 的程式架構往往「長的很像」，所以我們以前常會思考，是否能寫出一套「萬用」的 event loop 
框架；以後，只要是撰寫事件驅動程應<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->用程式，就照著套用就好了！ </p>
<p>Glib 把這個想法實現了。<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->對於「core application」的支援，Glib 不僅提供一套宏觀且通用的 event loop 定義，連其他好用的技術也都一併定義完成了。</p>
<p>我們在規劃程式的核心架構時，經常使用的技術也都有包含在 Glib 的支援裡。除了 Event Loop 外，還包含： </p>
<ul>
	<li>Threads </li>
	<li>Dynamic Loading of Modules，也就是 plug-ins 的功能。 </li>
	<li>Memory Allocation </li>
	<li>IO Channels，也就是檔案、pipes 與 sockets 的功能。 </li>
	<li>Message Output and Debugging Functions，常常用 printf() 在做除錯嗎？Glib 
	也定義了一組通用的介面！ </li>
	<li>Message Logging
	</li>
</ul>
<p>天啊！真是太好了，Glib 把以前常用的<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->技術都「提出共通的部份」，讓我們更容易寫程式。 </p>
<p><b>Threads</b> </p>
<p>POSIX thread（亦稱為 pthread）是重要的 Linux user-space thread 函數庫，雖然 pthread 並無法做到所謂的「效能提昇」，但是因為 application framework 規劃的需要，因此還是被大量採用。以往，有鑑於 thread 的產生與管理經常都是「差不多」的程式碼，因此我也會定義一組屬於自己的 
thread 通用 API，大部份都是以 macro 形式來定義。Glib 也提供<!-- "Copyright(C) 2007 www.jollen.org. All rights reserved." -->一組彈性相當好、高度可移植且容易使用的 thread 介面，透過 Glib 
的定義來實作 multi-threaded application 不但簡單，而且也很容易管理所有的 thread。 《待續》</p>
<p><font size="2">來源: <a href="http://www.jollen.org">www.jollen.org</a> </font>
</p></strong>]]></description>
         <link>https://www.jollen.org/blog/2007/05/introduction_glib_1.html</link>
         <guid>https://www.jollen.org/blog/2007/05/introduction_glib_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Fri, 25 May 2007 10:31:03 +0800</pubDate>
      </item>
            <item>
         <title>開放原碼架構設計：D-BUS 觀念小談</title>
         <description><![CDATA[近期以來，因為工作上的需求，花費許多時間在開放原碼的架構設計上；最近已經將相關的作業都準備的差不多了，我想，可以跟大家分享一些架構設計的小觀念。

首先，先由 [<a href="http://www.freedesktop.org/wiki/Software/dbus">D-BUS</a>] 介紹起。使用 D-Bus 來取代傳統的 IPC，並與外部工具（eg. mjpegtools）做整合。

D-BUS 本身屬於 low-level 的 library（libdbus），實務上，我採用了 glib binding 來實作程式，以簡化工作：

<a href="http://dbus.freedesktop.org/doc/dbus-glib/index.html">http://dbus.freedesktop.org/doc/dbus-glib/index.html</a>

D-BUS 是一種 'message bus'，未來將取代傳統 IPC 的使用。傳統 IPC 實作，必須將程式架構成 monolithic process，因此在 process 的行為控制，以及 application 間的整合上，都很一定的難度，非常不易於實作「系統」。

引進 D-BUS 技術後，傳統的 monolithic process 被區分為「pieces of D-BUS services」，每個服務，都可以透過「D-BUS patch」來尋找並叫用（invoke）；透過 D-BUS 所建立的框架（framework）或是架構，解決了以往難以整合各種應用程式的困境。此外，D-BUS 目前也透過 kobject 與 kernel 做整合，如此一來，D-BUS 便能輕易整合 kernel、application 與 desktop，真正解決以往「系統整合」所遇到的障礙。

例如，如何實作「按鍵觸發應用程式」的系統？

傳統的做法，會讓程式設計師進入「不斷使用 low-level C API 來 try-and-error」的可怕輪迴，但是使用 D-BUS 的架構後，我們只要修改 device driver，將按鍵的動作透過 kobject event layer 傳送（經由 netlink socket）給 D-BUS 即可，D-BUS 會根據我們所定義的路徑，來叫用 application。

D-BUS 所提供的 message bus 分為二種：

    * system bus
    * local bus 

System bus 最大的用途便是整合 kernel 與 application；local bus 為 session bus，即訊息的傳遞只限於該 session。 

<font size="-1">來源: <a href="http://www.jollen.org">www.jollen.org</a></font>]]></description>
         <link>https://www.jollen.org/blog/2007/05/talking_d-bus.html</link>
         <guid>https://www.jollen.org/blog/2007/05/talking_d-bus.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Thu, 24 May 2007 11:04:52 +0800</pubDate>
      </item>
            <item>
         <title>無痛入門：OpenMoko Toolchain</title>
         <description><![CDATA[接續幾天提到的「<a href="http://www.jollen.org/blog/2007/05/openembedded_subversion.html">Embedded Linux 應用的痛處: OpenEmbedded</a>」。對初入門的使用者或是學生來說，比較好的方法就是儘量提供一個使用簡便的開發環境，才能降低 Embedded Linux 開發的「恐懼感」。

針對 OpenMoko 的開發應用來說，目前我初步提供一個簡單的解決方案，大致概念如下：

- 希望可以 build applications，而不是 build the 'system'。
- OpenMoko framework 本身以原始碼形式提供，因為這是 OpenMoko 的核心，並且我們也會經常修改程式碼。
- OpenMoko 的 architecture 部份，除了 OpenMoko framework 本身外，其它的 library、dependencies library 以及相關的 infrastructure（例如 sysvinit、busybox 等），以 binary 形式提供。
- 如上，可略過 OpenEmbedded 的 build 程序。
- 由此可知，重點在於 toolchain 的建立。

以下大略展示一下此環境的操作。首先，是「OpenMoko-toolchain」的部份：

<blockquote>-rw-r--r-- 1 jollen jollen 119555927 2007-05-17 21:05 openmoko-toolchain-20070517.tar.bz2</blockquote>

這個概念相當簡單：我為大家事先建立了 'openmoko-toolchain'，因此，想要在 OpenMoko framework 上發展應用的同學，只需要事先安裝此 toolchain；接著，再將 OpenMoko 本身利用此 toolchain 做編譯（cross-compile）即可。整個過程，能呈現出以往我們所熟悉的「Hello, World!」編譯觀念，對使用者來說，是非常直覺的。

再來，準備好要進行編譯的 OpenMoko framework：

<img alt="openmoko-toolchain-1.JPG" src="http://www.jollen.org/blog/2007/05/20/openmoko-toolchain-1.JPG" width="587" height="152" />

執行後，先喝杯咖啡，因為需要一點點時間才能完成編譯。

不過，這種方式還有幾個小問題，需要再花點時間解決：

- 利用 openmoko-toolchain 來編譯 OpenMoko framework，犧牲了許多彈性。
- 後續最重要的 deploy 工作，需要一個自動化的 script，由 openmok-toolchain 將大量的 library 佈署至 root filesystem。

以下是編譯完成的 'libmokoui'：

<img alt="openmoko-toolchain-2.JPG" src="http://www.jollen.org/blog/2007/05/20/openmoko-toolchain-2.JPG" width="760" height="288" />


提供 openmoko-toolchain 的目的是希望能打造一個「無痛入門」的環境；在 OpenEmbedded 提供 'OpenMoko Distribution' 前，應該可以透過這個環境，提供一個輕鬆的入門環境。]]></description>
         <link>https://www.jollen.org/blog/2007/05/openmoko_toolchain.html</link>
         <guid>https://www.jollen.org/blog/2007/05/openmoko_toolchain.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Sun, 20 May 2007 14:06:15 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 應用的痛處: OpenEmbedded</title>
         <description><![CDATA[日前與 [<a href="http://blog.linux.org.tw/~jserv">jserv</a>] 兄聊天時談論到 OpenMoko 的 build system，我們都一致覺得基於 OpenEmbedded 的 OpenMoko build system 太過於複雜，雖然 OpenEmbedded 目前是一套頗流行的 meta data build system，但是對一些想要了解 OpenMoko 的朋友來說，這反而是一個無形的門檻。

今天在 [<a href="http://orzlab.blogspot.com/">OrzLab</a>] 上看到 jserv 將 OpenEmbedded 的 'repository' 轉成 Subversion 系統的做法，全文可參閱 [<a href="http://orzlab.blogspot.com/2007/05/openembeddedrepositorysubversion.html">轉換OpenEmbedded的repository為Subversion系統</a>]。OpenEmbedded 原本採用的是 [<a href="http://monotone.ca/">monotone</a>] 版本管理系統，現在將 OpenEmbedded 的版本管理系統轉換成 SVN 後，就可以延續以往 SVN 的使用經驗來降低入門難度，並實現一些分支管理的需求。

關於分支管理方面，有一個小小的問題。OpenEmbedded 以 [<a href="http://developer.berlios.de/projects/bitbake">bitbake</a>] 為基礎，建構了一套「彈性相當高」的 meta data build system，因此，我們可以針對自己的需求，用力修改 OpenEmbedded 的 bb file，特別是對 deploy 的方式做調整。如果我們對 OpenEmbedded 的 *.bb 做了客製化修改，也做了一套自己的分支，那麼要怎麼跟 upstream 的更新做合併（同步）呢？

客製化的 OpenEmbedded 要怎麼與 upstream 的 OpenEmbedded 做合併（同步），針對這個問題，jserv 兄採用 SVN 與 SVK 來進行。如此一來，透過「合併」的方式便解決了如何同步「分支」與「upstream」的問題。Embedded Linux 經常需要做分支（branch）的開發，SVN/SVK 解決了許多「管理面」的問題，這部份可參考 OrzLab 的介紹 [<a href="http://orzlab.blogspot.com/2007/05/svk.html">SVK與嵌入式系統開發</a>]。

回到 OpenEmbedded 與 OpenMoko 的議題。OpenEmbedded 的目標是針對常見的 FOSS 套件編寫 metadata（即 bb file），完整的 OpenEmbedded 已經收錄超過 4000 個套件了。但是 OpenMoko framework 只使用到 OpenEmbedded 裡頭大約 30 個套件而己。

對於做出一個專給 OpenMoko 使用的 OpenEmbedded 分支來說，第一個工作便是對 OpenEmbedded 的 repository 進行瘦身簡化的工作。許多套件並未包含在 OpenMoko framework 裡，因此需要將不用的部份移除；這個「簡化」的工作實行上並不算太難。

只是，當我們後續維護此分支時，需要不定期與 upstream 做合併，原本採用 monotone 的 OpenEmbedded 在這個地方又要讓 Embedded Linux 開發者抱頭痛哭了。還好，將 OpenEmbedded 的版本管理系統轉換成 Subversion 後，一切的工作突然變得簡單許多。

不過，在教育訓練的經驗裡讓我知道，這樣的 build system 對入門者而言仍然是一個大門檻；因此，提供一個彈性較低，但使用上較簡單的環境，將會是一個重要的工作。日後再將這個部份做整理分享。]]></description>
         <link>https://www.jollen.org/blog/2007/05/openembedded_subversion.html</link>
         <guid>https://www.jollen.org/blog/2007/05/openembedded_subversion.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 15 May 2007 00:18:34 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Device 等於 PC</title>
         <description><![CDATA[就現代的硬體來說，embedded device 和 PC 的界線是越來越小了，雖然有時參與的 project 是 'embedded device'，但是技術本質上就好像在做 PC 一樣。本週二參加 tossug 的心得分享，主辦單位邀請到 OpenMoko 來演講，而在整個活動的過程中，也多少透露了這個重要的技術轉變概念；[<a href="http://magicdesign.blogspot.com/">魔法設計師</a>] 在他的 blog 也提到這點，全文可參考 [<a href="http://orzlab.blogspot.com">OrzLab</a>] 的<a href="http://orzlab.blogspot.com/2007/05/tossugfree-your-phone-openmoko.html">轉載</a>。

這幾天持續關注 Ubuntu Mobile and Embedded 專案的新聞，很湊巧，在 OpenMoko 的 mailing list 裡看到一則 Ubuntu 專案成員所回覆的訊息，全文轉貼如下：

============ cut ============
On 5/9/07, Vincent *** wrote:
> I just read this: http://www.linuxdevices.com/news/NS2403415870.html
> Contrary to widespread rumors, Ubuntu Mobile will not target mobile phones. Instead, the "mobile and embedded" version of the popular desktop Linux distribution will be developed in partnership with Intel, and will target "mobile Internet devices" (MIDs), along the lines of Nokia's N800 web tablet.

As a member of the Ubuntu project, I can confidently say that the
embedded space is one of the key targets since 3 releases ago, <strong>but on
what area of embedded, we haven't really tackled in detail</strong>. The most
realistic that we could probably deliver by October of this year, is
<strong>have it running very smoothly on Intel's Classmate PC (some of the
Edubuntu people have actually made it happen already) or similar
devices</strong>.

Best,

Jerome Gotangco 
============ end ============

重要的概念部份，我用了粗體字標示。所以說，MID 就是 PC ;-0]]></description>
         <link>https://www.jollen.org/blog/2007/05/embedded_device_is_pc.html</link>
         <guid>https://www.jollen.org/blog/2007/05/embedded_device_is_pc.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Fri, 11 May 2007 20:33:22 +0800</pubDate>
      </item>
            <item>
         <title>struct map_desc 與抽象化程式碼小談</title>
         <description><![CDATA[<p>前日與客戶進行 Linux device driver 教育訓練時，簡單討論到有關 JK2410 的 IO memory layout 描述方式。在 
linux 2.6.20.x 的 BSP 實作中，kernel 提供用來描述 board-level（machine）IO mapping 的資料結構稱為 '<i>struct 
map_desc</i>'，其定義如下：</p>
<blockquote>
	<pre>1 /*
2  *  linux/include/asm-arm/map.h
3  *
4  *  Copyright (C) 1999-2000 Russell King
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  *  Page table mapping constructs and function prototypes
11  */
12 <b><font size="3">struct map_desc</font></b> {
13         unsigned long virtual;
14         unsigned long pfn;
15         unsigned long length;
16         unsigned int type;
17 };
18 
19 #define MT_DEVICE               0
20 #define MT_CACHECLEAN           1
21 #define MT_MINICLEAN            2
22 #define MT_LOW_VECTORS          3
23 #define MT_HIGH_VECTORS         4
24 #define MT_MEMORY               5
25 #define MT_ROM                  6
26 #define MT_IXP2000_DEVICE       7
27 #define MT_NONSHARED_DEVICE     8
28 
29 #ifdef CONFIG_MMU
30 extern void iotable_init(struct map_desc *, int);
31 #else
32 #define iotable_init(map,num)   do { } while (0)
33 #endif
34 </pre>
</blockquote>
<p>這是一個非常「輕薄」的設計，以 Jollen-Kit! Pro. 開發板來說，我的實作程式碼如下：</p>
<blockquote>
	<pre>diff -Naur linux-2.6.20.4_orig/arch/arm/mach-s3c2410/mach-smdk2410.c linux-2.6.20.4/arch/arm/mach-s3c2410/mach-smdk2410.c
--- linux-2.6.20.4_orig/arch/arm/mach-s3c2410/mach-smdk2410.c	2007-03-24 03:52:51.000000000 +0800
+++ linux-2.6.20.4/arch/arm/mach-s3c2410/mach-smdk2410.c	2007-04-04 17:48:05.000000000 +0800
@@ -55,7 +55,13 @@
 #include "common-smdk.h"
 
 static struct map_desc smdk2410_iodesc[] __initdata = {
-  /* nothing here yet */
+    /* ISA IO Space mapping for CS8900. Memory space is selected by A24. */
+        {
+                .virtual        = 0xd0000000,
+                .pfn            = S3C24XX_PA_CS8900,
+                .length         = 0x00100000,
+                .type           = MT_DEVICE,
+        }
 };</pre>
</blockquote>
<p>程式碼簡單乾淨，這能讓不懂 kernel 的硬體開發人員也能快速上手維護。這讓我們了解到，一些「抽象」設計的 kernel 
程式碼，雖然在某個程度來說，會讓 developer 多花一些時間才能 trace 到底層，但卻能適度提供硬體開發人員一些彈性空間。例如，不需要懂 kernel 
的硬體工程師，也能有自行修改 IO mapping 設定的能力。</p>
<p>附帶一提，S3C24XX_PA_CS8900 是 IO memory 的實體位址（physical address）。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/05/struct_map_desc.html</link>
         <guid>https://www.jollen.org/blog/2007/05/struct_map_desc.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 11 May 2007 11:29:04 +0800</pubDate>
      </item>
            <item>
         <title>Ubuntu Mobile and Embedded 後續報導</title>
         <description><![CDATA[繼前一則日記「<a href="http://www.jollen.org/blog/2007/05/ubuntu_mobile_and_embedded.html">Ubuntu Mobile and Embedded</a>」紀錄了 Ubuntu 將發展 "Mobile and Embedded" 的 Linux distrio 專案後，後續追蹤 LinuxDevices.com 上的二則新聞如下：

- <a href="http://linuxdevices.com/news/NS2593988568.html">Ubuntu eyes mobile Linux devices</a>

「The Ubuntu Mobile and Embedded Project」將在今年 10 月份與 Ubuntu 7.10 同步推出 Mobile and Emedded 的 Linux distro，但是這個特殊版本並非是要給 Linux mobile phone 使用，可參考另一則相關報導：

- <a href="http://linuxdevices.com/news/NS2403415870.html">Ubuntu Mobile to target MIDs, not phones</a>

引述一段該報導如下：

<blockquote>Contrary to widespread rumors, Ubuntu Mobile will not target mobile phones. Instead, the "mobile and embedded" version of the popular desktop Linux distribution will be developed in partnership with Intel, and will target "mobile Internet devices" (MIDs), along the lines of Nokia's N800 web tablet.</blockquote>

MID（Mobile Innteret Devices）概念與先前 Intel 所提的 UMPC 相近；目前，據該報導所描述，Ubuntu Mobile and Embedded 將會與 Intel 合作，並率先針對 Nokia N800 web tablet 產品線做推展。

]]></description>
         <link>https://www.jollen.org/blog/2007/05/ubuntu_mobile_and_embedded_next.html</link>
         <guid>https://www.jollen.org/blog/2007/05/ubuntu_mobile_and_embedded_next.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Fri, 11 May 2007 00:18:56 +0800</pubDate>
      </item>
            <item>
         <title>Ubuntu Mobile and Embedded</title>
         <description><![CDATA[方才在 OpenMoko 的 community mailing-list 上看到一則 post，標題是：

<blockquote>Ubuntu Mobile and Embedded</blockquote>

原來，二天前在 ubuntu-devel-announce maling list 上出現了一則 post，全文詳 [<a href="https://lists.ubuntu.com/archives/ubuntu-devel-announce/2007-May/000289.html">Ubuntu Mobile and Embedded Edition</a>]。雖然還沒有正式的消息，不過，「Mobile and Embedded」可望再加入一個強而有力的大型 community 的支援。

Ubuntu 也開始參與 Mobile and Embedded 的 Linux distribution 製作，如果「Ubuntu Mobile and Embedded Edition」的 project 能順利，未來可望結合一些 Linux mobile framework，推出標準且好用的 Linux distro。根據 post 的內文了解，第一個釋出版本會是在今天的 10 月份，引述一段全文如下：

<blockquote>We will start more detailed planning at the Ubuntu Developer Summit next
week in Seville and the first release of this edition will be in October
with Ubuntu 7.10. If you are interested in the project, please get involved.
We will be working through our normal development processes on Launchpad,
the developer mailing lists and IRC.</blockquote>

有興趣的朋友，可透過 mailinst list 與 IRC 與此 project 保持同步。]]></description>
         <link>https://www.jollen.org/blog/2007/05/ubuntu_mobile_and_embedded.html</link>
         <guid>https://www.jollen.org/blog/2007/05/ubuntu_mobile_and_embedded.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Tue, 08 May 2007 00:20:17 +0800</pubDate>
      </item>
            <item>
         <title>Linux 2.6.21 報馬仔</title>
         <description><![CDATA[<p>Linux 2.6.21 於二個星期前（25 April, 2007）正式釋出，詳細更新可參考   
[<a href="http://kernel.org/pub/linux/kernel/v2.6/ChangeLog-2.6.21">ChangeLog-2.6.21</a>]   
；Linux 2.6.15   
後，每一次的正式版本都更新了相當多東西。觀察近年來   
kernel   
及相關應用的發展趨勢，注意到了一些現象，這些「異象」意謂著   
Linux kernel 的發展正進入所謂的「壯年期」，同時也說明了   
Linux kernel community 及 Linux ecosystem 正快速的進步與演化。</p>  
<p>雖然 Linux 2.6.21 
早在二星期前就釋出，不過近期業務繁忙，一直到這幾天才能安排這項工作。</p> 
<p>本次更新包含了以下幾個與我有關，或是較感興趣的項目：</p>
<p>1. VMI 驅動程式如果先前所提到的消息，已正式加入 kernel。</p>  
<p>2. 值得注意的是，KVM 驅動程式也加入了 &quot;paravirtualized&quot;   
的支援，&quot;paravirtualized KVM&quot; 驅動程式在 context-switch   
的效能上有相當大的改進。此部份可參考 LKML 裡的原文：[<a href="http://lkml.org/lkml/2007/1/5/205">[announce] [patch] KVM paravirtualization for Linux</a>]。</p>  
<p>3. &quot;devres&quot; subsystem   
的更新，讓我們在撰寫驅動程式時，更容易 allocation / map   
&quot;resource&quot;，例如：IRQ request、memory mapped I/O 等；devres   
簡化驅動程式的 resource allocation / map   
程式碼，詳細介紹可參考 [<a href="http://lwn.net/Articles/222860/">The managed resource API</a>]。</p>  
<p>4. Embedded Linux 的應用經常需要撰寫 GPIO   
相關的驅動程式，現在 Linux 2.6.21 加入了 GPIO API 的支援。</p>  
<p>5. ARM 的部份，加入了 Samsung s3c2443 的 SOC 支援</p>  
<p>6. 此外，知名的 Samsung s3c2410 clone 'QT2410' 也正式加入至 Linux   
2.6.21；另人有點小驚訝的是，這個 patch 是由 OpenMoko   
所貢獻的實作：</p>
<blockquote>
<pre>1 /* linux/arch/arm/mach-s3c2410/mach-qt2410.c
2  *
3  * Copyright (C) 2006 by OpenMoko, Inc.
4  * Author: Harald Welte &lt;laforge@openmoko.org&gt;
5  * All rights reserved.
6  *
...</pre>
</blockquote>
<p><b>延伸閱讀</b></p>
<p>2007.03.29: <a href="http://www.jollen.org/blog/2007/03/virtual_machine_interface_vmi.html">Kernel   
2.6.21 將正式加入 VMI（Virtual Machine Interface）</a></p>  
<p>2006.10.19: <a href="http://www.jollen.org/blog/2006/10/kernel_26_kvm_kernel_virtual_m.html">Kernel   
2.6 的 KVM (Kernel Virtual Machine) 驅動程式來了</a></p>  ]]></description>
         <link>https://www.jollen.org/blog/2007/05/linux_2_6_21_release.html</link>
         <guid>https://www.jollen.org/blog/2007/05/linux_2_6_21_release.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 03 May 2007 23:57:40 +0800</pubDate>
      </item>
            <item>
         <title>Neo1973 份量夠，但樂趣足！</title>
         <description><![CDATA[今天在閱讀 OpenMoko 的 mailing-list 時，讀到一篇有趣的文章 [<a href="http://lists.openmoko.org/pipermail/community/2007-May/004997.html">Size and weight considerations for future Openmoko devices</a>]。驚！社群上的朋友 Sven Neuhaus 將 Neo1973 /iPhone/M600i/LG Prada 做了個比較，不過對照項目並非功能面或是作業系統，而是針對裝置的大小與重量做比較，還挺有趣的。結果如下：

<pre>             Neo1973  iPhone   M600i   LG Prada
length (mm)   120.7    115      107     98.8
width         62       61       57      54
height        18.5     11.6     15      12
weight (g)    184      135      112     85
screen (inch) 2.8      3.5      2.6     3.0</pre>

作者提到，Neo1973 在大小與尺寸上都是相對較大較重的，不過由於目前 OpenMoko 釋出的手機都是「開發機」，所以當然未來一定會在這個部份做改進的；LG Prada 似乎是最近頗熱門的手機，一推出就 touching women's heart，而且「Pra*a」的品牌魅力好像也頗為驚人。

文中提及，Neo1973 在重量與厚度，幾乎是 LG Prada 的二倍，所以作者說「When designing future models, I'd really like to see something light and small (but still with a touchscreen).」；當然，以目前的硬體製造能力來說，這是能解決的問題。

後續的回應也都提到目前的 Neo1973 開發機都太重太厚，看來「輕」與「薄」會是手機的重要 feature；話說回來，以開發機來說，「輕、薄」不是重點，畢竟「樂趣」才是目前我們想要在這台機器上得到的東西！
]]></description>
         <link>https://www.jollen.org/blog/2007/05/neo1973_size_weight.html</link>
         <guid>https://www.jollen.org/blog/2007/05/neo1973_size_weight.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Wed, 02 May 2007 23:50:06 +0800</pubDate>
      </item>
            <item>
         <title>充實的星期六, OpenMoko Day</title>
         <description><![CDATA[最近聽到非常多與 Free / Open Source Software 有關的驚喜消息，特別與 Linux mobile phone 有關的新聞最讓我感到興趣。上週六 [<a href="http://blog.linux.org.tw/~jserv/">jserv</a>] 兄邀請我與 <a href="http://www.openmoko.org">OpenMoko</a> 創辦人餐聚，很榮幸再次與 <a href="http://moss-pultz.com/">Sean Moss-Pultz</a> 會面。這次餐聚也見到了 <a href="http://gnumonks.org/~laforge/weblog/">Harald Welte</a> 與 <a href="http://people.debian.org.tw/~chihchun/">Rex</a>，還有大眾電腦的幾位長輩；是一次非常有意義的餐聚。

在前往聚會地點的途中，我心中就暗自盤算了一個問題，打算向 Sean 請益。前幾天<a href="http://www.jollen.org/blog/2007/04/gnome_embedded_system_mboile.html">提到 GMAE 的成立</a>，GMAE 對 Linux mobile phone 會是一個非常重要的 milestone；GNOME 發佈此消息後，不但吸引許多人的興趣與注意，對 UI 技術架構的發展也產生重要影響。

由於在 GMAE 的會員名單中看到了 OpenMoko，最令我感興趣的當然就是 GMAE 與 OpenMoko 的關係，也就是 OpenMoko 在 GMAE 裡所扮演的角色；餐會時沒有機會問，後來「續攤」時，便抓住時機向 Sean 請益。當然，最後得到令人滿意的答案。

當天 Harald 也分享許多有關 Free / Open Source Software 的觀念以及想法，也讓我重新架構了一些觀念，並得到一些啟發，這是令人興奮的收獲。傳說中只穿黑色衣服的 hacker，當天果然是一身黑衣打扮；不過，這種裝扮還挺適合 Harald 兄的，有一種傳教士的感覺。;-)

一個充實的週六！

附帶一提。不久前，jserv 兄才在 IRC 上跟我提到 <a href="http://www.cacaojvm.org/">Cacao</a> for OpenMoko，很快地，今天在 <a href="http://orzlab.blogspot.com">OrzLab</a> 已經看到 [<a href="http://orzlab.blogspot.com/2007/04/javaopenmoko.html">當Java遇到OpenMoko</a>] 的消息了，實在是太棒了。OpenMoko gets Java support！]]></description>
         <link>https://www.jollen.org/blog/2007/04/nice_openmoko_day.html</link>
         <guid>https://www.jollen.org/blog/2007/04/nice_openmoko_day.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Mon, 30 Apr 2007 18:48:34 +0800</pubDate>
      </item>
            <item>
         <title>全球最大 Embedded System 博覽會, Nov. 14-16, in Yokohama, Japan.</title>
         <description><![CDATA[號稱全球最大的嵌入式系統博覽會　Embedded Technology Conference (ET2007)，將於 Nov. 14-16 於日本 Yokohama 舉辦，今年將有 28,000 名參與人員出席。本年度 ET2007 博覽會的主軸如下：

- consumer electronics
- automotive electronics
- mobile/ubiquitous computing
- factory automation/robotics

此外，重點的技術議題包含：

- multicore processors
- WiMAX
- Linux
- real-time design
- testing strategies for complex embedded systems

現場將有 860 個攤位，展示的「產品」與「服務」涵蓋軟體、硬體、發展環境與開展工具，由於嵌入式系統的複雜度升高，再加上開發社群的興盛，嵌入式系統的開發模式將不再只是以「工具」和「產品」為主的技術，「技術服務（service）」也成為相當重要的設計環節。

詳細內容可參閱 LinuxDevices.com 的新聞 [<a href="http://www.linuxdevices.com/news/NS4941327307.html">Embedded technology show set for November in Yokohama</a>]。ET2007 博覽會網站 [<a href="http://www.embeddedtech.net/ET/">http://www.embeddedtech.net/ET/</a>]。]]></description>
         <link>https://www.jollen.org/blog/2007/04/et2007_japan.html</link>
         <guid>https://www.jollen.org/blog/2007/04/et2007_japan.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 26 Apr 2007 21:35:54 +0800</pubDate>
      </item>
            <item>
         <title>GNOME Mobile Platform</title>
         <description><![CDATA[近期 Embedded Linux 的發展與應用，不斷出現許多新興社群；前幾日在 Embedded Linux Conference, Santa Clara, USA（April 19, 2007）上出現一則令人興奮的消息：

<blockquote>The GNOME Foundation announced today the creation of the GNOME Mobile & Embedded Initiative (GMAE), and a software platform for user experience development across a wide range of device profiles.</blockquote>

由 GNOME 所正式發佈的消息「<a href="http://www.gnome.org/press/releases/gmae.html">THE GNOME FOUNDATION AND INDUSTRY LEADERS JOIN TO CREATE GNOME MOBILE & EMBEDDED INITIATIVE</a>」。是的，創辦於 1990s 的知名 Linux 桌面環境計畫 GNOME 也正式參與嵌入式與行動裝置的開發領域了！

GNOME 是相當知名且具影響力的 Open Source /Free Software Project，如今 GNOME Project  正式成立 <a href="http://www.gnome.org/mobile/">GNOME Mobile & Embedded Initiative</a> 後，可預期 Embedded Linux / GUI 的發展與應用將出現重要的轉折。

GNOME Mobile & Embedded Initiative（GMAE）所提供的基礎架構自然是基於 GNOME 原本的桌面技術，目前，已經可以在 GMAE 網站上看到 GNOME Mobile Platform 的初步架構圖了！從技術面而言，GNOME 架構在 Xorg/Glib/D-Bus 的優雅環境之上，UI toolkit & widgets 採用 GTK+。

GNOME Mobile Platform 針對手機應用也嚴選出幾套卓越的 free software：

- <a href="http://bluez.org/">BlueZ</a>
- <a href="http://www.gnome.org/projects/evolution/arch.shtml">E-D-S</a>
- <a href="http://telepathy.freedesktop.org/">Telepathy</a>
- <a href="http://avahi.org/">Avahi</a>
- <a href="http://gstreamer.freedesktop.org/">GStreamer</a>
- <a href="http://projects.o-hand.com/matchbox/">Matchbox</a>
- <a href="http://www.gnome.org/projects/gconf/">GConf</a>
- <a href="http://www.gnome.org/projects/gconf/">GnomeVFS</a>

許多 open source /free software 經過近十年（或是超過十年）的發展，終將邁向成熟之路，陸續看到一些重量級的軟體釋出重要的 milestone 版本，緊接著 embedded Linux hackers 也針對 embedded system 的應用為 open source / free software 進行客製化與調校，顯見 embedded Linux 的應用正快速擴張中，並且開發模式也在快速改變。Customization & Integration & Tuning 會是重要的工作之一（即平臺的觀念）。

GNOME Desktop 經過 customization/integration/tuning 成為 GNOME Mobile Platform 後，對 Linux 手機的開發再投下一枚震撼彈。從技術角度來看，許多發展中或是成熟的 handhelds 與 Linux mobile phone 軟體（例如：OpenMoko）專案非常多都是基於 GTK+，而 GMAE 的成立也對相關 project 產生相當大的助力：

- 強化 GTK+，為相關的手機專案打下穩定的基礎建設。
- 整合 GNOME desktop 至手機
- 以 GNOME Mobile Platform 為分支的商業性質 open source / free software project，能更健全地與 GNOME desktop 或是 GMAE developers 協同發展。
- GNOME Mobile Platform 基礎架構健全優雅（Xorg/Glib/D-Bus），讓整合與發展更快速。
- 更多、更多、更多的 community & developers

Open source / free software project 所提供的商業服務與支援，會是未來 Linux 手機軟體發展模式中的一個重要環節。

<font size="-1"><a href="http://www.jollen.org/blog/">Jollen's Blog</a></font>]]></description>
         <link>https://www.jollen.org/blog/2007/04/gnome_embedded_system_mboile.html</link>
         <guid>https://www.jollen.org/blog/2007/04/gnome_embedded_system_mboile.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 23 Apr 2007 18:07:01 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] Spinlock in Process-Context Code</title>
         <description><![CDATA[<p><span lang="zh-tw">紀錄 Linux device driver 課程，關於「spinlock」的討論。</span></p>
<p>Kernel synchronization <span lang="zh-tw">的機制中，semaphore 有二種版本可供使用：</span></p>
<blockquote>
  <p><span lang="zh-tw">- Semaphore: down(), up()</span></p>
  <p><span lang="zh-tw">- Spinlock</span></p>
</blockquote>
<p><span lang="zh-tw">二種不同的 semaphore 實作的<!--Copyright (C) 2007 www.jollen.org. All rights reserved-->差異在於「P operation」的實作：</span></p>
<blockquote>
  <p><span lang="zh-tw">- down/up, 採取 sleeping 方式</span></p>
  <p><span lang="zh-tw">- spinlock, 採取 busy loop 方式</span></p>
</blockquote>
<p><span lang="zh-tw">在 interrupt handler 的實作上，由於 kernel 並非與 process context 做切換（kernel 執行的是 interrupt-context code），無法<!--Copyright (C) 2007 www.jollen.org. All rights reserved-->進行系統的 rescheduling，所以只能使用 spinlock 來進入 critical section。另外，我們提到，在 process-context mode 也常會看到 spinlock 的使用；在一般的 system call 
實作函數裡，為什麼用 spinlock 而不是典型的 down/up 呢？道理在於，「如果 P operation 的等待時間相當短暫（semaphore holding 的時間很短），那麼使用 busy-loop 
的方式便會比排程 task 為 INTERRUPTIBLE/UNINTERRUPTIBLE 狀態（sleep）來得更有效率」。</span></p>
<p><span lang="zh-tw">Linux 提供了三類的 spinlock API：</span></p>
<blockquote>
  <p><span lang="zh-tw">- IRQ don't care, 不對 local interrupt 做任<!--Copyright (C) 2007 www.jollen.org. All rights reserved-->何設定</span></p>
  <p><span lang="zh-tw">- IRQ disabling, 將 local interrupt 設定為 disable</span></p>
  <p><span lang="zh-tw">- IRQ save/restore, 儲存/還原 local interrupt 狀態</span></p>
</blockquote>
<p><span lang="zh-tw">狀況一：當我們<!--Copyright (C) 2007 www.jollen.org. All rights reserved-->選擇在 non-interrupt mode 或是 process-context 
mode 使用 busy-loop 版本的 semaphore 時，若要確保 critical section 為 atomic operation，或是與 interrupt handler 做同步，此時就必須使用 
IRQ disabling 的 spinlock，以確保 critical section 不會被 interrupt 打斷，且讓 processor 不會再接受任何的 interrupt。IRQ-disabling spinlock API 
如下：</span></p>
<blockquote>
  <p><span lang="zh-tw">- spin_lock_irq</span></p>
  <p><span lang="zh-tw">- spin_unlock_irq</span></p>
</blockquote>
<p><span lang="zh-tw">同學可查閱課堂所發的補充資料，觀念上的疑問，可透過 forum 來詢問。</span></p>
<p><font size="-1">來源: <a href="http://www.jollen.org/blog/">Jollen's Blog</a></font></p>]]></description>
         <link>https://www.jollen.org/blog/2007/04/spinlock_in_process_context_code.html</link>
         <guid>https://www.jollen.org/blog/2007/04/spinlock_in_process_context_code.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sun, 22 Apr 2007 14:21:36 +0800</pubDate>
      </item>
            <item>
         <title>[教育訓練紀錄] Mmap and remap_page_range(), in the nutshell.</title>
         <description><![CDATA[<p>繼 Linux programming 課程紀錄「<a href="http://www.jollen.org/blog/2007/03/mmap_vma.html">小談 mmap() 與 VMA</a>」後，今天在 Linux device driver 課程再聊到有關 VMA 與 mmap driver function 的重要觀念；重點紀錄如下。

 
</p>
<p>當 user-space 呼叫 mmap() system call wrapper function 後，kernel 會在 process address space 裡建立新的 VMA，並在 callback mmap driver function 時將「該」VMA 傳遞給<!--Copyright (C) 2007 www.jollen.org. All rights reserved-->我們的驅動程式。

 
</p>
<p>因此，在驅動程式裡，只需要利用 remap_page_range() 將 kernel-space 的 memory：

</p>
<blockquote>
  <p>- I/O memory
</p>
  <p>- RAM（reserved pages）
</p>
  <p>- Virtual address space（reserved pages）

</p>
</blockquote>
<p>對應到該 VMA 即可。最後，寫出了<!--Copyright (C) 2007 www.jollen.org. All rights reserved-->以下的 skeleton code： </p>
<blockquote>

<pre>// refer to: bttv-dirver.c
int do_card_mmap(struct vm_area_struct *vma, char *adr, unsigned long size)
{
    unsigned long start = (unsigned long)adr;
    unsigned long page, pos;

    pos = (unsigned long)BaseIOAddress;
#if 1  // page or the total size ?
    while (size >0) {
       page = pos;
       if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
	   return -EAGAIN;
       start+=PAGE_SIZE;
       pos+=PAGE_SIZE;
       size-=PAGE_SIZE;
    }
#else
    if (remap_page_range(vma, start, pos, size, PAGE_SHARED))
       return -EAGAIN;
#endif
    return 0;
}

// refer to: videodev.c
int card_mmap(struct file *filp, struct vm_area_struct *vma)
{
   do_card_mmap(vma, (char *)vma->vm_start, (unsigned long)(vma->vm_end-vma->vm_start));
   return 0;
}
/**************************************************/

struct file_operations card_fops = {
  open:card_open,
  release:card_release,
  ioctl:card_ioctl,
  mmap:card_mmap,
};
</pre>

</blockquote>
<p>remap_page_range() <span lang="zh-tw">的原理是去修改 page table，但是</span>要不要以 "page" 為最小單位做 page table 的修改<span lang="zh-tw">（呼叫 
'remap_page_range'）</span>，必須視<!--Copyright (C) 2007 www.jollen.org. All rights reserved-->情況而定！</p>
<p>同學對此部份如有觀念上的疑問，可透過 forum 討論。</p>
<p><font size="-1">來源: <a href="http://www.jollen.org/blog/">Jollen's Blog</a></font></p>]]></description>
         <link>https://www.jollen.org/blog/2007/04/mmap_remap_page_range_nutshell.html</link>
         <guid>https://www.jollen.org/blog/2007/04/mmap_remap_page_range_nutshell.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">教育訓練紀錄</category>
        
        
         <pubDate>Sun, 22 Apr 2007 10:34:26 +0800</pubDate>
      </item>
            <item>
         <title>Qemu 模擬週邊的兩三事</title>
         <description><![CDATA[這二天跟幾個朋友聊技術，大家似乎都對 qemu 如何模擬週邊硬體都很感興趣，特別是原理的部份，雖然我只簡單帶過幾句話，不過還是簡單紀錄一下，也能跟大家分享。

關於「qemu 怎麼模擬週邊硬體」，看似神秘，不過，先三不一沒有來澄清觀念：

- 不是模擬硬體的 waveform
- 不是模擬硬體的 timing
- 不是模擬硬體的 schematics
- 沒有對硬體的行為做完整模擬

那麼，qemu system emulator 倒底模擬週邊硬體什麼東西？

- 反應 driver (如 U-Boot 或 Linux device driver) 對 I/O port 或 I/O memory 的 read/write
- 保存與更新硬體狀態，並在 driver read/write I/O port 或 I/O memory 時做回應。

以上是最基本的二個觀念，在 qemu system emulator 的架構中，是透過 I/O registration 的方式達到。稍後我會補上較為明確的說明。

<font size="-1">來源: <a href="http://www.jollen.org/blog">Jollen's Blog</a></font>]]></description>
         <link>https://www.jollen.org/blog/2007/04/qemu_hw_emulation_how.html</link>
         <guid>https://www.jollen.org/blog/2007/04/qemu_hw_emulation_how.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">U-Boot , ARM9 and Pre-OS 相關</category>
        
        
         <pubDate>Thu, 19 Apr 2007 23:32:33 +0800</pubDate>
      </item>
            <item>
         <title>再聊 CPUState、qemu 的 gdbserver</title>
         <description><![CDATA[<p>前一<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->則日記「<a href="http://www.jollen.org/blog/2007/04/qemu_cpustate_object.html">小聊 qemu 的 CPUState</a>」提到：</p>
<blockquote>
<p><i>相關應用，例如，我們只要觀察 r15（pc）暫存器的值，就可以知道現在這台機器的程式執行位置。實際上的應用，像是 qemu 所實作的 gdbserver 即是透過此 object 的資訊，來回覆 gdb client 的命令。</i></p>   
</blockquote>
<p>以下展示<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->實際的操作畫面。</p>
<p>下圖是<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->利用 <a href="http://http://www.jollen.org/blog/2007/04/qemu_neo1973_openmoko_jk2410.html">jk2410-emulator</a>   
模擬 Jollen-Kit! Pro. 的開機畫面，使用的 kernel 是 2.6.20.4：</p>  
<p><img border="0" src="http://www.jollen.org/blog/2007/04/18/cgdb_2.PNG" width="633" height="385"></p>
<p>在 kernel 檢查 root filesystem 是否為 initramfs   
的時候，我們啟動了 gdb client，並透過 socket 連到 qemu 的   
gdbserver；為了方便進行 source-level debug，我們使用 cgdb 
來操作，下圖<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->是 debug 畫面：</p>  
<p><img border="0" src="http://www.jollen.org/blog/2007/04/18/cgdb_1.PNG" width="648" height="416"></p>
<p>我們可以看到，此時 kernel 正執行到 <i> lib/inflate.c</i> 裡的第 59x   
行程式，我們利用 next 做單步執行。這種「一邊跑 kernel，一邊看   
kernel 跑到哪一個原始檔<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->的哪一行」正是使用 qemu   
做「整體系統」除錯的關鍵突破之一。</p>
<p>回到 qemu，找到 gdbserver 的實作 'gdbstub.c'；當 host 的 gdb（gdb   
client）端對 target 端 gdb server 下命令時（透過 gdb remote protocol），qemu   
實作的 gdb server 會先由 <i>CPUState</i> 讀取 register   
的內容，再回應給 gdb client。gdb remote debug protocol  
顯然不是我們目前的重點，所以先找到 'gdbstub.c'   
關於「讀取 ARM 處理<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->器 register」的主要程式片斷如下：</p>  
<blockquote>
<pre>#elif defined (TARGET_ARM)
static int cpu_gdb_read_registers(<font color="#FF0000"><b>CPUState *env</b></font>, uint8_t *mem_buf)
{
    int i;
    uint8_t *ptr;

    ptr = mem_buf;
    /* 16 core integer registers (4 bytes each).  */
    for (i = 0; i &lt; 16; i++)
      {
        *(uint32_t *)ptr = tswapl(env-&gt;regs[i]);
        ptr += 4;
      }
    /* 8 FPA registers (12 bytes each), FPS (4 bytes).
       Not yet implemented.  */
    memset (ptr, 0, 8 * 12 + 4);
    ptr += 8 * 12 + 4;
    /* CPSR (4 bytes).  */
    *(uint32_t *)ptr = tswapl (cpsr_read(env));
    ptr += 4;

    return ptr - mem_buf;
}</pre>
</blockquote>
<p>gdb server 回傳給 host 端的資訊，已經由 host 端的 gdb 為我<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->們解析，並根據 debug sections 來找到 <i> $pc</i>  
對應的原始程式位置，即 source-level debug。</p>  
<p>由於這種除錯模式，是對「整粒 ARM  
處理器」做除錯，概念上很像是 jtag debug，因此會比用 kgdb 或  
kdb 來得實用。原理上來看，gdb 單純是對 qemu 做除錯，而 qemu 
是一個可以<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->讓「完整 Linux 系統」在上面執行的 'system emulator'（for  
various processors），所以我們可以對模<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->擬器裡頭的程式概念上「直接」進行除錯，因為對  
host 端的 gdb 來說，這是通透的（transparent）。</p> 
<p>再者，對除錯的二大重要<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->工作：source-level debug  
或是「單步執行」來說，qemu 單純只回應 <i>$pc</i> 給 gdb client，qemu  
並不會知道現在跑的程式是什麼，同時也不需再加入（或修改）gdb  
stub 至 kernel 或 application，這<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->樣的通透性，讓此種除錯模式可說是非常「萬能」的。不過，在實務上仍有一些不足的地方，例如，我們需要在  
host 端編譯出具備除錯資訊的程式，才能搭配 qemu 
來除錯，並且，也要小心<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->注意 host 端的 loaded ELF image 與  
run-time 的程式是否相同。</p> 
<p>高通透性並非全然都<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->是好的，例如，即使 loaded kernel image（vmlinux）與  
run-time kernel 不同，我們還是會看到「目前跑到 source code  
什麼地方」，因此目前的 source code 位置<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->與 <i>$pc</i>  
的位址是對不起來的。實務上，host gdb 透過 qemu 來除錯 kernel  
的高度通透性，也帶來了一些小問題，有機會再做整理分享，或許可在  
[<a href="http://orzlab.blogspot.com">OrzLab</a>] 的讀書會上提<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->出討論。</p>
<p>不過，如果是因為忘了重新打包 kernel，就直接跑 emulator 
進行除錯，而發生上述的錯<!--"Copyright (C) 2007 www.jollen.org. All rights reserved."-->誤，實在是非戰之罪。</p>
<p><font size="2">來源: <a href="http://www.jollen.org/blog/">Jollen's Blog</a></font></p> ]]></description>
         <link>https://www.jollen.org/blog/2007/04/cpustate_qemu_gdbserver.html</link>
         <guid>https://www.jollen.org/blog/2007/04/cpustate_qemu_gdbserver.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Wed, 18 Apr 2007 00:06:35 +0800</pubDate>
      </item>
            <item>
         <title> ELF 之 Dynamic Linking 觀念教學, #2: DT_NEEDED 基本概念</title>
         <description><![CDATA[<p>所謂「如何找到程式所需（depends）的程式庫」並非一件很技術面的事情，程式經常會呼叫程式庫裡的函數，並且由於    
Linux 是 shared library 系統，因此<!--"Copyright (C) 2007 www.jollen.org. All rights reserved." --> 
程式執行時，如何找到程式需要的程式庫，其實是一件很自然的事情。</p> 
<p>程式所需的程式庫，稱為相依性程式庫，即 library    
dependencies；並且，程式庫也會再相依其他的程式庫，因此，找出    
library dependencies 是一種拓樸邏輯<!--"Copyright (C) 2007 www.jollen.org. All rights reserved." -->關係。</p>  
<p>由 dynamic linker 的實作角度來說，程式的 library dependencies    
紀錄在 dynamic segment 的 DT_NEEDED entry 裡，dynamic linker 便是由    
DT_NEEDED 為起點，建立 library dependencies 關<!--"Copyright (C) 2007 www.jollen.org. All rights reserved." -->係圖，此圖又稱做    
NEEDED list。</p>   
<p>例如，若程式<!--"Copyright (C) 2007 www.jollen.org. All rights reserved." -->相依 libm 與 libc，那麼，DT_NEEDED entry    
的內容就會是：</p>
<blockquote>
<pre># readelf -d test

Dynamic segment at offset 0x494 contains 21 entries:
  Tag        Type                         Name/Value
<font color="#000080"> 0x00000001 (NEEDED)                     Shared library: [libm.so.6]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]</font>
 0x0000000c (INIT)                       0x80482a4
 0x0000000d (FINI)                       0x8048460
...</pre>
</blockquote>
<p>使用<i> ldd</i>  也能<!--"Copyright (C) 2007 www.jollen.org. All rights reserved." -->觀察到 library dependencies：</p>  

<blockquote>

<pre># ldd test
<font color="#000080">        libm.so.6 =&gt; /lib/tls/libm.so.6 (0x40026000)
        libc.so.6 =&gt; /lib/tls/libc.so.6 (0x42000000)</font>
        /lib/ld-linux.so.2 =&gt; /lib/ld-linux.so.2 (0x40000000)</pre>

</blockquote>
<p>熟悉 embedded Linux  
系統建置的朋友想必對這個部份不感到陌生，現在我們終於知道  
library dependencies 是紀錄在 dynamic segment 的 DT_NEEDED 裡，對於 dyn<!--"Copyright (C) 2007 www.jollen.org. All rights reserved." -->amic linking 的研究又跨出了一大步。</p> 

<p><font size="2">來源: <a href="http://www.jollen.org/blog/">Jollen's Blog</a></font></p> ]]></description>
         <link>https://www.jollen.org/blog/2007/04/elf_dynamic_linking_2_dt_need.html</link>
         <guid>https://www.jollen.org/blog/2007/04/elf_dynamic_linking_2_dt_need.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Tue, 17 Apr 2007 22:53:42 +0800</pubDate>
      </item>
            <item>
         <title> ELF 之 Dynamic Linking 觀念教學, #1: 前言與簡介</title>
         <description><![CDATA[<p>接續前些時日的「<a href="http://www.jollen.org/EmbeddedLinux/Program_Loading.html">Program 
Loading</a>」專欄，本系列日<!--webbot bot="PurpleText"
PREVIEW="&lt;!--&quot;Copyright (C) 2007 www.jollen.org. All rghts reserved.&quot;--&gt;"
-->
記將會介紹「dynamic     
linking」的核心觀念。首先，我們由 dynamic segment，即 .dynamic     
section 切入；如果您還不清楚為何以此做切入，建議先行閱讀 
'Program Loading' 專欄，以建立基礎概念。</p>
<p>Dynamic section 是整個 dynamic linking 體<!--webbot bot="PurpleText"
PREVIEW="&lt;!--&quot;Copyright (C) 2007 www.jollen.org. All rghts reserved.&quot;--&gt;"
-->
系的觀念核心，絕對不能放過；在     
System V ABI 規格中，有詳細的 dynamic section 介紹。Dynamic section    
裡頭包含多個 'entry'，每一個 entry    
紀載不同的資訊。首先，我們先找出 dynamic section     
的資料結構定義如下：</p>
<blockquote>
  <pre>/* Dynamic section entry.  */

typedef struct
{
  Elf32_Sword   d_tag;                  /* Dynamic entry type */
  union
    {
      Elf32_Word d_val;                 /* Integer value */
      Elf32_Addr d_ptr;                 /* Address value */
    } d_un;
} Elf32_Dyn;</pre>
</blockquote>
<p>'d_tag' 用來標記該 entry 的用途，'d_val' 或 'd_ptr' 則是該 entry    
的內容。完整的 dynamic section 有哪些 entry？以及每一個 entry 
的用途說明為何？此部<!--webbot bot="PurpleText"
PREVIEW="&lt;!--&quot;Copyright (C) 2007 www.jollen.org. All rghts reserved.&quot;--&gt;"
-->
份請參閱 System V ABI    
文件的說明，該文件有一份列表整理出所有的 dynamic section    
entry 及其說明，在此不再重覆刊載。不過，我們可由 <i>elf.h</i>   
來快速了解 dynamic section 裡的主要（generic, processor-independent） entry：</p>  

<blockquote>

<pre>/* Legal values for d_tag (dynamic entry type).  */

#define DT_NULL         0               /* Marks end of dynamic section */
#define DT_NEEDED       1               /* Name of needed library */
#define DT_PLTRELSZ     2               /* Size in bytes of PLT relocs */
#define DT_PLTGOT       3               /* Processor defined value */
#define DT_HASH         4               /* Address of symbol hash table */
#define DT_STRTAB       5               /* Address of string table */
#define DT_SYMTAB       6               /* Address of symbol table */
#define DT_RELA         7               /* Address of Rela relocs */
#define DT_RELASZ       8               /* Total size of Rela relocs */
#define DT_RELAENT      9               /* Size of one Rela reloc */
#define DT_STRSZ        10              /* Size of string table */
#define DT_SYMENT       11              /* Size of one symbol table entry */
#define DT_INIT         12              /* Address of init function */
#define DT_FINI         13              /* Address of termination function */
#define DT_SONAME       14              /* Name of shared object */
#define DT_RPATH        15              /* Library search path (deprecated) */
#define DT_SYMBOLIC     16              /* Start symbol search here */
#define DT_REL          17              /* Address of Rel relocs */
#define DT_RELSZ        18              /* Total size of Rel relocs */
#define DT_RELENT       19              /* Size of one Rel reloc */
#define DT_PLTREL       20              /* Type of reloc in PLT */
#define DT_DEBUG        21              /* For debugging; unspecified */
#define DT_TEXTREL      22              /* Reloc might modify .text */
#define DT_JMPREL       23              /* Address of PLT relocs */
#define DT_BIND_NOW     24              /* Process relocations of object */
#define DT_INIT_ARRAY   25              /* Array with addresses of init fct */
#define DT_FINI_ARRAY   26              /* Array with addresses of fini fct */
#define DT_INIT_ARRAYSZ 27              /* Size in bytes of DT_INIT_ARRAY */
#define DT_FINI_ARRAYSZ 28              /* Size in bytes of DT_FINI_ARRAY */
#define DT_RUNPATH      29              /* Library search path */
#define DT_FLAGS        30              /* Flags for the object being loaded */
#define DT_ENCODING     32              /* Start of encoded range */
#define DT_PREINIT_ARRAY 32             /* Array with addresses of preinit fct*/
#define DT_PREINIT_ARRAYSZ 33           /* size in bytes of DT_PREINIT_ARRAY */
#define DT_NUM          34              /* Number used */</pre>

</blockquote>

<p>本系列專欄將針對以<!--webbot bot="PurpleText"
PREVIEW="&lt;!--&quot;Copyright (C) 2007 www.jollen.org. All rghts reserved.&quot;--&gt;"
-->
下幾個重要的 entry type    
做觀念上的分享：</p>

<ul>
  <li>DT_NEEDED&nbsp;&nbsp;&nbsp;&nbsp; 1               /* Name of needed library */</li>  
  <li> DT_HASH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4               /* Address of symbol hash table */</li>  
  <li>DT_STRTAB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5               /* Address of string table */</li>  
  <li>DT_SYMTAB&nbsp;&nbsp;&nbsp;&nbsp; 6               /* Address of symbol table */</li>  
</ul>

<p>我們可使用 Linux 常見的 ELF 工具 - 'readelf' 來觀<!--webbot
bot="PurpleText"
PREVIEW="&lt;!--&quot;Copyright (C) 2007 www.jollen.org. All rghts reserved.&quot;--&gt;"
-->
察 ELF  
executables 的 dynamic segment 資訊：</p> 

<blockquote>

<pre># readelf -d test

Dynamic segment at offset 0x404 contains <u>20 entries</u>:
  Tag        Type                         Name/Value
<font color="#000080"> 0x00000001 (NEEDED)                     Shared library: [libc.so.6]</font>
 0x0000000c (INIT)                       0x8048230
 0x0000000d (FINI)                       0x80483d0
 <font color="#000080">0x00000004 (HASH)                       0x8048128
 0x00000005 (STRTAB)                     0x80481a0
 0x00000006 (SYMTAB)                     0x8048150</font>
 0x0000000a (STRSZ)                      75 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x80494e0
 0x00000002 (PLTRELSZ)                   16 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8048220
 0x00000011 (REL)                        0x8048218
 0x00000012 (RELSZ)                      8 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x80481f8
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x80481ec
 0x00000000 (NULL)                       0x0</pre>

</blockquote>
<p>對於 dynamic linking 的說明，後續會先行整理這 4 個 entry 
的主要觀念，然後再針對「如何找到程式所需（depends）的程式庫」做分析，並談論有關符號（symbol）的連結（relocation）處<!--webbot
bot="PurpleText"
PREVIEW="&lt;!--&quot;Copyright (C) 2007 www.jollen.org. All rghts reserved.&quot;--&gt;"
-->
理機制。</p>

<p><font size="2">來源: <a href="http://www.jollen.org/blog/">Jollen's Blog</a></font></p>]]></description>
         <link>https://www.jollen.org/blog/2007/04/elf_dynamic_linking_1.html</link>
         <guid>https://www.jollen.org/blog/2007/04/elf_dynamic_linking_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 16 Apr 2007 13:17:12 +0800</pubDate>
      </item>
            <item>
         <title>小聊 qemu 的 CPUState</title>
         <description><![CDATA[<p>今天繼續來聊 qemu 的內部實作。在 qemu 裡頭，有一個 object 叫做 CPUState，這是一個重要的 qemu data structure，其實作如下（<i>cpu-all.h</i>）：</p>
<blockquote>

<p>#elif defined(TARGET_ARM)<br>
#define CPUState CPUARMState<br> 
...</p>
</blockquote>

<p>我們以 ARM 做為 target，再找到 <i> target-arm/cpu.h</i>：</p>
<blockquote>
<pre>typedef struct <b>CPUARMState</b> {
    /* Regs for current mode.  */
    uint32_t regs[16];
    /* Frequently accessed CPSR bits are stored separately for efficiently.
       This contains all the other bits.  Use cpsr_{read,write} to access
       the whole CPSR.  */
    uint32_t uncached_cpsr;
    uint32_t spsr;

    /* Banked registers.  */
    uint32_t banked_spsr[6];
    uint32_t banked_r13[6];
    uint32_t banked_r14[6];
    
    /* These hold r8-r12.  */
    uint32_t usr_regs[5];
    uint32_t fiq_regs[5];
    
    /* cpsr flag cache for faster execution */
    uint32_t CF; /* 0 or 1 */
    uint32_t VF; /* V is the bit 31. All other bits are undefined */
    uint32_t NZF; /* N is bit 31. Z is computed from NZF */
    uint32_t QF; /* 0 or 1 */

    int thumb; /* 0 = arm mode, 1 = thumb mode */

    /* System control coprocessor (cp15) */
    struct {
        uint32_t c0_cpuid;
        uint32_t c0_cachetype;
        uint32_t c1_sys; /* System control register.  */
        uint32_t c1_coproc; /* Coprocessor access register.  */
        uint32_t c2; /* MMU translation table base.  */
        uint32_t c3; /* MMU domain access control register.  */
        uint32_t c5_insn; /* Fault status registers.  */
        uint32_t c5_data;
        uint32_t c6_insn; /* Fault address registers.  */
        uint32_t c6_data;
        uint32_t c9_insn; /* Cache lockdown registers.  */
        uint32_t c9_data;
        uint32_t c13_fcse; /* FCSE PID.  */
        uint32_t c13_context; /* Context ID.  */
        uint32_t c15_cpar; /* XScale Coprocessor Access Register */
    } cp15;

    /* Coprocessor IO used by peripherals */
    struct {
        ARMReadCPFunc *cp_read;
        ARMWriteCPFunc *cp_write;
        void *opaque;
    } cp[15];

    /* Internal CPU feature flags.  */
    uint32_t features;

    /* exception/interrupt handling */
    jmp_buf jmp_env;
    int exception_index;
    int interrupt_request;
    int user_mode_only;
    int halted;

    /* VFP coprocessor state.  */
    struct {
        float64 regs[16];

        uint32_t xregs[16];
        /* We store these fpcsr fields separately for convenience.  */
        int vec_len;
        int vec_stride;

        /* Temporary variables if we don't have spare fp regs.  */
        float32 tmp0s, tmp1s;
        float64 tmp0d, tmp1d;
        
        float_status fp_status;
    } vfp;

    /* iwMMXt coprocessor state.  */
    struct {
        uint64_t regs[16];
        uint64_t val;

        uint32_t cregs[16];
    } iwmmxt;

#if defined(CONFIG_USER_ONLY)
    /* For usermode syscall translation.  */
    int eabi;
#endif

    CPU_COMMON

} CPUARMState;</pre>

</blockquote>
<p>這個 object 是 qemu 用來紀錄 ARM 
處理器狀態的資料結構，舉例來說，CPUState.regs 紀錄了 ARM 
的所有 register 狀態。相關應用，例如，我們只要觀察 r15（pc）暫存器的值，就可以知道現在這台機器的程式執行位置。實際上的應用，像是 
qemu 所實作的 gdbserver 即是透過此 object 的資訊，來回覆 gdb 
client 的命令。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/04/qemu_cpustate_object.html</link>
         <guid>https://www.jollen.org/blog/2007/04/qemu_cpustate_object.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Wed, 11 Apr 2007 20:16:12 +0800</pubDate>
      </item>
            <item>
         <title>分享一下最近修改 jk2410-emulator 的心得</title>
         <description><![CDATA[來分享一下最近修改 jk2410-emulator 的心得，簡單紀錄重點，不過應該對有志研究 qemu 的 developer 有些啟發作用；<a href="http://orzlab.blogspot.com/">OrzLab</a> 有意舉辦 qemu 的讀書會，到時一定要跟大家多多請教，了解這個模擬器的明日之星。

對於 qemu internal 的研究，我還算是新手，幾天前從 <a href="http://blog.linux.org.tw/~jserv">jserv</a> 那拿來了 openmoko-emulator 後，再參照 qemu-neo1973，做了一些小修改。最近在研究 qemu PC emulator，由於 qemu 可以研究的地方真的很多，像是：

- dynamic translator（解譯 target CPU opcode 的核心）
- accelerator（kqemu）
- translation cache
- MMU emulation
- Linux system call 的處理
- System emulator（vl）
- (...more)
  
所以如果不能結合大家的力量來 hacking 的話，的確還挺辛苦的。不過，其中 system emulator 的實作因為和週邊硬體的模擬有關，所以花了比較多時間在這個部份。以 S3C2410A 這顆 SoC 的模擬來說，target 是使用 'target-arm/' ，不過，由於官方發佈的 mainstram 版本似乎沒有定義 ARM920T 的 CPU ID，所以由 OpenMoko & OpenedHand 所實作的 'qemu-neo1973' 在 'target-arm/cpu.h' 加了相關定義：

<blockquote>#define ARM_CPUID_ARM920T   0x41129200</blockquote>

在 qemu system emulator 初始化「machine」時，會需要指定 CPU ID；其它的部份，像是 SoC 的 controller 部份，qemu-neo1973 已經對 qemu 註冊好 IO 與 memory 的資訊，對於 S3C2410A 這顆玻璃製品的支援算是完備。

最後，則是 machine 的模擬部份。Qemu system emulator 提供一個介面如下：

<blockquote><pre>int qemu_register_machine(QEMUMachine *m)
{<br />
    QEMUMachine **pm;
    pm = &first_machine;
    while (*pm != NULL)
        pm = &(*pm)->next;
    m->next = NULL;
    *pm = m;
    return 0;
}</pre></blockquote>

可參考 hw/neo1973.c 的實作：

<blockquote><pre>QEMUMachine neo1973_machine = {
    "neo",
    "Neo1973 phone aka FIC GTA01 aka OpenMoko (S3C2410A)",
    neo_init,
};</pre></blockquote>

'Machine' 的註冊是寫在 system emulator 裡，即 vl.c，並透過 qemu-system-arm 的 '-M' 參數來指定所要模擬的 machine。關於 machine 的部份，雖然同樣是基於 S3C2410A 的 PCB，但是週邊與 GPIO 的規格還是有差異，因此 jk2410-emulator 必須修改此部份。

<img alt="20070410-1.JPG" src="http://www.jollen.org/blog/2007/04/10/20070410-1.JPG" width="500" height="500" />

到這裡就只剩實作問題了，不過，由於 qemu-neo1973 並沒有完整模擬所有 S3C2410 controller 的行為（register read/write），所以可能要先自己再實作一小部份，或是等待 OrzLab 更新 <a href="http://orzlab.blogspot.com/2007/04/openmokoneo1973.html">openmoko-emulator</a>。 

另外，順帶一提，qemu 的 ELF loader（elfload.c）是由 kernel 移植而來：

<blockquote>/* This is the Linux kernel elf-loading code, ported into user space */</blockquote>

可由 elfload.c:elf_exec() 看起。]]></description>
         <link>https://www.jollen.org/blog/2007/04/about_jk2410_emulator.html</link>
         <guid>https://www.jollen.org/blog/2007/04/about_jk2410_emulator.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 10 Apr 2007 22:03:51 +0800</pubDate>
      </item>
            <item>
         <title>qemu-neo1973 / openmoko-emulator / jk2410-emulator</title>
         <description><![CDATA[[<a href="http://www.openmoko.org">OpenMoko</a>] 將 [<a href="http://wiki.openmoko.org/wiki/Category:Neo1973_Hardware">GTA01</a>] 硬體模擬功能加入到 qemu 裡了。這是由 OpenMoko 與 OpenedHand 所貢獻的 patch，稱為 qemu-neo1973，可由 OpenMoko 的 svn 下載 [http://svn.openmoko.org/trunk/src/host/qemu-neo1973/]。此外，[<a href="http://orzlab.blogspot.com">OrzLab</a>] 日前也提供一份 "openmoko-emulator" 的模擬器，這是 qemu-neo1973 的分支版本，由 OrzLab 負責維護，並且也對 qemu 本身做了瘦身的動作。其它訊息可延伸閱讀 OrzLab 的 blog：

<a href="http://orzlab.blogspot.com/2007/04/openmokoneo1973.html">OpenMoko/Neo1973硬體模擬</a>

另外，我也將 OrzLab 所維護的 openmoko-emulator 另外再做了一個分支稱為 "jk2410-emulator"，也就是 Jollen-Kit! 開發板（JK2410）的模擬器。以下是初步的模擬畫面，上圖是 LCD panel，下圖是 serial 畫面。

<img alt="jk2410-emu-01.jpg" src="http://www.jollen.org/blog/2007/04/09/jk2410-emu-01.jpg" width="252" height="345" />

<img alt="jk2410-emu-02.jpg" src="http://www.jollen.org/blog/2007/04/09/jk2410-emu-02.jpg" width="657" height="410" border="1" />

由於 Jollen-Kit! 與 OpenMoko 目前所採用的硬體（GTA01）都是基於 S3C2410A SoC，且週邊也有許多可共用之處，因此 jk2410-emulator 將會以 openmoko-emulator 做為分支(branch)對象。

關於 OpenMoko 模擬的部份，請參閱上述 OrzLab 的 Blog。感謝 OpenMoko 的精神與貢獻，這就是 open source / free software 令人驚奇與感動的地方。模擬器技術的進步，確實對 Embedded Linux 的系統發展與流程產生不少改變，甚致，對測試與除錯的方式也有相當大的貢獻。

]]></description>
         <link>https://www.jollen.org/blog/2007/04/qemu_neo1973_openmoko_jk2410.html</link>
         <guid>https://www.jollen.org/blog/2007/04/qemu_neo1973_openmoko_jk2410.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Sun, 08 Apr 2007 23:31:19 +0800</pubDate>
      </item>
            <item>
         <title>關於 Acceptable Time 概念：運用在我的 Event-Driven 架構中</title>
         <description><![CDATA[前一篇日記提到，「feedback scheduler」是 time-triggered 模式，但我所規劃的<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->架構為「full user-space」的實作，因此，怎麼在 user-space 計算時間，便成為一個核心議題；考慮到我們所定義的「acceptable time」有別於真實的系統<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->時間（clock），或是即時性（real-time），因此我定義了一個基本時間單位：T。

若下一個時間單位為 T2，則 T_interval = T2 - T1，也就是每個 T 時間可以取得一個真實<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->的系統時間，即 T_interval，但這不是我們想要的「時間」；假設 T_interval = 10ms，那個在我的系統中，每個基本時間單位 T 會等於真實時間的 10ms<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->。

我們定義 TS(n) 為：每個事件到達後，一直到它的「control task」被「啟動」所需的花費時間（latency）。再定義 TD(n) 為：control task 啟動後，到執行結束所需要的時間長度。由於在這個系統裡，是不考慮<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->真實時間的，只用「幾個單位」來表示所謂的「時間」，所以可以簡單將此系統的概念表示成：

<blockquote>TS(n) = n * T<br />
TD(n) = n * T</blockquote>

這樣有什麼用呢？例如，TS(3) 表示某一事件到達後，需要 3 個時間單位（3 個 T）來啟動 control task；TD(15) 表示該 control task 執行需要 15 個 T。那麼，假設我們的 embedded Linux 系統有一個按鍵，按下後會出現目前電池用量資訊，若此按鍵的事件稱為 battery_key，便能根據使用經<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->驗將 battery_key 的 TS(n) 定義為 100，寫成：

<blockquote><!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->TS_battery_key(100)</blockquote>

也就是若 battery_key 使用上述的 "TS(n)" 系統，則 TS_battery_key(100) = TS(100) = 100T。換算成真實時間可能是 100*10ms = 1000ms，也就是 1 秒；我們定義按鍵按下到「開始出現畫面」的最長間隔是 1 秒，我們認<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->為這是使用者視覺上可接受的時間，此外，對使用者來說，1 秒與 0.9 或是 0.8 秒感覺都是一樣的，所以我們用 "T"，也就是「時間單位」的概念來陳述。

接著，要「秀好畫面」，也就是 control task 要做完整個工作的時間（execution time）如果是 150 個 T 的話，寫成 TD_battery_key，同樣也<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->是使用與 TS_battery_key 相同的時間系統，那麼：
<blockquote>
Turn around time of event 'battery_key' = TS_battery_key(100) + TD_battery_key(150) <!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->= T(100) + T(150) = 100T + 150T = 250T</blockquote>

我們「定義」整個動作在 300 個時間單位內完成都是可被「使用者接受」，或是「難以被使用者查覺」的，寫成 Taccept_battery_key(300)，當然，acceptable time 也必須與上述二個時間使用<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->相同的時間系統；最後得到，我的排程器必須設法做到：

<blockquote>Turn <!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->around time of event 'battery_key'  <= Taccept_battery_key(300) = T(300) </blockquote>

對於在我的 event-driven 架構中所<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->使用的 "acceptable time" 概念，先簡單介紹到這裡。（待續）

<font size="-1">來源：<a href="http://www.jollen.org/blog/">Jollen's Blog</a></font>]]></description>
         <link>https://www.jollen.org/blog/2007/04/acceptable_time_event_driven.html</link>
         <guid>https://www.jollen.org/blog/2007/04/acceptable_time_event_driven.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Sun, 08 Apr 2007 14:34:05 +0800</pubDate>
      </item>
            <item>
         <title>Embedded System 與使用者的互動：Event-Triggered or Not？</title>
         <description><![CDATA[Event-Driven（或 Event-Trigger）的架構研究，通常伴隨嵌入式系統與控制的主題出現，我們以「嵌入式控制系統（Embedded Control Systems）」來泛指相關的研究主題。目前，在 embedded control system 的研究領域中，以 event-trigger 和 feedback scheduling 的技術為主；embedded control system 目前是 pervasive computing 的當紅題目。相關的研究，可在 [<a href="http://www.sensornet.cn/fxia/">Feng XIA</a>] 的網站中找到許多有用的 paper，建議可先由 "Event-Triggered Feedback Scheduling of Embedded Control Systems [1]" 此篇 paper 著手，以了解更多這方面的最新研究。

不過，我的目的並不是在了解 embedded control system 的設計理論，而是希望由相關的研究文獻中，找到一些適合「顯示與互動式」的嵌入式軟體架構。大略分享一下目前的初步研究如下。

互動是指使用者與機器的互動，而我想要的架構在於能簡化與有效設計「顯示（GUI）與互動（user interaction）」的軟體；其中，使用者的輸入或點擊（即互動），都可以表示成 event。因此，在人機界面的設計層，我們可以想辦法把整個人機界面表示成一組事件，並做細分。

根據 [1] 所提出的 event-triggered feedback scheduling structure 基礎架構，feedback scheduler 分為 event logic（time-triggered）與 scheduling algorithm（event-triggered）；其中，time-trigger 是以一段固定的時間間隔（time interval）來觸發執行，並發出 event-trigger 請求給 scheduling algorithm。另外，此架構中，real-time control tasks 共用 CPU。

根據 [1] 的說明，feedback scheduler 的目的在於有效地排程 real-time control tasks，並且根據 [1] 的摸擬與結論得到，event-driven 的架構似乎能取得較佳的 real-time control 能力。以 event-driven 的基礎來做 real-time control 或許會比較容易規劃與測試整個系統，由於，我們只需要在特定事件被觸發（驅動）後，在規定的到期時間（deadline）內做完 control tasks 的工作即可達到軟即時（soft real-time）的要求，不管到期時間是長是短（microseconds, seconds or minutes），都不必像以往的 real-time system（or time-triggered）實作，必須強迫 task 在一個標準的時間內做完，因此，較容易實作出可接受的即時系統。

更進一步，甚致可以讓不同的 control tasks 定義不同的 deadline。目前，我針對相關的 event-driven & real-time control 理論架構與觀念，正設法簡化太過複雜的理論架構，或是太過華麗不切實際的理論基礎，希望能擬出一套精簡的 event-driven 的軟體架構。此架構的原則與目的與大家分享如下：

1. 必須夠簡單、精簡，程式碼要「少」；
2. 必須易於實作；
3. 適合應用在嵌入式系統的使用者互動軟體設計上；
4. Full user-space architecture, based-on POSIX threads；
5. 可以很容易套用，縮短「行前教育」時間；
6. 設法以「definition」或「generation」方式勾勒框架（framework），而不是以程式實作（implementation）方式寫框架。
7. 特定使用在顯示與互動的嵌入式軟體，所以改用 "acceptable time" 來取代 "real-time"；希望換個 term 來區別二者的差異；
8. ...(more)

對於使用者互動與顯示的應用，其實比較容易設計基礎架構，但是我希望程式碼方面可以越簡化越好，所以決定 re-write 我的 prototype ;-)

<strong>參考資料</strong>

1.   Feng Xia, and Youxian Sun:<a href="http://www.sensornet.cn/fxia/papers/PCC05.pdf"> Event-Triggered Feedback Scheduling of Embedded Control Systems</a>. Proc. 1st National Conference on Pervasive Computing, Kunming, China, Oct. 2005, pp.660-666.]]></description>
         <link>https://www.jollen.org/blog/2007/04/embedded_system_event_triggered_or_not.html</link>
         <guid>https://www.jollen.org/blog/2007/04/embedded_system_event_triggered_or_not.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Sat, 07 Apr 2007 13:16:12 +0800</pubDate>
      </item>
            <item>
         <title>Embedded System 與使用者的互動：關於 Event-Driven 架構</title>
         <description><![CDATA[關於 embedded system 的應用程式架構，最廣為人知的模型就是「event-driven」的做法。傳統的應用程式若是採用「結構化」的方式來設計，是否能適合 embedded system 的應用還需要進一步評估。

典型的 embedded system 裝置經常需要處理圖形顯示與使用者互動，通常使用者是透過像是 touch panel 或是 GPIO 的硬體介面與應用程式溝通，再由應用程式更新圖形顯示。

因此，有沒有任何做法，可以讓我們在 Linux 系統下以直觀的「事件」方式設計程式呢？經常看到的實作方式是使用 pthread 的方式來實作，透過 pthread 來產生多個執行緒，每個執行緒負責設定一個以上的事件；所有的事件都在 parent process 裡的 software loop 做判斷處理。這是最簡本的做法。

上述所提的 parent process 負責「case」（C 的 swith 敘述）所有的事件，並做出相對應的處理，這個 process 通常也被叫做 thread manager，或是 event manager。以一個簡單的 scenario 來說明此架構的應用。

小明想寫一個程式，當使用者按下 ARM9 開發板上的按鍵後，螢幕隨即出現一張圖片；這時，小明必須先定義「按鍵事件」的變數：

int EVENT_KEY_DOWN = 0x01;

我們必須產生一個 thread，來 "polling" 硬體上的按鍵，如果底層的驅動程式是 blocking read 的實作，那麼我們只需要一個簡單的 read() 即可。當 thread 判斷到按鍵被按下後，便將事件指定為 EVENT_KEY_DOWN；此時，您的系統應該有一個主程式（也就是 thread manager）在背景執行，並不停的做 loop-and-case 事件的動作：

<pre>
while (1) {
   switch (EVENT) {
      case EVENT_KEY_DOWN: ... break;
      case ...
   }
}</pre>

許多軟體，像是 Nano-X，就是採取這種實作策略。不過，在特定只有「圖形顯示 v.s. 互動」的應用場合裡，這種架構並不容易寫出「互動性」良好的程式。（待續）

]]></description>
         <link>https://www.jollen.org/blog/2007/04/embedded_system_event-driven.html</link>
         <guid>https://www.jollen.org/blog/2007/04/embedded_system_event-driven.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 04 Apr 2007 14:43:38 +0800</pubDate>
      </item>
            <item>
         <title>愚人節的禮物？OrzLab Launch Day</title>
         <description><![CDATA[好友 [<a href="http://blog.linux.org.tw/~jserv/">jserv</a>] 兄稍早捎來一則訊息...。以下引述 jserv 來信的部份內容：

<blockquote>最近跟朋友弄了一個實驗室，想法是能讓更多創意透過自由軟體，落實於嵌入式裝置或者特定的硬體中，這個實驗室名稱為 "Open RazzmatazZ Laboratory "，簡稱 OrzLab，一言以蔽之，訴求就是： "Open RazzmatazZ Laboratory (OrzLab) funds an environment for freedom and creativity based on Free Software and Open Source efforts." </blockquote>

此外，jserv 提到他的願景：

<blockquote>避免重複的專案開發，並針對嵌入式系統或系統整合廠商需求，提出有價值的解決方案，建構一系列自由軟體為基礎的系統架構。也因此，針對特定主題，開發出足夠好且高度創意的專案，並確保以 GPL / BSD License / MIT X License 釋出。 </blockquote>

習慣閱讀 jserv's blog 的朋友應該都會知道 jserv 兄有個很神秘的「實驗室」，並且，jserv 本年度的許多重點工作也會以「實驗室」的形態呈現。就在愚人節前幾日，收到 jserv 的訊息，他的神秘實驗室將在愚人節當天正式對外發佈，這真是一個愚人節的好禮物！因此，特別寫了這篇祝賀文，並將 jserv 兄的實驗室介紹給大家認識。

Jserv 的實驗室正式名稱為「OrzLab」，是的，就叫做「Orz」實驗室，「Orz」有個好聽的全名「Open RazzmatazZ Laboratory」。Jserv 曾提及他成立實驗室的想法，我的感覺是，這是熱情與使命感驅使下的產物。OrzLab 是非營利的「創作型」實驗室，並且訴求「自由」與「創意」；為 OrzLab 加入生命的是一群充滿技術情熱與想法的自由軟體開發者。

目前，OrzLab 已上線的專案計畫都是針對嵌入式系統的應用為主，並且每個專案技術都在儘力解決一些關鍵性的技術議題，這樣的工作確實呈現了實驗室的本質，這些專案的原發想法都頗具創意。此外，OrzLab 更是一個技術同好的交誼平臺，任何新奇有趣的專案計畫，都可以加入 OrzLab 成為正式的專案項目之一。

在這樣的想法之下所呈現的是一個相當自由的「創作平臺」，大家都能在此平臺上看到由 OrzLab 所「創作」的各種「Open Source / Free Software」；對於 Open Source / Free Software 充滿熱情的創作者，也能透過 OrzLab 讓大家看到自己的創作成果。

再引述一段 jserv 的來信：

<blockquote>OrzLab 成員來自台灣的自由軟體貢獻者，以 Linux/BSD 為主要的開發環境，對嵌入式系統開發、多國語文處理、Web 應用程式，或者是低階系統控制皆有一定的掌握度。</blockquote>

最後，大家最想知道的一定是「OrzLab」的網站。OrzLab 開發者的 blog 如下： 

<a href="http://orzlab.blogspot.com/ ">http://orzlab.blogspot.com/ </a>

OrzLab 現階段的開發方向為：

1. Realtime Linux/BSD 
2. Embedded i18n 
3. Embedded Web/Ajax framework 
4. System emulation & Optimization 

基於既定的開發方向，OrzLab 希望能取得硬體廠商的支持，提供硬體與技術支援，當然最終成果也會是以自由軟體的形式釋出。OrzLab 的協調人暨聯絡人為 jserv 兄，可透過 email 與他連繫：jserv.tw (at) gmail (dot) com。

Happy hacking and enjoy free / open source software ;-)]]></description>
         <link>https://www.jollen.org/blog/2007/03/orzlab_launch.html</link>
         <guid>https://www.jollen.org/blog/2007/03/orzlab_launch.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Sat, 31 Mar 2007 20:18:24 +0800</pubDate>
      </item>
            <item>
         <title>Kernel 2.6.21 將正式加入 VMI（Virtual Machine Interface）</title>
         <description><![CDATA[在 LinuxDevices.com 上的一則新聞「<a href="http://linuxdevices.com/news/NS7181773586.html">Linux kernel gains paravirtualization option</a>」指出，kernel 2.6.21 將加入 VMI（virtual machine interface）的功能，讓 kernel 得以「 modify itself for faster performance when run under a hypervisor」另外可參考這篇較完整的報導「<a href="http://www.eweek.com/article2/0,1759,2107818,00.asp">Linux Kernel to Add VMI</a>」。VMI 是由 VMware 公司所貢獻的實作，VMware 是知名度相當高的虛擬機器軟體商，同時也是老牌子的虛擬機器軟體，目前，Intel 與 AMD 已經將虛擬化技術列入重要的產品規劃（roadmap）項目裡了，再加上 VMware 公司的貢獻，可見虛擬化技術真的是 2007 年的重要技術發展趨勢之一。

上面所提到的「Hypervisor」指的是支援虛擬化處理器的虛擬機器管理軟體（Virtual Machine Management），這種技術其實已經存在非常久的時間了，只是以前都只應用在伺服器（RISC 架構）的領域，直到近年 Intel 與 AMD 開始將 hypervisor 技術導入其一般用途的處理器設計中，才引發一波新的風潮。

Hypervisor 技術能讓作業系統在 VMM 上執行，也就是作業系統虛擬化，這類的 VMM 較知名的有 VMware 與 Xen。「Virtualized operating system（VOS）」能解決執行效能與裝置管理面的問題；簡單表達 VMI 與 hypervisor 技術的架構關係如下：

<pre>
+----------------------------+  \
|    Linux Kernel            |    \
+----------------------------+      => Virtualized OS
|    VMI                     |    /
+----------------------------+  /
|    Hypervisors             |  ====> VMM (eg. Xen)
+----------------------------+
|   虛擬化功能 CPU           |
+----------------------------+
</pre>

而其中最主要的概念就是「VOS 與 VMM 之間的通訊介面」與「通訊協定」，也就是必須修改傳統的作業系統，讓 OS 能與「各種」VMM 更快、更有效率地溝通，這種技術便稱為「paravirtualization」。Linux kernel 的 paravirtualization 框架為 'paravirt_ops'。

未來，「虛擬化」將成為嵌入式系統的重要技術之一，不但是應用上將取得重要突破，就連傳統的「開發模式」也會有重大的變革。近期在許多嵌入式計算的會議發現，虛擬化的議題己成為核心議題之一；由於現代人類的溝通方式與協調技術進步很快，再加上數以千萬計的聰明大腦，「未來」可能只是一年後。

期待 Embedded Linux 第二個時代的來臨。]]></description>
         <link>https://www.jollen.org/blog/2007/03/virtual_machine_interface_vmi.html</link>
         <guid>https://www.jollen.org/blog/2007/03/virtual_machine_interface_vmi.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 29 Mar 2007 23:12:48 +0800</pubDate>
      </item>
            <item>
         <title>看 Linux kernel 應具備的首要觀念是？</title>
         <description><![CDATA[<p>Linux kernel 是狀態機還是結構化程式流程？</p>

<p>作業系統（Operating System）的研究，我們以 Linux kernel 的探討來說明<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->一些應有的正確觀念。</p>

<p>對於 Linux kernel 的研究，最經<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->常聽到有人提起「kernel source code」的研讀與分析，並且最常看到的研究方式為「尋找 kernel 
進入點，並依照程式流程（flow）做循序研究」，不過，這卻是一種「大部份情況下都錯誤」的研究方式。</p>
<p>由於電腦系統是一種 foreground-background system，並且 Linux kernel 是在此系統上的作業系統，因此整體的 
kernel 行為是「control 
patch」，也就是「控制權<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->的轉移」與「系統狀態的改變」，並不是「程式（函數）流程（flow）」，或是「程式結構（structure）」的問題；最近有朋友問起這方面的議題，
正好也在進行 Linux device driver 的 training，因此特別整理這則日記，來與大家分享，若有任何論述上的失誤，或是有不同的觀點，歡迎在<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->這裡留言
分享。</p>
<p>許多人對於「作業系統」的觀念可能<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->真的有點薄弱；具體來說，對於 kernel 內部原理的研究，其方法應該是：</p>
<blockquote>
<p dir="ltr">1. kernel 的開機<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->階段，是以「流程（flow）」的角度來做討論。</p>
<p dir="ltr">2. kernel 完成開機後，會執行 init process，由此正式切換到 F/B system 
的觀念，也就是整個系統是一個偌大的狀態機，整個系統內部是一連串複雜的控制權轉移動作。<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." --></p>
</blockquote>
<p dir="ltr">最近朋友希<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->望我能針對「kernel 的執行流程」做簡單介紹，但是這個問題在命題上應當有修正或是觀念澄清的空間。由於電腦本身是一種 
foreground-background system，因此 foreground 的工作會<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->影響 CPU 將控制權移交到 background 
的哪一個部份；如果把 user-space 當成 foreground，kernel-space 當成 background，那麼整體系統便能以下圖表示。</p>
<img alt="fb_system.jpg" src="http://www.jollen.org/blog/2007/03/24/fb_system.jpg" width="640" height="453" border="1" />
<p dir="ltr">Background 受到 foreground 行為的影響，拿以下二個 process 為例，雖然執行結果<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->相同，但因為 
foreground 內部的行為不同，因此 control path 也會不同：</p>
<blockquote>
<pre>/* Process <b>P</b> */

int main(void)
{
   char buf[] = "Hello, World!";

   printf("%s", buf);
   return 0;
}



/* Process <b>Q</b> */

#define STR "Hello, World!"

int main(void)
{
   char *buf;

   buf = malloc(strlen(STR)+1);
   strcpy(buf, STR);
   
   printf("%s", buf);
   return 0;
}</pre>
</blockquote>
<p dir="ltr">此時，仍是以「流程圖」邏輯來思<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->考的話，當然無法領會箇中奧妙。簡單二個不同的 process，CPU 
將控制權移轉到作業系統的路徑（也就是 kernel 
執行了哪些程式碼），居然有這麼大的差異。如果再把<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->排程器（scheduler）加入，那麼整個控制路徑很可能「不同時間執行同一程式」也會不同。Tricky！</p>
<p dir="ltr">以研究方法來說，命題時觀念的失誤，終將無法得到正確且完善的結論，因此「希望就 kernel 
的執行流程」做討論的命題，應當做修正，並且給定一個更明確<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->的題目；
再者，若無法體認 kernel 
是一個大型的狀態機，而仍以「流程與程式結構」的觀念來思考，除了在問題的描述會有相當大的誤差外，可能也會對「如何了解 kernel source 
code」的方法摸不著頭<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->緒。</p>
<p dir="ltr">由此可知，整<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->體系統在 kernel 開完機並執行 init process 後，就必須針對其「行為」做分析與研究，而不<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->是徘徊在「結構化 
C 程式」的圈圈裡；這個觀念證明了「逐行看 code」並不是研究 kernel 原理的正確方法。</p>
<p dir="ltr">Kernel 
本身的<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->行為是一連串複雜的狀態改變與控制權轉移，因此是狀態機的觀念。至於，研究方法是採用流程或是控制路徑的觀念進行，就看我們想要讀的是哪一個部份的 source 
code。前面提到研究 kernel 的二個階段與方式，當中的轉捩點為 init process，接續 init process，建議可先由 program execution（<a target="_blank" href="http://www.jollen.org/EmbeddedLinux/Process_Creation.html">process 
creation</a>）的觀念開始切入，以了解整個作<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->業系統的行為。</p>

<p dir="ltr">所以，kernel 的研<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->究，是以其動態（run-time）時期行為（behavior）的分析與觀察為主。</p>

<p dir="ltr"><font size="2">來源: <a href="http://www.jollen.org">www.jollen.org</a>
</font></p>]]></description>
         <link>https://www.jollen.org/blog/2007/03/importance_hacking_linux_kernel.html</link>
         <guid>https://www.jollen.org/blog/2007/03/importance_hacking_linux_kernel.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 24 Mar 2007 16:09:30 +0800</pubDate>
      </item>
            <item>
         <title>小結 Program Loading 觀念</title>
         <description><![CDATA[<p>由「<a href="http://www.jollen.org/EmbeddedLinux/Program_Loading.html">Program 
Loading</a>」專欄所得到的觀念如下。</p>
<p>外部程式（stored program）的執行是透過 fork system call，先將 current process 複製一份成為他的 child 
process。接著再透過 exec system call 將外部程式的 ELF image 載入，並取代原來的 process。Kernel ELF 
loader 會讀取 ELF image，並將 text segment 與 data segment 重新 mapping，接著再找到程式的 program 
interpreter。</p>
<p>外部程式的最初執行是由 program interpreter 開始，program interpreter 透過 dynamic segment 完成 
dynamic linking，此時完整的 process image 已建立完成；program interpreter 再跳到程式的進入點，也就是 C 
run-time library（crt?.o）。</p>
<p>以上二段文字說明了 program loading 的核心觀念，每一句子觀念都能做出一份不算短的分析報告；此階段只整理出 program loading 
的主要重點，接著再把「dynamic linking」的主要觀念做研究後，大家應該就可以大致掌握「程式執行」的系統行為了。</p>
<p>另外，補充 program interpreter 的幾個觀念：</p>
<ul>
	<li>program interpreter（<i>ld.so</i>）的存在形式有 shared object 與獨立執行檔二種。</li>
	<li>當 program interpreter 是以獨立執行檔執行時，表示將 program interpreter 當成 dynamic 
	loader/linker 來使用；通常是經由使用者直接下指令的方式來執行，program interpreter 在發展與測試階段時也使用這種方式。</li>
	<li>當 program interpreter 是以 shared object 形式存在時，表示 program interpreter 是經由 
	kernel 來取得控制權；這種方式是將 program interpreter 當成 dynamic linker 來使用，這是較常見的做法。</li>
</ul>
<p>以下是將 program interpreter 當做獨立執行檔的使用範例：</p>
<blockquote>
	<pre>$ ldd hello
        libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
$ /lib/ld-linux.so.2 ./hello
Hello, World!</pre>
</blockquote>
<p>關於 program loading 的分享到此做個小結，接下來將會整理有關 dynamic linking 的日記。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/03/program_loading_conclusion.html</link>
         <guid>https://www.jollen.org/blog/2007/03/program_loading_conclusion.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 23 Mar 2007 10:56:44 +0800</pubDate>
      </item>
            <item>
         <title>哪些是 Free / Open Source 軟體人員的基本條件？</title>
         <description><![CDATA[前陣子 OpenMoko 在社群裡徵求 Free / Open Source 的開發人員，讓我們來看一下，對於一個這麼有經驗的 Free / Open Source 團隊而言，他們開出的基本條件是什麼（引用 mailing-list 原文）：

<blockquote><pre>
2) We don't have enough UI / Application developers -- If anybody
 meets (or knows somebody who can meet) the following qualifications:

        * >= 2 years experience with GTK
        * object oriented design and implementation w/ GObject
        * experience with writing GUI applications from scratch,
        * has software quality assets like:
         o writing maintainable and reusable code
         o refactoring
         o design patterns
         o identifying and extracting common application code
           into frameworks

 Familiarity with collaborative development tools such as:

        * bugtracker,
        * source control management,
        * wiki,
        * mailing lists</pre></blockquote>

<p>就非技術面的條件來看，幾個重要的「協同開發工具」成為了 Free / Open Source 開發者的必要技能，包含撰寫 Wiki 的能力也被列入考量的項目了。由這則徵人啟事的內容來看，幾個值得「傳統軟體開發人員」重視的條件有：</p>

<p>1.  Free / Open Source 開發人員需要「refactoring」的技能：在不變動介面的前提下，進行現有程式碼的改善。</p>

<p>2.  Free / Open Source 開發人員必須具備「辨認以及取出」程式碼至「framework」的能力：避免重造車輪，不要再去做別人已經做好的工作。</p>

<p>3. 會使用 wiki：非常贊成將編寫 wiki 的能力納入考量。</p>

<p>4. 會使用 mailing lists：這是基本條件，透過 mailing list 與 Free / Open Source 專案保持連絡，並與世界同步。</p>

<p>今天與幾位朋友分享並討論到這個議題，希望也能與大家分享，或是交流想法。</p>

]]></description>
         <link>https://www.jollen.org/blog/2007/03/open_source_developer.html</link>
         <guid>https://www.jollen.org/blog/2007/03/open_source_developer.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 21 Mar 2007 13:38:04 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 接受社群的建議加入 WiFi 模組</title>
         <description><![CDATA[<p>OpenMoko 專案最近透過他們自己的 ecosystem 向社群請求有關 GPL WiFi 模組的廠商以及驅動程式，昨天 OpenMoko 宣佈了以下這則消息：</p>

<blockquote><pre>主旨: [openmoko-announce] RE: Crossroads 	完全表頭
 
Dear Community,

A big thanks again for all your feedback! We're meeting with vendors
this week and are optimistic about our chances to find a WiFi module.
We'll keep you all posted and announce the winner of the free phone once
we find the right solution.

As for the UI / Application developer work request, we're still
processing these emails. Please bare with us as we are just overwhelmed
with tasks trying to get the devices built this month.

-Sean</pre></blockquote>

<p>從「request for GPL WiFi module」到「meeting with vendors」也不過短短幾天，再度另人見識到開放源碼社群的力量。由於 OpenMoko 是一個完全開放的 Linux 手機平臺，並且 OpenMoko 所形成的 ecosystem 正在快速成熟穩定中，因此可以預見不久的未來，Linux phone 的發展與應用將會更加完整，解決方案（solution）的可獲取性（availability）也會更好。</p>

<strong>延伸閱讀</strong>

<ul>
	<li>2007.02.24:
	<a href="http://www.jollen.org/blog/2007/02/openmoko_gsm_modem_gsmd.html">
	OpenMoko 的 gsmd：Linux 手機的 GSM Modem 通訊程式</a></li>
	<li>2007.02.17:
	<a href="http://www.jollen.org/blog/2007/02/openmoko_announce.html">OpenMoko 
	釋出原始碼了</a></li>
	<li>2007.01.22:
	<a href="http://www.jollen.org/blog/2007/01/openmoko_announce.html">OpenMoko 
	準備舉旗進攻了</a></li>
	<li>2006.11.08:
	<a href="http://www.jollen.org/blog/2006/11/mobile_20_openmoko_linux_smart.html">
	「Mobile 2.0 的思考」與第一隻採用 OpenMoko 的 Linux Smartphone</a></li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2007/03/openmoko_wifi_available.html</link>
         <guid>https://www.jollen.org/blog/2007/03/openmoko_wifi_available.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Tue, 20 Mar 2007 11:40:06 +0800</pubDate>
      </item>
            <item>
         <title>小談 mmap() 與 VMA</title>
         <description><![CDATA[<p dir="ltr">最近在 program loader 專欄裡，整理並分享了 kernel 的 ELF loader 主題；接著下來就是 
dynamic linking 的議題了。在正式介紹 dynamic linking 前，先來簡單提一下「memory 
mapping」的觀念；近期在進行 Linux programming 的教育訓練，正好可以跟同學做個小討論。</p>
<p dir="ltr">在 IPC 的課程中提到 mapped memory 的行程間通訊機制，是透過「shared file」來做訊息的傳遞；將 
shared file mapping 到 process address space 的 system call 為 'mmap()'。以下是其中一個簡單的程式範例
（<i>mmap_write.c</i>）：</p>
<blockquote>
  <pre>#include &lt;stdio.h&gt; 
#include &lt;fcntl.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/mman.h&gt;
#include &lt;sys/stat.h&gt;
#define FILE_LENGTH 0x400

int main(int argc, char *argv[])
{
   int fd;
   void *map_memory;

   /* Open a file to be mapped. */
   fd = open("/tmp/shared_file", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
   lseek(fd, FILE_LENGTH+1, SEEK_SET);
   write(fd, "", 1);
   lseek(fd, 0, SEEK_SET);

   /* Create map memory. */
   <b>map_memory = mmap(0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0);
   close(fd);</b>

   /* Write to mapped memory. */
   if (strlen(argv[1]) &lt; FILE_LENGTH)
      sprintf((char *)map_memory, "%s", argv[1]);

   <b><font color="#000080">sleep(10);</font></b>

   exit(0);
}</pre>
</blockquote>
<p dir="ltr">當程式將檔案 mapping 到自己的記憶體空間（process address space）時，mmap system call 
便會建立相對應的 VMA 區段；觀念上來說，只要透過 mmap system call 將檔案 mapping 到記憶體，便會產生對應的 VMA。</p>

<p dir="ltr">修改一下範例程式，讓程式在結束前小睡 10 秒鐘，並做觀察：</p>

<blockquote>

<pre># ./mmap_write hello&
[1] 14667
# cat /proc/14667/maps
08048000-08049000 r-xp 00000000 16:42 213757     /tmp/mmap_write
08049000-0804a000 rw-p 00000000 16:42 213757     /tmp/mmap_write
40000000-40015000 r-xp 00000000 16:42 12828674   /lib/ld-2.3.2.so
40015000-40016000 rw-p 00014000 16:42 12828674   /lib/ld-2.3.2.so
40016000-40017000 rw-p 00000000 00:00 0
<font color="#0000FF">40017000-40018000 -w-s 00000000 16:42 213753     /tmp/shared_file</font>
42000000-4212e000 r-xp 00000000 16:42 13991938   /lib/tls/libc-2.3.2.so
4212e000-42131000 rw-p 0012e000 16:42 13991938   /lib/tls/libc-2.3.2.so
42131000-42133000 rw-p 00000000 00:00 0
bfffe000-c0000000 rwxp fffff000 00:00 0</pre>

</blockquote>

<p dir="ltr">除了 mmap system call 外，IPC 技術中的 'shared memory' 同樣也能觀察到對應的 VMA 區段。</p>

<p dir="ltr"><b>延伸閱讀</b></p>

<ul>
  <li dir="ltr">

<p dir="ltr">2007.01.05:
<a href="http://www.jollen.org/blog/2007/01/linux_virtual_memory_areas_vma.html">
Linux 的 Virtual Memory Areas（VMA）：基本概念介紹</a></p>

  </li>
  <li dir="ltr">

<p dir="ltr">2007.01.15:
<a href="http://www.jollen.org/blog/2007/01/process_vma.html">Linux 的 Virtual 
Memory Areas（VMA）：Process 與 VMA 整體觀念</a></p>

  </li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2007/03/mmap_vma.html</link>
         <guid>https://www.jollen.org/blog/2007/03/mmap_vma.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Fri, 16 Mar 2007 17:38:00 +0800</pubDate>
      </item>
            <item>
         <title>ELF 之 Program Loading 教學文件, #4: Program Loader 整體流程</title>
         <description><![CDATA[<p>Program loader 的整體流程如下：<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." --></p>
<p>1. 使用者在 shell 模式下執行外部<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->程式（stored program）。</p>
<p>2. shell 以<i> </i>fork+exec system call 的方式執<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->行外部程式。</p>
<p>3. 透過 0x80H 軟體中斷（x86）叫<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->用 kernel 的 exec<i>（sys_execvp</i>）system call service。</p>
<table border="1" width="100%" id="table1" cellspacing="0" bordercolor="#000080">
	<tr>
		<td><b>Kernel Space Program Loa</b><!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." --><b>der</b><p>4. Exec system call 呼叫 program loader（ELF loader），將 process image（ELF 
image）載入。<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." --></p>
<p>5. Prog<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->ram loader 找到 <i>PT_INTERP</i> segment。</p>
<p>6. Program loader 將 <i>PT_LOAD</i>&nbsp; segment mapping 為<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->新的 text/data segment，text 
segment 由 vaddr. 0x0804_8000 開始，data segment 緊接其後。</p>
<p>7. Program loader 呼叫 interpreter loader 將 program interpreter（<i>ld.so</i>）載入，並 
mapping 到 process memory；interpreter 的 text segment 由 vaddr. 0x4000_0000 
開始，interpreter 的 data segment 緊接<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->其後。</p>
<p>8. Program loader 將 BS<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->S segment 準備好。</p>
<p>9. Program loader 將 process 的 reg<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->ister <i>%eip</i>（user-mode）修改為 program 
interpreter 的進入點；並將 <i>%sp</i> 設定為 user mode 的 stack。</p>
		</td>
	</tr>
</table>
<p>10. 所以任何 shared library 的外部<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->程式，一開始都是由 program interpreter 開始執行！</p>
<table border="1" width="100%" id="table2" cellspacing="0" bordercolor="#000080">
	<tr>
		<td><b>User Space Prog</b><!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." --><b>ram Loader / Linker</b><p>11. Program interpreter 會找到 process 所需的 sha<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->red library（名稱及其路徑）。</p>
<p>12. Program interpreter 使用 m<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->map system call 將 shared library mapping 到 
process memory，以完成整個 process image 的建立。</p>
<p>13. 更新 shared library 的符<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->號表。</p>
<p>14. Program interpreter 'jump' 到 process 的進<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->入點（紀錄在 ELF header 裡的 entry point）。</p>
		</td>
	</tr>
</table>
<p>15. 真正開始執行程<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->式！</p>
<p>步驟 1.~6. 已在以下的日記<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->介紹過：</p>
<ul>
	<li>2007.03.08:
	<a href="http://www.jollen.org/blog/2007/03/process_creation_6_exec.html">
	Process Creation, #6：Exec System Call 的觀念</a></li>
	<li>2007.03.05:
	<a href="http://www.jollen.org/blog/2007/03/elf_program_loading_1_segment.html">
	ELF 之 Program Loading 教學文件, #1: Segment 的觀念</a></li>
	<li>2007.03.08:
	<a href="http://www.jollen.org/blog/2007/03/elf_program_loading_2_pht.html">
	ELF 之 Program Loading 教學文件, #2: Program Header Table</a></li>
	<li>2007.03.09:
	<a href="http://www.jollen.org/blog/2007/03/elf_program_loading_3_segment_type.html">
	ELF 之 Program Loading 教學文件, #3: Segment Type 與 Kernel Space Loader</a></li>
</ul>
<p>步驟 7.~15. 將會在後續的日記再做整理。以整<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->體的 program loader 流程來說，可分為 kernel-space 與 user-space 
二大階段：</p>
<ul>
	<li>步驟 4.~9. 由 kernel 的 ELF loader 所<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->負責。</li>
	<li>步驟 11.~14 由 user-space 的 program interpreter（dynamic loader/linker，也就是 
	<i>/lib/ld-linux.so.2</i>）所負<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->責。</li>
</ul>
<p>Program interpreter 需要 dynamic segment 裡的相關資訊來完成後續的工作，此部份日後<!-- "Copyright(c) 2007 www.jollen.org. All rights reserved." -->再做整理分享。</p>
<p><font size="2">來源: <a href="http://www.jollen.org">www.jollen.org</a> </font></p>]]></description>
         <link>https://www.jollen.org/blog/2007/03/elf_program_loading_4_flow_chart.html</link>
         <guid>https://www.jollen.org/blog/2007/03/elf_program_loading_4_flow_chart.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Tue, 13 Mar 2007 14:26:20 +0800</pubDate>
      </item>
            <item>
         <title>ELF 之 Program Loading 教學文件, #3: Segment Type 與 Kernel Space Loader</title>
         <description><![CDATA[<p><b>Segment Type</b></p>
<p>Program loading 時期會處<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->理以下 
4 種 segment：</p>
<blockquote>
	<pre>#define <font color="#ff0000">PT_LOAD</font>         1               /* Loadable program segment */
#define <font color="#ff0000">PT_DYNAMIC</font>      2               /* Dynamic linking information */
#define <font color="#ff0000">PT_INTERP</font>       3               /* Program interpreter */
#define <font color="#ff0000">PT_PHDR</font>         6               /* Entry for header table itself */</pre>
</blockquote>
<p>簡要說明<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->這 4 種 
segment 的作用如下。</p>
<p><i>PT_LOAD</i>：即 text segment 或 data segment。</p>
<p><i>PT_DYNAMIC</i>：若 ELF 有此 segment，則表示 .dynamic section 會獨立成<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->一個實體的 
segment。<i>PT_DYNAMIC</i> 僅包含 .dynamic section。</p>
<p><i>PT_INTERP</i>：紀錄 program interpreter 路徑<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->與檔名的 
segment。<i>PT_INTERP</i> 僅包含 .interp section。</p>
<p><i>PT_PHDR</i>：Program head<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->er 
table segment。</p>
<p>Segment type 是很重要<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->的資訊，kernel 
的 ELF loader 與 program interpreter（ld.so）都是透過 segment type 來判斷 segment 的用途。</p>
<p><u>Kernel</u> 與 <u>program interpreter</u> 對不同的 segment 都有不同的處理方式。</p>
<p>另外，<i>PT_NOTE</i> 並不是一個必要的 segment，實際上<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->它也沒有重要的作用，因此忽略不做討論。</p>
<p><b>更多有關 Kernel ELF Loader</b></p>
<p>回到 Jollen's blog「<a href="http://www.jollen.org/EmbeddedLinux/Process_Creation.html">Process 
Creation</a>」專欄所談<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->論到的「Program 載入流程」裡，相當重要的一個觀念就是 &quot;exec&quot; system call。程式的載入涉及 exec systegm call service，exec system call 是 machine-dependent 的實作，以 x86 來說，此服務實作於 
<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/process.c">
linux/arch/i386/kernel/process.c</a> 檔案；在 SystemV ABI 規格中，program loading 的章節
被撰寫在 
'processor-specific' 文件裡。</p>
<p>在目前所討論的 ELF loading 主題中，我們還看不到與 processor 相關的議題，例如 .got/.plt 節區的用途；相關的技術細節在此較不適合做深入討論，未來會以分享一份技術簡報的方式來呈現，<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->並且以 ARM 做為討論標地。</p>
<p>Exec system call 叫用 kernel space 的 ELF loader 載入 ELF image，此觀念請參考 Jollen's 
Blog「<a href="http://www.jollen.org/blog/2007/03/process_creation_6_exec.html">Process 
Creation, #6：Exec System Call 的觀念</a>」。我們整理過 kernel 的 ELF loader 所做的工作有：</p>
<blockquote>
	<p>1. 讀取 ELF image 的 segment 資<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->訊（由 
	program header table）。</p>
	<p>2. 讀<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->出 
	segment 內容。</p>
	<p>3. 判斷是<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->否有 
	program interpreter；若有，再讀取 program interpreter。</p>
	<p>4. 將讀取<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->到的 
	segment 內容取代掉 <i>current </i>的內容，這個動作就是 <b>exec</b> system call 的重要特性。</p>
</blockquote>
<p>項目 1. 是由 ELF image 的 program header table 分析執<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->行檔的 
segment 資訊，並判斷 segment type；ELF loader 會依照 segment 的類型來做不同的處理，說明如後（項目 2.）。</p>
<p>項目 3. 則是在找到 <i>PT_INTERP</i>&nbsp; segment 時，呼叫 interpreter loader 將 
interpreter 載入。Program interpreter 我們先前曾提過，就是 <i>ld.so</i>（dynamic 
loader/linker），這個部份將在下一篇日記再做<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->說明。</p>
<p><b>PT_INTERP</b></p>
<p>若 ELF loader 判斷此 segment 為 <i>PT_INTERP</i>，則叫用 interpreter 的 loading 
來做載入的工作，在標準的 GNU/Linux 系統底下，由於 program interpreter 也是 ELF 格式，因此叫<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->用 
kernel 的 <i>load_elf_interp() </i>API（<i>fs/binfmt_elf.c</i>）將 program 
interpreter 讀取並載入。</p>
<p><b>PT_LOAD</b></p>
<p>若 ELF loader 判斷此 segment 為 <i>PT_LOAD</i>，表示這是 text segment 或 data 
segment，也就是最主要的程式碼與資料區段，此時便透過 <i>do_mmap()</i> 來將 text/data segment mapping 到記<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->憶體。</p>
<p>依照 ELF 規格，text segment 會由 virtual address 0x0804_8000 的地方開始 mapping，data 
segment 則是<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->緊接在 
text segment 之後。</p>
<p>另外，text segment 與 data segment 都是 <i>PT_LOAD</i> 類型，因此<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->需要根據 
<b>Flg</b> 欄位的屬性來知道該 segment 是 text segment 還是 data segment。詳見前一篇日記有關 segment 
permissions 的說明。</p>
<p><b>小結</b></p>
<p>總結本專欄到目前為止所得到的資訊如下。灰色部份是 interpreter 負責的工作，將在下篇日記討論。</p>
<p>請注意，本日記是針對 kernel 本身所做的 ELF loading 工作來做整理，即 kernel-space 的 program 
loading。另外一半的 program loading 是由 user-space 的 dynamic loader/linker 所完成，這裡所提的 
dynamic loader/linker 就是我們不斷提到的 program interpreter。</p>
<table id="table1" border="1" width="100%">
	<tr>
		<td width="145"><font color="#000080"><b>Segment</b></font></td>
		<td width="236"><font color="#000080"><b>Type</b></font></td>
		<td width="533"><b><font color="#000080">Kernel or Program Interpreter</font></b></td>
	</tr>
	<tr>
		<td width="145" bgcolor="#E0E0E0">00</td>
		<td width="236" bgcolor="#E0E0E0">PHDR</td>
		<td width="533" bgcolor="#E0E0E0">Interpreter 用<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->來計算 
		base address 用。</td>
	</tr>
	<tr>
		<td width="145">01</td>
		<td width="236">INTERP<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." --></td>
		<td width="533">Kernel ELF loader 用來載入 interpreter，並交由 interpreter 做 
		shared library 的 dynamic linking。</td>
	</tr>
	<tr>
		<td width="145">02<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." --></td>
		<td width="236">LOAD</td>
		<td width="533">Kernel 將此 segment mapping 為新的 text segment 或 data 
		segment。</td>
	</tr>
	<tr>
		<td width="145">03</td>
		<td width="236">LOAD<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." --></td>
		<td width="533">Kernel 將此 segment mapping 為新的 text segment 或 data 
		segment。</td>
	</tr>
	<tr>
		<td width="145" bgcolor="#E0E0E0">04</td>
		<td width="236" bgcolor="#E0E0E0">DYNAMIC</td>
		<td width="533" bgcolor="#E0E0E0">由 interpreter 處理<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." --></td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2007/03/elf_program_loading_3_segment_type.html</link>
         <guid>https://www.jollen.org/blog/2007/03/elf_program_loading_3_segment_type.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 09 Mar 2007 19:46:46 +0800</pubDate>
      </item>
            <item>
         <title> Process Creation, #6：Exec System Call 的觀念</title>
         <description><![CDATA[<p>有關 exec system call 的前言，除了以<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->下日記外：</p>
<ul>
	<li>2006.12.31:
	<a href="http://www.jollen.org/blog/2006/12/process_creation_1_shell.html">Process Creation, #1：由 shell 執行外部程式《基本觀念與範例》</a></li>
	<li>2007.01.02:
	<a href="http://www.jollen.org/blog/2007/01/process_creation_2.html">Process 
Creation, #2：Running a &quot;User Process&quot;</a></li>
</ul>
<p>也請閱讀以<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->下日記：</p>
<ul>
	<li>2007.01.05:
	<a href="http://www.jollen.org/blog/2007/01/linux_virtual_memory_areas_vma.html">Linux 的 Virtual Memory Areas（VMA）：基本概念介紹</a></li>
	<li>2007.01.15: 
	<a href="http://www.jollen.org/blog/2007/01/process_vma.html">Linux 的 Virtual Memory Areas（VMA）：Process 與 VMA 整體觀念</a></li>
</ul>
<p><b>Exec</b> system call 的 service routine 'sys_execve' 主要的工作是「將外部程式（ELF 
image）取代掉原來的 process」，以 kernel 的角度來說，「原來的 proc<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->ess」就是 <i>current</i>。以下簡單分析 exec 
system call 重要關鍵，我們由 <i>do_execve</i> 函數看起。</p>
<p><i>do_execve</i> 負責執行 <i>sys_execve</i> 的主要工作，在 <i>do_execve</i> 
的工作流程中，我們不難發現，<i>do_execve</i> 最後會呼叫一個很重要的 API -&nbsp; <i>search_binary_handler()</i>；在 
<i>search_binary_handler()</i> API 裡，我們發<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->現，此 API 會呼叫到 kernel space 的 ELF  
loader 來載入 ELF image 至 process memory。</p>
<p>Kernel 2.6 的 ELF loader 實作位於 <i>load_elf_binary()</i> ，此 API 實<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->作於 <i>fs/binfmt_elf.c</i> 
檔案。ELF 
loader 並非直接在前面所提到的 <i>search_binary_handler()</i> 裡呼叫。</p>
<p>ELF loader 與其它執行檔格式的 loader，是在 kernel 
開<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->機時，註冊至 kernel 裡：</p>
<blockquote>
	<pre>static struct linux_binfmt elf_format = {
		.module		= THIS_MODULE,
		.load_binary	= <font color="#FF0000">load_elf_binary</font>,
		.load_shlib	= load_elf_library,
		.core_dump	= elf_core_dump,
		.min_coredump	= ELF_EXEC_PAGESIZE
};

static int __init init_elf_binfmt(void)
{
	return <b>register_binfmt</b>(&elf_format);
}</pre>
</blockquote>
<p>當 <b>exec</b> system call 被叫用時，最終<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->就會在&nbsp; <i>search_binary_handler()</i> 裡 
<u>callback</u> ELF loader 來載入 ELF image。</p>
<p><b>Kernel space 的 ELF loader</b></p>
<p>Kernel space 的 ELF loader 是以 segment 的觀念來讀取執行檔（ELF image），相關<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->觀念請見以下日記：</p>
<ul>
	<li>2007.03.05:
	<a href="http://www.jollen.org/blog/2007/03/elf_program_loading_1_segment.html">
	ELF 之 Program Loading 教學文件, #1: Segment 的觀念</a></li>
	<li>2007.03.08:
	<a href="http://www.jollen.org/blog/2007/03/elf_program_loading_2_pht.html">
	ELF 之 Program Loading 教學文件, #2: Program Header Table</a></li>
</ul>
<p><i>load_elf_binary()</i> 所做的工作有：</p>
<blockquote>
	<p>1. 讀取 ELF image 的 segment 資<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->訊（由 program header table）。</p>
	<p>2. 讀<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->出 segment 內容。</p>
	<p>3. 判斷是<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->否有 program interpreter；若有，再讀取 program interpreter。</p>
	<p>4. 將讀取<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->到的 segment 內容取代掉 <i>current </i>的內容，這個動作就是 <b>exec</b> system call 
的重要特性。</p>
</blockquote>
<p>Program interpreter 就是 dynamic loader/linker，即 <i>/lib/ld-linux.so.2</i>。Program 
interpreter 的作用是「loading shared libraries（dynamic linking）」。</p>
<p>我們看到 <i>current</i> 被外部（stored）的 ELF image 取代了，這是一個重要<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->的里程碑，背後的意義是：<u>ELF image 
被載入到記憶體後，議題將由 ELF image 被帶入更高層次的主題 - Process Memory</u>。</p>
<p>當 ELF image 成為 process memory 的形式後，就能開<!-- Copyright(c) 2007 www.jollen.org. All rights reserved." -->始研究 process memory、VMA 與 program 
execution（例如：scheduling）有關的議題了。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>See also.</b></font></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
			<li>2006.12.31:
			<a href="http://www.jollen.org/blog/2006/12/process_creation_1_shell.html">Process Creation, #1：由 shell 執行外部程式《基本觀念與範例》</a></li>
			<li>2007.01.02:
			<a href="http://www.jollen.org/blog/2007/01/process_creation_2.html">Process 
Creation, #2：Running a &quot;User Process&quot;</a></li>
			<li>2007.01.08:
			<a href="http://www.jollen.org/blog/2007/01/process_creation_3_sys_fork.html">Process Creation, #3：sys_fork《基本觀念》</a></li>
			<li>2007.01.11:
			<a href="http://www.jollen.org/blog/2007/01/process_creation_4_sys_fork.html">Process Creation, #4：sys_fork《核心實作》</a></li>
			<li>2007.01.14:
			<a href="http://www.jollen.org/blog/2007/01/process_creation_5_copy_process.html">Process Creation, #5：copy_process()</a></li>
		</ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2007/03/process_creation_6_exec.html</link>
         <guid>https://www.jollen.org/blog/2007/03/process_creation_6_exec.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 08 Mar 2007 19:47:00 +0800</pubDate>
      </item>
            <item>
         <title>ELF 之 Program Loading 教學文件, #2: Program Header Table</title>
         <description><![CDATA[<p>了解<u>系統行為</u>的研究方法，我認為有效的步驟應分成三個<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->階段來進行。</p>
<p>
初次入門：一開始進行研究時，因為對於系統的基本觀念還不夠完備，因此「學中做、做中學」成了最有效率的入門方式，透過「概念的實作」與「實作讀到的概念」的過程，最可以幫助我們<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->在短時間內掌握重要的核心知識。以 
ELF 專欄為例，在入門時期，我用了 loader 0.1~0.5 共 5 個小範例來陳述 ELF 的格式以及 section 的觀念。</p>
<p>
掌握觀念：首先發表一個自己的看法。「將所有理論或概念全部動手實作一遍」，在我看來，並不是很有效率的辦法，這種做法應當很有幫助，但是卻會延緩學習速度，因此，這個時
期的重點如果是在「<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->通盤掌握整體的重要關鍵」，那麼「善用工具來做分析」自然是最有成
效的方式。以「工具來操作並驗證觀念」是建議的做法，另外一個理由是，這種做法比較能貼近實務面。在「ELF 之 Program Loading 教學文件」的日記裡，我將會以此做法來分享教學文件。</p>
<p>思考與研究：這個階段有點<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->像是「實驗室」的做法，在這裡不再贅述。</p>
<p><b>Program Header Table</b></p>
<p>這裡有幾個重要的<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->觀念：</p>
<p>1. Program header table 是程式要能執行的重要資訊，program header table <!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->紀錄 ELF image 裡的 
'segment' 分佈，請參考 Jollen's Blog「<a href="http://www.jollen.org/blog/2007/03/elf_program_loading_1_segment.html">ELF 
之 Program Loading 教學文件, #1: Segment 的觀念</a>」的說明。</p>
<p>2. 對 dynamic loader/linker 來說，ELF 的 'section' 
是通透性的（transparent），也就是在整個載入的過程裡，dynamic loader/linker 並不會知道他所載入的 'segment' 與實際的 
section 有何關係。<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->整個載入過程只讀取 program header table 所紀錄的 'segment'。</p>
<p>3. 也就是說，section header table 在此時期是用不到的，就算沒<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->有 section header table 
也不影響程式載入與執行；這個觀念可參考 Jollen's Blog「<a href="http://www.jollen.org/blog/2007/01/truncate_it_tricky_source.html">「Truncate 
It」小技倆的原始碼與原理</a>」。</p>
<p>使用 <i>readelf</i> 來觀察執行檔的 program he<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->ader table：</p>
<blockquote>
	<pre># readelf -l hello

Elf file type is EXEC (Executable file)
Entry point 0x8048278
There are 6 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000c0 0x000c0 R E 0x4
  INTERP         0x0000f4 0x080480f4 0x080480f4 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00408 0x00408 R E 0x1000
  LOAD           0x000408 0x08049408 0x08049408 0x00100 0x00104 RW  0x1000
  DYNAMIC        0x000414 0x08049414 0x08049414 0x000c8 0x000c8 RW  0x4
  NOTE           0x000108 0x08048108 0x08048108 0x00020 0x00020 R   0x4

 Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame
   03     .data .dynamic .ctors .dtors .jcr .got .bss
   04     .dynamic
   05     .note.ABI-tag</pre>
</blockquote>
<p>Program header table 的<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."--> data structure 如下（<i>/usr/include/elf.h</i>）：</p>
<blockquote>
	<pre>/* Program segment header.  */

typedef struct
{
  Elf32_Word    <font color="#FF0000">p_type</font>;                 /* Segment type */
  Elf32_Off     p_offset;               /* Segment file offset */
  Elf32_Addr    p_vaddr;                /* Segment virtual address */
  Elf32_Addr    p_paddr;                /* Segment physical address */
  Elf32_Word    p_filesz;               /* Segment size in file */
  Elf32_Word    p_memsz;                /* Segment size in memory */
  Elf32_Word    p_flags;                /* Segment flags */
  Elf32_Word    p_align;                /* Segment alignment */
} Elf32_Phdr;</pre>
</blockquote>
<p>'readelf' 的列表與 program header table 的 data structure 相符，每個 field 
所紀錄<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->的資訊可參考註解說明，或是參照完整的 System V ABI 文件。</p>
<p>幾個重要資訊說明如下。</p>
<p>1. Segment 的個數與大<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->小紀錄在 ELF 檔頭裡：</p>
<blockquote>
	<pre># readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048278
  Start of program headers:          52 (bytes into file)
  Start of section headers:          1784 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  <font color="#FF0000">Size of program headers:           32 (bytes)</font>
  <font color="#FF0000">Number of program headers:         6</font>
  Size of section headers:           40 (bytes)
  Number of section headers:         25
  Section header string table index: 24</pre>
</blockquote>
<p>2. Program header table 的 <i>p_type</i> 欄位必須先進<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->行研究（<i>/usr/include/elf.h</i>）：</p>
<blockquote>
	<pre>/* Legal values for p_type (segment type).  */

#define PT_NULL         0               /* Program header table entry unused */
#define <font color="#FF0000">PT_LOAD</font>         1               /* Loadable program segment */
#define <font color="#FF0000">PT_DYNAMIC</font>      2               /* Dynamic linking information */
#define <font color="#FF0000">PT_INTERP</font>       3               /* Program interpreter */
#define <font color="#FF0000">PT_NOTE</font>         4               /* Auxiliary information */
#define PT_SHLIB        5               /* Reserved */
#define <font color="#FF0000">PT_PHDR</font>         6               /* Entry for header table itself */
#define PT_TLS          7               /* Thread-local storage segment */
#define PT_NUM          8               /* Number of defined types */
#define PT_LOOS         0x60000000      /* Start of OS-specific */
#define PT_GNU_EH_FRAME 0x6474e550      /* GCC .eh_frame_hdr segment */
#define PT_LOSUNW       0x6ffffffa
#define PT_SUNWBSS      0x6ffffffa      /* Sun Specific segment */
#define PT_SUNWSTACK    0x6ffffffb      /* Stack segment */
#define PT_HISUNW       0x6fffffff
#define PT_HIOS         0x6fffffff      /* End of OS-specific */
#define PT_LOPROC       0x70000000      /* Start of processor-specific */
#define PT_HIPROC       0x7fffffff      /* End of processor-specific */</pre>
</blockquote>
<p>標示紅色的部份是我們在 'readelf -l' 列表中所看到的 type。</p>
<p>3. <i>p_flags</i> 的意義也要先行了解（<i>/usr/include/elf.h</i>）：</p>
<blockquote>
	<pre>/* Legal values for p_flags (segment flags).  */

#define PF_X            (1 << 0)        /* Segment is executable */
#define PF_W            (1 << 1)        /* Segment is writable */
#define PF_R            (1 << 2)        /* Segment is readable */
#define PF_MASKOS       0x0ff00000      /* OS-specific */
#define PF_MASKPROC     0xf0000000      /* Processor-specific */</pre>
</blockquote>
<p><b>Segment Permissions</b></p>
<p>Segment permission 是 <i>p_flags</i> 的組合，在 'readelf -l' 列表中則<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->是相對應於 <b>Flg</b> 
一欄。</p>
<p><b>Segment Contents</b></p>
<p>Segment 由哪些 section 所構成，同樣<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->可由 'readelf -l' 列表的「Section to Segment 
mapping」部份觀察。</p>
<p><b>小結</b></p>
<p>以下表格整理目前為止所得<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."-->到的結論。</p>
<table border="1" width="100%" id="table1">
	<tr>
		<td width="93"><font color="#000080"><b>Segment</b></font></td>
		<td width="359"><font color="#000080"><b>Sections</b></font></td>
		<td width="99"><font color="#000080"><b>Type</b></font></td>
		<td width="111"><font color="#000080"><b>Flg</b></font></td>
	</tr>
	<tr>
		<td width="93">00</td>
		<td width="359">　</td>
		<td width="99">PHDR</td>
		<td width="111">PF_R + PF_X<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."--></td>
	</tr>
	<tr>
		<td width="93">01</td>
		<td width="359">.interp</td>
		<td width="99">INTERP</td>
		<td width="111">PF_R </td>
	</tr>
	<tr>
		<td width="93">02</td>
		<td width="359">.interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version 
		.gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame</td>
		<td width="99">LOAD</td>
		<td width="111">PF_R + PF_X</td>
	</tr>
	<tr>
		<td width="93">03</td>
		<td width="359">.data .dynamic .ctors .dtors .jcr .got .bss<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."--></td>
		<td width="99">LOAD</td>
		<td width="111">PF_R + PF_W</td>
	</tr>
	<tr>
		<td width="93">04</td>
		<td width="359">.dynamic</td>
		<td width="99">DYNAMIC</td>
		<td width="111">PF_R + PF_W</td>
	</tr>
	<tr>
		<td width="93">05<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."--></td>
		<td width="359">.note.ABI-tag</td>
		<td width="99">NOTE</td>
		<td width="111">PF_R<!--"Copyright(c) 2007 www.jollen.org. All rights reserved."--></td>
	</tr>
</table>
<p>下則日記將會針對 segment type 所討論。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/03/elf_program_loading_2_pht.html</link>
         <guid>https://www.jollen.org/blog/2007/03/elf_program_loading_2_pht.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 08 Mar 2007 16:19:41 +0800</pubDate>
      </item>
            <item>
         <title>小聊 .dynstr 節區</title>
         <description><![CDATA[<p>昨天與同學小談了一下 .dynstr 節區，簡單紀錄如下。</p>
<p>我們知道 <i>ld.so</i>（dynamic linker/loader）與 <i>ldd</i> 指令，而這二個程式都會參考到 ELF 的 .dynstr（dynamic 
linking 的 string table）。以 <i>ldd</i> 來說，我們用 <i>ldd</i> 來找出 ELF 執行檔的相依動態程式庫：</p>
<blockquote>
	<pre># ldd hello
        libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)</pre>
</blockquote>
<p>這個動作依賴 .dynstr 節區所提供的資訊，也就是說，當 .dynstr 的內容有誤或是不存在時，<i>ld.so</i> 與<i> ldd </i>
便無法正常工作。例如：</p>
<blockquote>
	<pre># strip -R .dynstr hello
# ldd hello
./hello: ./hello: no version information available (required by ./hello)
# ./hello
./hello: ./hello: no version information available (required by ./hello)
./hello: relocation error: ./hello: symbol , version  not defined in file  with link time reference</pre>
</blockquote>
<p>另外再補充一點，在 .dynamic 節區裡的列表中，包含了一筆指向 .dynstr 節區的紀錄。使用簡單的工具操作此觀念如下：</p>
<blockquote>
	<pre># readelf  -S hello
...
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        080480f4 0000f4 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048108 000108 000020 00   A  0   0  4
  [ 3] .hash             HASH            08048128 000128 000028 04   A  4   0  4
  [ 4] .dynsym           DYNSYM          08048150 000150 000050 10   A  5   1  4
  [ 5] <font color="#FF0000">.dynstr</font>           STRTAB          <font color="#FF0000">080481a0</font> 0001a0 00004c 00   A  0   0  1
  [ 6] .gnu.version      VERSYM          080481ec 0001ec 00000a 02   A  4   0  2
...</pre>
</blockquote>
<p>接著：</p>
<blockquote>
	<pre># readelf  -d hello

Dynamic segment at offset 0x414 contains 20 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8048230
 0x0000000d (FINI)                       0x80483d4
 0x00000004 (HASH)                       0x8048128
 <font color="#FF0000">0x00000005 (STRTAB)                     0x80481a0</font>
 0x00000006 (SYMTAB)                     0x8048150
 0x0000000a (STRSZ)                      76 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x80494f0
 0x00000002 (PLTRELSZ)                   16 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8048220
 0x00000011 (REL)                        0x8048218
 0x00000012 (RELSZ)                      8 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x80481f8
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x80481ec
 0x00000000 (NULL)                       0x0</pre>
</blockquote>
<p>這裡的 &quot;dynamic segment&quot; 專指 &quot;.dynamic&quot; section，與上一篇日記提到的 &quot;dynamic segment&quot; 
含義不同，特別一提，以避免混淆。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/03/dynstr_section.html</link>
         <guid>https://www.jollen.org/blog/2007/03/dynstr_section.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 05 Mar 2007 12:31:00 +0800</pubDate>
      </item>
            <item>
         <title>ELF 之 Program Loading 教學文件, #1: Segment 的觀念</title>
         <description><![CDATA[<p>「Program Loading」的議題在討論「如何將程式載入記憶體」，以便後續的「執行」。在「ELF（Executable and Linking 
Format）格式教學文件」第 1~8 篇文章裡，我們了解基本的 ELF 觀念，並建立所謂的「節區」知識。</p>
<p>本系列日記「ELF 之 Program Loading 教學文件」將會介紹有關程式載入（program loading）的核心主題，在此之前，請先閱讀 
Jollen 的「<a href="http://www.jollen.org/EmbeddedLinux/Executable_Linking_Format.html">Executable 
and Linking Format</a>」專欄，以基本基礎的先備知識。</p>
<p><b>Segments</b></p>
<p>由 &quot;execution view&quot; 的角度來看程式（即 
process），所謂的節區（section）已經被進化成區段（segment）的觀念了。廣義來說，section 可被分為以下 3 種 segment：</p>
<ul>
	<li>Text segment - 指存放<b>唯讀</b>（read-only）程式碼與資料的所有 section 。</li>
	<li>Data segment - 指存放<b>可寫</b>（writable data）程式碼與資料的所有 section。</li>
	<li>BSS segment - 即 .bss section。</li>
</ul>
<p>另外，還有一類的 segment：</p>
<ul>
	<li>Dynamic segment - 用來存放 dynamic linking 資訊的 section。</li>
</ul>
<p>不過，當我們講到「process segment」時，指的是 text segment、data segment 與 BSS 
segment；process address space 就是由這三種 segment 所構成。</p>
<p>Text segment 是由以下的 section 組成：</p>
<ul>
	<li>.text</li>
	<li>.rodata</li>
	<li>.hash</li>
	<li>.dynsym</li>
	<li>.dynstr</li>
	<li>.plt</li>
	<li>.rel.got</li>
</ul>
<p>Data segment 是由以下的 section 組成：</p>
<ul>
	<li>.data</li>
	<li>.dynamic</li>
	<li>.got</li>
	<li>.bss</li>
</ul>
<p>依照 SystemV ABI 規格，我們看到 .bss section 被放到 data segment 裡，但嚴格來說，我們應把 .bss 
section 另外獨立出來討論。基本的 .bss section 觀念，可參考 Jollen 的「<a href="http://www.jollen.org/EmbeddedLinux/BSS_Section_Concepts.html">BSS 
Section Concepts</a>」專欄。</p>
<p>此外，在 text segment 與 data segment 裡，包含了 4 個與 dynamic linking 有關的 
section，整理如下圖。</p>
<p><img alt="dynamic_linking_sections.JPG" src="http://www.jollen.org/blog/2007/03/05/dynamic_linking_sections.JPG" width="640" height="459" />
</p>
<p>與 dynamic linking 有關的 4 個 section <b>並非另外組成所謂的 dynamic segment</b>，這只是我們在研究 
dynamic linking 時邏輯上的用語。</p>
<p>到這裡，我們了解了一個相當重要的一個觀念：當程式載入後，是以 &quot;segment&quot; 的觀念存在於記憶體中，並且也是以 segment 的觀念被 Linux 
kernel 管理。ELF 執行檔被載入到記憶體的過程中，最重要的資訊就是前導專欄裡所提到的「Program Header Table」；就 ELF 
execution view 來說，program header table 是必要的，下篇日記會針對此議題做介紹。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/03/elf_program_loading_1_segment.html</link>
         <guid>https://www.jollen.org/blog/2007/03/elf_program_loading_1_segment.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Mon, 05 Mar 2007 12:05:15 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 的 gsmd：Linux 手機的 GSM Modem 通訊程式</title>
         <description><![CDATA[Linux 手機是 Embedded Linux 的重要應用項目之一，要能具備「手機」的功能，最基本的規格之一當然就是要支援 GSM（Global System for Mobile Communications）標準，也就是我們稱之為 2G 的行動電話標準。

GSM 系統使用一種稱之為 GSM modem 的裝置（也就是我們耳熟能詳的「數據機」）來做電話的「外撥（dial out）」與「接聽（answer）」功能。因此，Linux 手機要能打電話與接聽電話，必須將 target device 外接 GSM modem，並透過 GSM modem 的指令集（AT command）來對 GSM modem 下撥號或接聽指令。

GSM modem 與 target device 是以 UART（RS-232）介面連接，因此需要一個與 GSM modem 溝通的 RS-232 通訊程式。在此次 OpenMoko 所釋出的原始碼裡頭，就包含了一個 GSM modem 的通訊管理程式，稱為 gsmd（GSM daemon）；此外，gsmd 也包含一個 libgsmd 的 API 程式庫供開發者使用。

OpenMoko 也提供一個 ' libgsmd-tool' 的展示範例，此程式可執行 Power On/Off、answer incoming call、dial out 與 hangup call（掛斷）的基本功能。同時，'libgsmd-tool' 也提供 AT 指令集模式（atcmd mode），讓我們可以「直接」對 GSM modem 下達 GSM 07.07 標準的指令集。

gsmd 的使用說明，已出現在 OpenMoko 的 wiki 裡了，可參考 [<a href="http://wiki.openmoko.org">OpenMoko's Wiki</a>] 有關 [<a href="http://wiki.openmoko.org/wiki/Gsmd">Gsmd</a>] 的部份。

<strong>延伸閱讀</strong>

<ul>
<li><a href="http://www.jollen.org/blog/2007/02/openmoko_announce.html">OpenMoko 釋出原始碼了</a></li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2007/02/openmoko_gsm_modem_gsmd.html</link>
         <guid>https://www.jollen.org/blog/2007/02/openmoko_gsm_modem_gsmd.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Sat, 24 Feb 2007 22:38:46 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 釋出原始碼了</title>
         <description><![CDATA[<p>OpenMoko 於台北時間 2007-02-15 正式開放原始碼下載！如果您還不曉得 OpenMoko 專案的重要性，可先行閱讀以下幾則消息：</p>

<ul>
<li><a href="http://www.jollen.org/blog/2007/01/openmoko_announce.html">OpenMoko 準備舉旗進攻了</a></li>
<li><a href="http://www.jollen.org/blog/2006/11/mobile_20_openmoko_linux_smart.html">「Mobile 2.0 的思考」與第一隻採用 OpenMoko 的 Linux Smartphone</a></li>
</ul>

<p>OpenMoko 是史上頭一遭完全開放原始碼的 Linux 手機，OpenMoko 專案強調 100% 的開放與社群；隨著這個專案的正式問世，未來的 Linux mobile phone 生態想必將會出現大變化。以下是 OpenMoko announce 的 mail，為了見證這重要的歷史時刻，我將收到的 mailing-list 全文刊載：</p>

<blockquote><pre>日期: Wed, 14 Feb 2007 18:02:37 +0100   
寄件者: Harald Welte <laforge@openmoko.org>         
回給: contact@openmoko.org
收件者: announce@lists.openmoko.org
副本: community@lists.openmoko.org, openmoko-devel@lists.openmoko.org
主旨: [openmoko-announce] [ANNOUNCE] openmoko.org goes public
   
Hi! 

It is my pleasure to announce that as of now, we have opened public 
access to 

Our main portal: 
    http://www.openmoko.org/ 

Our public wiki: 
    http://wiki.openmoko.org/ 

Our subversion server: 
    http://svn.openmoko.org/ 
    http://svnweb.openmoko.org/ 

Our bugzilla: 
    http://bugzilla.openmoko.org/ 

Our 'developer file dump' 
    http://people.openmoko.org/ 

Our GForge installation 
    http://projects.openmoko.org/ 

Please also note that 
    http://lists.openmoko.org/ 
now has a number of more mailinglists.  To understand which list is used 
for what, I suggest reading 
https://wiki.openmoko.org/wiki/Development_resources#Mailing_Lists 

As indicated before, we are far from a finished end-user ready product. 

Also, please note, that given our current small team size, we will 
probably take quite a bit until we can respond to all your 
suggestions/comments and even contributions.  We hope for your 
understanding and patience. 

Cheers, 
-- 
- Harald Welte <laforge@openmoko.org>                       http://openmoko.org/ 
============================================================================ 
Software for the world's first truly open Free Software mobile phone </pre></blockquote> 

<p>OpenMoko 專案的第一支手機為 Neo1973，可以在 LinuxDevices.com 上的新聞看到他的實體照片 [<a href="http://www.linuxdevices.com/news/NS2657814070.html">Linux-powered iPhone killer available online in March</a>]。OpenMoko & Neo1973 若推廣順利，想必許多 end-user 對這個專案的「崛起」可能會感到驚奇又不解。</p>

<p>OpenMoko 的成功與其重要性是可預見的，「宏觀」來看其致勝因素有：</p>

<ul>
<li>1. 強調 open & community 的定位，而不是打著 Linux 手機的旗號在作戰。</li>
<li>2. OpenMoko 一開始就推出相當完整的社群工具（可參考 www.openmoko.org）。</li>
<li>3. Open source：端給社群愛好者的是很完整的 patch 與 application framework。</li>
<li>4. 從 OpenMoko 第一次 release project 開始，一直到目前實體產品快上市為止，幾近完美的 pre-ecosystem 建立手法與步驟，不但高明而且漂亮。</li>
</ul>

<p>目前，在 OpenMoko 的 svn 可以找到 GSM 模組的 source code、GPRS 的 kernel patch、MMC/SD 的 kernel/U-Boot patch 與 GSM power management 的 Linux device driver 等。OpenMoko 端出了非常多的好東西給大家，並且也提供 OpenEmbedded 的 distribution 環境設定，真是感動了。</p>

<p>Sean，you're good！迎接 Mobile 2.0 世代的來臨。</p>
]]></description>
         <link>https://www.jollen.org/blog/2007/02/openmoko_announce.html</link>
         <guid>https://www.jollen.org/blog/2007/02/openmoko_announce.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Sat, 17 Feb 2007 18:08:29 +0800</pubDate>
      </item>
            <item>
         <title>Linux 2.6.20 報馬仔</title>
         <description><![CDATA[<p>Linux 2.6.20 早在 2007-02-04 就釋出了，不過 Jollen 
一直到今天才去看 Linux 2.6.20 的 Changelog，順便在這裡做一點筆記。</p>
<p>2 個星期前釋出的 kernel v2.6.20 又多了許多功能與 device driver，詳細的 
Changelog 說明可參考 kernelnewbies.org 的 [<a href="http://kernelnewbies.org/LinuxChanges">LinuxChanges</a>]。此次的更新，幾個與我的工作較密切的有：</p>
<p>1. Linux 2.6.20 在 <i>arch/powerpc/</i> 裡新增了 Sony Platstation 3 
的支援。</p>
<p>2. Networking device driver 方面，更新了 Tsi 108/109 的 driver。Tsi 
108/109 是 Tundra 的 north bridge for PowerPC 750 晶片。</p>
<p>3. 在 Workqueue API 方面也做了更新。</p>
<p>4. MMC 方面，加入了 high speed（50 Hz）SD card 的支援、v4 high 
speed mode 與 v4 wide-bus mode；另外，SDHCI high speed 
的支援也加入了。</p>
<p>附帶一提，<a href="http://kvm.sourceforge.net/">KVM</a> 
驅動程式果然也正式加入 Linux 2.6.20 了。</p>
<p><b>延伸閱讀</b></p>
<ul>
  <li>2007.01.31: <a href="http://www.jollen.org/blog/2007/01/linux_kernel_powerpc.html">Linux/PowerPC 
    新世代《序幕》</a></li>
  <li>2006.12.12: <a href="http://www.jollen.org/blog/2006/12/kvm_driver_coming.html">Linux 
    2.6.20 將正式加入 KVM 驅動程式</a></li>
  <li>2006.10.19: <a href="http://www.jollen.org/blog/2006/10/kernel_26_kvm_kernel_virtual_m.html">Kernel 
    2.6 的 KVM (Kernel Virtual Machine) 驅動程式來了</a></li>
  <li>2006.11.02: <a href="http://www.jollen.org/blog/2006/11/kvm_howto.html">KVM 
    驅動程式的 HOWTO</a></li>
  <li>2006.12.06: <a href="http://www.jollen.org/blog/2006/12/linux_kernel_changelog_howto.html">我要怎麼知道 
    kernel 更新了什麼東西？</a></li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2007/02/kernel_2_6_20_release.html</link>
         <guid>https://www.jollen.org/blog/2007/02/kernel_2_6_20_release.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 14 Feb 2007 23:47:59 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 測試：Full root filesystem for ARM9 階段《NFS Mount》</title>
         <description><![CDATA[Bootstrap root filesystem 是一個基本且可開機的 root filesystem，針對 ARM9 的 root filesystem 建構與<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->開發，要如何有效率的進行測試與發展是必修的一門功課。傳統的 Embedded Linux 應用，大多是以 NFS 的方式來測試 target device 的完整 root filesystem（full root filesystem）；另外還有一種較「先進」的測試方式是使用 sshfs 的方式來進行，可參考 Jserv 兄的 blog「<a href="http://blog.linux.org.tw/~jserv/archives/001866.html">sshfs 在 Embedded Linux 開發的應用</a>」。

以 NFS 進行 Embedded Linux 的開發測試，主要是針對 target device 的 full root filesystem 做「立即（right now）」的系統執行測試（run-time），免除「不斷打包 image file、開機」的惡夢。這是一<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->種流行很久的 Embedded Linux 系統測試與開發方式，其概念如下：

1. 將 target 的完整 root filesystem（例如 ARM9 root filesystem）建置後，存放於 host 端的某個目<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->錄下，例如 <em>/home/rootfs</em>。
2. 為 target 製作一個「NFS root filesystem」，也就是 bootstrap root filesystem + NFS functionality，並<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->使用 NFS root filesystem 將 target device 開機。
3. 設定 host 端為 NFS server。
4. 以 NFS mount 方式將 server 上的 root filesystem 目錄「mount」進來，即可在 target device 上執行 full root filesystem 裡的應用程式。

這種方式不但簡單，而且方便，需要的基礎建設如下：

1. Target device 使用的 kernel 必須支援 NFS。
2. 製作 bootstrap root filesystem 時，需<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->要加入 <em>mount</em> 指令，並且開啟 mount 指令的 NFS 功能。
3. 加入 NFS functionality 至 bootstrap root filesystem。
4. 當然，您必須知道<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->如何設定 NFS server。

項目 3. 為 NFS root filesystem 的製作，Jollen 提供一個事先建立好的 NFS root filesystem 供大家取用，請下載「<a href="http://tw.jollen.org/root-filesystem/nfsroot_arm.img">nfsroot_arm.img</a>」，這<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->是 U-Boot 格式的 image file，請使用 U-Boot  開機。ARM9 的平臺，都能使用 <em>nfsroot_arm.img</em>。

<strong>簡單的使用案例（scenario）</strong>

原本老王是使用 Busybox 的 <em>tftp</em> 指令來下載程式至 target device，可是老王覺得這樣太麻煩了，所以老<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->王就設定 NFS server，將 <em>/tftpboot</em> 目錄整個 mount 在 target device 的 root filesystem 下，這樣就方便多了。首先，老王要先修改 NFS 的 export 設定檔：

<blockquote># vi /etc/exports</blockquote>

加入以下內容：

<blockquote>/tftpboot      *(ro,sync)</blockquote>

然後啟動 NFS server，以 Red Hat Linux 9 來說，應該執行以下指令：

<blockquote># /etc/rc.d/init.d/nfs start</blockquote>

Ubuntu 的使用者，必須安裝 nfs-server 套件，接著再<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->啟動 NFS server：：

<blockquote>$ sudo apt-get install nfs-server<br />
$ sudo /etc/init.d/nfs-kernel-server start</blockquote>

再把 <em>/etc/exports</em> 的內容「輸出」，Red Hat Linux 9/Ubuntu 都是執行以下指令：

<blockquote># exportfs -a<br />
# exportfs        （做檢查）<br />
/tftpboot       &lt;world></blockquote>

實際畫面如下圖。我以 <em>nfsroot_arm.img</em> 開機，然後設定 IP address，接著再啟動 portmap service。

<img alt="nfs_root_1.jpg" src="http://www.jollen.org/blog/2007/02/12/nfs_root_1.jpg" width="640" height="403" />

<img alt="nfs_root_2.jpg" src="http://www.jollen.org/blog/2007/02/12/nfs_root_2.jpg" width="640" height="403" />

接著，利用 <em>nfsroot_arm.img</em> 將 target device 開機，設定好 target device 的 IP 後，「請務<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->必啟動 <em>portmap</em>」再做 NFS mount：

<blockquote># portmap<br />
# mkdir /nfs<br />
# mount -t nfs 10.1.36.4:/tftpboot /nfs</blockquote>

我們將 NFS server 上的 <em>/tftpboot</em> 掛載到 target device裡，如下圖。

<img alt="nfs_root_3.jpg" src="http://www.jollen.org/blog/2007/02/12/nfs_root_3.jpg" width="640" height="403" />

成功後，便能在 <em>/nfs</em> 目錄下看到「host 端 <em>/tftpboot</em> 目錄裡的檔案」，恭喜老王學會了一種<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->非常基本，而且方便的 root filesystem for cross development 測試與開發方法。

<strong>更多有關 NFS mount</strong>

另外一種應用是「把整個 root filesystem」掛進來，這時<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->就會用到在「<a href="http://www.jollen.org/blog/2007/01/embedded_linux_testing_rfs.html">Embedded Linux 測試：Bootstrap root filesystem（x86）階段《程式執行測試》</a>」日記中所提到的 <em>chroot</em> 指令。

再舉一例 scenario 做說明。例如，我把 ARM9 的完整 root filesystem 放在 host 端的 <em>/home/rootfs</em> 目錄，所以先修改 NFS server 設定：

<blockquote>/home/rootfs    *(rw,sync,no_root_squash)</blockquote>

接下來一樣要用 NFS root filesystem 將 target device 開機，並在做完 NFS mount 後，立即以 <em>chroot</em>（for kernel 2.4）指令切<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->換根目錄：

<blockquote># mkdir /nfs<br />
# mount -t nfs 10.1.36.4:/home/rootfs /nfs<br />
# chroot /nfs /bin/sh</blockquote>

'chroot' 做法需要注意幾個細節：

1. 執行時期（run-time）目錄的問題，像是 <em>/dev</em>、<em>/sys</em>（kernel 2.6）與 <em>/proc</em>。
2. Initial script 要手動執行；或是 <em>chroot</em> 時，將第一個執行的動作指定為 init process。
3. 有時我<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->只會 mount root filesystem 裡的特定目錄。

只 mount root filesystem 裡的特定目錄，實務上有<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->時頗有用。例如，修改 NFS server 設定如下：

<blockquote>/home/rootfs/bin        *(ro,sync)<br />
/home/rootfs/sbin       *(ro,sync)</blockquote>

開機後做 NFS mount：

<blockquote># mount -t nfs 10.1.36.4:/home/rootfs/bin /bin<br />
# mount -t nfs 10.1.36.4:/home/rootfs/sbin /sbin</blockquote>

另外，有同學問到「portmap」service 的問題，這是<!-- "Copyright (c) 2007 www.jollen.org. All rights reserved." -->使用 NFS 必要的 service，請務必加入 NFS root filesystem，並在 mount 前啟動 portmap！]]></description>
         <link>https://www.jollen.org/blog/2007/02/nfs_root_filesystem_arm9.html</link>
         <guid>https://www.jollen.org/blog/2007/02/nfs_root_filesystem_arm9.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 12 Feb 2007 16:11:53 +0800</pubDate>
      </item>
            <item>
         <title>（無關 Embedded Linux）修改論壇註冊方式</title>
         <description><![CDATA[雖然 Linux 的技術發展快速，不過，spam 的技術發展更是驚人。許多 anti-spam 的軟體似乎都敗陣下來了，還好我的 mailbox 還挺得住。

前幾天，Jollen's forum 被大量的 spam 機器人註冊並發送垃圾信，造成「Forum 訂閱會員」被大量的 spam 間接攻擊。<strong>在這裡先向 Jollen's forum 會員們道歉，前幾天，被 spam bot 瘋狂註冊我的 forum，並張貼大量的廣告信，造成 forum 會員無端收到由 forum.jollen.org 發出的 spam，造成您的諸多困擾，深感抱歉。</strong>

目前已將 forum 的註冊方式改為「經 email 認證後，由管理員手動開啟權限」，為加速您的權限啟用速度，請在註冊表單的「學員碼」欄，輸入「iamnotspam」，以加速您的帳號啟用速度（如下圖）。未填寫者，需要透過「人腦 spam 辨識」流程，在啟用速度上，可能會慢些。還請多加見諒。

<img alt="iamnotspam.JPG" src="http://www.jollen.org/blog/2007/02/08/iamnotspam.JPG" width="467" height="103" />

目前，用來對付 spam bot 的方法之一是「手動輸入驗證碼」（如下圖），這是流行許久的方法，不過已經被攻破了。Jollen's forum 就是苦主之一。

<img alt="anti_spam_is_hard.JPG" src="http://www.jollen.org/blog/2007/02/08/anti_spam_is_hard.JPG" width="490" height="138" />
]]></description>
         <link>https://www.jollen.org/blog/2007/02/i_hate_spam.html</link>
         <guid>https://www.jollen.org/blog/2007/02/i_hate_spam.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 08 Feb 2007 17:45:46 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux / ARM9 課程的範例：root filesystem</title>
         <description><![CDATA[<p>提供 Jollen 的 Embedded Linux / ARM9 課程的範例，本課程重心放在 root filesystem 的建構技術與觀念解說。第 
1~2 天的課程實作產出範例如下：</p>
<ul>
	<li><a href="http://tw.jollen.org/root-filesystem/95C123/">
	arm9.so-busybox.tar.bz2</a></li>
	<li><a href="http://tw.jollen.org/root-filesystem/95C123/">
	arm9.so-hello.tar.bz2</a></li>
	<li><a href="http://tw.jollen.org/root-filesystem/95C123/">
	jk2410_pkgs_ports-20070204.tar.bz2</a></li>
</ul>
<p>第一份是「bootstrap root filesystgem」的製作、第二份是 cross compile「Hello World」for ARM9 
的實例、第三份是 madplay project 與 nano-X project（MP3 player 與 AVI player）的實例。</p>
<p>第一份範例的內容可參考 Jollen 的「<a href="http://www.jollen.org/blog/2007/01/arm9_bootstrap_root_filesystem.html">製作 
ARM9 的 Bootstrap Root Filesystem</a>」教學文件，第二份範例與第三份範例的教學文件尚在調校中，未來也將會發表在 
Jollen's Blog 與大家分享。良好的觀念，能得到組織結構較良好的實作，並且也會讓整個實作過程比較系統化；希望將我們上課的內容與大家分享。</p>
<p>Bootstrap root filesystem 的實作與觀念是 Embedded Linux 
的基本功，建議同學們在建立基本觀念後，再反覆練習，直到熟練為止，才能得到最大學習成效！</p>]]></description>
         <link>https://www.jollen.org/blog/2007/02/embedded_linux_root_filesystem_arm9.html</link>
         <guid>https://www.jollen.org/blog/2007/02/embedded_linux_root_filesystem_arm9.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Wed, 07 Feb 2007 17:30:55 +0800</pubDate>
      </item>
            <item>
         <title>研究 Dynamic Loader, #1: dlopen</title>
         <description><![CDATA[<p>學到 ELF 的格式，並了解 .text/.data/.bss section 後，接下來絕對不能錯過「ELF loader」領域最經典的題目 - 
dynamic loader。不把 dynamic loader 從頭到腳好好研究一遍的話，實在是可惜了！</p>
<p>要學習 dynamic loader 的議題，並深入核心實作，最好可以由 <i>ld.so</i> 程式設計的主題切入。<i>ld.so</i> 是 
Linux 的 dynamic loader，如果 man ld.so 的話，可以得到以下解釋：</p>
<blockquote>
	<pre>DESCRIPTION
       ld.so loads the shared libraries needed by a program, prepares the pro-
       gram to run, and then runs it.  Unless  explicitly  specified  via  the
       -static  option to ld during compilation, all Linux programs are incom-
       plete and require further linking at run time.</pre>
</blockquote>
<p>與 dynamic loader 有關的<u>系統管理面</u>議題如下，這些是基本功課，應先行了解：</p>
<ul>
	<li><i>/etc/ld.so.conf</i>（ld.so 的設定檔）</li>
	<li><i>ldconfig</i></li>
	<li><i>LD_LIBRARY_PATH</i></li>
</ul>
<p><em>ld.so</em> 其實就是我們所熟悉的 <em>/lib/ld-linux.so.2</em> <u>執行檔</u>。至於 <i>ld.so</i> 的程式設計，則是由以下 3 個主要的函數切入：</p>
<blockquote>
	<pre>#include &lt;dlfcn.h></pre>
	<pre>void *<b>dlopen</b>(const char *filename, int flag); 
void *<b>dlsym</b>(void *handle, const char *symbol); 
int <b>dlclose</b>(void *handle); </pre>
</blockquote>
<p>這個部份可以 'man dlopen'，便能得到非常詳盡的說明，同時還有一個範例程式。以下的範例是由 <i>dlopen</i> 的 man page 
節錄出來的，我在裡頭加上了註解供您參考：</p>
<blockquote>
	<pre>/* Filename: dl_call.c */
#include &lt;stdio.h>
#include &lt;stdlib.h>
#include &lt;dlfcn.h>

int main(void) 
{
    void *handle;	/* shared library 的 'handle' 指標 */
    double (*cosine)(double);   /* 指向 shared library 裡的函數 */
    char *error;	/* 記錄 dynamic loader 的錯誤訊息 */

    /* 開啟 shared library 'libm' */
    handle = dlopen ("libm.so", <b>RTLD_LAZY</b>);
    if (!handle) {
        fprintf (stderr, "%s\n", dlerror());
        exit(1);
    }

    dlerror();    /* Clear any existing error */

    /* 在 handle 指向的 shared library 裡找到 "cos" 函數,
     * 並傳回他的 memory address 
     */
    cosine = dlsym(handle, "cos");
    if ((error = dlerror()) != NULL)  {
        fprintf (stderr, "%s\n", error);
        exit(1);
    }

    /* indirect function call (函數指標呼叫),
     * 呼叫所指定的函數
     */
    printf ("%f\n", (*cosine)(2.0));
    dlclose(handle);
    return 0;
}</pre>
</blockquote>
<p><i>dlopen()</i> 的 <i>RTLD_LAZY</i> 參數說明如下（節錄自 man page）：</p>
<pre>RTLD_LAZY
    Perform lazy binding. Only resolve symbols as the code that references 
them is executed. If the symbol is never referenced, then it is never 
resolved. (Lazy binding is only performed for function references; references 
to variables are always immediately bound when the library is loaded.)</pre>
<p>由於使用到 <i>dlopen()</i> 函數，編譯時請與<i> libdl</i> 做連結：</p>
<blockquote>
	<p>$ gcc -o dl_call dl_call.c -ldl</p>
</blockquote>
<p>以上的做法等於：</p>
<blockquote>
	<p>&nbsp;printf (&quot;%f\n&quot;, cos(2.0));</p>
</blockquote>
<p>直接呼叫 <i>libm</i> 的函數時，則是與 <i>libm</i> 做連結：</p>
<blockquote>
	<p>$ gcc -o dl_call dl_call.c -lm</p>
</blockquote>
<p>單純由程式設計的角度來看，利用 dlopen()+dlsym() 來「呼叫函數」與「直接呼叫函數」有什麼應用上的差異呢？事實上，<i>libdl</i> 
的使用是很普遍的，許多軟體將自己的功能模組（modules）做成 shared library，並以 <i>dlopen()</i> 載入後使用，由於 shared 
library 是「可抽換」的，因此可透過 <i>libdl</i> 來做出「Plug-ins」這樣的功能。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/02/dynamic_loader_1_dlopen.html</link>
         <guid>https://www.jollen.org/blog/2007/02/dynamic_loader_1_dlopen.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 05 Feb 2007 23:54:13 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的 I/O, #4: fops-&gt;ioctl 實作</title>
         <description><![CDATA[<p dir="ltr">延續前一篇文章的介紹，在了解 <i>copy_to_user() </i>與 <i>copy_from_user()</i> 二個 API 後，接著 Jollen 將由 Linux device driver 的架構層來討論 user-space 與 kernel-space 的 I/O 機制。
同時，也延續在「架構層」系列教學專欄的 debug card 範例。</p>

<p dir="ltr"><b>基本觀念</b></p>
<p>需要由 user-space 讀取資料，或是寫入資料給 user-space 的主要 3 個 driver method 為：read、write 與 
ioctl。指向 user-space 資料空間（buffer）的指標是 kernel 回呼 driver method 時所傳遞進來的，我們由 
read、write 與 ioctl 的函數原型宣告來說明如何撰寫程式：</p>
<p>˙int card_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, 
unsigned long <b>arg</b>);<br>
˙ssize_t write(struct file *filp, const char <b>*buff</b>, size_t count, loff_t *offp);<br>
˙ssize_t read(struct file *filp, char <b>*buff</b>, size_t count, loff_t *offp);</p>
<p><i>fops-&gt;ioctl</i> 的參數 <b><i>arg</i></b>、<i>fops-&gt;write</i> 與 <i>fops-&gt;read</i> 的參數  
<b><i>buff</i></b> 是指向 user-space 
資料的指標。撰寫程式時，要注意資料型別上的不同。</p>
<p><b>實作 fops-&gt;ioctl</b></p>
<p>ioctl 代表 input/output control 的意思，故名思義，ioctl system call 是用來控制 I/O 
讀寫用的，並且是支援user application存取裝置的重要 system call。因此，在 Linux 驅動程式設計上，我們會實作 ioctl 
system call 以提供 user application 讀寫（input/output）裝置的功能。</p>
<p>依此觀念，當 user application 需要將數字顯示到 debug card 時。範例 debug card 0.1.0 便需要實作 ioctl 
system call，然後在 <i>fops-&gt;ioctl</i> 裡呼叫 <i>outb()</i> 將 user application 所指定的數字輸出至 I/O port 
80H。</p>
<p>User application 使用 GNU LIBC 的 <i>ioctl() </i>函數呼叫 device driver 
所提供的命令來「控制」裝置，因此驅動程式必須實作 <i>fops-&gt;ioctl</i>以提供「命令」給使用者。 </p>
<p><i>fops-&gt;ioctl</i> 函數原型如下：</p>
<blockquote>
<p>int ioctl(struct inode *, struct file *, unsigned int, unsigned long);</p>
</blockquote>
<p>Linux 驅動程式以一個唯一且不重覆的數字來代表 ioctl 的命令，設計 Linux 驅動程式時，我們必須使用 kernel 
所提供的巨集來宣告命令。根據命令的「方向」，kernel 提供以下 4 個巨集供我們宣告 ioctl 命令：,</p>
<ul>
  <li><i>_IO(type,nr)</i>：表示此 ioctl 命令不指定資料向方</li>
  <li><i>_IOR(type,nr,dataitem)</i>：此 ioctl 命令由裝置 (driver) 讀取資料</li>
  <li><i>_IOW(type,nr,dataitem)</i>：此 ioctl 命令將資料寫入裝置</li>
  <li><i>_IOWR(type,nr,dataitem)</i>：此 ioctl 命令同時讀寫資料</li>
</ul>
<p>若 user application 呼叫到驅動程式未提供的命令，則回傳 <i>-ENOTTY</i> 錯誤代碼。</p>
<p>debug card 0.1.0 範例裡，我們宣告了一個 <i>IOCTL_WRITE</i> 命令，當 user application 呼叫此命令後，驅動程式會將 
user application 所指定的數字顯示在 debug card 上。由於我們的資料方向為「寫入裝置」，因此使用的宣告巨集為 <i>_IOW</i>。</p>
<p>Debug card 0.1.0 實作<i> fops-&gt;ioctl</i> 的完整程式片斷如下：</p>
<blockquote>
<pre>#include &lt;linux/ioctl.h>

#define	DEV_MAJOR	121
#define	DEV_NAME	"debug"
#define  	DEV_IOCTLID	0xD0
#define	IOCTL_WRITE	_IOW(DEV_IOCTLID, 10, int)

unsigned long IOPort = 0x80;

void write_card(unsigned int);

void write_card(unsigned int num)
{
	outb((unsigned char)num, IOPort);
}

int card_ioctl(struct inode *inode, struct file *filp,
	  unsigned int cmd, unsigned long arg)
{
	switch (cmd) {
		case IOCTL_WRITE:
			write_card((unsigned int)arg);
			break;
		default:
			return -1;
	}
    	return 0;
}</pre>
</blockquote>
<p><i>struct file_operation</i> 的定義並未列出，不過請別忘了在<b> fops </b>裡加上 ioctl system call 的欄位。</p>
<p><b>User-space</b></p>
<p>以 debug card 0.1.0 驅動程式為例，user-space 的測試程式寫法如下：</p>
<blockquote>
<pre>int main(int argc, char *argv[])
{
    int devfd;
    int num = 0;

    if (argc > 1) num = atoi(argv[1]);
    if (num < 0) num = 0xff;

    <b>devfd = open("/dev/debug", O_RDONLY);</b>
    if (devfd == -1) {
	printf("Can't open /dev/debug\n");
	return -1;
    }

    printf("Write 0x%02x...\n", num);
    <b>ioctl(devfd, IOCTL_WRITE, num);</b>
    printf("Done. Wait 5 seconds...\n");
    sleep(5);
    close(devfd);

    return 0;
}</pre>
</blockquote>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
    <tr>
      <td bgcolor="#ff5959"><img alt height="1" width="1"></td>
    </tr>
  </table>
  <table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
    <tr>
      <td bgcolor="#ffd8ca"><font size="-1"><b>Also See</b></font></td>
    </tr>
  </table>
  <table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
    <tr>
      <td width="100%">
      <ul>
        <li>2006.12.26:
        <a href="http://www.jollen.org/blog/2006/12/linux_device_driver_io_3.html">
        Linux 驅動程式的 I/O, #3: kernel-space 與 user-space 的「I/O」</a></li>
        <li>2006.12.22:&nbsp;<a href="http://www.jollen.org/blog/2006/12/inux_device_driver_io_1.html">Linux 
        驅動程式的 I/O, #1: 基本概念</a></li>
        <li>2006.12.20:
        <a href="http://www.jollen.org/blog/2006/12/linux_device_driver_io2.html">
        Linux 驅動程式的 I/O, #2: I/O 存取相關函數</a></li>
      </ul>
      </td>
    </tr>
  </table>]]></description>
         <link>https://www.jollen.org/blog/2007/02/linux_device_driver_io_4.html</link>
         <guid>https://www.jollen.org/blog/2007/02/linux_device_driver_io_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 02 Feb 2007 21:28:57 +0800</pubDate>
      </item>
            <item>
         <title>Linux/PowerPC 新世代《序幕》</title>
         <description><![CDATA[Linux/PPC 現在已經改為 Linux/PowerPC。由於 IBM 成立 power.org 組織後，便以 Power Architecture 來稱呼 PowerPC 處理器晶片，因此，以往所慣用的 PPC 現在必須正名為 PowerPC。也就是說，用 PowerPC 來統稱新世代 Power Architecture 處理器晶片會比較好；以往的 PPC 簡稱將會被捨棄。

Linux kernel 由 2.6.15 版開始，已經把 <em>arch/ppc/</em> 重新組識到 <em>arch/powerpc/</em>，不過在整個 migration 的工作完成前，<em>arch/ppc/</em> 仍會持續存在，只不過 <em>arch/ppc/</em> 將會停止發展；接下來的 Linux kernel for PowerPC 將會轉移到 <em>arch/powerpc/</em> 的新架構下繼續發展。

另外，以往 PPC platform 是被寫在 <em>arch/ppc/platforms/*.c</em> 裡，組識結構不甚良好，現在終於被重新架構了；PowerPC platform（即 ARM Linux 的 "machine"）被重新組識在 <em>arch/powerpc/platforms/<Platform Name>/</em> 目錄下，一個 platform 一個目錄乾淨多了：

<blockquote># ls arch/powerpc/platforms/<br />
4xx/   85xx/  apus/  embedded6xx/  maple/     prep/<br />
82xx/  86xx/  cell/  iseries/      pasemi/    pseries/<br />
83xx/  8xx/   chrp/  Makefile      powermac/<br />
</blockquote>

此外，最近在 kernel 的 GIT 裡出現了 game box 的 platform 支援，第一個即將被正式加入Linux kernel 的是 PS3 遊戲機，2.6.20 將會加入 PS3 的 PowerPC platform。讓我們一同期待 PowerPC 新世代的來臨！]]></description>
         <link>https://www.jollen.org/blog/2007/01/linux_kernel_powerpc.html</link>
         <guid>https://www.jollen.org/blog/2007/01/linux_kernel_powerpc.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 31 Jan 2007 14:56:17 +0800</pubDate>
      </item>
            <item>
         <title>關於 U-Boot 的程式整體進入點</title>
         <description><![CDATA[<p>關於 U-Boot 的程式進入點，我以 SMDK2410 平臺為例來說明。首先，在每張 board 的目錄下，都會有一個 linker 
script，程式的整體進入點可以閱讀此檔案得知。以 SMDK2410 來說，我們應該由 <i>&lt;U-Boot&gt;/board/smdk2410/u-boot.lds</i> 
檔案看起，以下是其內容節錄：</p>
<blockquote>
	<pre>ENTRY(_start)
SECTIONS
{
        . = 0x00000000;

        . = ALIGN(4);
        .text      :
        {
          <b>cpu/arm920t/start.o</b>   (.text)
          *(.text)
        }
</pre>
</blockquote>
<p>在 linker script 的 SECTIONS 命令區塊中，.text section 一開始被放進 <i>&lt;U-Boot&gt;/cpu/arm920t/start.o</i> 
檔，所以能了解到此檔案是整個 U-Boot 程式的進入點。因此，「整體的 U-Boot 啟動流程，是由 <i>&lt;U-Boot&gt;/cpu/arm920t/start.o</i> 
檔案開始的」，<i>start.o</i> 是 U-Boot for ARM9 整個程式碼中，唯一的 assembly code，其原始程式的檔名為 <i>
start.S</i>。</p>
<p>所以，由 <i>&lt;U-Boot&gt;/cpu/arm920t/start.S</i> 開始研讀，是了解 U-Boot 
整個執行過程的起始點。另外，附帶一提，就概念上來說，<i>start.S</i> 最主要的工作是：</p>
<ul>
	<li>設定中斷向量表</li>
	<li>設定 processor</li>
	<li>Initialization Sequence</li>
	<li>Relocation</li>
</ul>
<p>因此，我們把 <i>start.S</i> 也稱為「hardware bring-up code」，而且是「前期」的硬體帶動碼（bring-up 
code）。在 U-Boot 的 <i>cpu/</i> 目錄下，可以看到 U-Boot 為各種不同的處理器所撰寫的 bring-up code，可見 
U-Boot 真的是一個「萬用 bootloader」。</p>
<p>下篇日記，我將會分享 <i>start.S</i> 的研讀筆記，歡迎對 U-Boot 有興趣的朋友來信指教。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/01/u-boot_entry_point.html</link>
         <guid>https://www.jollen.org/blog/2007/01/u-boot_entry_point.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">U-Boot , ARM9 and Pre-OS 相關</category>
        
        
         <pubDate>Tue, 30 Jan 2007 23:41:53 +0800</pubDate>
      </item>
            <item>
         <title>U-Boot：Porting a new Board（如何在 U-Boot 裡新增自己的 board）</title>
         <description><![CDATA[<p dir="ltr">U-Boot 在 <i>board/</i> 目錄下存放個別板子的支援程式碼（board support codes、board support 
package），並且是一張板子一個目錄。有時，基於一些理由，我們會想到新增一個新的 board 目錄來存放我們的 BSP。例如，以 Jollen-Kit!（JK2410）為例，JK2410 
是基於 SMDK2410 所發展，若將 U-Boot 設定為 '<i>smdk2410_config</i>'，也能支援我們的 JK2410。但是，我們會想要另外增加一個 
 
<i>&lt;U-Boot&gt;/board/jk2410/</i> 目錄，來擺放 JK2410 的程式碼，即便 <i>&lt;U-Boot&gt;/board/smdk2410/</i> 與  
<i>&lt;U-Boot&gt;/board/jk2410/</i> 裡的程式碼是 100% 
相同的。</p>
<p dir="ltr">有這樣需要的幾個具體理由是：</p>
<ul>
  <li dir="ltr">
  <p dir="ltr">練習 U-Boot 移植。</li>
  <li dir="ltr">
  <p dir="ltr">避免修改到原始的 BSP 程式碼。</li>
  <li dir="ltr">
  <p dir="ltr">門面問題，讓 U-Boot 也能有專屬於我們板子的支援，而不是基於其他的板子。</li>
  <li dir="ltr">
  <p dir="ltr">將來會正式釋出 patch。</li>
</ul>
<p dir="ltr">以下是「Porting a New Board」的具體做法：</p>
<p dir="ltr">1. 產生 JK2410 的 BSP 專屬目錄。</p>
<blockquote>
  <p dir="ltr"><i># cd &lt;U-Boot&gt;</i></p>
  <p dir="ltr"><i># cd board</i></p>
  <p dir="ltr"><i># cp -pa smdk2410/ jk2410/</i></p>
</blockquote>
<p dir="ltr"><i>jk2410/</i> 目錄下的程式碼與 <i>smdk2410/ </i>完全相同。</p>
<p dir="ltr">2. 建立專屬的 config 檔。</p>
<blockquote>
  <p dir="ltr"><i># cd &lt;U-Boot&gt;</i></p>
  <p dir="ltr"><i># cd include/configs/</i></p>
  <p dir="ltr"><i># cp smdk2410.h jk2410.h</i></p>
</blockquote>
<p dir="ltr">基於 <i>smdk2410.h</i> 來產生我們專屬的 <i>jk2410.h</i> 設定檔。</p>
<p dir="ltr">3. 修改 <i>Makefile</i>，基於 SMDK2410 來加入 JK2410 的 rule，紅色部份是新加入的設定：</p>
<blockquote>
<pre>smdk2410_config :       unconfig
        @./mkconfig $(@:_config=) arm arm920t smdk2410 NULL s3c24x0

<font color="#FF0000">jk2410_config   :       unconfig
        @./mkconfig $(@:_config=) arm arm920t <b>jk2410</b> NULL s3c24x0</font></pre>
</blockquote>
<p dir="ltr">紅色粗體字部份是板子的目錄名稱，即 <i>&lt;U-Boot&gt;/board/&lt;Board Name&gt;</i>。</p>

<p dir="ltr"><b>專屬於 JK2410 的設定選項</b></p>

<p dir="ltr">完成後，我們就能以專屬的設定，將 U-Boot 設定為 JK2410 的支援：</p>

<blockquote>
<p dir="ltr"><i># cd &lt;U-Boot&gt;</i></p>

<p dir="ltr"><i># make <b>jk2410_config</b><br>
Configuring for jk2410 board...</i></p>

</blockquote>
<p dir="ltr"><i>Let's start and happy porting !!</i></p>]]></description>
         <link>https://www.jollen.org/blog/2007/01/u-boot_porting_a_new_board.html</link>
         <guid>https://www.jollen.org/blog/2007/01/u-boot_porting_a_new_board.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">U-Boot , ARM9 and Pre-OS 相關</category>
        
        
         <pubDate>Mon, 29 Jan 2007 16:14:11 +0800</pubDate>
      </item>
            <item>
         <title>LiMo Foundation（Linux 手機發展基金會）開張了</title>
         <description><![CDATA[國際大廠「合作」成立「非營利組織」的基金會，似乎成為一種運動了，這意謂著更開放、更自由與更社群化的企業手法，已成為建構 ecosystem 的重要方針之一。

LiMo 基金會開門營業了。今天在 LinuxDevices.com 看看到這則消息：<a href="http://www.linuxdevices.com/news/NS2923387573.html">Cellphone giants unveil mobile Linux foundation</a>，六家手機大廠（Motorola, NEC, NTT DoCoMo, Panasonic Mobile Communications, Samsung Electronics, and Vodafone.）共同贊助並成立一個名為「LiMo」的基金會，引述一小段新聞原文如下：

<blockquote>The collaboration of giants now known as the LiMo Foundation was first announced in mid-June of last year. The group's goals and official name remained a mystery until today,</blockquote>

相當有趣的一段話，但是也顯示出 Linux mobile phone 未來可能隨時爆發的一股神秘力量。以下是 LiMo 基金會的主要目標（引述報導原文）：

<blockquote>- An API specification<br />
- An architecture<br />
- References to open source code<br />
- New source code-based reference implementation components (to be developed and contributed by Foundation members)<br />
- Specifications for referenced third party software</blockquote>

下圖是 LiMo 初步發表的 Linux 手機平臺架構。此架構中，Application UI Framework 部份，採用的是 GTK+。先前我們所注意的幾個 Linux 手機 framework 中，同樣也採用 GTK+ 做為 UI 的解決方案，可見 GTK+ 已經成為 Embedded Linux 應用於 handheld device 的重要 UI 解決方案了。


<img alt="limo_foundation_architecture_diag.gif" src="http://www.jollen.org/blog/2007/01/26/limo_foundation_architecture_diag.gif" width="588" height="425" />
圖（圖片來源：http://www.linuxdevices.com/news/NS2923387573.html）：LiMo Foundation architecture

另外，LiMO 的 APIs 授權方式採用「Foundation Public License（FPL）」授權，也就是 LiMo 自己的授權方式，不過關於 kernel 與 middleware 方面，當然還是保留原本的 GPL 授權方式。以下引述報導中，關於 LiMo 授權方式的說明：

<blockquote>From a legal perspective, it appears that the LiMo Foundation will license its APIs to members on a royalty-free basis, using a "Foundation Public License" (FPL). Of course, the kernel and middleware defined by the Foundation's specifications will remain under open source licenses such as the GPL.</blockquote>

LiMo 的授權方式是比較需要注意的地方，因為報導中提及 FPL 的授權方式中，對非會員的處理方式是「However, the license forbids source code distribution to non-members of the Foundation under any terms.」；不過，LiMo 也有針對 3rd party 的授權方式，LiMo 經由此授權方式，讓非會員也能利用 LiMo 的 API 來開發手機應用軟體。

LiMo Foundation 的官方網站為：<a href="http://www.limofoundation.org/sf/sfmain/do/home">http://www.limofoundation.org/sf/sfmain/do/home</a>。

<strong>延伸閱讀</strong>

<ul>
	<li>2007.01.22:
	<a href="http://www.jollen.org/blog/2007/01/openmoko_announce.html">OpenMoko 
	準備舉旗進攻了</a></li>
	<li>2006.11.08:
	<a href="http://www.jollen.org/blog/2006/11/mobile_20_openmoko_linux_smart.html">
	「Mobile 2.0 的思考」與第一隻採用 OpenMoko 的 Linux Smartphone</a></li>
	<li>2006.12.25:
	<a href="http://www.jollen.org/blog/2006/12/hiker_application_framework_linux_mobile.html">
	Hiker：另一個 GTK+ based 的 Linux mobile phone application framework</a></li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2007/01/limo_foundation.html</link>
         <guid>https://www.jollen.org/blog/2007/01/limo_foundation.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Fri, 26 Jan 2007 18:38:36 +0800</pubDate>
      </item>
            <item>
         <title>製作 ARM9 的 Bootstrap Root Filesystem</title>
         <description><![CDATA[<p>更新「Building ARM9 Bootstrap Root Filesystem」教學文件，在此提供與各位朋友分享，並請多多指正，以讓文件內容更加完善。以下文件以 MS-Word 轉檔，請多<!-- Copyright(c) 2007 www.jollen.org. All rights reserved. -->見諒。</p>
<hr>

<div class=Section1 style='layout-grid:18.0pt'>

<p class=MsoNormal align=center style='text-align:center'><b><font size=5
face=新細明體><span style='font-size:16.0pt;font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman";font-weight:bold'>《</span></font></b><span
class=SpellE><b><font size=5><span lang=EN-US style='font-size:16.0pt;
font-weight:bold'>Jollen</span></font></b></span><b><font size=5 face=新細明體><span
style='font-size:16.0pt;font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>的</span></font></b><b><font
size=5><span lang=EN-US style='font-size:16.0pt;font-weight:bold'>Root <span
class=SpellE>Filesystem</span></span></font></b><b><font size=5 face=新細明體><span
style='font-size:16.0pt;font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>建</span></font></b><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><b><font size=5 face=新細明體><span
style='font-size:16.0pt;font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>置技術系列》</span></font></b><b><font
size=5><span lang=EN-US style='font-size:16.0pt;font-weight:bold'><o:p></o:p></span></font></b></p>

<p class=MsoNormal align=center style='text-align:center'><b><font size=5
face=新細明體><span style='font-size:20.0pt;font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman";font-weight:bold'>製作</span></font></b><b><font
size=5><span lang=EN-US style='font-size:20.0pt;font-weight:bold'>ARM9</span></font></b><b><font
size=5 face=新細明體><span style='font-size:20.0pt;font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman";font-weight:bold'>的</span></font></b><b><font
size=5><span lang=EN-US style='font-size:20.0pt;font-weight:bold'>Bootstrap </span></font></b><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. -->
<b><font
size=5><span lang=EN-US style='font-size:20.0pt;font-weight:bold'>Root
<span class=SpellE>Filesystem</span><o:p></o:p></span></font></b></p>

<p class=MsoNormal><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>作者／陳俊宏</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --></p>

<p class=MsoNormal><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><a href="http://www.jollen.org/">http://www.jollen.org</a></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>更新日期：</span></font><span lang=EN-US>2007/1/23</span><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=2 face=新細明體><span style='font-size:10.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman";font-weight:bold'>在「完整註明出處」的前提下（</span></font></b><b><font
size=2><span lang=EN-US style='font-size:10.0pt;font-weight:bold'><a
href="http://www.jollen.org/license.html"><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>註明方式說明</span></font></a></span></font></b><b><font size=2
face=新細明體><span style='font-size:10.0pt;font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman";font-weight:bold'>），您能立即擁有轉貼與引用的授權，且毋需知會作者。</span></font></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman";font-weight:bold'>目的</span></font></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>製作</span></font><span lang=EN-US> bootstrap root <span
class=SpellE>filesyst</span></span><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><span lang=EN-US><span
class=SpellE>em</span></span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（</span></font><span
lang=EN-US>base root <span class=SpellE>filesystem</span></span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>）以提供一個最簡單、陽春且可開機的環境；製作完成的系統可開機到</span></font><span
lang=EN-US>shell</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>模式，並可使用</span></font>

<span class=SpellE><span lang=EN-US>busybox</span></span><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>提供的指令。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman";font-weight:bold'>準備工作</span></font></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>首先，您必須準備一台</span></font><span lang=EN-US> host </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>開發環境</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>，並安裝好</span></font><span
lang=EN-US> cross <span class=SpellE>toolchain</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>；接著，由於本文是做實機測試，因此，如果您沒有</span></font><span lang=EN-US> ARM9 </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>開發板，可以考慮使用</span></font><span
lang=EN-US> <span class=SpellE>Qemu</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>來做模擬測試。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>以下的操作示範，只節錄重點指令片段，您可能必須根據自己</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的整體實作流程，來微調指令的順序，或是參數等。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-weight:bold'>Step 1</span></font></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>：建立工作目錄</span></font></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>建立一個專用的工作</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>目錄，命名為</span></font> <i><span lang=EN-US
style='font-style:italic'>arm9.so-busybox/</span></i><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mkdir</span></span> arm9.so-busybox/</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>cd</span></span> arm9.so-busybox/<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>接著在</span></font> <i><span lang=EN-US style='font-style:
italic'>arm9.so-busybox/</span></i><span lang=EN-US> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>目錄下建立</span></font><span lang=EN-US> 4 </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>個子目錄：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mkdir</span></span> <span class=SpellE>src</span>/
install/ <span class=SpellE>mnt</span>/ pub/ build/<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>實際進行</span></font><span lang=EN-US> root <span class=SpellE>filesystem</span>
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>實作時，我們應該養成將檔案分類擺放的好習慣。以本專案為例，</span></font><i><span
lang=EN-US style='font-style:italic'>build/</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>目錄用來編譯程式，</span></font><span
class=SpellE><i><span lang=EN-US style='font-style:italic'>src</span></i></span><i><span
lang=EN-US style='font-style:italic'>/</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>目錄用來存放原始程式碼，</span></font><i><span
lang=EN-US style='font-style:italic'>install/</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>目錄則用來擺</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>放我們最後的</span></font><span
lang=EN-US> root <span class=SpellE>filesystem</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-weight:bold'>Step 2</span></font></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>：建立目錄架構</span></font></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>根據</span></font><span lang=EN-US> FHS </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的目錄</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>架構標準，在</span></font> <span
lang=EN-US>root <span class=SpellE>filesystem</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>目錄下（</span></font><i><span lang=EN-US style='font-style:
italic'>install/</span></i><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>）建立目錄階層架構：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>cd</span></span> install/</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mkdir</span></span> bin/ dev/ etc/ <span
class=SpellE>mnt</span>/ proc/ <span class=SpellE>sbin</span>/ <span
class=SpellE>usr</span>/<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>另外還有二個必要的目錄：</span></font><i><span lang=EN-US
style='font-style:italic'>/<span class=SpellE>var</span></span></i><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>與</span></font> <i><span
lang=EN-US style='font-style:italic'>/<span class=SpellE>tmp</span></span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>，由於這二個目錄</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>都需要具備寫入權限，所以在這裡我們是以</span></font><span
lang=EN-US> <span class=SpellE>ramdisk</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的做法來</span></font><span lang=EN-US> mount </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>這二個目錄。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-weight:bold'>Step 3</span></font></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>：建立裝置檔</span></font></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>在</span></font><span lang=EN-US> root <span class=SpellE>filesystem</span>
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的</span></font> <i><span
lang=EN-US style='font-style:italic'>dev/</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>目錄下建立必</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>要的裝置檔：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>crw</span></font></span><span lang=EN-US>------- 1
root <span class=SpellE>root</span> 5</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>，</span></font><span lang=EN-US> 1 <span class=SpellE>1</span></span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>月</span></font><span lang=EN-US> 1
1970 console</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>crw</span></font></span><span lang=EN-US>------- 1
root <span class=SpellE>root</span> 29</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>，</span></font><span lang=EN-US> 0 1</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>月</span></font><span lang=EN-US> 1 1970 fb0</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>crw</span></font></span><span lang=EN-US>------- 1
root <span class=SpellE>root</span> 1</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>，</span></font><span lang=EN-US> 3 1</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>月</span></font><span lang=EN-US> 1 1970 null</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>brw</span></font></span><span lang=EN-US>------- 1
root <span class=SpellE>root</span> 1</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>，</span></font><span lang=EN-US> 0 1</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>月</span></font><span lang=EN-US> 1 1970 ram0</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>crw</span></font></span><span lang=EN-US>------- 1
root <span class=SpellE>root</span> 5</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>，</span></font><span lang=EN-US> 0 1</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>月</span></font> <span lang=EN-US>1 1970 <span class=SpellE>tty</span></span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>crw</span></font></span><span lang=EN-US>------- 1
root <span class=SpellE>root</span> 4</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>，</span></font><span lang=EN-US> 0 1</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>月</span></font><span lang=EN-US> 1 1970 tty0</span><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>此階段使用</span></font> <span class=SpellE><i><span lang=EN-US
style='font-style:italic'>mknod</span></i></span><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>指令來完成。請先</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>切換到</span></font><span
lang=EN-US> root <span class=SpellE>filesystem</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的</span></font> <i><span lang=EN-US style='font-style:italic'>dev/</span></i><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>目錄下，接著執行以下指令：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mknod</span></span> console c 5 1</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mknod</span></span> fb0 c 29 0</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mknod</span></span> null c 1 3</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mknod</span></span> ram0 1 0</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mknod</span></span> <span class=SpellE>tty</span>
c 5 0</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mknod</span></span> tty0 c 4 0<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>對於需要產生大量</span></font><span lang=EN-US> device file </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的場合來說，可以</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>改用</span></font> <i><span
lang=EN-US style='font-style:italic'>genext2fs</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的</span></font> <span lang=EN-US>‘-D’ </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>參數來製作。詳見</span></font><span
lang=EN-US> <span class=SpellE>Jollen’s</span> <span class=SpellE>Blog</span></span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>：</span></font><span lang=EN-US>[<a
href="http://www.jollen.org/blog/2006/12/genext2fs_device_file_table.html"><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>使用</span></font> genext2fs <font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的</span></font> '-D'<font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>（</span></font>device file table<font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>）來建立</span></font> root filesystem</a>]</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-weight:bold'>Step 4</span></font></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>：加入</span></font><span
class=SpellE><span lang=EN-US>Busybox</span></span></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>編譯並</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>安裝</span></font><span lang=EN-US> <span class=SpellE>Busybox</span></span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>（動態程式庫方式）。將取得的</span></font><span
class=SpellE><span lang=EN-US>Busybox</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>原始碼解壓縮至</span></font><span lang=EN-US> project </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>目錄裡的</span></font> <span class=SpellE><i><span
lang=EN-US style='font-style:italic'>src</span></i></span><i><span lang=EN-US
style='font-style:italic'>/</span></i><span lang=EN-US> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>子目錄下，以下是幾個注意事項：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>
<ul>
	<li>
	<p class=MsoNormal style='margin-left:24.0pt;text-indent:-24.0pt;mso-list:l0 level1 lfo4;
tab-stops:list 24.0pt'><span dir=LTR><font face=新細明體>
	<span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>本教學文件使用</span></font><span lang=EN-US> <span class=SpellE>Busybox</span>
1.3.1</span></span></p></li>
	<li>
	<p class=MsoNormal style='margin-left:24.0pt;text-indent:-24.0pt;mso-list:l0 level1 lfo4;
tab-stops:list 24.0pt'><span dir=LTR><span class=SpellE><span
lang=EN-US>Busybox</span></span><span lang=EN-US> 1.3.0 </span></span>
	<font
face=新細明體>
	<span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>開始，使</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>用</span></font><span lang=EN-US>

Linux Kernel </span><font face=新細明體>
	<span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的</span></font><span
lang=EN-US> <span class=SpellE>Makefile</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>（因為開始支援</span></font> <i>
	<span lang=EN-US style='font-style:
italic'>CONFIG_DESKTOP</span></i><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>）。</span></font><span
lang=EN-US>Cross compile </span><font face=新細明體>
	<span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>時，需要修改</span></font>
	<span class=SpellE><i><span lang=EN-US style='font-style:italic'>Makefile</span></i></span><span
lang=EN-US> </span><font face=新細明體>
	<span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>如下：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p></li>
</ul>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>ARCH<span
style='mso-spacerun:yes'>            </span>?= arm<o:p></o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>CROSS_COMPILE<span
style='mso-spacerun:yes'>   </span>?=
/opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-</span></font></p>

<p class=MsoNormal><i><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-style:italic'>CROSS_COMPILE</span></font></i><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的設定是</span></font><span
lang=EN-US> cross <span class=SpellE>toolchain</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的「</span></font><span lang=EN-US>PREFIX</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>」，視您的</span></font><span lang=EN-US> <span
class=SpellE>toolchain</span> </span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>而定。您可由</span></font>

<span lang=EN-US><a href="http://www.jollen.org/kit/">http://www.jollen.org/kit/</a>
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>下載本文所使用的</span></font><span
lang=EN-US> GCC 3.4.1 ARM9 <span class=SpellE>toolchain</span></span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>，以使用與本文完全相同</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的修改。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><span class=SpellE><font size=3 face="Times New Roman"><span
lang=EN-US style='font-size:12.0pt'>Busybox</span></font></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>整合了常用的指令與工具，我們可以設</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>定</span></font><span
lang=EN-US> <span class=SpellE>Busybox</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>，以勾選我們需要的功能選項。進入</span></font><span lang=EN-US> <span
class=SpellE>Busybox</span> </span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的設定選單：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=GramE>make</span> <span class=SpellE>menuconfig</span></span></font></p>

<p class=MsoNormal><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>請注意，</span></font><span lang=EN-US>init </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>與</span></font><span lang=EN-US> shell
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>是必選的項目，請檢</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>查是否有勾選這二個功能。同時，也別忘了設定</span></font><span
lang=EN-US> <span class=SpellE>Busybox</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的安裝路徑，將安裝路徑指到我們</span></font><span lang=EN-US> root <span
class=SpellE>filesystem</span> </span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>目錄下。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>接著直接進行編譯（</span></font><span lang=EN-US>cross compile</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>）：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=GramE>make</span></span></font></p>

<p class=MsoNormal><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>編譯完成後，將</span></font> <span class=SpellE><span lang=EN-US>Busybox</span></span><span
lang=EN-US> </span><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. -->
<font face=新細明體>
<span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>安裝至我們的</span></font>

<span lang=EN-US>root <span class=SpellE>filesystem</span> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>目錄（即</span></font> <span lang=EN-US>Step
2 </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的</span></font> <i><span
lang=EN-US style='font-style:italic'>install/ </span></i><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>目錄）：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=GramE>make</span> install</span></font></p>

<p class=MsoNormal><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>此時，您</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>應該可以在</span></font><span lang=EN-US> root <span
class=SpellE>filesystem</span> </span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>目錄下看到</span></font><span
lang=EN-US> <span class=SpellE>Busybox</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>所安裝的檔案。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-weight:bold'>Step 5</span></font></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>：加入動態</span></font></b><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>程式庫</span></font><span
lang=EN-US><o:p></o:p></span></b></p>

<p class=MsoNormal><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>編譯完成的</span></font> <span class=SpellE><span lang=EN-US>Busybox</span></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>已經是給</span></font> <span
lang=EN-US>ARM9 </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>執行的格式了，但我們的編譯設定是將</span></font><span
class=SpellE><span lang=EN-US>Busybox</span></span><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>編譯成</span></font> <span lang=EN-US>shared
library </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>架構，因此</span></font> <span
class=SpellE><span lang=EN-US>Busybox</span></span><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>執行時</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>需要以下的檔案：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>˙</span></font> <i><span lang=EN-US style='font-style:italic'>libc.so.6</span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>：</span></font><span lang=EN-US>C
library</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>標準</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>程式庫。</span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>˙</span></font> <i><span lang=EN-US style='font-style:italic'>ld-linux.so.2</span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>：</span></font><span lang=EN-US>Native
dynamic loader</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>。</span></font></p>

<p class=MsoNormal><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>請由</span></font> <span class=SpellE><span lang=EN-US>toolchain</span></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>將以上二個檔案複</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>製至</span></font>
<span lang=EN-US>root <span class=SpellE>filesystem</span> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的</span></font> <i><span lang=EN-US
style='font-style:italic'>lib/</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>目錄下：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>cd</span></span><span class=GramE> ..</span>/../install
</span></font><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（切換至</span></font><span
lang=EN-US>root <span class=SpellE>filesystem</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>根目錄）</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># cp
/opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/ld-linux.so.2
lib/ </span></font><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（複制</span></font><span
lang=EN-US>native dynamic loader</span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>。以上命令請勿斷行）</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># cp
/opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/arm-9tdmi-linux-gnu/lib/libc.so.6
lib/ </span></font><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（複製</span></font><span
lang=EN-US>C library</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>。以上命令請勿斷行）</span></font></p>

<p class=MsoNormal><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font><span class=SpellE><font size=3 face="Times New Roman"><span
lang=EN-US style='font-size:12.0pt'>Busybox</span></font></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>會因版本與功能選項</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>設定的差異，而需要更多的程式庫。請使用</span></font><span
lang=EN-US> cross <span class=SpellE>toolchain</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的</span></font> <span class=SpellE><i><span lang=EN-US
style='font-style:italic'>objdump</span></i></span><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>指令來檢查</span></font><span lang=EN-US> <span
class=SpellE>Busybox</span> </span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的程式庫相依問題（無法使用</span></font>

<span class=SpellE><i><span lang=EN-US style='font-style:italic'>ldd</span></i></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>指令），並將所需的程式庫由</span></font><span
lang=EN-US> <span class=SpellE>toolchain</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>複製到</span></font><span lang=EN-US> root <span class=SpellE>filesystem</span>
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的</span></font> <i><span
lang=EN-US style='font-style:italic'>lib/</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>目錄下。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-weight:bold'>Step 6</span></font></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>：加入系統</span></font></b><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>檔案</span></font></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>加入</span></font><span lang=EN-US>2</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>個重要的系統檔</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>案於</span></font> <i><span lang=EN-US
style='font-style:italic'>etc/</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>目錄下：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>˙</span></font><span lang=EN-US><span style='mso-tab-count:
1'>    </span><span class=SpellE><i><span style='font-style:italic'>fstab</span></i></span></span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>：</span></font><span lang=EN-US>mount
table</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>。</span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>˙</span></font><span lang=EN-US><span style='mso-tab-count:
1'>    </span><span class=SpellE><i><span style='font-style:italic'>inittab</span></i></span></span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>：系統初始表（</span></font><span lang=EN-US>init
table</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>）。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><i><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-style:italic'>etc/<span class=SpellE>fstab</span></span></font></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>內容如下：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>/dev/ram0
/ ext2 defaults 1 <span class=SpellE>1</span></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=GramE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>none</span></font></span><span lang=EN-US> /proc <span
class=SpellE>proc</span> defaults 0 <span class=SpellE>0</span></span><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>/dev/ram1
/<span class=SpellE>tmp</span> <span class=SpellE>ramfs</span> defaults 0 <span
class=SpellE>0</span><o:p></o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>/dev/ram2
/<span class=SpellE>var</span> <span class=SpellE>ramfs</span> defaults 0 <span
class=SpellE>0</span> 
<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><span class=SpellE><font size=3 face="Times New Roman"><span
lang=EN-US style='font-size:12.0pt'>fstab</span></font></span><span lang=EN-US>
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>第一行設定，目的在將</span></font><span
lang=EN-US> /dev/ram0 </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>重新附掛成</span></font>
<span lang=EN-US>‘/’</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（</span></font><span
lang=EN-US>root</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>），此動作用意在於重新指定</span></font>
<span lang=EN-US>‘/’ </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的檔案系統為</span></font><span
lang=EN-US> ext2</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>。最後二行的目的是為了以</span></font><span
lang=EN-US> <span class=SpellE>ramfs</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>來</span></font><span lang=EN-US> mount </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>重</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>要的二個目錄：</span></font><i><span
lang=EN-US style='font-style:italic'>/<span class=SpellE>var</span> </span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>與</span></font> <i><span lang=EN-US
style='font-style:italic'>/<span class=SpellE>tmp</span></span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>；如此一來，就算開機沒做</span></font><span
lang=EN-US> remount root</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（詳見後文說明），也能對</span></font>

<i><span lang=EN-US style='font-style:italic'>/<span class=SpellE>tmp</span></span></i><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>與</span></font> <i><span
lang=EN-US style='font-style:italic'>/<span class=SpellE>var</span></span></i><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>目錄做寫入的動作</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><i><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-style:italic'>etc/<span class=SpellE>inittab</span></span></font></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>內容如下：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=GramE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>:0:sysinit</span></font></span><span lang=EN-US>:/etc/rc.d/rc.init</span><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=GramE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>:0:respawn</span></font></span><span lang=EN-US>:/bin/sh</span><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>根據這個</span></font> <span class=SpellE><i><span lang=EN-US
style='font-style:italic'>inittab</span></i></span><i><span lang=EN-US
style='font-style:italic'> </span></i><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>設定，當系統開機後</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>便會進入</span></font>

<span lang=EN-US>run level 0</span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>，在</span></font>
<span lang=EN-US>run level 0 </span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>模式下，</span></font><span
lang=EN-US>init process</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>會執行</span></font><span
lang=EN-US>2</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>個動作：</span></font><span
lang=EN-US>(1) </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>執行</span></font> <i><span
lang=EN-US style='font-style:italic'>/etc/<span class=SpellE>rc.d/rc.init</span></span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>，此即「</span></font><span lang=EN-US>init
script</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>」；</span></font><span
lang=EN-US>(2) </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>執行</span></font> <i><span
lang=EN-US style='font-style:italic'>/bin/<span class=SpellE>sh</span></span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>，即進入</span></font> <span lang=EN-US>shell

</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>模式。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>在此我們並沒有參照</span></font> <span lang=EN-US>LSB </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的標準來設定</span></font> <span lang=EN-US>run
level</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>，而且</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>也沒有使用</span></font>
<span class=SpellE><i><span lang=EN-US style='font-style:italic'>getty</span></i></span><i><span
lang=EN-US style='font-style:italic'> </span></i><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>來讓使用者登入（多使用者模式）。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-weight:bold'>Step 7</span></font></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>：編寫</span></font><span
lang=EN-US> Initial Script</span></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>根據</span></font> <span class=SpellE><i><span lang=EN-US
style='font-style:italic'>inittab</span></i></span><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的設定，我們</span></font><span lang=EN-US>

root <span class=SpellE>filesystem</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>的</span></font><span lang=EN-US> init script </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>位於</span></font> <i><span lang=EN-US
style='font-style:italic'>/etc/<span class=SpellE>rc.d/rc.init</span></span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>。以下提供一</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>個供</span></font><span
lang=EN-US> Embedded Linux </span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>使用的</span></font><span
lang=EN-US> init script </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>範本：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=GramE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>#!/</span></font></span><span lang=EN-US>bin/<span
class=SpellE>sh</span></span><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>automount</span></span> (/etc/<span
class=SpellE>fstab</span>)</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=GramE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>mount</span></font></span><span lang=EN-US> -a</span><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=GramE>remount</span> root</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=GramE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>mount</span></font></span><span lang=EN-US> -o remount

<span class=SpellE>rw</span> /</span><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>#</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><span class=GramE><font size=3 face="Times New Roman"><span
lang=EN-US style='font-size:12.0pt'>mkdir</span></font></span></span><span
lang=EN-US> /<span class=SpellE>var</span>/lock</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><span class=GramE><font size=3 face="Times New Roman"><span
lang=EN-US style='font-size:12.0pt'>mkdir</span></font></span></span><span
lang=EN-US> /<span class=SpellE>var/lock/subsys</span></span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><span class=GramE><font size=3 face="Times New Roman"><span
lang=EN-US style='font-size:12.0pt'>mkdir</span></font></span></span><span
lang=EN-US> /<span class=SpellE>var</span>/run</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>&nbsp;</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># start
other applications (Running application automatically during</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=GramE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'># booting up.</span></font></span><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>eg</span></span>. /bin/<span class=SpellE>thttpd</span>
–p 80 –d /<span class=SpellE>var</span>/www</span></font></p>

<p class=MsoNormal><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>當我們執行「</span></font><span lang=EN-US>mount –a</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>」後，</span></font><i><span lang=EN-US
style='font-style:italic'>mount</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>便會</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>去讀取前一步驟所設定的</span></font> <span
class=SpellE><i><span lang=EN-US style='font-style:italic'>fstab</span></i></span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>，並根據此表格的內容來做</span></font><span
lang=EN-US> mount </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的動作。另外，這裡有一個</span></font><span
lang=EN-US> remount </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的動作：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=GramE>mount</span> -o remount <span class=SpellE>rw</span> /<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>此動作的目的</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>是將</span></font><span lang=EN-US> root</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>（</span></font><span lang=EN-US>’/’</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>）重新</span></font><span lang=EN-US>

mount </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>成可讀寫，此動作是選擇性的，若省略不做，請務必保持</span></font>
<i><span lang=EN-US style='font-style:italic'>/<span class=SpellE>var</span></span></i><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>與</span></font> <i><span
lang=EN-US style='font-style:italic'>/<span class=SpellE>tmp</span></span></i><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>目錄是能寫入的（建議以</span></font><span
lang=EN-US> <span class=SpellE>ramdisk</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>方式實作為佳）。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>若</span></font><span lang=EN-US> root <span class=SpellE>filesystem</span>
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>未包含</span></font> <span
class=SpellE><i><span lang=EN-US style='font-style:italic'>inittab</span></i></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>設定檔，則</span></font><span
lang=EN-US> <span class=SpellE>Busybox</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>會使用以下的內建設定：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>:<span
class=GramE>:<span class=SpellE>sysinit</span></span><span class=SpellE>:/etc/init.d/rcS</span></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>:<span
class=GramE>:<span class=SpellE>askfirst</span></span><span class=SpellE>:/bin/sh</span></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>:<span
class=GramE>:<span class=SpellE>ctrlaltdel</span></span><span class=SpellE>:/sbin/reboot</span></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>:<span
class=GramE>:shutdown</span>:/<span class=SpellE>sbin/swapoff</span> -a</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>:<span
class=GramE>:shutdown</span>:/bin/<span class=SpellE>umount</span> -a -r</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>:<span
class=GramE>:restart</span>:/<span class=SpellE>sbin</span>/init<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>不過，還是建議編寫自己的</span></font> <span class=SpellE><i><span
lang=EN-US style='font-style:italic'>inittab</span></i></span><span lang=EN-US>
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>設</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>定檔。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-weight:bold'>Step 8</span></font></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>：製作</span></font><span
lang=EN-US> Root <span class=SpellE>Filesystem</span> </span></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>映像檔（</span></font><span
lang=EN-US>Image File</span></b><b><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman";
font-weight:bold'>）</span></font></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>截至目前為止，我們的檔案系統</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>已經擁有基本的系統指令與工具。接下來，我們即可將建置完成的</span></font><span
lang=EN-US> root <span class=SpellE>filesystem</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>製作成</span></font><span lang=EN-US> ext2 </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>格式的映像檔。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>以下提供二種</span></font><span lang=EN-US> ext2fs image file </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的製</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>作方式：</span></font><span lang=EN-US>(1)
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>土方法；</span></font><span
lang=EN-US>(2) </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>使用</span></font><span
lang=EN-US> genext2fs </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>工具。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>先說明傳統的土方法。首先，先利用</span></font><span class=SpellE><i><span
lang=EN-US style='font-style:italic'>dd</span></i></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>指令做出一個空白的檔案，大小為</span></font> <span lang=EN-US>4M</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>（</span></font><span lang=EN-US>bytes</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>）：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>dd</span></span> if=/dev/zero of=ext2fs <span
class=SpellE>bs</span>=1k count=4096<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>我們將檔案命名為</span></font> <i><span lang=EN-US style='font-style:
italic'>ext2fs</span></i><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>，接著</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>再將</span></font>
<i><span lang=EN-US style='font-style:italic'>ext2fs </span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>製作成</span></font> <span lang=EN-US>ext2
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>格式的檔案系統：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>#
mkfs.ext2 <b><span style='font-weight:bold'>ext2fs</span></b></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>mke2fs
1.32</span></font><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（</span></font><span
lang=EN-US>09-Nov-2002</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>）</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=GramE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>ext2fs</span></font></span><span lang=EN-US> is not a
block special device.</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>Proceed
anyway<span class=GramE>?<font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（</span></font></span>y</span></font><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>，</span></font><span lang=EN-US>n</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>）</span></font><span lang=EN-US>y</span><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>選擇</span></font><span lang=EN-US>y</span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>後出現以下畫面：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><span
class=SpellE><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'>Filesystem</span></font></span><span lang=EN-US>
label=</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>OS type</span></font><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>：</span></font><span lang=EN-US> Linux</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>Block
size=1024</span></font><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（</span></font><span
lang=EN-US>log=0</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>）</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>Fragment
size=1024</span></font><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（</span></font><span
lang=EN-US>log=0</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>）</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>128 <span
class=SpellE>inodes</span></span></font><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>，</span></font><span
lang=EN-US> 1024 blocks</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>51
blocks</span></font><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（</span></font><span
lang=EN-US>4.98%</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>）</span></font><span
lang=EN-US>reserved for the super user</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>First
data block=1</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>1 block
group</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>8192
blocks per group</span></font><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>，</span></font><span
lang=EN-US> 8192 fragments per group</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>128 <span
class=SpellE>inodes</span> per group</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>Writing
<span class=SpellE>inode</span> tables</span></font><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>：</span></font><span lang=EN-US> done</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>Writing
<span class=SpellE>superblocks</span> and <span class=SpellE>filesystem</span>
accounting information</span></font><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>：</span></font><span
lang=EN-US> done</span></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>This <span
class=SpellE>filesystem</span> will be automatically checked every 26 mounts or</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>180
days</span></font><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>，</span></font><span
lang=EN-US> whichever comes first. Use tune2fs -c or -<span class=SpellE>i</span>
to override.</span><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>到這裡我們已經做好一個檔案格</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>式為</span></font> <span lang=EN-US>ext2 </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的空白映像檔，再來只要將先前做好的</span></font><span
lang=EN-US> root <span class=SpellE>filesystem</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>全部複製到</span></font> <i><span lang=EN-US style='font-style:
italic'>ext2fs</span></i><span lang=EN-US> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>映像檔「裡面」即可。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>先將</span></font> <i><span lang=EN-US style='font-style:italic'>ext2fs</span></i><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>附掛至任一空</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>目錄，例如</span></font>
<span class=SpellE><i><span lang=EN-US style='font-style:italic'>mnt</span></i></span><i><span
lang=EN-US style='font-style:italic'>/</span></i><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mkdir</span></span> <span class=SpellE>mnt</span>/</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># mount
-t ext2 -o loop ext2fs <span class=SpellE>mnt</span>/ </span></font><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>（指定檔案系統為</span></font><span
lang=EN-US> ext2</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>）</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>複製檔案系統時，我們不</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>使用</span></font> <i><span lang=EN-US>cp</span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'> 指令，而是利用 </span></font><i><span lang=EN-US>tar</span></i><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'><i> </i>來完成：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>cd</span></span> install/</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># tar <span
class=SpellE>cz</span> * <span class=GramE>&gt; ..</span>/<span class=SpellE>install.tar.gz</span>

</span></font><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（將檔案系統做成</span></font><span
class=SpellE><span lang=EN-US>tarball</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>，同時也備份</span></font><span lang=EN-US> root <span
class=SpellE>filesystem</span></span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>。）</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>cd</span></span><span class=GramE> ..</span></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>cd</span></span> <span class=SpellE>mnt</span>/</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=GramE>tar</span> <span class=SpellE>zxvf</span> ../<span class=SpellE>install.tar.gz</span>
</span></font><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（再將</span></font><span
class=SpellE><span lang=EN-US>tarball</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>解至映像檔）</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>接著將映像檔</span></font> <span class=SpellE><span lang=EN-US>umount</span></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>並壓</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>縮即可：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>cd</span></span><span class=GramE> ..</span></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>umount</span></span> <span class=SpellE>mnt</span>/</span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>gzip</span></span> -9c ext2fs &gt; pub/ext2fs.gz<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>最後得到的</span></font> <i><span lang=EN-US style='font-style:
italic'>ext2fs.gz</span></i><span lang=EN-US> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>即是完成品。請注意，若不使用</span></font><i><span lang=EN-US
style='font-style:italic'> tar</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>來說，也應該使用</span></font> <span
class=SpellE><i><span lang=EN-US style='font-style:italic'>cpio</span></i></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>來複製</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>檔案，避免使用</span></font>

<i><span lang=EN-US style='font-style:italic'>cp</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>指令。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman";font-weight:bold'>使</span></font></b><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><b><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman";font-weight:bold'>用</span></font> <span lang=EN-US>genext2fs</span></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><i><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-style:italic'>genext2fs</span></font></i><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>是一個</span></font><span
lang=EN-US> ext2 <span class=SpellE>filesystem</span> image file </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的製作工具，可以讓我們很方便地將</span></font> <span
lang=EN-US>root <span class=SpellE>filesystem</span> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>製作成</span></font> <span lang=EN-US>image </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>檔。請由</span></font> <span lang=EN-US>genext2fs

</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的官方網站下</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>載原始碼套件：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>http://genext2fs.sourceforge.net/<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>編譯後可以取得</span></font> <i><span lang=EN-US style='font-style:
italic'>genext2fs</span></i><span lang=EN-US> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>檔案，以下</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>是將</span></font> <i><span lang=EN-US style='font-style:
italic'>install/</span></i><span lang=EN-US> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>目錄製作成</span></font> <span lang=EN-US>ext2fs image </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>檔的指令：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># genext2fs
-b 8192 -<span class=SpellE>i</span> 1024 -d install/ ext2fs<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>執行後，會得到檔名為</span></font> <i><span lang=EN-US
style='font-style:italic'>ext2fs</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的</span></font> <span lang=EN-US>image

</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>檔，大小為</span></font> <span
lang=EN-US>8 MB</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（透過</span></font> <span
lang=EN-US>‘-b’ </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>參數指定</span></font><span
lang=EN-US> image file </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>大小）；接著同樣再用</span></font>
<span class=SpellE><i><span lang=EN-US style='font-style:italic'>gzip</span></i></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>將</span></font> <i><span
lang=EN-US style='font-style:italic'>ext2fs</span></i><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>檔壓縮即可。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt;font-weight:bold'>Step 9</span></font></b><b><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman";font-weight:bold'>：在</span></font> <span
lang=EN-US>Target </span></b><b><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman";
font-weight:bold'>端做</span></font></b><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><b><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman";
font-weight:bold'>測試</span></font></b><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>本步驟以</span></font><span lang=EN-US> <span class=SpellE>Jollen</span>-Kit!
</span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>為例，</span></font><span
class=SpellE><span lang=EN-US>Jollen</span></span><span lang=EN-US>-Kit! </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>是由</span></font> <span lang=EN-US><a
href="http://www.jollen.org/">www.jollen.org</a> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>所推出的</span></font><span lang=EN-US> ARM9 training board</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>，詳細介紹請參考</span></font><span
lang=EN-US> [<a href="http://www.jollen.org/kit/">http://www.jollen.org/kit/</a>]</span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>。請注意，本階段的操作，視</span></font><span
lang=EN-US> target device </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的不同而不同，因此以下示範只適用於</span></font><span
lang=EN-US> <span class=SpellE>Jollen</span>-Kit! </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>或是其他的</span></font><span lang=EN-US> SMDK2410 </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>平臺。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>步驟</span></font> <span lang=EN-US>8 </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>所得到的</span></font> <i><span lang=EN-US style='font-style:
italic'>ext2.gz</span></i><span lang=EN-US> </span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>必須再包</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>裝成</span></font> <span lang=EN-US>U-Boot </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>的格式，才能透過</span></font> <span
lang=EN-US>U-Boot </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>載入到</span></font>

<span lang=EN-US>RAM</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>，以成為</span></font><span
lang=EN-US> kernel </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>的</span></font><span
lang=EN-US> initial <span class=SpellE>ramdisk</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>（</span></font><span class=SpellE><span lang=EN-US>initrd</span></span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>）：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'># <span
class=SpellE><span class=GramE>mkimage</span></span> -A arm -O <span
class=SpellE>linux</span> -T <span class=SpellE>ramdisk</span> -C none -a
0x30800000 -e 0x30800000 -n <span class=SpellE>ramdisk</span> -d ext2fs.gz <span
class=SpellE>urootfs.img</span><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><font size=3 face=新細明體><span style='font-size:12.0pt;
font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>執行後可得到</span></font> <span class=SpellE><i><span lang=EN-US
style='font-style:italic'>urootfs.img</span></i></span><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>檔案，在測試階</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>段為了方便起見，我們可以直接將</span></font> <span
class=SpellE><i><span lang=EN-US style='font-style:italic'>urootfs.img</span></i></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>載到</span></font> <span
lang=EN-US>RAM </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>做測試；</span></font><span
lang=EN-US>U-Boot </span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>指令如下：</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal style='margin-left:12.0pt;mso-para-margin-left:1.0gd'><font
size=3 face="Times New Roman"><span lang=EN-US style='font-size:12.0pt'>jollen.org
# <span class=SpellE>tftpboot</span> 32000000 <span class=SpellE>urootfs.img</span>;
<span class=SpellE>tftpboot</span> 30F00000 <span class=SpellE>uimage.img</span>;
<span class=SpellE>bootm</span> 30F00000 32000000<o:p>&nbsp;</o:p></span></font></p>

<p class=MsoNormal><span class=SpellE><i><font size=3 face="Times New Roman"><span
lang=EN-US style='font-size:12.0pt;font-style:italic'>urootfs.img</span></font></i></span><span
lang=EN-US> </span><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>是我們</span></font><!-- Copyright(c) 2007 www.jollen.org. All rights reserved. --><font face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:
"Times New Roman";mso-hansi-font-family:"Times New Roman"'>製作的</span></font><span
lang=EN-US> root <span class=SpellE>filesystem</span></span><font face=新細明體><span
style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:
"Times New Roman"'>，</span></font><span class=SpellE><i><span lang=EN-US
style='font-style:italic'>uimage.img</span></i></span><span lang=EN-US> </span><font
face=新細明體><span style='font-family:新細明體;mso-ascii-font-family:"Times New Roman";
mso-hansi-font-family:"Times New Roman"'>則是給</span></font><span lang=EN-US> <span
class=SpellE>Jollen</span>-Kit! </span><font face=新細明體><span style='font-family:
新細明體;mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>使用的</span></font><span
lang=EN-US> Linux kernel</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>（</span></font><span
lang=EN-US>pre-built</span><font face=新細明體><span style='font-family:新細明體;
mso-ascii-font-family:"Times New Roman";mso-hansi-font-family:"Times New Roman"'>）。</span></font><font size=3 face="Times New Roman"><span lang=EN-US
style='font-size:12.0pt'><o:p>&nbsp;</o:p></span></font></p>

</div>

<p dir="ltr"><b>延伸閱讀</b></p>
<ul>
  <li dir="ltr">
  <p dir="ltr">2006.10.03:
  <a href="http://www.jollen.org/blog/2006/10/library_dependency_1.html">Library 
  Dependency 的議題要點</a></li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2007/01/arm9_bootstrap_root_filesystem.html</link>
         <guid>https://www.jollen.org/blog/2007/01/arm9_bootstrap_root_filesystem.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 23 Jan 2007 18:25:34 +0800</pubDate>
      </item>
            <item>
         <title>OpenMoko 準備舉旗進攻了</title>
         <description><![CDATA[OpenMoko 二天前釋出他的最新 roadmap 規劃，OpenMoko 的 project leader "Sean Moss-Pultz" 在 1/20 日發表了「<a href="http://lists.openmoko.org/pipermail/announce/2007-January/000000.html">釋放你的手機（Free Your Phone）</a>」，此文提到，Neo1973/OpenMoko 將採三階段來「釋放手機」，首先即將到來的是「2007-02-11 Phase 0: Developer Preview」。

第一階段的計畫中，開發者將能取得 OpenMoko 的原始碼；完整的 OpenMoko Linux distribution 與原始碼將會開放。以下是 FIC Neo1973 手機的硬體規格：

* 120.7 x 62 x 18.5 (mm)
* 2.8" VGA (480x640) TFT Screen
* Samsung s3c2410 SoC @ 266 MHz
* Global Locate AGPS chip
* Ti GPRS (2.5G not EDGE)
* Unpowered USB 1.1
* Touchscreen
* micro-sd slot
* 2.5mm audio jack
* 2 additional buttons
* 1200 mAh battery (charged over USB)
* 128 MB SDRAM
* 64 MB NAND Flash
* Bluetooth (2.0)

軟體方面，OpenMoko Linux distribution 內建於 OpenEmbedded 中，並使用到以下主要的軟體套件：

* Linux 2.6.17.14
* gcc 4.1.1
* binutils 2.17.50.0.5
* glibc 2.4
* Xorg 7.1
* glib 2.6.4
* gtk 2.6.10
* dbus 0.9
* eds
* (more)

OpenMoko 本身是一套 Linux mobile phone 的完整 application framework，OpenMoko 基於 GTK+2 / Dbus / GConf / EDS，並提供 C APIs 給手機開發者使用。此外，OpenMoko application framework 的 UI 基於 GObject，並提供以下的 UI class（引述原文）：

* MokoApplication -- base application class
* MokoPanedWindow -- base class for stylus main windows
* MokoMenuBox -- menu widget holding application and filter menu
* MokoFingerWindow -- base class for finger main windows
* MokoFingerWheel -- rotary finger wheel
* MokoFingerToolBox -- finger tool box
* (more)

OpenMoko 手機釋出計畫的第二階段為「2007-03-11 Phase 1: Official Developer Launch」，此階段將能在 OpenMoko.com 上「網購」每支 US$ 350 的 Neo1973 手機。第三階段則是「2007-09-11 Phase 2: Mass Market Sale」，看來 OpenMoko 舉大旗的時間將會是在今年的 9 月份。值得期待！

由這幾個月來的討論，與二天前所 announce 的消息來看，更能確立 OpenMoko 當初 presentation 時，所要掌握的「Mobile 2.0」核心精神：Open、open、open；Community、community、community。

相信這是值得期待的一個 open source 專案。

<strong>延伸閱讀</strong>

<ul>
<li>2006.11.08: <a href="http://www.jollen.org/blog/2006/11/mobile_20_openmoko_linux_smart.html">「Mobile 2.0 的思考」與第一隻採用 OpenMoko 的 Linux Smartphone</a></li>
<ul>

]]></description>
         <link>https://www.jollen.org/blog/2007/01/openmoko_announce.html</link>
         <guid>https://www.jollen.org/blog/2007/01/openmoko_announce.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Openmoko</category>
        
        
         <pubDate>Mon, 22 Jan 2007 20:16:27 +0800</pubDate>
      </item>
            <item>
         <title>[筆記] Linux 2.6 的 MMC Core</title>
         <description><![CDATA[<p>以下整理自 Jollen 筆記（非教學文件），許多地方未能清楚交待，這部份有請大家自行補齊了。本文分享給有志研究 Linux MMC 
驅動程式實作（MMC Core）的朋友參考。以下分析基於 Linux 2.6.17.7，更新版本的 kernel 加入了許多 patch（例如 Linux 2.6.19 的 
<a href="http://www.archivesat.com/Linux_kernel_discussion/thread2328642.htm">SDHC patch</a>），這些更新內容不在討論之列。</p>
<p>續前一篇日記「<a href="http://www.jollen.org/blog/2007/01/open_source_sd_mmc_sdio.html">Linux（open 
source）的 SD/MMC/SDIO 支援現況概要</a>」所提到的，目前的 Linux SD/MMC/SDIO 「嚴格來說」，只支援 MMC 記憶卡，如果是要插上 SD 記憶卡，使用上則會有諸多限制。</p>
<p>由 Linux 驅動程式的角度來看，單就 MMC 的部份來分析的話，Linux 的 SD/MMC 驅動程式層包含以下實作（<i>Kconfig</i>）：</p>
<ul>
	<li><i>CONFIG_MMC</i></li>
	<li><i>CONFIG_MMC_BLOCK</i></li>
</ul>
<p>相關檔案位於 <i>drivers/mmc/</i> 目錄，我們由 <i>Makefile</i> 來找到實作檔案：</p>
<blockquote>
	<pre>#                                                       
# Core                                                  
#                                                       
<font color="#FF0000">obj-$(CONFIG_MMC)               += mmc_core.o   </font>        
                                                        
#                                                       
# Media drivers                                         
#                                                       
<font color="#FF0000">obj-$(CONFIG_MMC_BLOCK)         += mmc_block.o   </font>       
                                                        
#                                                       
# Host drivers                                          
#                                                       
obj-$(CONFIG_MMC_ARMMMCI)       += mmci.o               
obj-$(CONFIG_MMC_PXA)           += pxamci.o             
obj-$(CONFIG_MMC_IMX)           += imxmmc.o             
obj-$(CONFIG_MMC_SDHCI)         += sdhci.o              
obj-$(CONFIG_MMC_WBSD)          += wbsd.o               
obj-$(CONFIG_MMC_AU1X)          += au1xmmc.o            
obj-$(CONFIG_MMC_OMAP)          += omap.o               
obj-$(CONFIG_MMC_AT91RM9200)    += at91_mci.o           
                                                        
mmc_core-y := <font color="#FF0000">mmc.o mmc_queue.o mmc_sysfs.o  </font>           
</pre>
</blockquote>
<p>Host controller 驅動程式的部份先不討論，MMC Core API 層的實作檔案整理如下：</p>
<ol>
	<li><i>drivers/mmc/mmc.c</i>：主要的 MMC command 與 protocol 實作。</li>
	<li><i>drivers/mmc/mmc_queue.c</i>：I/O Request Queue 的實作。</li>
	<li><i>drivers/mmc/mmc_sysfs.c</i>：Linux 2.6 的 <b>kobject </b>與 <b>sysfs </b>
	實作。</li>
	<li><i>drivers/mmc/mmc_block.c</i>：區塊層架構實作，即 interface to user-space 的 file 
	operation 部份。</li>
</ol>
<p>由此可知，MMC Core 層包含以下原始程式碼：</p>
<ul>
	<li><i>drivers/mmc/mmc.c</i></li>
	<li><i>drivers/mmc/mmc_queue.c</i></li>
	<li><i>drivers/mmc/mmc_sysfs.c</i></li>
</ul>
<p>區塊層部份，<i>mmc_block.c</i> 以 devfs 的方式向 kernel 註冊：</p>
<blockquote>
	<pre>static struct mmc_driver <b><font color="#FF0000">mmc_driver</font></b> = {
	.drv		= {
		.name	= "mmcblk",
	},
	.probe		= mmc_blk_probe,
	.remove		= mmc_blk_remove,
	.suspend		= mmc_blk_suspend,
	.resume		= mmc_blk_resume,
};

static int __init mmc_blk_init(void)
{
	int res = -ENOMEM;

	res = <b><font color="#FF0000">register_blkdev(major, "mmc");</font></b>
	if (res < 0) {
		printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n",
		       major, res);
		goto out;
	}
	if (major == 0)
		major = res;

	<b><font color="#FF0000">devfs_mk_dir("mmc");</font></b>
	return <b><font color="#FF0000">mmc_register_driver(&mmc_driver);</font></b>

 out:
	return res;
}
...
module_init(mmc_blk_init);</pre>
</blockquote>
<p><i>mmc_register_driver()</i> 向 MMC Core 層註冊，接著 MMC Core 再對 <b>kobject</b> 
做註冊。學過 Linux 2.6 驅動程式的朋友都曉得，Core API 層必須呼叫 <i>driver_register()</i> 向 <b>kobject</b> 
註冊為 <b>Driver</b>；對於底層（machine-dependent）的 host controller 驅動程式而言，則必須向 <b>
kobject</b> 註冊為 <b>Platform Driver</b>。</p>
<p>由於 <b>kobject</b> 會 callback <b>fops </b>的 <i>probe</i> method，所以 <i>mmc_blk_probe()</i> 
函數就是 MMC 區塊層的進入點（entry point）。所以，MMC 區塊層的一切動作就要由 <i>mmc_blk_probe()</i> 
函數看起。Linux 2.6.17.7 的 MMC 區塊層使用到大家所熟悉的 <i>genhd.c</i> 層。</p>
<p>至於 Linux 區塊層驅動程式最重要的「初始化 I/O request queue」動作，則是同樣在 <i>mmc_blk_probe() </i>
階段呼叫到 MMC Core 層的 <i>mmc_init_queue()</i> 來完成。</p>
<p>了解 Linux 的 MMC 整體架構後，便能開始深入研究「規格的實作」部份。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/01/linux_26_mmc_core.html</link>
         <guid>https://www.jollen.org/blog/2007/01/linux_26_mmc_core.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 20 Jan 2007 00:06:38 +0800</pubDate>
      </item>
            <item>
         <title>Linux（open source）的 SD/MMC/SDIO 支援現況概要</title>
         <description><![CDATA[<p><b>SD（Secure Digital）與 MMC（Multimedia Card）</b></p>
<p>SD 是一種 flash memory card 的標準，也就是一般常見的 SD 記憶卡，而 MMC 則是較早的一種記憶卡標準，目前已經被 SD 
標準所取代。在維基百科上有相當詳細的 SD/MMC 規格說明：[<a href="http://zh.wikipedia.org/wiki/Secure_Digital">http://zh.wikipedia.org/wiki/Secure_Digital</a>]。</p>
<p><b>SDIO（Secure Digital I/O）</b></p>
<p>SDIO 是目前我們比較關心的技術，SDIO 故名思義，就是 SD 的 I/O 
介面（interface）的意思，不過這樣解釋可能還有點抽像。更具體的說明，SD 本來是記憶卡的標準，但是現在也可以把 SD 
拿來插上一些週邊介面使用，這樣的技術便是 SDIO。</p>
<p>所以 SDIO 本身是一種相當單純的技術，透過 SD 的 I/O 接腳來連接外部週邊，並且透過 SD 上的 I/O 資料接位與這些週邊傳輸資料，而且 SD 
協會會員也推出很完整的 SDIO stack 驅動程式，使得 SDIO 週邊（我們稱為 SDIO 卡）的開發與應用變得相當熱門。</p>
<p>現在已經有非常多的手機或是手持裝置都支援 SDIO 的功能（SD 標準原本就是針對 mobile device 而制定），而且許多 SDIO 
週邊也都被開發出來，讓手機外接週邊更加容易，並且開發上更有彈性（不需要內建週邊）。目前常見的 SDIO 週邊（SDIO 卡）有：</p>
<ul>
	<li>Wi-Fi card（無線網路卡）</li>
	<li>CMOS sensor card（照相模組）</li>
	<li>GPS card</li>
	<li>GSM/GPRS modem card</li>
	<li>Bluetooth card</li>
	<li>Radio/TV card（很好玩）</li>
</ul>
<p>SDIO 的應用將是未來嵌入式系統最重要的介面技術之一，並且也會取代目前 GPIO 式的 SPI 介面。</p>
<p><b>SD/SDIO 的傳輸模式</b></p>
<p>SD 傳輸模式有以下 3 種：</p>
<ul>
	<li>SPI mode（required）</li>
	<li>1-bit mode</li>
	<li>4-bit mode</li>
</ul>
<p>SDIO 同樣也支援以上 3 種傳輸模式。依據 SD 標準，所有的 SD（記憶卡）與 SDIO（週邊）都必須支援 SPI mode，因此 SPI mode 
是「required」。此外，早期的 MMC 卡（使用 SPI 傳輸）也能接到 SD 插糟（SD slot），並且使用 SPI mode 或 1-bit 
mode 來讀取。</p>
<p><b>SD 的 MMC Mode</b></p>
<p>SD 也能讀取 MMC 記憶體，雖然 MMC 標準上提到，MMC 記憶體不見得要支援 SPI mode（但是一定要支援 1-bit 
mode），但是市面上能看到的 MMC 卡其實都有支援 SPI mode。因此，我們可以把 SD 設定成 SPI mode 的傳輸方式來讀取 MMC 記憶卡。</p>
<p>SD 的 MMC Mode 就是用來讀取 MMC 卡的一種傳輸模式。不過，SD 的 MMC Mode 雖然也是使用 SPI 
mode，但其物理特性仍是有差異的：</p>
<ul>
	<li>MMC 的 SPI mode 最大傳輸速率為 20 Mbit/s；</li>
	<li>SD 的 SPI mode 最大傳輸速率為 25 Mbit/s。</li>
</ul>
<p>為避免混淆，有時也用 SPI/MMC mode 與 SPI/SD mode 的寫法來做清楚區別。</p>
<p><b>Linux 的 SD/MMC 驅動程式</b></p>
<p>Linux 2.6.17 正式加入 SD/MMC 驅動程式，「官方版」的 release 
當然只能在 ARM 平臺上使用，若不討論 host controller 的支援，目前的 Linux SD/MMC/SDIO 支援狀況（Core 
API）如下（Open source compatible for SD/MMC/SDIO）：</p>
<blockquote>
	<p>1. 支援 MMC。Open source 的實作即是 SD-MMC 的驅動程式。</p>
	<p>2. Core API 「可以做到」支援 SD/SPI mode <b>only</b>，因為有辦法做到支援 SPI mode，當然 1-bit mode 也「可以想辦法」做出來。將 1-bit mode 擴充至 4-bit mode，網路上雖然有人做，不過會有版權問題。</p>
	<p>3. SDIO 目前只看到 Atheros Wi-Fi card 的支援（可在 
	sourceforge 上找到，已正式釋出 source code）。</p>
        <p>4. 完整的 SD stack 是要付錢的，請特別注意，SD Card Association product license agreement 並「不」允許 open source 的驅動程式實作。所以也不用再問，為什麼沒有 open source 的 SD 驅動程式了；Linux 是無罪的！</p>
</blockquote>
<p>請特別留意的是，以上所述是「官方」的正式支援，並不包含玩家所分享的各種 patch。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/01/open_source_sd_mmc_sdio.html</link>
         <guid>https://www.jollen.org/blog/2007/01/open_source_sd_mmc_sdio.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Fri, 19 Jan 2007 17:32:39 +0800</pubDate>
      </item>
            <item>
         <title>SD/SDIO 的開發板</title>
         <description><![CDATA[<p><a href="http://jinvanisystech.com/">
Jinvani Systech</a> 推出 SD/SDIO 的發展平臺，特別的是，這是一張 PCI-based 的 host controller 
卡！相當的有用處，詳見 LinuxDevices.com 上的新聞全文 [<a href="http://www.linuxdevices.com/news/NS7542044278.html">http://www.linuxdevices.com/news/NS7542044278.html</a>]。</p>
<p>我以「相當的有用處」來形容這個發展平臺的理由是，在 
SD/SDIO 相關的專案裡頭，host controller 的發展與移植並非首要的工作，重點工作反而是：</p>
<p>1. 基於 SD/SDIO 週邊介面卡的 device driver，例如 WiFi 
card、Bluetooth、GPS modem、SD memory 等。</p>
<p>2. SD/SDIO card 的相關應用程式，例如數位相框軟體（基於 SD memory 
card）等。</p>
<p>因此，如果可以在 host 
端（x86）插上一張這種卡，事情就美妙許多了，因為可以減化部份應用程式的測試工作，同時也能加速 SDIO device driver 
的開發。以往，雖然寫的是單純的 SD/SDIO 應用程式，可是總是要做 target 端的實機測試，總覺得有點不方便；或是，無法在 host 
端使用模擬平臺（例如：Qemu）來做驅動程式的測試，非得要丟上 target board 才有辦法實測，相當費力不說，也不方便除錯（debug）。</p>
<p>對於 Embedded Linux 的應用開發，這張 SD/SDIO 
開發板確實很有幫助。以下是這張 SD/SDIO 開發板的規格（節錄自 LinuxDevices.com 新聞）：</p>

<li>Supports two SD card slots</li>
<li>SD memory and SDIO capable</li>
<li>SD Host 1.0 compatible</li>
<li>Supports high capacity / high speed SD memory cards</li>
<li>Supports security commands</li>
<li>Supports SD 1-bit and 4-bit modes (no SPI)</li>
<li>Supports SD Sleuth II</li>
<p>此外，這張 SD/SDIO 開發板也支援 SD Memory 開機！</p>]]></description>
         <link>https://www.jollen.org/blog/2007/01/sdsdio.html</link>
         <guid>https://www.jollen.org/blog/2007/01/sdsdio.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Fri, 19 Jan 2007 15:49:48 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 測試：Bootstrap root filesystem（x86）階段《程式執行測試》</title>
         <description><![CDATA[<p dir="ltr">本文是單純實作時的測試方法說明，「Bootstrap root filesystem」的<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->做法、步驟、概念與隱含的觀念請自行參考相關文件。</p>
<p dir="ltr"><b>前情提要：由 Busybox 開始</b></p>
<p dir="ltr">這個階段的實作「提要」如下。</p>
<p dir="ltr">我們到 Busybox 官方網站下載原始碼：<a href="http://busybox.net/downloads/busybox-1.3.1.tar.bz2">http://busybox.net/downloads/busybox-1.3.1.tar.bz2</a>。如果您是初次「把玩」open 
source package，必須了解一個<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->重要的概念：對任何的 open source 套件而言，以下二份文件是必讀的：</p>
<ul>
  <li dir="ltr">
  <p dir="ltr">README</li>
  <li dir="ltr">
  <p dir="ltr">INSTALL</li>
</ul>
<p dir="ltr">大多數的套件都包含以上二個檔案，並且許多重要的資訊<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->都寫在這二份文件檔裡。將 Busybox 套件解開後，先檢查以下的 utility 
是否有勾選：</p>
<ul>
  <li dir="ltr">
  <p dir="ltr">init process</li>
  <li>shell（請優先選用 ash）<!--Copyright(c) 2007 www.jollen.org. All rights reserved.--></li>
</ul>
<p>並且將安裝路徑（install prefix）設定到事先建立的 root filesystem 
空目錄。接著進行編譯，在編譯過程中可能會產生一些錯誤，請先把產生錯誤的選項取消，試著將 Busybox 編譯出<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->來後並安裝至 root filesystem 
目錄下。</p>
<p><b>我要怎麼測試此階段 root filesystem 的正</b><!--Copyright(c) 2007 www.jollen.org. All rights reserved.--><b>確性？</b></p>
<p>假設 root filesystem 放在 <i>/tmp/busybox_project/rootfs</i> 目錄下，那<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->麼直接用 <i>chroot
</i>來做第一次測試是不錯的方式。指令如下：</p>
<blockquote>
  <p>$ chroot /tmp/busybox_project/rootfs/ /bin/sh</p>
</blockquote>
<p>若出現以下訊息，表示此 root filesystem 是無法<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->運作的：</p>
<blockquote>
  <p>chroot: /bin/sh: No such file or directory</p>
</blockquote>
<p>以下是正確無誤<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->的畫面：</p>
<blockquote>
<pre># chroot /tmp/busybox_project/rootfs/ /bin/sh


BusyBox v1.3.1 (2007-01-17 14:42:55 CST) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

# ls
bin      lib      linuxrc  sbin     usr</pre>
</blockquote>
<p>進到 shell 模式後，就可以跑 root filesystem 裡的應用程式了。這種測試方式，主要是對 root filesystem 
裡的應用程式進行執行測試，主要的目的<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->可能有：</p>

<ol>
  <li>了解 shell 與應用程式是否能正常執行。</li>
  <li>了解 library dependencies 的正確性。</li>
  <li>了解應用程式的執行<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->結果是否正確。</li>
  <li>了解應用程式的設定檔是否正確設定。</li>
  <li>Root filesystem 是否還缺少什麼元件（檔案、工具、設定檔等等）。<!--Copyright(c) 2007 www.jollen.org. All rights reserved.--></li>
</ol>
<p><b>常見原因</b></p>

<p>此階段產生錯誤的原<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->因可能有：</p>

<ol>
  <li>缺少 shell。</li>
  <li>library dependencies 問題。</li>
</ol>
<p>這種測試方式，可能無法真正有<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->效測試到 init process 階段的問題，因此變更 root 根目錄後的第一個執行程式應該指定為 shell。</p>

<p>如果您想練習 chroot 測試，必須下載 Busybox 並自行建構基本的 root filesystem。我也提供了此階段的成果檔案 
[<a href="http://tw.jollen.org/root-filesystem/busybox_project_001.tar.bz2">http://tw.jollen.org/root-filesystem/busybox_project_001.tar.bz2</a>]，以方便您<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->練習 
chroot 用法。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/01/embedded_linux_testing_rfs.html</link>
         <guid>https://www.jollen.org/blog/2007/01/embedded_linux_testing_rfs.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Wed, 17 Jan 2007 15:43:08 +0800</pubDate>
      </item>
            <item>
         <title>Shared Memory 的 Race Condition</title>
         <description><![CDATA[<p dir="ltr">今天在討論基本的 shared memory 機制時聊到，shared memory 
有同步性的問題（synchronization），主要的原因是 Linux 並未對 shared memory 做同步的控制。以下單純由 user-space 
programming 的角度來探討此觀念。</p>
<p dir="ltr">首先，試作以下 3 個小程式：</p>
<ul>
  <li dir="ltr">
  <p dir="ltr">shm_allocate.c，用來請求 shared memory</li>
  <li dir="ltr">
  <p dir="ltr">shm_read.c，用來讀取 shared memory</li>
  <li dir="ltr">
  <p dir="ltr">shm_write.c，用來寫入 shared memory</li>
</ul>
<p dir="ltr">以上程式可由 [<a href="http://tw.jollen.org/ipc-programming/shm_race.tar.bz2">http://tw.jollen.org/ipc-programming/shm_race.tar.bz2</a>] 
下載。以下是操作方法：</p>
<blockquote>
  <p dir="ltr"># ./shm_allocate jollen<br>
  Shared Memory Segment ID: 98306</p>
</blockquote>
<p dir="ltr">先執行 <i>shm_allocate</i> 配置 shared memory，並任意填入一個初始字串，程式會印出此 shared 
memory 的 Segment ID。接著：</p>
<blockquote>
  <p dir="ltr"># ./shm_read 98306<br>
  Message of Shared Memory: jollen<br>
  Message of Shared Memory: jollen<br>
  Message of Shared Memory: jollen<br>
  Message of Shared Memory: jollen<br>
  ...</p>
</blockquote>
<p dir="ltr">再執行 <i>shm_read</i>，命令列參數加上 shared memory 的 Segment ID。<i>shm_read</i> 
會持續不斷的讀取 shared memory 的內容。然後，在另一個 terminal 執行 shared memory 的寫入程式。</p>
<p dir="ltr">我們透過 <i>shm_write</i> 寫入 [00000000,11111111,22222222,...,99999999] 
字串到 shared memory，並觀察輸出。由於 <i>shm_write </i>寫入 shared memory 
的資料是「相同數字的字串」，因此，若觀察到以下的結果，表示 <i>shm_read</i> 與 <i>shm_write</i> 間存在 <b>race 
condition</b> 問題：</p>
<blockquote>
  <p dir="ltr">...<br>
  Message of Shared Memory: <font color="#FF0000">98888888</font><br>
  Message of Shared Memory: 33333322<br>
  Message of Shared Memory: 11111111<br>
  Message of Shared Memory: 66666666<br>
  Message of Shared Memory: <font color="#FF0000">44443333</font><br>
  Message of Shared Memory: <font color="#FF0000">77777666</font><br>
  ...</p>
</blockquote>
<p dir="ltr">因此，使用 shared memory 做為 IPC 機制時，必須實作同步的演算法。由演算法層面來討論，我們區分以下 3 
個層面的探討：</p>
<ul>
  <li dir="ltr">
  <p dir="ltr">All Read：不需考慮 race condition。</li>
  <li dir="ltr">
  <p dir="ltr">1-Write、n-Read（n &gt;= 1）</li>
  <li dir="ltr">
  <p dir="ltr">n-Write、n-Read（n &gt;= 1）</li>
</ul>
<p dir="ltr">可此可知，當「只有有人寫入 shared memory，便要考慮 race conditon 
問題」。若由演算法層面來思考此問題，以下是相對較為單純簡單的做法：</p>
<ul>
  <li dir="ltr">
  <p dir="ltr">1-Write、n-Read（n &gt;= 1）：可使用 lock file 的機制。</li>
  <li dir="ltr">
  <p dir="ltr">n-Write、n-Read（n &gt;= 1）：lock file 若是以 polling 方式做 waiting，必須配合 
  queue（或 circular queue）來實作，才能真正有效解決問題。</li>
</ul>
<p dir="ltr">有機會的話，再跟大家分享朋友寫的 code。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/01/shared_memory_race_condition.html</link>
         <guid>https://www.jollen.org/blog/2007/01/shared_memory_race_condition.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Tue, 16 Jan 2007 15:54:34 +0800</pubDate>
      </item>
            <item>
         <title> Linux 的 Virtual Memory Areas（VMA）：Process 與 VMA 整體觀念</title>
         <description><![CDATA[<p dir="ltr">要了解 VMA（Virtual Memory Area）的「整體觀念」，最好的<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->方式就是圖解說明。下圖說明了 process 與 VMA 
的整體觀念。</p>
<img alt="process_vma.jpg" src="http://www.jollen.org/blog/2007/01/15/process_vma.jpg" width="640" height="452" />
<p dir="ltr">圖：Process 與 VMA 整體觀念</p>
<p dir="ltr"><b>Memory Descriptor</b></p>

<p dir="ltr">Linux 的「Process Descriptor」資料<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->結構為<i> struct task_struct</i>（include/linux/sched.h）。Process 
descriptor 裡的 <i>mm</i> field 紀錄了 process 的 VMA 資訊：</p>

<blockquote>

<pre>struct task_struct {
	...
	<b>struct mm_struct *mm;</b>
	...
}</pre>

</blockquote>

<p dir="ltr"><i>struct mm_struct </i>即是 Linux 提供的「Memory Descriptor」資料結<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->構，以下是 <i>
struct mm_struct</i> 的原型宣告：</p>

<blockquote>

<pre>struct mm_struct {
	<b>struct vm_area_struct * mmap;</b>	/* list of VMAs */
	struct rb_root mm_rb;
	struct vm_area_struct * mmap_cache;	/* last find_vma result */
	unsigned long (*get_unmapped_area) (struct file *filp,
				unsigned long addr, unsigned long len,
				unsigned long pgoff, unsigned long flags);
	void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
	unsigned long mmap_base;		/* base of mmap area */
	unsigned long task_size;		/* size of task vm space */
	unsigned long cached_hole_size;      /* if non-zero, the largest hole below free_area_cache */
	unsigned long free_area_cache;	/* first hole of size cached_hole_size or larger */
	pgd_t * pgd;
	atomic_t mm_users;			/* How many users with user space? */
	atomic_t mm_count;			/* How many references to "struct mm_struct" (users count as 1) */
	int map_count;				/* number of VMAs */
	struct rw_semaphore mmap_sem;
	spinlock_t page_table_lock;		/* Protects page tables and some counters */

	struct list_head mmlist;		/* List of maybe swapped mm's.  These are globally strung
					 * together off init_mm.mmlist, and are protected
					 * by mmlist_lock
					 */

	/* Special counters, in some configurations protected by the
	 * page_table_lock, in other configurations by being atomic.
	 */
	mm_counter_t _file_rss;
	mm_counter_t _anon_rss;

	unsigned long hiwater_rss;	/* High-watermark of RSS usage */
	unsigned long hiwater_vm;	/* High-water virtual memory usage */

	unsigned long total_vm, locked_vm, shared_vm, exec_vm;
	unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
	unsigned long start_code, end_code, start_data, end_data;
	unsigned long start_brk, brk, start_stack;
	unsigned long arg_start, arg_end, env_start, env_end;

	unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */

	unsigned dumpable:2;
	cpumask_t cpu_vm_mask;

	/* Architecture-specific MM context */
	mm_context_t context;

	/* Token based thrashing protection. */
	unsigned long swap_token_time;
	char recent_pagein;

	/* coredumping support */
	int core_waiters;
	struct completion *core_startup_done, core_done;

	/* aio bits */
	rwlock_t		ioctx_list_lock;
	struct kioctx		*ioctx_list;
};</pre>

</blockquote>

<p>Memory descriptor 故名思義，是用來描述 process 記憶體資訊的資料結構。由 <i>struct 
mm_struct</i> 裡可以看到一個稱為 <i>mmap</i> 的 field，<i>mmap</i> 的 data type 為 <i>struct vm_area_struct</i>，這個資料結構即是我們在「<a target="_blank" href="http://www.jollen.org/blog/2007/01/linux_virtual_memory_areas_vma.html">Linux 
的 Virtual Memory Areas（VMA）：基本概念介紹</a>」所介紹的 VMA 資料結<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->構。</p>

<p dir="ltr"><b>VMA 與 ELF Image 的對映關係</b></p>

<p dir="ltr">在「<a target="_blank" href="http://www.jollen.org/blog/2007/01/linux_virtual_memory_areas_vma.html">Linux 
的 Virtual Memory Areas（VMA）：基本概念介紹</a>」曾經介紹過，Process 的 VMA 對映，可以由 <i>/proc/<b>&lt;pid&gt;</b>/map</i>s 
檔案查詢；例如 pid 1（init）的 VMA mapping 為：</p>

<blockquote>
  <pre>$ <b>cat /proc/1/maps</b>
08048000-0804e000 r-xp 00000000 08:01 12118      /sbin/init
0804e000-08050000 rw-p 00005000 08:01 12118      /sbin/init
08050000-08054000 rwxp 00000000 00:00 0
40000000-40016000 r-xp 00000000 08:01 52297      /lib/ld-2.2.4.so
40016000-40017000 rw-p 00015000 08:01 52297      /lib/ld-2.2.4.so
40024000-40025000 rw-p 00000000 00:00 0
40025000-40157000 r-xp 00000000 08:01 58241      /lib/i686/libc-2.2.4.so
40157000-4015c000 rw-p 00131000 08:01 58241      /lib/i686/libc-2.2.4.so
4015c000-40160000 rw-p 00000000 00:00 0
bfffe000-c0000000 rwxp fffff000 00:00 0　</pre>
</blockquote>
<p>列表結果便能用來說明 VMA 與 ELF image 之間的關係。搭配上圖來說明列表結果的 VMA 對映關係，如下：</p>
<ol>
  <li>第 1 列（row）是 ELF 執行檔（<i>/sbin/init</i>）的 code section VMA mapping；</li>
  <li>第 2 列是 ELF 執<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->行檔的 data section VMA mapping；</li>
  <li>第 3 列是 ELF 執行檔的 .bss section VMA mapping。</li>
  <li>第 4 列是 dynamic loader（<i>/lib/ld-2.2.4.so</i>）的 code section VMA mapping；</li>
  <li>第 5 列是 dynamic loader 的 data section VMA mapping；</li>
  <li>第 6 列是 dynamic loader 的 .bss section VMA mapping。</li>
  <li>第 7 列是 libc 的 code section VMA ma<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->pping；</li>
  <li>第 8 列是 libc 的 data section VMA mapping；</li>
  <li>第 9 列是 libc 的 .bss section VMA mapping。</li>
</ol>
<p>另外，要留意的是，在文中所指的 code section 與 data section 不見得就是 ELF 的 .text section 與 .data 
section；我們以 code section 來表示所有可執行的節區，以 data section 來表示包<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->含資料的節區。</p>

<p>在整個 VMA 的討論過程中，我們只針對 code section 與 data section 做討論（如圖），至於 .bss section <!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->的話，原則上另案來討論其核心實作會比較實際一些。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>

<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
			<li>2007.01.05:
            <a href="http://www.jollen.org/blog/2007/01/linux_virtual_memory_areas_vma.html">
            Linux 的 Virtual Memory Areas（VMA）：基本概念介紹</a></li>
		</ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2007/01/process_vma.html</link>
         <guid>https://www.jollen.org/blog/2007/01/process_vma.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Mon, 15 Jan 2007 15:27:18 +0800</pubDate>
      </item>
            <item>
         <title>Process Creation, #5：copy_process()</title>
         <description><![CDATA[<p><b>Before We Start</b></p>
<p>Trace Linux kernel 時，有幾個相當重要的原則要<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->掌握住：</p>
<ol>
	<li>不要對 Linux kernel 做逐行的研讀（trace 
	line-by-line、最忌諱的做法），而是<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->掌握我們想要了解的核心觀念，並針對此核心觀念來了解相關的實作細節。</li>
	<li>必須整理觀念與程式碼的對應，但並不是要大家對原始碼做逐行註解，而是針對細部觀念，將相關的實作<u>片斷</u>整理出來<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->即可。</li>
	<li>每個 kernel API 的實作原本就會包<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->含許多的「OS 基礎概念與理論」，例如：mutex、semaphore、locking 
	等等。與這些基礎知識相關<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->的程式碼，本來就是屬於整體的部份，所以應該「跳脫」個別的 kernel API 實作。</li>
	<li>如 
	3.，也就是說，如果我原本就懂這些東西，就應該很清楚了解這些「片段程式碼」的作用，應避免「完美主意」作祟：不要急於看懂這些片斷的程式細節，以免因小<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->失大。</li>
	<li>如 
	3.，如果我原本就不懂這些東西，可以試著把這<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->些程式碼當做「黑盒子」來看待。這樣的，這也是「學習方法」的問題，並非要刻意逃避這些自己不懂的東西。</li>
</ol>
<p>以本日記為例，我們想要了解 Linux kernel 如何產<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->生新的 process，而其中的關鍵便是 <i>copy_process()</i> 
函數。但是，目前我們在做的是 <i>sys_fork()</i> 的 trace，而 <i>sys_fork()</i> 並不指定任何的 <i>
clone_flags</i> 參數值，因此，在 trace <i>copy_process()</i> 的過程中，我們可<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->以先行<u>略過與 <i>
clone_flags</i> 有關的特定處理</u>。</p>
<p><b>copy_process()</b></p>
<p>以下是 <i>copy_process()</i> 的完整實作，我把現階段可略過的部份<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->標示為灰體字。</p>
<blockquote>
	<pre>/*
 * This creates a new process as a copy of the old one,
 * but does not actually start it yet.
 *
 * It copies the registers, and all the appropriate
 * parts of the process environment (as per the clone
 * flags). The actual kick-off is left to the caller.
 */
static task_t *copy_process(unsigned long clone_flags,
				 unsigned long stack_start,
				 struct pt_regs *regs,
				 unsigned long stack_size,
				 int __user *parent_tidptr,
				 int __user *child_tidptr,
				 int pid)
{
	int retval;
	struct task_struct *p = NULL;

<font color="#C0C0C0">	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
		return ERR_PTR(-EINVAL);</font>

<font color="#C0C0C0">	/*
	 * Thread groups must share signals as well, and detached threads
	 * can only be started up within the thread group.
	 */
	if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
		return ERR_PTR(-EINVAL);</font>

<font color="#C0C0C0">	/*
	 * Shared signal handlers imply shared VM. By way of the above,
	 * thread groups also imply shared VM. Blocking this case allows
	 * for various simplifications in other code.
	 */
	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
		return ERR_PTR(-EINVAL);</font>

	retval = security_task_create(clone_flags);
	if (retval)
		goto fork_out;

	retval = -ENOMEM;
	p = dup_task_struct(current);
	if (!p)
		goto fork_out;

	retval = -EAGAIN;
	if (atomic_read(&p->user->processes) >=
			p->signal->rlim[RLIMIT_NPROC].rlim_cur) {
		if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&
				p->user != &root_user)
			goto bad_fork_free;
	}

	atomic_inc(&p->user->__count);
	atomic_inc(&p->user->processes);
	get_group_info(p->group_info);

	/*
	 * If multiple threads are within copy_process(), then this check
	 * triggers too late. This doesn't hurt, the check is only there
	 * to stop root fork bombs.
	 */
	if (nr_threads >= max_threads)
		goto bad_fork_cleanup_count;

	if (!try_module_get(task_thread_info(p)->exec_domain->module))
		goto bad_fork_cleanup_count;

	if (p->binfmt && !try_module_get(p->binfmt->module))
		goto bad_fork_cleanup_put_domain;

	p->did_exec = 0;
	copy_flags(clone_flags, p);
	p->pid = pid;
	retval = -EFAULT;
<font color="#C0C0C0">	if (clone_flags & CLONE_PARENT_SETTID)
		if (put_user(p->pid, parent_tidptr))
			goto bad_fork_cleanup;</font>

	p->proc_dentry = NULL;

	INIT_LIST_HEAD(&p->children);
	INIT_LIST_HEAD(&p->sibling);
	p->vfork_done = NULL;
	spin_lock_init(&p->alloc_lock);
	spin_lock_init(&p->proc_lock);

	clear_tsk_thread_flag(p, TIF_SIGPENDING);
	init_sigpending(&p->pending);

	p->utime = cputime_zero;
	p->stime = cputime_zero;
 	p->sched_time = 0;
	p->rchar = 0;		/* I/O counter: bytes read */
	p->wchar = 0;		/* I/O counter: bytes written */
	p->syscr = 0;		/* I/O counter: read syscalls */
	p->syscw = 0;		/* I/O counter: write syscalls */
	acct_clear_integrals(p);

 	p->it_virt_expires = cputime_zero;
	p->it_prof_expires = cputime_zero;
 	p->it_sched_expires = 0;
 	INIT_LIST_HEAD(&p->cpu_timers[0]);
 	INIT_LIST_HEAD(&p->cpu_timers[1]);
 	INIT_LIST_HEAD(&p->cpu_timers[2]);

	p->lock_depth = -1;		/* -1 = no lock */
	do_posix_clock_monotonic_gettime(&p->start_time);
	p->security = NULL;
	p->io_context = NULL;
	p->io_wait = NULL;
	p->audit_context = NULL;
	cpuset_fork(p);
<font color="#C0C0C0">#ifdef CONFIG_NUMA
 	p->mempolicy = mpol_copy(p->mempolicy);
 	if (IS_ERR(p->mempolicy)) {
 		retval = PTR_ERR(p->mempolicy);
 		p->mempolicy = NULL;
 		goto bad_fork_cleanup_cpuset;
 	}
	mpol_fix_fork_child_flag(p);
#endif</font>

<font color="#C0C0C0">#ifdef CONFIG_DEBUG_MUTEXES
	p->blocked_on = NULL; /* not blocked yet */
#endif</font>

	p->tgid = p->pid;
<font color="#C0C0C0">	if (clone_flags & CLONE_THREAD)
		p->tgid = current->tgid;</font>

	if ((retval = security_task_alloc(p)))
		goto bad_fork_cleanup_policy;
	if ((retval = audit_alloc(p)))
		goto bad_fork_cleanup_security;
	/* copy all the process information */
	if ((retval = copy_semundo(clone_flags, p)))
		goto bad_fork_cleanup_audit;
	if ((retval = copy_files(clone_flags, p)))
		goto bad_fork_cleanup_semundo;
	if ((retval = copy_fs(clone_flags, p)))
		goto bad_fork_cleanup_files;
	if ((retval = copy_sighand(clone_flags, p)))
		goto bad_fork_cleanup_fs;
	if ((retval = copy_signal(clone_flags, p)))
		goto bad_fork_cleanup_sighand;
	if ((retval = copy_mm(clone_flags, p)))
		goto bad_fork_cleanup_signal;
	if ((retval = copy_keys(clone_flags, p)))
		goto bad_fork_cleanup_mm;
	if ((retval = copy_namespace(clone_flags, p)))
		goto bad_fork_cleanup_keys;
	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
	if (retval)
		goto bad_fork_cleanup_namespace;

	p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
	/*
	 * Clear TID on mm_release()?
	 */
	p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
	p->robust_list = NULL;
<font color="#C0C0C0">#ifdef CONFIG_COMPAT
	p->compat_robust_list = NULL;
#endif</font>
<font color="#C0C0C0">	/*
	 * sigaltstack should be cleared when sharing the same VM
	 */
	if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)
		p->sas_ss_sp = p->sas_ss_size = 0;</font>

	/*
	 * Syscall tracing should be turned off in the child regardless
	 * of CLONE_PTRACE.
	 */
	clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
<font color="#C0C0C0">#ifdef TIF_SYSCALL_EMU
	clear_tsk_thread_flag(p, TIF_SYSCALL_EMU);
#endif</font>

	/* Our parent execution domain becomes current domain
	   These must match for thread signalling to apply */
	   
	p->parent_exec_id = p->self_exec_id;

	/* ok, now we should be set up.. */
	p->exit_signal = (clone_flags & CLONE_THREAD) ? -1 : (clone_flags & CSIGNAL);
	p->pdeath_signal = 0;
	p->exit_state = 0;

	/*
	 * Ok, make it visible to the rest of the system.
	 * We dont wake it up yet.
	 */
	p->group_leader = p;
	INIT_LIST_HEAD(&p->thread_group);
	INIT_LIST_HEAD(&p->ptrace_children);
	INIT_LIST_HEAD(&p->ptrace_list);

	/* Perform scheduler related setup. Assign this task to a CPU. */
	sched_fork(p, clone_flags);

	/* Need tasklist lock for parent etc handling! */
	write_lock_irq(&tasklist_lock);

	/*
	 * The task hasn't been attached yet, so its cpus_allowed mask will
	 * not be changed, nor will its assigned CPU.
	 *
	 * The cpus_allowed mask of the parent may have changed after it was
	 * copied first time - so re-copy it here, then check the child's CPU
	 * to ensure it is on a valid CPU (and if not, just force it back to
	 * parent's CPU). This avoids alot of nasty races.
	 */
	p->cpus_allowed = current->cpus_allowed;
	if (unlikely(!cpu_isset(task_cpu(p), p->cpus_allowed) ||
			!cpu_online(task_cpu(p))))
		set_task_cpu(p, smp_processor_id());
<font color="#C0C0C0">
	/* CLONE_PARENT re-uses the old parent */
	if (clone_flags & (CLONE_PARENT|CLONE_THREAD))
		p->real_parent = current->real_parent;
	else
		p->real_parent = current;</font>
	p->parent = p->real_parent;

	spin_lock(&current->sighand->siglock);

	/*
	 * Process group and session signals need to be delivered to just the
	 * parent before the fork or both the parent and the child after the
	 * fork. Restart if a signal comes in before we add the new process to
	 * it's process group.
	 * A fatal signal pending means that current will exit, so the new
	 * thread can't slip out of an OOM kill (or normal SIGKILL).
 	 */
 	recalc_sigpending();
	if (signal_pending(current)) {
		spin_unlock(&current->sighand->siglock);
		write_unlock_irq(&tasklist_lock);
		retval = -ERESTARTNOINTR;
		goto bad_fork_cleanup_namespace;
	}

<font color="#C0C0C0">	if (clone_flags & CLONE_THREAD) {
		/*
		 * Important: if an exit-all has been started then
		 * do not create this new thread - the whole thread
		 * group is supposed to exit anyway.
		 */
		if (current->signal->flags & SIGNAL_GROUP_EXIT) {
			spin_unlock(&current->sighand->siglock);
			write_unlock_irq(&tasklist_lock);
			retval = -EAGAIN;
			goto bad_fork_cleanup_namespace;
		}

		p->group_leader = current->group_leader;
		list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group);

		if (!cputime_eq(current->signal->it_virt_expires,
				cputime_zero) ||
		    !cputime_eq(current->signal->it_prof_expires,
				cputime_zero) ||
		    current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY ||
		    !list_empty(&current->signal->cpu_timers[0]) ||
		    !list_empty(&current->signal->cpu_timers[1]) ||
		    !list_empty(&current->signal->cpu_timers[2])) {
			/*
			 * Have child wake up on its first tick to check
			 * for process CPU timers.
			 */
			p->it_prof_expires = jiffies_to_cputime(1);
		}
	}</font>

	/*
	 * inherit ioprio
	 */
	p->ioprio = current->ioprio;

	if (likely(p->pid)) {
		add_parent(p);
		if (unlikely(p->ptrace & PT_PTRACED))
			__ptrace_link(p, current->parent);

		if (thread_group_leader(p)) {
			p->signal->tty = current->signal->tty;
			p->signal->pgrp = process_group(current);
			p->signal->session = current->signal->session;
			attach_pid(p, PIDTYPE_PGID, process_group(p));
			attach_pid(p, PIDTYPE_SID, p->signal->session);

			list_add_tail_rcu(&p->tasks, &init_task.tasks);
			__get_cpu_var(process_counts)++;
		}
		attach_pid(p, PIDTYPE_PID, p->pid);
		nr_threads++;
	}

	total_forks++;
	spin_unlock(&current->sighand->siglock);
	write_unlock_irq(&tasklist_lock);
	proc_fork_connector(p);
	return p;

bad_fork_cleanup_namespace:
	exit_namespace(p);
bad_fork_cleanup_keys:
	exit_keys(p);
bad_fork_cleanup_mm:
	if (p->mm)
		mmput(p->mm);
bad_fork_cleanup_signal:
	cleanup_signal(p);
bad_fork_cleanup_sighand:
	__cleanup_sighand(p->sighand);
bad_fork_cleanup_fs:
	exit_fs(p); /* blocking */
bad_fork_cleanup_files:
	exit_files(p); /* blocking */
bad_fork_cleanup_semundo:
	exit_sem(p);
bad_fork_cleanup_audit:
	audit_free(p);
bad_fork_cleanup_security:
	security_task_free(p);
bad_fork_cleanup_policy:
#ifdef CONFIG_NUMA
	mpol_free(p->mempolicy);
bad_fork_cleanup_cpuset:
#endif
	cpuset_exit(p);
bad_fork_cleanup:
	if (p->binfmt)
		module_put(p->binfmt->module);
bad_fork_cleanup_put_domain:
	module_put(task_thread_info(p)->exec_domain->module);
bad_fork_cleanup_count:
	put_group_info(p->group_info);
	atomic_dec(&p->user->processes);
	free_uid(p->user);
bad_fork_free:
	free_task(p);
fork_out:
	return ERR_PTR(retval);
}</pre>
</blockquote>
<p>有許多沒有標成灰色字體的程式片斷，其實也應該先行省略不看，像是：資料結構的操作、spinlock、錯誤處理、變數初始化等等。</p>
<p><b>copy_process() 的關鍵在哪裡？</b></p>
<p>Trace 到這裡後，我會先就程式碼本身的實作，節錄「深入 <i>sys_fork()</i> 底層」相關的實作片斷。以下供您參考<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->：</p>
<p>1. 新的 process description：</p>
<pre>	struct task_struct *p = NULL;</pre>
<p>2. &quot;dup&quot; <i>current</i> 成為 <i>p</i>：</p>
<pre>	p = dup_task_struct(current);
	if (!p)
		goto fork_out;</pre>
<p>3. <i>copy_process()</i> 會判斷目前的 process 數是否過<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->多：</p>
<pre>	if (nr_threads >= max_threads)
		goto bad_fork_cleanup_count;</pre>
<p><i>max_threads</i> 是在<i> fork_init()</i> 階段算出來的，可參考「<a target="_blank" href="http://www.jollen.org/blog/2006/11/jollen_linux_3_fork_init.html">Jollen 
的 Linux 核心分享包,#3: fork_init()《講義6》</a>」。</p>
<p>4. 開始 &quot;copy&quot; <i>current</i> 給新的 process：</p>
<pre>	if ((retval = copy_semundo(clone_flags, p)))
		goto bad_fork_cleanup_audit;
	if ((retval = copy_files(clone_flags, p)))
		goto bad_fork_cleanup_semundo;
	if ((retval = copy_fs(clone_flags, p)))
		goto bad_fork_cleanup_files;
	if ((retval = copy_sighand(clone_flags, p)))
		goto bad_fork_cleanup_fs;
	if ((retval = copy_signal(clone_flags, p)))
		goto bad_fork_cleanup_sighand;
	if ((retval = copy_mm(clone_flags, p)))
		goto bad_fork_cleanup_signal;
	if ((retval = copy_keys(clone_flags, p)))
		goto bad_fork_cleanup_mm;
	if ((retval = copy_namespace(clone_flags, p)))
		goto bad_fork_cleanup_keys;
	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
	if (retval)
		goto bad_fork_cleanup_namespace;</pre>
<p><b>作業還沒完</b></p>
<p>解析出精華的目的，當然是為了做「更深入」且「有效率」的研究。我用我的學習方法，呈現「Process Creation」系列專欄的推導過程；希望我的做法對您是真<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->正有幫助的。</p>
<p>在繼續進行前，必須了解幾個基礎知識。<u>Keyword</u> 如下：</p>
<ul>
	<li>process relationship</li>
	<li>process descriptor</li>
	<li>memory descriptor</li>
</ul>
<p>「Process Creation」系列專欄到此告一<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->段落，不過「作業」仍會完成，並不是就此結束。我會起另外一個專欄，來把剩下的 hacking 功課完成。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>

<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
			<li>2007.01.11:
			<a href="http://www.jollen.org/blog/2007/01/process_creation_4_sys_fork.html">
			Process Creation, #4：sys_fork《核心實作》</a></li>
			<li>2007.01.08:
			<a href="http://www.jollen.org/blog/2007/01/process_creation_3_sys_fork.html">
			Process Creation, #3：sys_fork《基本觀念》</a></li>
			<li>2007.01.02:
			<a href="http://www.jollen.org/blog/2007/01/process_creation_2.html">
			Process Creation, #2：Running a &quot;User Process&quot;</a></li>

			<li>2006.12.31:
			<a href="http://www.jollen.org/blog/2006/12/process_creation_1_shell.html">
			Process Creation, #1：由 shell 執行外部程式《基本觀念與範例》</a></li>
		</ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2007/01/process_creation_5_copy_process.html</link>
         <guid>https://www.jollen.org/blog/2007/01/process_creation_5_copy_process.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 14 Jan 2007 01:59:19 +0800</pubDate>
      </item>
            <item>
         <title>Nano-X 程式設計, #4：設定 Window Manager（wm.c）</title>
         <description><![CDATA[<p><i>wm.c</i> 以<i> image.c</i> 為範本，<i>wm.c </i>執行後在畫面上只會看到圖片圖（沒有視窗標題列與邊框），並且可<u>使用滑鼠來拖曳圖片</u>。本範例主要在展示以下的 
Nano-X API 程式設<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->計方法：</p>
<p>˙ 如何設定視窗屬性（window manager）。<br>
˙ 如何與使用<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->者做互動（human interactive）。</p>
<p>要寫出「托曳圖片」的功能，首先必須把視窗的標題列與邊框去除，否則只能在標題列上托曳整個「視窗」。做法非常簡單，只要在建立視窗<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->時設定 window 
manager 即可：</p>
<pre>   wid = GrNewWindowEx(<b>GR_WM_PROPS_APPWINDOW</b> | 
                       <b>GR_WM_PROPS_NODECORATE</b> | 
                       <b>GR_WM_PROPS_NOAUTOMOVE</b>,
                       NULL,
                       GR_ROOT_WINDOW_ID, 
                       0, 0, 
                       image_jollen.width, /* 圖片寬度 */
                       image_jollen.height /* 圖片高度 */, 
                       0xFFFFFF);</pre>
<p>粗體字的地方是新加入的 window manager 屬性，為了達到我們的要求，我們指定了3個屬性值：<!--Copyright(c) 2007 www.jollen.org. All rights reserved.--></p>
<ul>
  <li><i>GR_WM_PROPS_APPWINDOW</i>：使用 window manager 的外觀。</li>
  <li><i>GR_WM_PROPS_NODECORATE</i>：不要有視窗<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->邊框。</li>
  <li><i>GR_WM_PROPS_NOAUTOMOVE</i>：第一次顯示視窗時不移動視窗。</li>
</ul>
<p>由於我們並未指定 <i>GR_WM_PROPS_CAPTION</i>，因此<u>不會有視窗標題列</u>。
在處理使用者互動上，我們<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->需要自行處理3個滑鼠事件：</p>
<ul>
  <li><i>GR_EVENT_MASK_MOUSE_POSITION</i>：滑鼠移動事件。</li>
  <li><i>GR_EVENT_MASK_BUTTON_UP</i>：放開滑鼠<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->按鍵。</li>
  <li><i>GR_EVENT_MASK_BUTTON_DOWN</i>：按下滑鼠按鍵。</li>
</ul>
<p>因此選擇事件的程式碼應修<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->改成：</p>
<pre>   GrSelectEvents(wid, <b>GR_EVENT_MASK_MOUSE_POSITION</b> | 
                           <b>GR_EVENT_MASK_BUTTON_UP</b> |
                           <b>GR_EVENT_MASK_BUTTON_DOWN</b>);<!--Copyright(c) 2007 www.jollen.org. All rights reserved.--></pre>
<p>接下來，要特別注意的地方是「顯示圖片的時機」。我們在這裡設計成<u>在視窗顯示後、進入 event loop 前</u>處理。首先是顯示圖片的程<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->式寫法：</p>
<pre>   GrMapWindow(wid);
   GrDrawImag<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->eBits(wid, gc, 0, 0, &image_jollen);</pre>
<p>接著，修改 event loop，並加上處理<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->以上 3 個事件（粗體字部份）的程式碼：</p>
<blockquote>
<pre>void event_handler (GR_EVENT *event)
{
   switch (event->type)
   {
      case GR_EVENT_TYPE_CLOSE_REQ: 
            GrClose();
      case <b>GR_EVENT_TYPE_MOUSE_POSITION</b>:
            position_eve<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->nt(&event->mouse);
            break;
      case <b>GR_EVENT_TYPE_BUTTON_UP</b>:
      case <b>GR_EVENT_TYPE_BUTTON_DOWN</b>:
            button_event(&event->button);
            break;
      default: break;
   }
}</pre>
</blockquote>
<p>
對於滑鼠按鍵的處理方式為：當滑鼠被按下時，便記錄滑鼠的新座標，然後將視窗移到最上層；若此時使用者移動滑鼠，則將整個「視窗」移到新座標位置。如此一來，使用者就<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->會看到
「整個圖片被托曳」的效果。</p>
<p>處理滑鼠按鍵的程式寫法如下：</p>
<blockquote>
<pre>void button_event(GR_EVENT_BUTTON *e)
{
   if (<b>e->type == GR_EVENT_TYPE_BUTTON_DOWN</b>) {
      newx = e->x;
      newy = e->y;

      button_down = 1;
      GrRaiseWindow(e->wid);
   } else {
      button_down = 0;
   }
}</pre>
</blockquote>
<p>先判斷目前所產生的事件是否為 <i>GR_EVENT_TYPE_BUTTON_DOWN</i>；如果是，才記錄新座標，並將 <i>button_down</i> 設為 1。若不是 
<i>GR_EVENT_TYPE_BUTTON_DOWN</i> 事件，表示滑鼠按<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->鍵已放開，此時將 <i>button_down</i> 設為 0。<br>
處理滑鼠移動事件的程式寫法如下：</p>
<blockquote>
<pre>void position_event(GR_EVENT_MOUSE *e)
{
   if (!button_down) return;

   <b>GrMoveWindow(e->wid, e->rootx-newx, </b><!--Copyright(c) 2007 www.jollen.org. All rights reserved.--><b>e->rooty-newy);</b>
}</pre>
</blockquote>
<p>先判斷 <i>button_down</i> 是否為 false，若 <i>button_down</i>等於0，表示滑鼠按鍵是放開的，因此不做任何動作。反正，若 
<i>button_down</i> 為true，代表滑鼠按鍵仍「持續」按住，此<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->時才能呼叫 <i>GrMoveWindow()</i> 移動視窗。</p>
<p>以下是 <i>wm.c</i> 的完整程式，粗體字是新加入或修改過的程式碼。</p>
<blockquote>
<pre>/*
 * Copyright(c) 2003,2004 www.jollen.org
 *
 * - Nano-X API example.
 * - wm.c
 *
 */

#include &lt;stdio.h>
#define MWINCLUDECOLORS
#include &lt;microwin/nano-X.h>
#include &lt;microwin/nxcolors.h>

GR_WINDOW_ID wid;
GR_GC_ID gc;

/* 外部影像 */
extern GR_IMAGE_HDR image_jollen;

void event_handler (GR_EVENT *event);

/* 滑鼠座標 */
<b>int newx, newy;
int button_down;</b>

int main (void)
{
   if (GrOpen() < 0) {
        fprintf (stderr, "GrOpen failed");
        return -1;
   }

   gc = GrNewGC();
   GrSetGCForeground (gc, 0xFF0000);

   wid = GrNewWindowEx(<b>GR_WM_PROPS_APPWINDOW |
                       GR_WM_PROPS_NODECORATE | 
                       GR_WM_PROPS_NOAUTOMOVE</b>,
                       NULL,
                       GR_ROOT_WINDOW_ID, 
                       0, 0, 
                       image_jollen.width, /* 影像寬度 */
                       image_jollen.height /* 影像高度 */, 
                       0xFFFFFF);

   GrSelectEvents(wid, <b>GR_EVENT_MASK_MOUSE_POSITION |
                    GR_EVENT_MASK_BUTTON_UP |
                    GR_EVENT_MASK_BUTTON_DOWN</b>);

   GrMapWindow(wid);
   <b>GrDrawImageBits(wid, gc, 0, 0, &image_jollen);</b>

   GrMainLoop(event_handler);

   return 0;
}

<b>void button_event(GR_EVENT_BUTTON *e)
{
   if (e->type == GR_EVENT_TYPE_BUTTON_DOWN) {
      newx = e->x;
      newy = e->y;

      button_down = 1;
      GrRaiseWindow(e->wid);
   } else {
      button_down = 0;
   }
}

void position_event(GR_EVENT_MOUSE *e)
{
   if (!button_down) return;

   GrMoveWindow(e->wid, e->rootx-newx, e->rooty-newy);
}</b>

void event_handler (GR_EVENT *event)
{
   switch (event->type)
   {
      case GR_EVENT_TYPE_CLOSE_REQ: 
	   GrClose();
      <b>case GR_EVENT_TYPE_MOUSE_POSITION:
	   position_event(&event->mouse);
	   break;
      case GR_EVENT_TYPE_BUTTON_UP:
      case GR_EVENT_TYPE_BUTTON_DOWN:
           button_event(&event->button);
	   break;</b>
      default: break;
   }
}
</pre>
</blockquote>
<p><b>注釋</b></p>
<ul>
  <li>2007.01.13 編修（Revision）</li>
</ul>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
          <li class="archive-list-item">2004.08.11:
          <a href="http://www.jollen.org/blog/2004/08/linux_microwindows_1.html">
          Nano-X 程式設計, #1：介紹與安裝</a></li>
          <li class="archive-list-item">2004.04.26:
          <a href="http://www.jollen.org/blog/2004/04/nano-x_programming_2.html">
          Nano-X 程式設計, #2：「Hello World」</a></li>
          <li class="archive-list-item">2007.01.13:
          <a href="http://www.jollen.org/blog/2007/01/nano-x_programming_3.html">
          Nano-X 程式設計, #3：顯示圖片（image.c）</a></li>
        </ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2007/01/nano-x_programming_4.html</link>
         <guid>https://www.jollen.org/blog/2007/01/nano-x_programming_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Sat, 13 Jan 2007 23:11:50 +0800</pubDate>
      </item>
            <item>
         <title>Nano-X 程式設計, #3：顯示圖片（image.c）</title>
         <description><![CDATA[<p>程式範例 <i>image.c</i> 是以 <i>hello.c</i> 為基礎，加上顯示<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->圖片的功能。透過 <i>image.c</i> 
我們可以學到以下的 Nano-X 程式設計方法：</p>
<p>˙ 如何使用嵌入式圖片<br>
˙ 如何將圖片顯示<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->於視窗上</p>
<p>由檔案讀取圖片檔並<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->顯示顯示圖片是一般常見的做法，這裡我們所要實作的範例是希望可以將圖片直接嵌入程式裡，而不是由外部檔案讀取。</p>
<p><b>如何使用嵌入式圖片</b></p>
<p>要將圖片嵌入於程式裡，首先必須將圖片轉換成數值資料形式的 C 程式。Nano-X 提供一個檔名為 <i>convbmp</i> 的工具來將 BMP 
格式的圖片轉換成 C 程<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->式。</p>
<p><i>convbmp</i>工具的原始程式位於 <i>src/mwin/bmp/convbmp.c</i>，這是提供給 Microwindows API 
使用者的工具，因此我們在設定 Nano-X 編譯選項時，除了勾選 Nano-X API 外，還要勾選 Microwindows API 選項才能產生 <i>
convbm</i>p 執行檔。編譯後可以在<i> src/bin/</i> 目錄下找到 <i>convbmp</i>，我們手動將此工具安裝到 <i>
/usr/bin/</i>目錄下，以方便我<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->們使用：</p>
<blockquote>
<p># cd microwin-0.89/<br>
# cp src/bin/convbmp /usr/bin</p>
</blockquote>
<p>先將取得的圖檔轉換成 BMP 的<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->格式，再利用 <i>convbmp</i> 轉換成 C 程式。例如，我想轉換圖檔 <i>jollen.bmp</i>，那麼將圖檔轉換成 C 程式的指令就是：</p>
<blockquote>
<p>$ convbmp jollen.bmp</p>
</blockquote>
<img alt="jollen.bmp" src="http://www.jollen.org/blog/2007/01/13/jollen.bmp" width="126" height="120" />
<p>圖（jollen.bmp）</p>
<p>轉換後便會得到 <i>jollen.c</i>。接著我們<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->再修改 <i>hello.c</i> 將圖片顯示於視窗上。</p>
<p><b>如何將圖片顯示於視</b><!--Copyright(c) 2007 www.jollen.org. All rights reserved.--><b>窗上</b></p>
<p>因為圖片資料屬於外部變數，因此先在程式裡<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->宣告外部圖片變數：</p>
<blockquote>
	<p>extern GR_IMAGE_HDR image_jollen;</p>
</blockquote>
<p><i>image_jollen</i> 是一個陣列，存放圖檔的 pixel 資料，此陣列由 <i>convbmp</i> 轉換後產生，詳見 <i>jollen.c</i> 程式。接著，在處理 <i>GR_EVENT_TYPE_EXPOSURE</i> 事件的地方呼叫 <i>GrDrawImageBits() </i>
函數畫出圖片<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->即可：</p>
<blockquote>
	<p>GrDrawImageBits(wid, gc, 0, 0, &amp;<b>image_jollen</b>);</p>
</blockquote>
<p>編譯時別忘了與 <i>jollen.c</i> 程式做連結，這個部份可以寫一個<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->簡單的 <i>Makefile</i> rule來完成：</p>
<blockquote>
<pre>mage: image.o jollen.o
	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@<!--Copyright(c) 2007 www.jollen.org. All rights reserved.--></pre>
</blockquote>
<p>以下是 <i>image.c</i> 的完整程式，粗體字是新加入的程式碼。</p>
<blockquote>
<pre>/*
 * Copyright(c) 2003,2004 www.jollen.org
 *
 * - Nano-X API example.
 * - image.c
 */

#include &lt;stdio.h>
#define MWINCLUDECOLORS
#include &lt;microwin/nano-X.h>

GR_WINDOW_ID wid;
GR_GC_ID gc;

/* 外部圖片 */
<b>extern GR_IMAGE_HDR image_jollen;</b>

void event_handler (GR_EVENT *event);

int main (void)
{
   if (GrOpen() < 0) {
        fprintf (stderr, "GrOpen failed");
        return -1;
   }

   gc = GrNewGC();
   GrSetGCForeground (gc, 0xFF0000);

   wid = GrNewWindowEx(GR_WM_PROPS_APPFRAME |
                       GR_WM_PROPS_CAPTION  |
                       GR_WM_PROPS_CLOSEBOX,
                       "jollen.org",
                       GR_ROOT_WINDOW_ID, 
                       0, 0, 
                       image_jollen.width, /* 圖片寬度 */ image_jollen.height /* 圖片高度 */, 
                       0xFFFFFF); GrSelectEvents(wid, GR_EVENT_MASK_CLOSE_REQ | GR_EVENT_MASK_EXPOSURE);

   GrMapWindow(wid);
   GrMainLoop(event_handler);

   return 0;
}

void event_handler (GR_EVENT *event)
{
   switch (event->type)
   {
      case GR_EVENT_TYPE_EXPOSURE:
           <b>GrDrawImageBits(wid, gc, 0, 0, &image_jollen);</b>
	   break;
      case GR_EVENT_TYPE_CLOSE_REQ: 
	   GrClose();
      default: break;
   }
}</pre>

</blockquote>
<p><b>注釋</b></p>
<ul>
  <li>2007.01.13 編修（Revision）</li>
</ul>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>

<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
          <li class="archive-list-item">2004.08.11:
          <a href="http://www.jollen.org/blog/2004/08/linux_microwindows_1.html">
          Nano-X 程式設計, #1：介紹與安裝</a></li>
          <li class="archive-list-item">2004.04.26:
          <a href="http://www.jollen.org/blog/2004/04/nano-x_programming_2.html">
          Nano-X 程式設計, #2：「Hello World」</a></li>
        </ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2007/01/nano-x_programming_3.html</link>
         <guid>https://www.jollen.org/blog/2007/01/nano-x_programming_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Sat, 13 Jan 2007 20:41:17 +0800</pubDate>
      </item>
            <item>
         <title>Process Creation, #4：sys_fork《核心實作》</title>
         <description><![CDATA[<p>接續前一記日記的觀<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->念：「Linux 以 <i>sys_fork</i> 或 <i>sys_clone</i> 來產生新的 process，而這二個 
system call 最後都會呼叫到 <i>do_fork()</i> 函數，<i>do_fork() </i>是 Linux 主要的 
fork-routine。」</p>
<p>將此觀念以實作角度來說明的話，<i>do_fork()</i> 必須要做的工作便是「copy 原來的 process 成為另一個新的 
process」。Linux 的 <i>sys_fork() </i>內部實作就<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->是以「copy process」的方式來實作。也就是說，當 user 
program 呼叫 <i>fork() </i>wrapper function 後，<i>sys_fork()</i> 便會「copy」原來的 
process，以得到一個新的 process。</p>
<p>由此觀念的推導，我們便能了解到，<i>sys_fork()</i> 的內部實作關鍵便是：</p>
<p>1. 如何 copy process。</p>
<p>2. 要 copy process 的「哪個<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->部份」？</p>
<p>這二個關鍵，都是相當值得玩味的題目，同時，透過探討「copy process」的核心實作，我們也可以強化「process address 
space」的觀念。以下先將 <i>sys_fork()</i> 的內部流程先大略 trace 一遍後，再討論「copy process」的主題；而「copy 
what」則會在「clone()」的專欄裡<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->再做介紹。</p>
<p>首先，<i>sys_fork()</i> 與 <i>sys_clone()</i> 都呼叫到 <i>do_fork</i> routine。以下是 <i>
do_fork()</i> 的原始碼：</p>
<blockquote>
	<pre>/*
 *  Ok, this is the main fork-routine.
 *
 * It copies the process, and if successful kick-starts
 * it and waits for it to finish using the VM if required.
 */
long do_fork(unsigned long clone_flags,
	      unsigned long stack_start,
	      struct pt_regs *regs,
	      unsigned long stack_size,
	      int __user *parent_tidptr,
	      int __user *child_tidptr)
{
	<b><font color="#FF0000">struct task_struct *p; // 請見 1.</font></b>
	int trace = 0;
	<font color="#FF0000"><b>struct pid *pid = alloc_pid(); // 請見 2.</b></font>
	long nr;

	if (!pid)
		return -EAGAIN;
	nr = pid->nr;
	if (unlikely(current->ptrace)) {
		trace = fork_traceflag (clone_flags);
		if (trace)
			clone_flags |= CLONE_PTRACE;
	}
	<b><font color="#FF0000">// 請見 3.</font></b>
	<font color="#FF0000"><b>p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, nr);</b></font>
	/*
	 * Do this prior waking up the new thread - the thread pointer
	 * might get invalid after that point, if the thread exits quickly.
	 */
	if (!IS_ERR(p)) {
		struct completion vfork;

		if (clone_flags & CLONE_VFORK) {
			p->vfork_done = &vfork;
			init_completion(&vfork);
		}

		if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
			/*
			 * We'll start up with an immediate SIGSTOP.
			 */
			sigaddset(&p->pending.signal, SIGSTOP);
			set_tsk_thread_flag(p, TIF_SIGPENDING);
		}

		if (!(clone_flags & CLONE_STOPPED))
			wake_up_new_task(p, clone_flags);
		else
			p->state = TASK_STOPPED;

		if (unlikely (trace)) {
			current->ptrace_message = nr;
			ptrace_notify ((trace << 8) | SIGTRAP);
		}

		if (clone_flags & CLONE_VFORK) {
			wait_for_completion(&vfork);
			if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
				ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
		}
	} else {
		free_pid(pid);
		nr = PTR_ERR(p);
	}
	return nr;
}</pre>
</blockquote>
<p>我把重要的地方用紅色字體標示出來，一開始，我<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->們必須先了解此部份的實作：</p>
<p>1. 宣告一個  
<a target="_blank" href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_364_s.html">process descriptor</a>。</p>
<p>2. 要求一個 PID 給新的 process 使用。</p>
<p>3. 呼叫 <i>copy_process()</i>，以<u>複制</u>出新的 process。</p>
<p>由此可知，Linux kernel 的 <i>copy_process()</i> API 是重要的「process creation」API。</p>
<p>接著，把 
<i>copy_process() </i>的原始碼 trace 出來：</p>
<blockquote>
	<pre>/*
 * This creates a new process as a copy of the old one,
 * but does not actually start it yet.
 *
 * It copies the registers, and all the appropriate
 * parts of the process environment (as per the clone
 * flags). The actual kick-off is left to the caller.
 */
static task_t *copy_process(unsigned long clone_flags,
				 unsigned long stack_start,
				 struct pt_regs *regs,
				 unsigned long stack_size,
				 int __user *parent_tidptr,
				 int __user *child_tidptr,
				 int pid)
{
...
}</pre>
</blockquote>
<p><i>copy_process()</i> 程式碼有<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->點多，這裡只先列出其函數原型。</p>
<p>看到 <i>copy_process()</i> 的第一個參數 <i>clone_flags</i>，這個<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->參數一開始是由 
<i>sys_fork()</i> 或是 <i>sys_clone()</i> 所傳遞進來的，並且 <i>copy_process()</i> 會根<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->據 <i>
clone_flags</i> 來決定「copy what」。</p>
<p>那麼我怎麼知道 <i>clone_flags </i>有哪些值？這個<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->部份定義在 &lt;linux/sched.h&gt; 標頭檔裡，以下是 <i>clone_flags
</i>的 bitwise 值定義：</p>
<pre>/*
 * cloning flags:
 */
#define CSIGNAL		0x000000ff	/* signal mask to be sent at exit */
#define CLONE_VM		0x00000100	/* set if VM shared between processes */
#define CLONE_FS		0x00000200	/* set if fs info shared between processes */
#define CLONE_FILES	0x00000400	/* set if open files shared between processes */
#define CLONE_SIGHAND	0x00000800	/* set if signal handlers and blocked signals shared */
#define CLONE_PTRACE	0x00002000	/* set if we want to let tracing continue on the child too */
#define CLONE_VFORK	0x00004000	/* set if the parent wants the child to wake it up on mm_release */
#define CLONE_PARENT	0x00008000	/* set if we want to have the same parent as the cloner */
#define CLONE_THREAD	0x00010000	/* Same thread group? */
#define CLONE_NEWNS	0x00020000	/* New namespace group? */
#define CLONE_SYSVSEM	0x00040000	/* share system V SEM_UNDO semantics */
#define CLONE_SETTLS	0x00080000	/* create a new TLS for the child */
#define CLONE_PARENT_SETTID	0x00100000	/* set the TID in the parent */
#define CLONE_CHILD_CLEARTID	0x00200000	/* clear the TID in the child */
#define CLONE_DETACHED		0x00400000	/* Unused, ignored */
#define CLONE_UNTRACED		0x00800000	/* set if the tracing process can't force CLONE_PTRACE on this clone */
#define CLONE_CHILD_SETTID		0x01000000	/* set the TID in the child */
#define CLONE_STOPPED		0x02000000	/* Start in stopped state */

/*
 * List of flags we want to share for kernel threads,
 * if only because they are not used by them anyway.
 */
#define CLONE_KERNEL	(CLONE_FS | CLONE_FILES | CLONE_SIGHAND)</pre>
<p>以 <i>sys_fork()</i> 的實<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->作來看：</p>
<blockquote>
	<pre>asmlinkage int sys_fork(struct pt_regs regs)
{
	return do_fork(<b>SIGCHLD</b>, regs.esp, &regs, 0);
}</pre>
</blockquote>
<p>呼叫 <i>fork() </i>wrapper function 時，並無<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->法讓 user 自行定義 clone flags；因此，「在學會 <i>clone()</i> <u>函數</u>的用法前」，其實可以先暫時跳過 
clone flags 這個部份。</p>
<p>到這裡是<i> sys_fork() </i>內部實作的 trace，雖然我們<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->了解到 clone flags 的作用，但是由於 <i>sys_fork()</i> 
並不指定此參數，所以先不討論 clone flags。不過，我們的 <i>sys_fork()</i> trace 
功課還沒完成，下一篇日記將會是「Process Creation, #5：copy process」。</p>
<p>以上 kernel trace，皆使用 Linux 2.6.17.7 原始<!--Copyright(c) 2007 www.jollen.org. All rights reserved.-->程式碼。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
			<li>2007.01.08:
			<a href="http://www.jollen.org/blog/2007/01/process_creation_3_sys_fork.html">
			Process Creation, #3：sys_fork《基本觀念》</a></li>
			<li>2007.01.02:
			<a href="http://www.jollen.org/blog/2007/01/process_creation_2.html">
			Process Creation, #2：Running a &quot;User Process&quot;</a></li>
			<li>2006.12.31:
			<a href="http://www.jollen.org/blog/2006/12/process_creation_1_shell.html">
			Process Creation, #1：由 shell 執行外部程式《基本觀念與範例》</a></li>
		</ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2007/01/process_creation_4_sys_fork.html</link>
         <guid>https://www.jollen.org/blog/2007/01/process_creation_4_sys_fork.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 11 Jan 2007 23:17:21 +0800</pubDate>
      </item>
            <item>
         <title>.bss section：C 語言所種下的因</title>
         <description><![CDATA[由於當初 C 語言標<!--Copyright(c) 2007 www.jollen.org-->準提到「未初始化的全域變數（un-initialized global variables）其初始值為零（zero）」，所以得到的結果便是「程式執行時，必須將未初始化的全域變數都初始化成零」。

Linux 針對這種狀況的解決方式是「配置 zeroed pages 給 .bss section」，因此 un-initialized global variables 的值（value）便會為<!--Copyright(c) 2007 www.jollen.org-->零。

註：Un-initialized global variables 會被編<!--Copyright(c) 2007 www.jollen.org-->譯器放到 .bss section，可參考 Jollen 的「<a href="http://www.jollen.org/EmbeddedLinux/BSS_Section_Concepts.html">BSS Section 觀念教學</a>」專欄。

此外，global variable 被初始化為<!--Copyright(c) 2007 www.jollen.org-->零時，也會被 GCC 放到 .bss section 裡。所以，以下的寫法：

<blockquote>int foo;

main()
{
...
}</blockquote>

會等於：

<blockquote>int foo = 0;

main()
{
...
}</blockquote>

以上二種寫法都會讓 <em>foo</em> 被放到 .bss section。由此可知，以下二種狀況，變數都會被放在 .bss secti<!--Copyright(c) 2007 www.jollen.org-->on：

1. 當 global variable 未被初始化<!--Copyright(c) 2007 www.jollen.org-->時；
2. 或是 global variable 被初始<!--Copyright(c) 2007 www.jollen.org-->化成零時。

<strong>-fno-zero-initialized-in-bss</strong>

如果（但是確實有這種應用場合）我們不想讓 variable 被放到 .bss section 呢？做法有二。第一種方式是「傳統做法」，程式設計師只要將 global variable 初始化為「非零」值即可，舉以下程<!--Copyright(c) 2007 www.jollen.org-->式為例：

<blockquote>#include &lt;stdio.h>

int foo = 1;

int main(void)<br>
{<br>
   printf("foo is %d.\n", foo);

   return 0;<br>
}</blockquote>

將此程式編譯：

<blockquote>$ gcc -O2 -o bss bss.c</blockquote>

利用 <em>objdump</em> 來觀察後，會發現 <em>foo </em>變數被 GCC 放到 .data section 裡了。這種做法是<!--Copyright(c) 2007 www.jollen.org-->基於 coding 時的做法。

第二種做法是 GCC 3.4.x 後所支援的「-fno-zero-initialized-in-bss」最佳化選項。將程式修改<!--Copyright(c) 2007 www.jollen.org-->如下：

<blockquote>#include &lt;stdio.h>

int foo = 0;

int main(void)<br>
{<br>
   printf("foo is %d.\n", foo);

   return 0;<br>
}</blockquote>

請特別留意，global variable 仍要做初始化，所以「foo」一定要做「assignment」為零值的動作。將程式編<!--Copyright(c) 2007 www.jollen.org-->譯：

<blockquote>$ gcc -fno-zero-initialized-in-bss -O2 -o bss bss.c</blockquote>

GCC 會把 <em>foo</em> 放到 .data section。同樣可利用 objdump 來觀察。

<strong>良好的 Embedded Linux 程式寫作習慣</strong>

了解以上的觀念後，我要來說明一個重要<!--Copyright(c) 2007 www.jollen.org-->的觀念。由於某些特定應用，或是 target 需要將變數放在 .data section 裡，因此若是 Embedded Linux 的應用，建議應對全域變數做初始化，例如：

<blockquote>int x;<br>
int y;<br>
int z;<br><br>
int main(void)<br>
{<br>
...<br>
}
</blockquote>

應將這種傳統 C 的寫作習慣調整為：

<blockquote>int x = 0;<br>
int y = 0;<br>
int z = 0;<br><br>
int main(void)<br>
{<br>
...<br>
}
</blockquote>

良好的習慣養成，未來將會得到許多好處。

這種寫法並非傳統 C 語言所講的「多此一舉」，應該以 Linux systems software 的角度來思考：這種寫法便能搭配 GCC 的「-fno-zero-initialized-in-bss」或是「-fzero-initialized-in-bss（預設）」選項，來決<!--Copyright(c) 2007 www.jollen.org-->定 global variable 要放到 .bss section 或是 .data section。]]></description>
         <link>https://www.jollen.org/blog/2007/01/no-zero-initialized-in-bss.html</link>
         <guid>https://www.jollen.org/blog/2007/01/no-zero-initialized-in-bss.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 10 Jan 2007 22:44:21 +0800</pubDate>
      </item>
            <item>
         <title>「Truncate It」小技倆的原始碼與原理</title>
         <description><![CDATA[先前提到的「<a href="http://www.jollen.org/blog/2007/01/truncate_it_tricky.html">一個防止程式被玩耍的小技倆</a>」中，Jollen 提<!--copyright (c) 2007 www.jollen.org-->供了一個稱為「Truncate It」的小工具，我把他的原始碼放在此處「<a href="http://tw.jollen.org/elf-programming/truncate_it.tar.bz2">http://tw.jollen.org/elf-programming/truncate_it.tar.bz2</a>」，有興趣的朋友可下載回家把玩<!--copyright (c) 2007 www.jollen.org-->。

用法請參考前一則日記的介紹，另外，「Truncate It」只是一個「呈現概念的原型」，並未良好的 coding，除了結果是輸出<!--copyright (c) 2007 www.jollen.org-->到 stdout 外，現階段也只能處理 IA32 的 ELF image。

<strong>Truncate It 的原理</strong>

Truncate It 的原理相當簡單，我只是把「Section Header Table」等資訊由 ELF image 中移除，因此「標準工具」便無法處理 ELF image；這是由於 GNU binutils 的工具都是以 linking view 來解讀 ELF image 之故，若要正常反<!--copyright (c) 2007 www.jollen.org-->組譯「truncated ELF image」，就要以 execution view 的角度來解讀執行檔。

此外，Section Header Table 在 execution view 時是 optional 的，可參考 Jollen 先前的日記「<a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_1.html">ELF（Executable and Linking Format）格式教學<!--copyright (c) 2007 www.jollen.org-->文件, #1: ELF 簡介</a>」。

以下以一個操作流程來說明 Truncate It 的<!--copyright (c) 2007 www.jollen.org-->原理：

1. 如下圖（<em>objdump</em>）。

<img alt="truncate_it_01.JPG" src="http://www.jollen.org/blog/2007/01/09/truncate_it_01.JPG" width="577" height="385" />

2. 把前半段的內容<!--copyright (c) 2007 www.jollen.org-->切出來，存成 <em>hello.trunc</em>。

<blockquote># dd if=hello of=hello.trunc bs=1308 count=1<br>
讀入了 1+0 個區段<br>
輸出了 1+0 個區段<br>
# chmod a+x hello.trunc
</blockquote>

0x51c 等於十進位 1308。其中，.bss section 的長<!--copyright (c) 2007 www.jollen.org-->度為 4，但是 0x51c 並不需要再加上 4，原因請參考 Jollen 的「<a href="http://www.jollen.org/EmbeddedLinux/BSS_Section_Concepts.html">BSS Section Concepts: .bss section 的基本觀念介紹專欄</a>」。

此外，.bss section 在一般情況<!--copyright (c) 2007 www.jollen.org-->下都會是「<em>strip</em> 後的最後一個 section」。.bss section 不佔實體檔案空間，會有 .bss section 的存在，主要的原因是「因為當初 C 語言標準所種下的因。」]]></description>
         <link>https://www.jollen.org/blog/2007/01/truncate_it_tricky_source.html</link>
         <guid>https://www.jollen.org/blog/2007/01/truncate_it_tricky_source.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Tue, 09 Jan 2007 23:41:47 +0800</pubDate>
      </item>
            <item>
         <title>Qt Centre Programming Contest 2007：與一些自己的小想法</title>
         <description><![CDATA[<a href="http://www.qtcentre.org/">Qt Centre</a>（The Ultimate Qt Community）釋出一則消息：「<a href="http://contest.qtcentre.org/">Qt Centre Programming Contest 2007</a>」。嘿，Qt Centre 也和幾個 partners（這裡<!--copyright(c) 2007 www.jollen.org-->面當然有 Trolltech）辦起 Qt4 的程式設計比賽了。

<strong>社群手法</strong>

先前曾提到的「<a href="http://www.jollen.org/blog/2006/12/embedded_linux_2006.html">Embedded Linux 2006 十大回顧！</a>」都是很「具體」的事件，不過若大環境來看，絕對可以加上一條「開放源碼的社群經營<!--copyright(c) 2007 www.jollen.org-->手法在 2006 年展現氣勢。」大家可以思考 Li<!--copyright(c) 2007 www.jollen.org-->nux mobile 的爆炸性成長過程，與其策略手法，甚致是 IBM 的幾個 case study，便能了解「社群手法」的重要性。

因為形成了這<!--copyright(c) 2007 www.jollen.org-->樣的 ecosystem，因此更能促進 open source 運動的發展，這絕對是好事一樁，。

<strong>回歸正題</strong>

2007 年的開放源碼世界，除了「Linux mobile」會持續躍進外，「community」的經營手法當然也會是本年度的重<!--copyright(c) 2007 www.jollen.org-->點戲。

每次看到一些消息，腦筋都不免會跳脫常軌，出現一些奇怪的聯想。還是回到主題來吧。Qt4 的比賽當然不會是一個「解題（problem solving）」的比賽<!--copyright(c) 2007 www.jollen.org-->，看一下他的「Guidelines on how to win the contest」，特別強調的是： 

1. idea。關鍵你的 idea 要「大」。
2. completeness。成果要完整能動。
3. portability。在幾個主要的 Qt4 平臺都能執行。
4. design。程式碼本身的設計要良好。
5. documentation。良好的註解與文件。
6. code quality。良好的程式碼。
7. tests。必須透過一些解決方案來測試程式碼。
8. dependencies。基於 Qt 技術平臺。
9. team size。這個是說，團隊的人要多，一人隊伍是無法贏得比賽的。

我覺得這幾個 keywords 是很不錯的，可以比較一般化的把這幾個想法應用在 Embedded Linux 專案計畫中。例如：code quality 方<!--copyright(c) 2007 www.jollen.org-->面，「self-explainable variable names」是基本功；又如，在「dependencies」方面，因為是 Embedded Linux 開發，限制程式人員所能使用的 library 與 application 則是合理的。]]></description>
         <link>https://www.jollen.org/blog/2007/01/qt_centre_programming_contest.html</link>
         <guid>https://www.jollen.org/blog/2007/01/qt_centre_programming_contest.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Mon, 08 Jan 2007 23:32:52 +0800</pubDate>
      </item>
            <item>
         <title>Process Creation, #3：sys_fork《基本觀念》</title>
         <description><![CDATA[<p><i>fork()</i> 是「Process Creation」議題的重要里程碑：提到 <u>system call</u> 代表著我們的研究要正式進入 kernel space 的層面了。</p>
<p>值得附帶一提的是，<i>sys_fork</i> 是一個 machine-dependent 的 system call，以 i386 為例，其實作位於 
<a href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/process.c">
linux/arch/<strong>i386</strong>/kernel/process.c</a>。<sup>*1</sup></p>
<p><b>Operating System 端的觀念</b></p>
<p><i>sys_fork</i> 是非常重要的 system call，從作業系統的角度來解釋的話，這就是「建立 process」的主要 system 
call。當外部程式（即 ELF image）被使用者鍵入指令後，shell 便會呼叫 <i>fork() </i>系統呼叫，並透過作業系統的 fork 
system call 來產生新的 process，以執行此外部程式。這種類型的 fork 也稱做 spawn。</p>
<p>可參考先前 Jollen 所分享的「Process Creation」前二則日記，以便了解 spawn、fork、exec 等等觀念：</p>
<ul>
	<li class="archive-list-item">2007.01.02:
	<a href="http://www.jollen.org/blog/2007/01/process_creation_2.html">Process 
	Creation, #2：Running a &quot;User Process&quot;</a></li>
	<li class="archive-list-item">2006.12.31:
	<a href="http://www.jollen.org/blog/2006/12/process_creation_1_shell.html">
	Process Creation, #1：由 shell 執行外部程式《基本觀念與範例》</a></li>
</ul>
<p>強烈建議您先閱讀以上二則日記，並了解 user-space 端的 process 觀念後，再繼續往下學習，才能更容易「感受」fork system 
call 的觀念。</p>
<p><b>Linux 端的觀念</b></p>
<p>Linux 並沒有 spawn system call，根據「Process Creation」日記的解說，Linux 下的 fork + exec 等於 
spawn。而其中最核心的觀念則是在於 fork 的實作，也就是「如何產生新的 process」。</p>
<p>Linux 以 <i>sys_fork</i> 或 <i>sys_clone</i> 來產生新的 process，而這二個 system call 
最後都會呼叫到 <i>do_fork()</i> 函數，<i>do_fork() </i>是 Linux 主要的 fork-routine。繼續探討 <i>
do_fork() </i>時，便會接觸到以下二個重要議題：<span lang="en">&nbsp;</span></p>
<ul>
	<li>memory descriptor（struct mm_struct）</li>
	<li>memory area（VMA）</li>
</ul>
<p>關於更深入的 <i>fork()</i>，我們就先在此打住，下篇日記《核心實作》再做介紹。以下是 <i>sys_fork()</i> 與 <i>
sys_clone()</i> 的程式碼（Linux 2.6.17.7::<a href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/process.c">arch/i386/kernel/process.c</a>）：</p>
<blockquote>
	<pre>asmlinkage int sys_fork(struct pt_regs regs)
{
	return <b>do_fork</b>(<u>SIGCHLD</u>, regs.esp, &regs, 0, NULL, NULL);
}

asmlinkage int sys_clone(struct pt_regs regs)
{
	unsigned long clone_flags;
	unsigned long newsp;
	int __user *parent_tidptr, *child_tidptr;

	clone_flags = regs.ebx;
	newsp = regs.ecx;
	parent_tidptr = (int __user *)regs.edx;
	child_tidptr = (int __user *)regs.edi;
	if (!newsp)
		newsp = regs.esp;
	return <b>do_fork</b>(<u>clone_flags</u>, newsp, &regs, 0, parent_tidptr, child_tidptr);
}</pre>
</blockquote>
<p><i>do_fork()</i> 的函數原型：</p>
<blockquote>
	<pre>/*
 *  Ok, this is the main fork-routine.
 *
 * It copies the process, and if successful kick-starts
 * it and waits for it to finish using the VM if required.
 */
long do_fork(unsigned long clone_flags,
	      unsigned long stack_start,
	      struct pt_regs *regs,
	      unsigned long stack_size,
	      int __user *parent_tidptr,
	      int __user *child_tidptr);</pre>
</blockquote>
<p><i>clone_flags</i> 是 <i>do_fork()</i> 觀念的核心，也是很有趣的議題。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
			<li><font color="#FF0000"><sup>*1</sup></font>2006.10.11:
            <a href="http://www.jollen.org/blog/2006/10/linux_26_system_call12.html">
			Linux 2.6 的 System Call：12 大類</a></li>
		</ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2007/01/process_creation_3_sys_fork.html</link>
         <guid>https://www.jollen.org/blog/2007/01/process_creation_3_sys_fork.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Mon, 08 Jan 2007 13:44:14 +0800</pubDate>
      </item>
            <item>
         <title>Linux 的 Virtual Memory Areas（VMA）：基本概念介紹</title>
         <description><![CDATA[<p>由 user process 角度來說明的話，VMA 是 user process 裡一段 virtual address space 區塊；virtual address space 是<!-- "copyright (c) 2007 www.jollen.org. All rights reserved." -->連續的記憶體空間，當然 VMA 也會是連續的空間。VMA 對 Linux 的主要好處是，可以記憶體的使用更有效率，並且更容易管理 user process address space。</p>
<p>從另一個觀念來看，VMA 可以讓 Linux kernel 以 process 的<!-- "copyright (c) 2007 www.jollen.org. All rights reserved." -->角度來管理 virtual address space。Process 
的 VMA 對映，可以由 <i>/proc/<b>&lt;pid&gt;</b>/map</i>s 檔案查詢；例如 pid 1（init）的 VMA mapping 為：</p>
<blockquote>
	<pre>$ <b>cat /proc/1/maps</b>
08048000-0804e000 r-xp 00000000 08:01 12118      /sbin/init
0804e000-08050000 rw-p 00005000 08:01 12118      /sbin/init
08050000-08054000 rwxp 00000000 00:00 0
40000000-40016000 r-xp 00000000 08:01 52297      /lib/ld-2.2.4.so
40016000-40017000 rw-p 00015000 08:01 52297      /lib/ld-2.2.4.so
40024000-40025000 rw-p 00000000 00:00 0
40025000-40157000 r-xp 00000000 08:01 58241      /lib/i686/libc-2.2.4.so
40157000-4015c000 rw-p 00131000 08:01 58241      /lib/i686/libc-2.2.4.so
4015c000-40160000 rw-p 00000000 00:00 0
bfffe000-c0000000 rwxp fffff000 00:00 0　</pre>
</blockquote>
<p>列表中的欄位格式如下：</p>
<blockquote>
	<pre>start-end perm offset major:minor inode image</pre>
</blockquote>
<p>Linux 以 <i>struct vm_area_struct </i>資料結構來<!-- "copyright (c) 2007 www.jollen.org. All rights reserved." -->紀錄每一「區塊」的 VMA 資訊（<i>include/linux/mm.h</i>）：</p>
<blockquote>
	<pre>struct vm_area_struct {
        struct mm_struct * vm_mm;
        unsigned long vm_start;
        unsigned long vm_end;

        struct vm_area_struct *vm_next;

        pgprot_t vm_page_prot;
        unsigned long vm_flags;

        rb_node_t vm_rb;

        struct vm_area_struct *vm_next_share;
        struct vm_area_struct **vm_pprev_share;

        struct vm_operations_struct * vm_ops;

        unsigned long vm_pgoff;

        struct file * vm_file;
        unsigned long vm_raend;
        void * vm_private_data;
};</pre>
</blockquote>
<p><i>struct vm_area_struct</i> 裡有 3 個欄位，用來來維<!-- "copyright (c) 2007 www.jollen.org. All rights reserved." -->護 VMA 資料結構：</p>
<blockquote>
	<p>˙ <i>unsigned long vm_start</i>：記<!-- "copyright (c) 2007 www.jollen.org. All rights reserved." -->錄此 VMA 區塊的開始位址（start address）。<br>
	˙ <i>unsigned long vm_end</i>：記<!-- "copyright (c) 2007 www.jollen.org. All rights reserved." -->錄此 VMA 區塊的結束位址（end address）。<br>
	˙<i> struct vm_area_struct *vm_next</i>：指向下一個 VMA 區塊結構的指<!-- "copyright (c) 2007 www.jollen.org. All rights reserved." -->標（Linux 以 linked 
	list 資料結構維護每一個 VMA 區塊）。</p>
</blockquote>
<p>VMA 的實作主要是為了能更有<!-- "copyright (c) 2007 www.jollen.org. All rights reserved." -->效率地管理記憶體，並且是基於 paging 系統之上所發展出的；VMA 是比原始 paging 理論更高階的記憶體管理方法。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/01/linux_virtual_memory_areas_vma.html</link>
         <guid>https://www.jollen.org/blog/2007/01/linux_virtual_memory_areas_vma.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 05 Jan 2007 14:08:01 +0800</pubDate>
      </item>
            <item>
         <title>應用在 Embedded Linux 場合的 Busybox 有了 &quot;CONFIG_DESKTOP&quot;</title>
         <description><![CDATA[<a href="http://www.busybox.net/">Busybox</a> 於 2006-12-14 釋出 1.3.0（stable）版，並於 2006-12-27 釋出 1.3.1（stable）更新。不過，吸引我的並不是這個平凡的版本更新資訊，而是看到以下這段文字：

<blockquote>This release has CONFIG_DESKTOP option which enables features needed for busybox usage on desktop machine.</blockquote>

'CONFIG_DESKTOP' 的出現，讓原本是應用在 Embedded Linux 場合的 Busybox，也開始支援 Desktop Linux 的應用了。於是，我心裡浮現出以下的感想...

Busybox 原先的設計理念是基於「簡化的系統工具」與「簡單化的指令集」，並朝 footprint 的目標而發展；當初由於 target device 硬體環境的貧乏（如 RAM 只有 4Mbytes），因此這種做法不但合情合理，並且也很「直覺」（make sense）。

現在，target device 硬體端技術的進步，讓 Embedded Linux 與 Desktop Linux 之間的界線越來越模糊。「根本上的定義」，Embedded Linux 是最小化的 Linux 系統，於是我們要學會 root filesystem 的建置，並儘最大力氣把它做到最小（down size）；如今，最小化，或是對尺寸的敏感性，在一些 Embedded Linux 的應用場合上，都不再是重點，甚致可以用「精簡大小不 make sense」來回應。

當 Embedded Linux 隱約等於 Desktop Linux 時，根本上的 Embedded Linux 技能（也就是建置最小化的 root filesystem）仍然是必備的基本能力，但是許多「Auto Build」的環境才是實用的做法；在這樣的前提下，所謂的 Embedded Linux 可能需要再重新定義。如果由「技術上的定義」來解釋，BSP 與週邊界面可能才能叫做「基本能力」，例如：如何 porting 某 SOC NAND flash controller 的 Linux 驅動程式，至於 root filesystem 的話，「就交給滑鼠左鍵吧」。
]]></description>
         <link>https://www.jollen.org/blog/2007/01/embedded_linux_busybox_desktop.html</link>
         <guid>https://www.jollen.org/blog/2007/01/embedded_linux_busybox_desktop.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Fri, 05 Jan 2007 00:05:17 +0800</pubDate>
      </item>
            <item>
         <title>一個防止程式被玩耍的小技倆</title>
         <description><![CDATA[<p>今天在分享自己實作上的經驗時，聊到「如何防止程式被人家玩了！」。我們所要實現的想法很簡單，就是設法防止「執行檔」被 Linux 
下的「標準程式工具」給把玩了！例如：</p>
<ul>
  <li>防止被 &quot;objdump&quot; 工具讀取</li>
  <li>防止被 &quot;objdump –d&quot; 做反組譯</li>
  <li>防止被 <i>gdb</i> 除錯</li>
  <li>防止 symbol table 被 <i>nm</i> 讀出</li>
  <li>防止被其它的標準程式工具（GNU bintuils，如 file 等）所操作</li>
</ul>
<p>我分享了一個簡單的方法，這個方法在以往「Linux Systems 
Programming」的課或多或少也曾向同學介紹過；不過，大家要知道的是，這只是一個有趣的小東西，或者說是一個「小手段」，任何高手級的 Linux 玩家，大概只要不到一小時就能輕易
反擊這個做法。</p>
<p><b>實測</b></p>
<p>就拿 &quot;tar&quot; 指令來看，以下是正常的操作：</p>
<blockquote>
<pre># <b>objdump -d /bin/tar</b>

/bin/tar:     file format elf32-i386

Disassembly of section .init:

08049734 <.init>:
 8049734:       55                      push   %ebp
 8049735:       89 e5                   mov    %esp,%ebp
 8049737:       83 ec 08                sub    $0x8,%esp
 804973a:       e8 b1 07 00 00          call   0x8049ef0
 804973f:       e8 0c 08 00 00          call   0x8049f50
 8049744:       e8 93 b7 01 00          call   0x8064edc
 8049749:       c9                      leave
 804974a:       c3                      ret
Disassembly of section .plt:
...</pre>
</blockquote>
<p dir="ltr">把 &quot;tar&quot; 做「處理」後，這些標準的工具全都失效了。先用 Jollen 提供的小工具處理<i> /bin/tar</i>：</p>
<blockquote>
<pre># <b>./truncate_it /bin/tar > ./tar.trunc</b>
# <b>chmod a+x tar.trunc</b></pre>
</blockquote>
<p>再執行 <i>objdump </i>試試：</p>
<blockquote>
<pre># objdump -d tar.trunc
objdump: tar.trunc: <b>File truncated</b></pre>
</blockquote>
<p>連 <i>nm</i>、<i>gdb</i> 等，也都失效了：</p>
<blockquote>
<pre># nm tar.trunc
nm: tar.trunc: <b>File truncated</b>
[root@mail tmp]# gdb tar.trunc
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"..."/tmp/tar.trunc": <b>not in executable format: File truncated</b>

(gdb) r
Starting program:
No executable file specified.
Use the "file" or "exec-file" command.</pre>
</blockquote>
<p><u>但是程式是可以正常使用的，不會有什麼問題</u>：</p>
<blockquote>
<pre># ./tar.trunc --help|more
GNU `tar' saves many files together into a single tape or disk archive, and
can restore individual files from the archive.

Usage: ./tar.trunc [OPTION]... [FILE]...

Examples:
  ./tar.trunc -cf archive.tar foo bar  # Create archive.tar from files foo a
nd bar.
  ./tar.trunc -tvf archive.tar         # List all files in archive.tar verbo
sely.
  ./tar.trunc -xf archive.tar          # Extract all files from archive.tar.

If a long option shows an argument as mandatory, then it is mandatory
for the equivalent short option also.  Similarly for optional arguments.
...</pre>
</blockquote>
<p>基本上，「執行時期都正常」，但是「程式的操作工具都失效」是這個小技巧能達成的效果。反過來想，既然 run-time 是正常的，那麼也就難逃 
run-time 除錯工具的荼毒了。</p>
<p><b>下載程式</b></p>
<p>做法與原理是相當簡單的，只要知道「如何活用 ELF 規格」，箇中奧妙就不難懂了。大家可以下載 Jollen 寫的小程式「<a href="http://tw.jollen.org/elf-programming/truncate_it">Truncate 
It</a>」，這是一個執行檔，下篇日記我會介紹「Truncate It」的原理並提供 source code。使用方法如下：</p>
<blockquote>
  <p><i># ./truncate_it /bin/tar &gt; tar.trunc</i></p>
</blockquote>
<p><u>記得 chmod 成可執行</u>：</p>
<blockquote>
<pre><i><font size="3"># chmod a+x tar.trunc</font></i></pre>
</blockquote>
<p>然後可以在「目前的目錄下」得到<i> tar.trunc</i> 執行檔。</p>
<p><i>truncate_it</i> 可以針對 ELF 的執行檔做一些小動作，並產生一個新的執行檔（結果輸出到 stdout）。被<i> 
truncate_it</i> 處理過的檔案「大部份」都能正常執行，並且無法使用 GNU binutils 或是 <i>gdb</i> 對他做作何動作。</p>]]></description>
         <link>https://www.jollen.org/blog/2007/01/truncate_it_tricky.html</link>
         <guid>https://www.jollen.org/blog/2007/01/truncate_it_tricky.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Thu, 04 Jan 2007 01:51:10 +0800</pubDate>
      </item>
            <item>
         <title>Process Creation, #2：Running a &quot;User Process&quot;</title>
         <description><![CDATA[<p><b>Process Creation</b></p>
<p dir="ltr">在討論「Process Creation」議題時，首先要了解的就是「Linux 的三種 
Process」。同樣是「執行中的程式（process）」，但是依其「特性（design perspective）」區分的話，可以歸納為以下三種：</p>
<ul>
  <li dir="ltr">
  <p dir="ltr">idle process</li>
  <li dir="ltr">
  <p dir="ltr">kernel threads</li>
  <li dir="ltr">
  <p dir="ltr">user process</li>
</ul>
<p dir="ltr">其中「user process」就是我們此系列日記所要介紹的對象。User process 是很單純的一種 
process，簡單來說，以下二種「執行程式的方式」就是 user process：</p>
<ul>
  <li dir="ltr">
  <p dir="ltr">在 shell 模式中輸入 Linux command 所執行的外部程式</li>
  <li dir="ltr">
  <p dir="ltr">由 init process 所執行的外部程式</li>
</ul>
<p dir="ltr">那麼 user process 是怎麼執行的呢？嗯，先前我們提過的日記「<a href="http://www.jollen.org/blog/2006/12/process_creation_1_shell.html">Process 
Creation, #1：由 shell 執行外部程式《基本觀念與範例》</a>」便介紹了這樣的觀念。</p>
<p dir="ltr"><b>Process Creation：Running a Program</b></p>
<p dir="ltr">接著，我們就以一張圖來展示「在 shell 模式執行外部程式（running a program）」的流程。</p>
<img alt="running_a_program.jpg" src="http://www.jollen.org/blog/2007/01/02/running_a_program.jpg" width="480" height="339" border="1" />
<p dir="ltr">這樣就很清楚了：</p>
<ol>
  <li dir="ltr">
  <p dir="ltr">由 user 鍵入 UNIX command</li>
  <li dir="ltr">
  <p dir="ltr">shell 在 <em>PATH</em> 路徑中尋找 command，即外部程式（stored program）</li>
  <li dir="ltr">
  <p dir="ltr">shell fork/clone 自己，然後 child process 會將自己取代為 ELF image<sup>*1</sup></li>
</ol>
<p dir="ltr">至於「idle process」與「kernel threads」則不適用此圖！</p>
<p dir="ltr"><i>*1 即外部程式：Linux 的執行檔為 ELF 的格式，所以稱其為「ELF image」</i></p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">

	<tr><td width="100%">

		<ul>
			<li>2006.12.31:
            <a href="http://www.jollen.org/blog/2006/12/process_creation_1_shell.html">
            Process Creation, #1：由 shell 執行外部程式《基本觀念與範例》</a></li>

		</ul>

	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2007/01/process_creation_2.html</link>
         <guid>https://www.jollen.org/blog/2007/01/process_creation_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Tue, 02 Jan 2007 23:08:35 +0800</pubDate>
      </item>
            <item>
         <title>2007 開工了！</title>
         <description><![CDATA[新的 2007 年，「Jollen's Blog」也會把重點放在「Linux kernel」與「Linux device driver」；期待您的意見與指教。

<img alt="Year_2007.JPG" src="http://www.jollen.org/blog/2007/01/01/Year_2007.JPG" width="813" height="565" />
]]></description>
         <link>https://www.jollen.org/blog/2007/01/2007_new_year.html</link>
         <guid>https://www.jollen.org/blog/2007/01/2007_new_year.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Mon, 01 Jan 2007 00:52:44 +0800</pubDate>
      </item>
            <item>
         <title>Process Creation, #1：由 shell 執行外部程式《基本觀念與範例》</title>
         <description><![CDATA[<p>本系列專欄是「了解 ELF 動態<!--copyright (c) 2006 www.jollen.org-->時期行為」的前導專欄，建議您先行爬文「<a href="http://www.jollen.org/blog/2006/12/enabling_dynamic_loader_2.html#more">理解 dynamic loader 內部原理的幾個先備知識（下）：Kernel 端的議題</a>」。</p>

<strong>Shell 執行外部程式的<!--copyright (c) 2006 www.jollen.org-->觀念</strong>

<p>在 2006 年的最後一天，就先來解說一下「下指令後、程式怎麼載入與執行」的<u>觀念</u>。首先，您必須知<!--copyright (c) 2006 www.jollen.org-->道並具備以下的 Linux 系統程式觀念：</p>

<p>1. <em>fork()</em> 系統呼叫：用來建立 child process。<br />
2. exec 系列系統呼叫：以一個「外部程式」來取代自己（current process）的執行空間<!--copyright (c) 2006 www.jollen.org-->。<br />
3. parent process、child process 與<!--copyright (c) 2006 www.jollen.org--> Linux process tree。</p>

<p>了解以上三個主題後，就可以很容易理解「下指令後、程式怎麼載入與執行」的過程；這是很重要且基本的三個議題，若您不是很清楚這三個議題的觀念，可以<!--copyright (c) 2006 www.jollen.org-->查詢「Linux Systems Programming」的相關文件。</p>

<p>接下來，首先我們要知道的是，Linux 並沒有提供 "spawn" 的 system call；再來，shell 執行外部程式的做法是使用「spawn 版」的 <em>fork()</em> 實作。</p>

<p>好像又把大家搞胡塗了。所謂的「spawn 版<!--copyright (c) 2006 www.jollen.org-->的 <em>fork()</em>」，用對照的方式來說明會很清楚：</p>

<p>1. 真正的 <em>spawn()</em> 是「建立一<!--copyright (c) 2006 www.jollen.org-->個外部程式的 child process」；<br />
2. 而 Linux 下討論的 <em>fork()</em> 則是「建立一個與父程序（parent process）完全相<!--copyright (c) 2006 www.jollen.org-->同的 process」。</p>

<p>也就是說，Linux 下的 f<em>ork()</em> 是「non-spawn behavior」，因為他不會去跑一個外部程式，而是「複製 parent process 成為 child process」；那麼，要怎<!--copyright (c) 2006 www.jollen.org-->麼做出「spawn behavior」的 <em>fork()</em> 呢？方法很簡單：<p>

<p>
1. 我們已經學過， exec 系列系統呼叫會以一個「外部程式」來取代 current process 的執行空間，所以不<!--copyright (c) 2006 www.jollen.org-->能在 parent process 裡叫用 exec system call 來跑外部程式；否則 parent process 會消失。<br />
2. <em>fork()</em> 可以建立 child process，並且不讓 parent process 消失。<br />
3. 所以，如果先 <em>fork()</em> 出 child process，再讓外部程式取代 child process，那麼就可以<!--copyright (c) 2006 www.jollen.org-->實作出「spawn style 的 <em>fork()</em>」了。</p>

<strong>小結</strong>

<p>Jollen 一開始就打算以邏輯思考（理論推演）的方式來介紹「觀念」，所以講了這麼多，希望不會造<!--copyright (c) 2006 www.jollen.org-->成您閱讀上的困擾。引用「書上寫的」應可以勝過以上的長篇大論：<p>

<p>Shell 執行外部程式的做法是先 fork 出自己的 child process，然後在 child process 裡使用 exec 系統呼叫外<!--copyright (c) 2006 www.jollen.org-->部程式，以執行外部程式。<p>

<p>很簡單的概念，只是記結論的話多少會少掉一些思考的樂趣。依此來看，邏輯上（Linux programming view）「可視 spawn 為 fork + exec」；實際上，若由 kernel view 來解讀，spawn 並不<!--copyright (c) 2006 www.jollen.org-->等於 fork + exec。</p>

<strong>程式範例</strong>

<p>看此範例需要知道 <em>fork() </em>與 exec 系列函數的用法，您可參考「Linux Systems Programming」相關文件。</p>

<blockquote><pre>/*
 * Copyright(c) 2003,2004,2005,2006 www.jollen.org
 *
 * Spawn-style Linux fork().
 *
 * This file is GPLed.
 */
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/types.h&gt;

int spawn(char *prog, char **arg_list)
{
   pid_t child;

   child = fork();

   if (child != 0) {
      return child;
   } else {
      execvp(prog, arg_list);
      fprintf(stderr, "spawn error\n");
      return -1;
   }
}
<!--copyright (c) 2006 www.jollen.org-->
int main()
{
   char *arg_list[] = {	
      "ls",  
      "-l",
      "/tmp",
      NULL };	

   spawn("ls", arg_list);

   return 0;
}</pre></blockquote>

<p>以上範例可由「<a href="http://tw.jollen.org/elf-programming/spawn_fork.c">http://tw.jollen.org/elf-programming/spawn_fork.c</a>」下載。</p>

<p>Happy New Year ;-)</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/process_creation_1_shell.html</link>
         <guid>https://www.jollen.org/blog/2006/12/process_creation_1_shell.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 31 Dec 2006 20:14:16 +0800</pubDate>
      </item>
            <item>
         <title>理解 dynamic loader 內部原理的幾個先備知識（下）：Kernel 端的議題</title>
         <description><![CDATA[前些日子寫到「<a href="http://www.jollen.org/blog/2006/12/enabling_dynamic_loader_1.html">理解 dynamic loader 內部原理的幾個先備知識（上）</a>」時提及，必須略<!--copyright (c) 2006 www.jollen.org-->懂 .bss section 的觀念，並且最後也提到「之後再寫日記，以一段 code 來說明。」。其實之後幾天我就把介紹 .bss section 觀念的日記寫好了，只是還沒有加入到專欄索引，趕快來把索引做好！

今天<!--copyright (c) 2006 www.jollen.org-->加入「<a href="http://www.jollen.org/EmbeddedLinux/BSS_Section_Concepts.html">BSS Section Concepts: .bss section 的基本觀念介紹</a>」的專欄索引。

以下是必讀的<!--copyright (c) 2006 www.jollen.org-->二個專欄（及理由）：

1. 必須先懂 ELF 到底是什麼東西，才會知道 .bss 是 ELF 的「一個節區」。可以參考 Jollen 的「 <a href="http://www2.jollen.org/EmbeddedLinux/Executable_Linking_Format.html">Executable and Linking Format: 重要的 ELF 格式介紹</a>」專<!--copyright (c) 2006 www.jollen.org-->欄。

2. 接著才是讀「<a href="http://www.jollen.org/EmbeddedLinux/BSS_Section_Concepts.html">BSS Section Concepts: .bss section 的基本觀念介紹</a>」專欄。.bss section 是「動態時期（run time）」，而不是「靜態時期（stored）」的觀念，這真的是很<!--copyright (c) 2006 www.jollen.org-->重要。

除了「理解 dynamic loader 內部原理的幾個先備知識（上）」提到以 ELF 為主軸的幾個重要先備知識外，在開始研究 dynamic loader 前，還有<!--copyright (c) 2006 www.jollen.org-->幾個重要觀念也是要先懂的。其中與 kernel 相關的議題整理如下：

1. 就整體來看，最好了解 kernel 的<!--copyright (c) 2006 www.jollen.org-->開機流程，也就是由 bootloader 載入 kernel 後一直到 <em>init </em>process（user-space）的整個過程。這部份，Jollen 正在準備一篇簡單的文章來跟大家分享。

2. 就內部來看，必須知道「下指令後、程式怎麼載入與執行」的流程。大致來看，這涉及 shell、fork system call、<!--copyright (c) 2006 www.jollen.org-->exec system call 三大主題。這部份，Jollen 也打算寫篇日記來介紹。

了解以上二個重要觀念後，再切入研究 dynamic loader（例如：process 與 libc.so.6 如何 "dynamic" linking）時，腦細胞的連<!--copyright (c) 2006 www.jollen.org-->結才會比較順暢。]]></description>
         <link>https://www.jollen.org/blog/2006/12/enabling_dynamic_loader_2.html</link>
         <guid>https://www.jollen.org/blog/2006/12/enabling_dynamic_loader_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 29 Dec 2006 21:20:29 +0800</pubDate>
      </item>
            <item>
         <title>更新 uClinux-dist 的教學專欄</title>
         <description><![CDATA[uClinux 的 distribution 稱為 uClinux-dist，今天把 uClinux-dist 使用的教學專欄做了小小修正與整理，並提供給有需要的朋友。此系列教學專欄共有 5 篇文章，如下：

2004.02.26: uClinux-dist 使用教學, #1：uClinux 與 uClinux-dist 介紹
2004.02.26: uClinux-dist 使用教學, #2：安裝 uClinux 的 GNU ARM toolchain
2004.02.26: uClinux-dist 使用教學, #3：uClinux-dist 的使用
2004.02.26: uClinux-dist 使用教學, #4：編譯 uClinux-dist 與常見問題
2004.02.26: uClinux-dist 使用教學, #5：使用 GDB/ARMulator 來做測試

專欄的目錄索引「<a href="http://www.jollen.org/EmbeddedLinux/uClinux-dist.html">http://www.jollen.org/EmbeddedLinux/uClinux-dist.html</a>」，需要時歡迎隨時上線取用。
]]></description>
         <link>https://www.jollen.org/blog/2006/12/update_uclinux-dist.html</link>
         <guid>https://www.jollen.org/blog/2006/12/update_uclinux-dist.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 27 Dec 2006 14:17:08 +0800</pubDate>
      </item>
            <item>
         <title>（非關 Embedded Linux）Google Trends 反應人心！</title>
         <description><![CDATA[（非關 Embedded Linux）今天晚上跟小弟弟提到「<a href="http://www.google.com/trends">Google Trends</a>」可以用來幫助我們了解「關鍵字的搜尋趨勢」，結果丁小弟寄了以下二張圖片給我。

<img alt="fun_1.JPG" src="http://www.jollen.org/blog/2006/12/27/fun_1.JPG" width="604" height="402" />

<img alt="fun_2.JPG" src="http://www.jollen.org/blog/2006/12/27/fun_2.JPG" width="595" height="354" />

好一個「罄竹難書」。
]]></description>
         <link>https://www.jollen.org/blog/2006/12/google_trends.html</link>
         <guid>https://www.jollen.org/blog/2006/12/google_trends.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 27 Dec 2006 01:51:56 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的 I/O, #3: kernel-space 與 user-space 的「I/O」</title>
         <description><![CDATA[<p><b>重要觀念</b></p>
<p>任何作業系統底下的「驅動程式」，都需要分二個層面來討論所謂的「I/O 處理」：</p>
<blockquote>
	<p>1. 實體層：驅動程式 v.s. 硬體。</p>
	<p>2. 虛擬層：驅<!--this article is copyright by www.jollen.org-->動程式 v.s. user process</p>
</blockquote>
<p>在前一篇日記「<a href="http://www.jollen.org/blog/2006/12/linux_device_driver_io2.html">Linux 
驅動程<!--this article is copyright by www.jollen.org-->式的 I/O, #2: I/O 存取相關函數</a>」中所提到的 I/O 函數是處理「實體層」的 I/O；本日記所要介紹的 <i>copy_to_user()</i> 
與 <i>copy_from_user()</i> 則是在處理「虛擬層」的 I/O。另外，在繼續往下讀之前，您必須了解以下的觀念<!--this article is copyright by www.jollen.org-->都是「等價」的：</p>
<blockquote>
	<p>1. 驅動程式與 user process 的 I/O；等於</p>
	<p>2. 驅動<!--this article is copyright by www.jollen.org-->程式與 user process 間的 data communication；等於</p>
	<p>3. kernel-space 與 user-space 間的 data c<!--this article is copyright by www.jollen.org-->ommunication。</p>
</blockquote>
<p>此外，還要了解：</p>
<blockquote>
	<p>1. user-space 無法「直接」存取 kernel-space 的記憶體。</p>
	<p>2. 「Linux device d<!--this article is copyright by www.jollen.org-->river」與「user-space」間的 I/O 會與<i> fops-&gt;read</i>、<i>fops-&gt;write</i> 
	與 <i>fops-&gt;ioctl </i>共三<!--this article is copyright by www.jollen.org-->個 system call 有關。</p>
</blockquote>
<p><b>copy_to_user() 與 copy_from_user()</b></p>
<p>了解以上的觀念後，再來「直接殺進重點」就很容易懂了：從 user-space 讀取資料至 kernel-space，或是將 kernel-space 
的資料寫至 user-space，「必須」透過<!--this article is copyright by www.jollen.org--> kernel 提供的 2 個 API 來進行。這二個 API 如下：</p>
<p>˙ long copy_to_user(void *to, const void *from, long n);<br>
˙ long copy_from_user(void *to, const void *from, long n);</p>
<p>參數說明，以 <i>copy_to_user()</i> 來說：</p>
<p>˙ <b>to</b>：資料的目<!--this article is copyright by www.jollen.org-->的位址，此參數為一個指向 user-space 記憶體的指標。<br>
˙<b> from</b>：資料的來源位址，此參數為一個指向 kernel-space 記憶體的指標。<br>
<font color="#000080"><b>˙ 口<!--this article is copyright by www.jollen.org-->訣：copy data <u>to</u> user-space <u>from</u> 
kernel-space</b></font></p>
<p>以 <i>copy_from_user()</i> 來說：</p>
<p>˙ <b>to</b>：資料的目<!--this article is copyright by www.jollen.org-->的位址，此參數為一個指向 kernel-space 記憶體的指標。<br>
˙<b> from</b>：資料的來源位址，此參數為一個指向 user-space 記憶體的指標。<br>
<font color="#000080"><b>˙ 口<!--this article is copyright by www.jollen.org-->訣：copy data <u>from</u> user-space <u>to</u> 
kernel-space</b></font></p>
<p>由 user-space 讀取資料，或是寫入資料給 user-space 的 3 個 driver method 為：read、write與ioctl。</p>
<p>另外，指向 
user-space 的指標是 kernel 回呼 driver method 時所傳遞進來的，可由 read、write 與 ioctl driver 
function 的函數原型宣告來觀察（紅色<!--this article is copyright by www.jollen.org-->部份）：</p>
<p>˙ int card_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, 
unsigned long <b><font color="#FF0000">arg</font></b>);<br>
˙ ssize_t write(struct file *filp, const char <b><font color="#FF0000">*buff</font></b>, size_t count, loff_t *offp);<br>
˙ ssize_t read(struct file *filp, char <b><font color="#FF0000">*buff</font></b>, size_t count, loff_t *offp);</p>
<p><i>fops-&gt;ioctl</i> 的參數 <b>arg</b>、fops-&gt;write 與 fops-&gt;read 的參數 <b>buff</b> 
是指向 user-space 資料的指標。撰寫程式時，要注意資料型別上的不同。</p>
<p>下一篇日記再寫一個範例來配合著研究，大家應<!--this article is copyright by www.jollen.org-->該會更清楚。</p>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%"><tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr></table><table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr></table><table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td width="100%"><ul><li>2006.12.22:&nbsp;<a href="http://www.jollen.org/blog/2006/12/inux_device_driver_io_1.html">Linux 驅動程式的 I/O, #1: 基本概念</a></li>
<li>2006.12.20: <a href="http://www.jollen.org/blog/2006/12/linux_device_driver_io2.html">Linux 驅動程式的 I/O, #2: I/O 存取相關函數</a></li></ul></td></tr></table>]]></description>
         <link>https://www.jollen.org/blog/2006/12/linux_device_driver_io_3.html</link>
         <guid>https://www.jollen.org/blog/2006/12/linux_device_driver_io_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Tue, 26 Dec 2006 23:40:04 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 2006 十大回顧！</title>
         <description><![CDATA[LinuxDevices.com 推出「<a href="http://www.linuxdevices.com/news/NS6295316644.html">Embedded Linux 2006 十大回顧</a>」！內容提到今年 10 個主要的 embedded Linux 發展趨勢（以下是簡要翻譯，請閱讀<a href="http://www.linuxdevices.com/news/NS6295316644.html">原文</a>有很完整的說明）：

1. Linux 也有自己的手機囉！

2. 許多廠商開始提供完整（"complete"）的 mobile phone software stacks 了，參賽者有  Trolltech, a la Mobile, Cellunite, Aplix, Access！（Jollen 編註：事實上 2006 年出現在地球上的 mobile phone software stack（application framework）不只於此）

3. 關鍵性的 embedded Linux 開發工具！（Jollen 編註：最令人印像深刻的是 TimeSys 的「現成 RFS 服務」」）

4. AMD & Intel 賣掉他們「non-x86」的 mobile phone 處理器產品線！（Jollen 編註：你們這哥倆好要繼續努力喔！）

5. 真的不是蓋的，越來越多 Eclipse-based 的 embedded Linux 開發工具了！（Jollen 編註：其中以「<a href="http://www.jollen.org/blog/2006/11/montavista_dev_rock_5_linux_id.html">MontaVista 推出 Dev Rock 5</a>」是最重大的變革）

6. GPLv3 大激戰！

7. 除了手機外，還有更多的 design wins！

8. VoIP、TV 與 Set-top-box 的產品！

9. Linux kernel 有了重大的 real-time enhancements！

10. 許多 Linux 新規格的釋出！

今年隨時保持 embedded Linux 發展動態的朋友，對這幾條新聞的出線絕對是不感到意外的。不容易被外力改變的方向叫做「趨勢」，希望未來的一年 embedded Linux 繼續加油！]]></description>
         <link>https://www.jollen.org/blog/2006/12/embedded_linux_2006.html</link>
         <guid>https://www.jollen.org/blog/2006/12/embedded_linux_2006.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 25 Dec 2006 21:05:32 +0800</pubDate>
      </item>
            <item>
         <title>Hiker：另一個 GTK+ based 的 Linux mobile phone application framework</title>
         <description><![CDATA[本年度與明年度的既定工作之一是「survey of application framework for Linux mobile phones」以及「what are they target」。今天是歡樂的 Christmas，不過是要工作的一天；去年與前年的耶誕節正好遇到假日都是放假。但是也有許多公司今天很人性化的放了一天假，結果就在閒逛網站時，又瞄到一則新聞「<a href="http://www.linuxdevices.com/news/NS6832325233.html">Framework aims to commercialize mobile Linux apps</a>」。

如果我的國文造詣還可以的話，以「雨後春筍」來形容「Linux application framework for mobile devices」應該是挺合適的；今年（２００６）如果要寫 Linux 年度大事記的話，這個主題絕對是前 10 名的。

本週是２００６年最後一週，也是為２００７年做準備的最後一週，輕鬆一下也是不錯的。

<strong>回歸正題</strong>

另一個以 GTK+ 為基礎的 Linux mobile devices 的「application framework」也釋出了，這個 application framework 叫做「<a href="http://www.access-company.com/about/opensource/download.html">Hiker Application Framework</a>」，他是以 GTK+ 做為發展基礎的 Linux mobile devices 應用框架。

<a href="http://www.jollen.org/blog/2006/11/mobile_20_openmoko_linux_smart.html">不久前曾經提到過</a>的「<a href="http://www.openmoko.com">OpenMoko</a>」也是以 GTK+ 為基礎的 Linux mobile devices 應用框架（application framework）；OpenMoko 與 Hiker Application Framework 在授權上採取不同的策略。OpenMoko 以 GPL（but not as usual mentioned by OpenMoko's web site），Hiker Application Framework 則是以 <a href="http://www.mozilla.org/MPL/MPL-1.1.html">MPL</a>（Mozilla Public License）的授權釋出。

Hiker Application Framework 的資訊可參閱他的官方網站「<a href="http://www.access-company.com/">Access</a>」，並且可<a href="http://www.access-company.com/about/opensource/download.html">在此下載</a>。

Hiker 於 2006 年 10 月份曾經發出過一份消息，可<a href="http://linuxdevices.com/news/NS5705421616.html">按此瀏覽</a>。另外，Access 預計在 2007 年上半年釋出 SDK（software development kit）與 PDK（product development kit），值得一提的是，SDK 所提供的 GUI 工具是「Eclipse-based 」。]]></description>
         <link>https://www.jollen.org/blog/2006/12/hiker_application_framework_linux_mobile.html</link>
         <guid>https://www.jollen.org/blog/2006/12/hiker_application_framework_linux_mobile.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 25 Dec 2006 20:28:34 +0800</pubDate>
      </item>
            <item>
         <title>Merry Christmas 2007 祝各位佳節愉快！</title>
         <description><![CDATA[<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%">
  <tr>
    <td align="center">
    <table border="0" cellpadding="0" cellspacing="0" width="100%">
      <tr>
        <td align="center">
        <table background="http://img.123greetings.com/images/bg_pics/bground23.jpg" border="0" cellpadding="0" cellspacing="5" width="100%">
          <tr>
            <td><font color="#800000" face="Courier" size="6">Merry Christmas 
            and Happy New Year 2007!</font>
            <center>
            <p><br>
            <img src="http://img.123greetings.com/eventsnew/edec_c_family_brosis/8540-054-07-1027.gif" alt="A Christmas Gift For You !, Free Christmas Greetings Cards For Brother/ Sister Cards" id="cardimage" border="0" width="525" height="325">
            </p>
            <div style="visibility: hidden;" id="audio_panel_holder">
              <div id="rak_media">
　</div>
            </div>
            <div id="music_cont" align="center">
              <div id="normal_audio" align="center">
　</div>
            </div>
            <p>
            <font color="#800000" face="Courier" size="6">祝 jollen.org 
            的所有朋友耶誕佳節愉快。</font> </p>
            </center>
            <p align="center"><i><font color="#800000" face="Courier" size="6">
            --jollen</font> </i></p>
            </td>
          </tr>
        </table>
        </td>
      </tr>
    </table>
    </td>
    <td rowspan="2" bgcolor="#cccccc" valign="top" width="5">
    <img src="http://img.123greetings.com/images/space.jpg" align="bottom" border="0" width="5" height="5"></td>
  </tr>
  <tr>
    <td bgcolor="#cccccc">
    <img src="http://img.123greetings.com/images/space.jpg" align="bottom" border="0" width="5" height="5"></td>
  </tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/12/chirstmas_2007_merry.html</link>
         <guid>https://www.jollen.org/blog/2006/12/chirstmas_2007_merry.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Fri, 22 Dec 2006 23:57:33 +0800</pubDate>
      </item>
            <item>
         <title>non-GPL 授權的 Linux kernel module 要禁用？</title>
         <description><![CDATA[non-GPL 授權的 Linux kernel module 是否在 2008 年的一月份起開始禁止！？

「<a href="http://www.osnews.com/user.php?uid=3660">SEJeff</a>」在 <a href="http://www.osnews.com/index.php">OSNews.com</a> 上寫了一篇 Blog，內容提到「<a href="http://www.osnews.com/story.php/16720/Non-GPL-Linux-Kernel-Modules-Banned-Starting-January-2008">non-GPL Linux kernel modules will not work after January 2008</a>」，結果更可怕的是截至目前（本日記出版時）為止，已經有 27x 篇的「comments」在討論這個話題。

Comments 裡頭各式各樣的延伸看法都有，大家可以去瞧瞧。]]></description>
         <link>https://www.jollen.org/blog/2006/12/non-gpl_linux_kernel_module.html</link>
         <guid>https://www.jollen.org/blog/2006/12/non-gpl_linux_kernel_module.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 21 Dec 2006 23:47:24 +0800</pubDate>
      </item>
            <item>
         <title>Linux 的 SDIO Stack 也有 open-source 的版本了！</title>
         <description><![CDATA[<p dir="ltr">SDIO 是走 SD 插糟的週邊介面，SDIO 也是普及於 PDA/Mobile 的週邊規格，實際應用例如：透過 SDIO（SD 插糟），可以外接許多 SDIO 的週邊卡，像是 SDIO WiFi card 或是 SDIO CMOS sensor。Linux 從 2.6.17 開始正式加入了 SD/MMC 的 stack driver（也就是 API core 層），但是並沒有 SDIO 的 stack driver。</p>
<p dir="ltr">因此，「玩家」可以試著由現有的 SD/MMC stack 做修改，並實作 machine-dependent 的驅動程式（例如：s3c24x0）。或是花錢買解決方案，例如：MontaVista、印度的 embWise 公司。</p>
<p dir="ltr"><b>Linux-SDIO Project</b></p>
<p dir="ltr">不過，有好消息。今天在 LinuxDevices.com 上看到一則新聞：<a href="http://www.linuxdevices.com/news/NS6177996610.html">MontaVista, 
Atheros contribute open-source SDIO stack</a>。MontaVista 與 Atheros 攜手貢獻 
open-source 的 SDIO stack，並且未來也會加到主流（mainline）的 Linux kernel（註：即 Linus 維護的 
kernel）。這個動作解決目前幾個 SD/MMC 應用的問題：</p>
<p dir="ltr">1. Linux SDIO stack（Core API layer）終於有了完整的 open-source 
版本。（但是不知道實際採用的意願高不高，不過對這個部份有興趣的玩家，終於不用再面對一疊厚厚的 spec. 發呆了。）</p>
<p dir="ltr">2. Linux 未來將能支援更多的 SDIO 週邊。目前除了 SD memory card、SDIO WiFi card 與 
SDIO CMOS sensor 可以跑之外，不知道還有什麼 SDIO 週邊可以上到 Linux 的。</p>
<p dir="ltr">由於 Atheros 有 Linux 2.6 的 SDIO WiFi card patch，所以除了 Atheros 
的卡可以上之外，好像也沒辦法在「網路上」找到別家的。</p>
<p dir="ltr">這裡有 [<a href="http://source.mvista.com/~dsingleton/sdio/">MontaVista 
所維護的 Atheros WiFI card SDIO 驅動程式</a>]。</p>
<p dir="ltr">3. MontaVista 與 Atheros 合作的這個 open-source 專案，做了一件很重要的事（引述報導原文）：</p>
<blockquote>
  <p dir="ltr"><i><font face="Arial,Helvetica" size="3">The linux-SDIO project 
  will apparently endeavor to merge the Codetelligence/Atheros stack with the 
  mainline kernel's existing linux-mmc subsystem. </font></i></p>
</blockquote>
<p dir="ltr">此外（引述報導原文）：</p>
<blockquote>
  <p dir="ltr"><i>our long term goal is to follow the community and just submit 
  SDIO drivers to Pierre [Ossman, Linux's MMC subsystem maintainer], instead of 
  duplicating the SDIO framework of host controller drivers and bus drivers. </i>
  </p>
</blockquote>
<p dir="ltr">所以 Linux-SDIO project 將會整合現有的 Linux-MMC subsystem（即 Stack 
層、API）。表示未來 Linux 的 SD/MMC subsystem 只會有一套，而不是像現在的情況。例如：有些 MMC host controller 的 
vendor 除了實作自己的 machine-dependent driver 外，也寫自己的 stack 層（只是沒有那麼完整、通用）。</p>
<p dir="ltr">4. Open-source 的價值之一是「製造出」各種完善的商業性支援（commerical support），所以許多工作並不需要自己來，或是自己做不來的工作也能輕鬆取得支援。以 SDIO 
stack 解決方案來說，未來將會有更多 IDH（Independent Design House）提供 SDIO 的協助，商業性的支援不會再是有錢人的專利了。</p>
<p dir="ltr">目前 Linux 2.6 現有的 SD/MMC stack 
架構與程式碼實作，難度並不是很高，相當好理解，大家可以花點時間來研究這個部份，會很有幫助的。</p>
<p dir="ltr">Open-source SDIO stack，這真是個不錯的消息！</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/linux_sdio_stack_open_source.html</link>
         <guid>https://www.jollen.org/blog/2006/12/linux_sdio_stack_open_source.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 21 Dec 2006 17:09:23 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的 I/O, #2: I/O 存取相關函數</title>
         <description><![CDATA[<strong>I/O 存取相關函數</strong>

要提到「I/O 處理」當然要整理 Linux 提供的相<!--This article is copyright by www.jollen.org (c) 2006-->關函數，以下分 3 大類來整理：

1. I/O port
2. I/O memory
3. PCI configuration space
4. ioremap

<strong>I/O Port</strong>

以下是 Linux 提供最原始<!--This article is copyright by www.jollen.org (c) 2006-->的 I/O port 存取函數：

    ˙ unsigned inb(unsigned port);
    ˙ unsigned inw(unsigned port);
    ˙ unsigned inl(unsigned port);<!--This article is copyright by www.jollen.org (c) 2006-->
    ˙ void outb(unsigned char byte, unsigned port);
    ˙ void outw(unsigned short word, unsigned port);
    ˙ void outl(unsigned long word, unsigned port);

<strong>I/O Memory</strong>

以下是 Linux 提供最原始的 I/O memory 存取函數：

    ˙ unsigned readb(unsigned port);
    ˙ unsigned readw(unsigned port);
    ˙ unsigned readl(unsigned port);
    ˙ void writeb(unsigned char byte, unsigned port);
    ˙ void writew(unsigned short word, unsigned port);
    ˙ void writel(unsigned long word, unsigned port);

對於 I/O memory 的操作，Linux 也提供 memory copy 系列函數如下：

    ˙ memset_io(address, value, count);<!--This article is copyright by www.jollen.org (c) 2006-->
    ˙ memcpy_fromio(dest, source, num);
    ˙ memcpy_toio(dest, source, num);

以上在「<a href="http://www.jollen.org/blog/2006/05/linux_6_physical_device_driver_1.html">Linux 驅動程式觀念解析, #6: 依流程來實作 -- Physical Device Driver</a>」介紹過一次，並且也搭配了一個簡單範例做說明，您可<!--This article is copyright by www.jollen.org (c) 2006-->參考該文。

<strong>PCI Configuration Space</strong>

Linux 也提供讀寫 PCI configuration space（PCI BIOS）的函數：

    ˙ int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
    ˙ int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
    ˙ int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
    ˙ int pci_write_config_byte(struct pci_dev *dev, int where, u8 val);
    ˙ int pci_write_config_word(struct pci_dev *dev, int where, u16 val);
    ˙ int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);

有些朋友可能<!--This article is copyright by www.jollen.org (c) 2006-->看過開頭是 pcibios_* 的函數版本，「不過這是舊的<!--This article is copyright by www.jollen.org (c) 2006-->函數，請勿再使用」。

<strong>ioremap()</strong>

這個 API 就重要到不行了，任何時候，Linux device driver 都「不能直接存取 physical address」。所以，「使用以上的 I/O 相關函<!--This article is copyright by www.jollen.org (c) 2006-->數時，只能傳 virtual address，不能傳入 physical address」，ioremap() 就是用來將 physical address 對應到 virtual address 的 API。

<strong>小結</strong>

對於 I/O 函數的使用，應該在「深諳」Linux驅動程式架構與Linux作業系統原理的情況下使用，「單純的 kernel module + IO APIs」並不叫做 Linux 驅動程式，再更進一步的「kernel module + read/write/ioctl + IO APIs」也只是小<!--This article is copyright by www.jollen.org (c) 2006-->聰明（編註），還是稱不上 Linux「驅動程式」。建構在作業系統裡的驅動程式，90% 都是在實作良好的機制與行為，因此「OS 原理與機制的研究」，才是正確的思考方向。與大家分享自己的心得，希望對您的學<!--This article is copyright by www.jollen.org (c) 2006-->習有幫助。

編註：這是 Linux device driver 的「開始」但不是全部，也只是冰山一角。但是許多教育訓練機構的課程卻是以此為做為<!--This article is copyright by www.jollen.org (c) 2006-->規劃方向，並不是很妥當。

<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%"><tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr></table><table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr></table><table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td width="100%"><ul><li>2006.12.22:&nbsp;<a href="http://www.jollen.org/blog/2006/12/inux_device_driver_io_1.html">Linux 驅動程式的 I/O, #1: 基本概念</a></li></ul></td></tr></table>]]></description>
         <link>https://www.jollen.org/blog/2006/12/linux_device_driver_io2.html</link>
         <guid>https://www.jollen.org/blog/2006/12/linux_device_driver_io2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Wed, 20 Dec 2006 22:57:48 +0800</pubDate>
      </item>
            <item>
         <title>用 SIMH 的 PDP-11 模擬器跑 2.11BSD</title>
         <description><![CDATA[<p dir="ltr">為了能看到 2.11BSD on PDP-11 跑起來的樣子，所以要找一個簡單的方法先讓東西跑起來。由 tuhs 上 mirror 
回來的一堆東西裡，找到 <i>PDP-11/Boot_Images/2.11_on_Simh/211bsd.tar.gz</i> 
這個檔案，直接取用這個檔案，把它解開後發現裡頭有這麼多東西：</p>
<p dir="ltr">
<img alt="pdp11_1.JPG" src="http://www.jollen.org/blog/2006/12/20/pdp11_1.JPG" width="530" height="84" /></p>
<p dir="ltr">這裡頭的檔案是給 SIMH 模擬器用的，也就是「<a href="http://simh.trailing-edge.com/">Bob 
Supnik's PDP simulator</a>」。我用的是 SIMH V3.6-1，以下是編譯後得到的一堆古董機模擬器：</p>
<p dir="ltr">
<img alt="pdp11_2.JPG" src="http://www.jollen.org/blog/2006/12/20/pdp11_2.JPG" width="635" height="64" />
</p>
<p dir="ltr">我把 211bsd.tar.gz 解在這個目錄下。接著用 PDP-11 的模擬器來開機：</p>
<blockquote>
  <p dir="ltr"><i>$ ../pdp11 ./211bsd.simh</i></p>
</blockquote>
<p dir="ltr">結果跑出這個畫面：</p>
<p dir="ltr">
<img alt="pdp11_3.JPG" src="http://www.jollen.org/blog/2006/12/20/pdp11_3.JPG" width="492" height="184" /></p>
<p dir="ltr">一時之間不曉得發生什麼事，結果按了個 [Enter] 後：</p>
<p dir="ltr">
<img alt="pdp11_4.JPG" src="http://www.jollen.org/blog/2006/12/20/pdp11_4.JPG" width="629" height="384" /></p>
<p dir="ltr">接下來，大家應該會做跟我一樣的動作：</p>
<p dir="ltr">
<img alt="pdp11_5.JPG" src="http://www.jollen.org/blog/2006/12/20/pdp11_5.JPG" width="586" height="340" /></p>
<p dir="ltr">還蠻酷的。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/simh_pdp-11_211bsd.html</link>
         <guid>https://www.jollen.org/blog/2006/12/simh_pdp-11_211bsd.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 20 Dec 2006 18:16:21 +0800</pubDate>
      </item>
            <item>
         <title>The Unix Heritage Society（古老的 UNIX）與 PDP-11 模擬器</title>
         <description><![CDATA[有時候換個心境來「考古」一下，比學習新技術更有樂趣。

<strong>The Unix Heritage Society</strong>

「<a href="http://www.tuhs.org/">The Unix Heritage Society（tuhs）</a>」保留了很久以前的 UNIX 系統，引述 tuhs 網站上的一句話：

<blockquote>The preservation and maintenance of historical and non-mainstream UNIX systems.</blockquote>

tuhs 也提供 rsync 服務，所以我就把上面的檔案都抓回來了！根據 tuhs 網站上的說明，我下的 rsync 命令如下：

<blockquote><pre>rsync -avz minnie.tuhs.org::UA_Root .
rsync -avz minnie.tuhs.org::UA_Applications Applications
rsync -avz minnie.tuhs.org::UA_4BSD 4BSD
rsync -avz minnie.tuhs.org::UA_PDP11 PDP-11
rsync -avz minnie.tuhs.org::UA_PDP11_Trees PDP-11
rsync -avz minnie.tuhs.org::UA_VAX VAX
rsync -avz minnie.tuhs.org::UA_Other Other</pre></blockquote>

抓了好久，因為檔案差不多有 1.1 GB 左右。抓回這些東西：

<blockquote><pre>jollen@jollen-work:~/tuhs$ ls
4BSD/                checksums.gz    lists/     Other/   rsync.sh  VAX/
Applications/        COPYRIGHTS      misc/      PDP-11/  tools/
Caldera-license.pdf  Documentation/  nohup.out  README   updates</pre></blockquote>

又找了 DEC PDP-11 的模擬器，想來玩玩看這些古老級的 Unix。

<strong>The DEC Emulation Website</strong>

在這裡可以找到 DEC 的模擬器：<a href="http://www.aracnet.com/~healyzh/decemu.html">http://www.aracnet.com/~healyzh/decemu.html</a>，DEC PDP-11 的模擬器：<a href="http://www.aracnet.com/~healyzh/pdp11emu.html">http://www.aracnet.com/~healyzh/pdp11emu.html</a>。不過還沒有實際裝起來玩過，只是覺得蠻酷的。

<strong>Bob Supnik's PDP simulator</strong>

從 tuhs 抓回來的東西裡找到可以在 Bob Supnik's simulator 上跑的 211BSD：

<blockquote><pre>jollen@jollen-work:~/tuhs$ ls PDP-11/Boot_Images/2.11_on_rl02/
211bsd.boot.gz  211bsd.vol3.gz  211bsd.vol6.gz  211bsd.vol9.gz  README
211bsd.vol1.gz  211bsd.vol4.gz  211bsd.vol7.gz  patches         script
211bsd.vol2.gz  211bsd.vol5.gz  211bsd.vol8.gz  pdpsim.tz</pre></blockquote>

Bob Supnik's PDP simulator 首頁：<a href="http://simh.trailing-edge.com/">http://simh.trailing-edge.com/</a>，裡頭有好多古董機的模擬器，PDP-11 模擬器的功能列表：<a href="http://simh.trailing-edge.com/pdp11.html">http://simh.trailing-edge.com/pdp11.html</a>。]]></description>
         <link>https://www.jollen.org/blog/2006/12/the_unix_heritage_society_unix.html</link>
         <guid>https://www.jollen.org/blog/2006/12/the_unix_heritage_society_unix.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 20 Dec 2006 16:07:40 +0800</pubDate>
      </item>
            <item>
         <title>使用 genext2fs 的 &apos;-D&apos;（device file table）來建立 root filesystem</title>
         <description><![CDATA[本文只適合「Embedded Linux / x86: 基礎能力與系統管理」的學員、對 root filesystem 建立有基本概念的朋友，或是「Embedded Linux 嵌入式系統實作演練, 2e」的讀者。

本日記介紹更方便的 nano-X 專案建立方式，以下做法使用的 shell script 並「不是」Jollen 原創，而是 Jollen 的恩師「Joe」貼心提供給學員使用的。目前 Joe 老師在大學主持嵌入式 Linux 實驗室，上過 Joe 老師課的同學仍然可以寫 email 跟他請教問題。

<strong>步驟</strong>

1. 利用 <em>gennanox </em>來產生主要的目錄（骨幹）：

# sh ./gennanox
generating fstab
generating group
generating passwd
generating inittab
generating rc.d/rc.init
generating rc.d/rc.sysinit
generating /bin/microwin

2. 得到 nanox/ 目錄：

# ls -l nanox/
總計 8
drwxr-xr-x    4 root     root         4096 12月 19 18:55 i386.so
drwxr-xr-x    2 root     root         4096 12月 19 18:55 src
# ls -l nanox/i386.so/
總計 8
drwxr-xr-x   12 root     root         4096 12月 19 18:55 install
drwxr-xr-x    2 root     root         4096 12月 19 18:55 pub

<em>nanox/install/</em> 存放我們的 root filesystem。

3. 把 package source tarball 都放到 <em>nanox/src/</em>，build 好的 binary 放到 <em>nanox/i386.so/install/</em>。

4. root filesystem 的 <em>dev/</em> 不用再手動建立檔案。有一種更方便的方法是寫一個 device txt 檔，透過 <em>genext2fs</em> 來做，把下面的內容存成 <em>dev.txt</em>：

<blockquote><pre># name    type mode uid gid major minor start inc count
/dev      d    755  0    0    -    -    -    -    -
/dev/console c 666  0    0    5    1    0    0    -
/dev/fb   c    666  0    0   29    0    0    1    1
/dev/mouse c   666  0    0   10    1    0    0    -
/dev/ram  b    666  0    0    1    0    0    1    4
/dev/tty  c    666  0    0    5    0    0    0    -
/dev/tty  c    666  0    0    4    0    0    1    2
/dev/gpmdata p 666  0    0    -    -    -    -    -
/dev/null c    666  0    0    1    3    0    0    -
/dev/zero c    666  0    0    1    5    0    0    -
/dev/hda  b    666  0    0    3    0    0    0    -
/dev/hda  b    666  0    0    3    1    1    1    9
/dev/hdc  b    666  0    0   22    0    0    0    -
/dev/hdc  b    666  0    0   22    1    1    1    9<br />
# usb mouse
#/dev/input/mice c 666 0  0   13   63    0    0    -
#/dev/mouse  c 666 0  0   13   63    0    0    -
/dev/input  d 755  0    0    -    -    -    -    -
/dev/input/mouse c 666 0  0   13   32    0    1    2
/dev/input/event c 666 0  0   13   64    0    1    4</pre></blockquote>

5. 打包 RFS 時，透過 <em>genext2fs</em> 的 '-D' 參數來建立 device file。指令如下：

<blockquote><pre>
# cd nanox/i386.so/
# genext2fs -D dev2.txt -d install/ -b 2048 pub/ext2fs
# gzip -9 pub/ext2fs
#</pre></blockquote>

把 RFS（<em>install/</em>）打包成 image file 時，<em>genext2fs</em> 會根據 <em>dev.txt</em> 的設定來建立 device file。打包好的 image file 要放到 <em>nanox/i386.so/pub/</em> 目錄下，以便能系統性的管理整個 project。

以上<em>的 dev.txt </em>同樣由 Joe 老師提供，他也把 USB mouse 的 device file 設定好了。<em>gennanox</em> 可以說是「用來產生 nano-X project 基本骨架（framework）」的 script。

Thanks Joe!]]></description>
         <link>https://www.jollen.org/blog/2006/12/genext2fs_device_file_table.html</link>
         <guid>https://www.jollen.org/blog/2006/12/genext2fs_device_file_table.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 19 Dec 2006 18:50:46 +0800</pubDate>
      </item>
            <item>
         <title>簡易的 initramfs 製作方式</title>
         <description><![CDATA[Linus 本人在 Linux 2.6 時代所提出的 "initramfs" 是一種更好的 "root=" 做法。在這裡先不對 initramfs 做詳細介紹與原由說明，不過，簡單來說，initramfs 就是「kernel 2.6 的 initrd」。Initrd（initial ramdisk）即 /dev/ram0，是一種普遍使用在 embedded Linux 的觀念；embedded Linux 利用 initrd 來載入 root filesystem（或是 bootstrap root filesystem 後再以 NFS 掛進完整 RFS）。

本日記介紹製作 Linux 2.6 的 initramfs 簡易方法。假設 RFS 的目錄為 /home/rootfs，則製作 initramfs 的指令為：

<blockquote># cd /home/rootfs<br />
# find . | cpio -o -H newc | gzip -9 >../cramfs.gz</blockquote>

更明確地說，initramfs 是 "compressed" ramfs（ram filesystem），ramfs 並不等於傳統的 ramdisk；像是以 "genext2fs" 工具所製作的 initrd（ext2 格式的 image file）才是基於 ramdisk 的傳統做法，也就是說，initramfs 並不等於傳統上的 initrd，這點要請大家多留意。
]]></description>
         <link>https://www.jollen.org/blog/2006/12/root_filesystem_initramfs.html</link>
         <guid>https://www.jollen.org/blog/2006/12/root_filesystem_initramfs.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 19 Dec 2006 01:10:53 +0800</pubDate>
      </item>
            <item>
         <title>bss section 的觀念：執行時期的結構說明</title>
         <description><![CDATA[<p>目前已經了解到：.bss section 在 linking view 時是不佔檔案長度的，在 execution view 時，根據其長度來佔用記憶體大小。</p>
<p>關於 .bss section 的結構，其實一張圖就夠了。<!--this article is copyright by www.jollen.org 2006-->直接切入重點吧！</p>
<p><b>前言</b></p>
<p>先重新編譯 <i>bss.c</i> 範例：</p>
<blockquote>
  <p># gcc -g -o bss bss.c<br>
  # ./bss<!--this article is copyright by www.jollen.org 2006--><br>
  .bss section starts at 0x8049588<br>
  foo is 0.<br>
  foo is 12345.<br>
  .bss section starts at 0x8049588</p>
</blockquote>
<p>依照先前日記的說明，.bss section 的長度為 12 bytes。無論程式是否有 uninitialized data，process 一定會有 
.bss section，並且 .bss section 的長度至少為 4 bytes（IA32），第一筆資料是 &quot;completed.1&quot;，該筆資料紀錄 .bss 
section 的起啟位址。</p>
<p>另外，在「linker script」裡，定義了一個叫 &quot;__bss_start&quot; 的符號，此符號才是紀錄 .bss section 
的真正起始位址。不過，在此先不討論這個部份。</p>
<p><b>Process 的 .bss section 結</b><!--this article is copyright by www.jollen.org 2006--><b>構</b></p>
<p>Process 的 .bss section 佔用的記憶體大小，是根據 .bss section 的長度，在執行時期為每一筆 uninitialized 
data 保留下來的。其結構如下圖所示，我們以 <i>bss.c</i> <!--this article is copyright by www.jollen.org 2006-->的範例來說明。</p>
<img alt="bss_section_structure.jpg" src="http://www.jollen.org/blog/2006/12/17/bss_section_structure.jpg" width="524" height="226" />
<p>圖 .bss section 結構</p>
<p>由圖可以知道，範例的 .bss section 其 start address 為 0x8049584，這是直接查詢 .bss section 裡的 
&quot;completed.1&quot; 符號得知的。'completed.1' 的 address 可利用 nm 查詢：</p>
<blockquote>
  <p># nm -v bss|grep 'completed.1'<br>
  08049584 b completed.1<!--this article is copyright by www.jollen.org 2006--></p>
</blockquote>
<p>由此了解，.bss section 真正的 start address 應該是 'completed.1'；不過，若把第一筆資料的 start address 當做 .bss section 的 start address 其實也無妨，或者說這是 .bss section「放 data」的 start address。</p>
<p><b>利用 gdb 來觀察</b></p>
<blockquote>
  <p># gdb ./bss<br>
  ...<br>
  (gdb) disassemble 0x8049588<br>
  Dump of assembler code for function foo:<br>
  <font color="#000080">0x08049588 &lt;foo+0&gt;: add %al,(%eax)<br>
  0x0804958a  </font><!--this article is copyright by www.jollen.org 2006-->
  <font color="#000080">&lt;foo+2&gt;: add %al,(%eax)</font><br>
  End of assembler dump.<br>
  (gdb) disassemble 0x8049584<br>
  Dump of assembler code for function completed.1:<br>
  <font color="#000080">0x08049584 &lt;completed.1+0&gt;: add %al,(%eax)<br>
  0x08049586 &lt;completed.1+2&gt;: add %al,(%eax)</font><br>
  (gdb) disassemble 0x804958c<br>
  Dump of assembler code for function bar:<br>
  <font color="#000080">0x0804958c &lt;bar+0&gt;: add %al,(%eax)<br>
  0x0804958e &lt;bar+2&gt;: add %al,(%eax)</font><br>
  End of assembler dump.</p>
</blockquote>
<table border="0" cellpadding="0" cellspacing="0" width="100%">	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr></table><table border="0" cellpadding="2" cellspacing="0" width="100%">	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr></table><table border="0" cellpadding="2" cellspacing="0" width="100%">	<tr><td width="100%">		<ul><li>2006.12.15: <a href="http://www.jollen.org/blog/2006/12/bss_section_uninitialized_data.html">.bss section 的觀念：uninitialized data section</a></li><li>
2006.12.15: <a href="http://www.jollen.org/blog/2006/12/bss_section_2.html">.bss 
section 的觀念：執行時期的長度</a></li></ul></td></tr></table>]]></description>
         <link>https://www.jollen.org/blog/2006/12/bss_section_3.html</link>
         <guid>https://www.jollen.org/blog/2006/12/bss_section_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 17 Dec 2006 22:02:26 +0800</pubDate>
      </item>
            <item>
         <title>.bss section 的觀念：執行時期的長度</title>
         <description><![CDATA[在「<a href="http://www.jollen.org/blog/2006/12/enabling_dynamic_loader_1.html#more">理解 dynamic loader 內部原理的幾個先備知識（一）</a>」講到：.bss 節區「linking view」上不佔檔案空間。這點可以用 <em>readelf </em>來做 ELF linking view 端的印證：

<blockquote><pre># readelf -e bss|more  (bss 是我們的範例執行檔)
...
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        080480f4 0000f4 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048108 000108 000020 00   A  0   0  4
  [ 3] .hash             HASH            08048128 000128 000028 04   A  4   0  4
  [ 4] .dynsym           DYNSYM          08048150 000150 000050 10   A  5   1  4
  [ 5] .dynstr           STRTAB          080481a0 0001a0 00004c 00   A  0   0  1
  [ 6] .gnu.version      VERSYM          080481ec 0001ec 00000a 02   A  4   0  2
  [ 7] .gnu.version_r    VERNEED         080481f8 0001f8 000020 00   A  5   1  4
  [ 8] .rel.dyn          REL             08048218 000218 000008 08   A  4   0  4
  [ 9] .rel.plt          REL             08048220 000220 000010 08   A  4   b  4
  [10] .init             PROGBITS        08048230 000230 000017 00  AX  0   0  4
  [11] .plt              PROGBITS        08048248 000248 000030 04  AX  0   0  4
  [12] .text             PROGBITS        08048278 000278 0001b8 00  AX  0   0  4
  [13] .fini             PROGBITS        08048430 000430 00001b 00  AX  0   0  4
  [14] .rodata           PROGBITS        0804844c 00044c 000031 00   A  0   0  4
  [15] .eh_frame         PROGBITS        08048480 000480 000004 00   A  0   0  4
  [16] .data             PROGBITS        08049484 000484 00000c 00  WA  0   0  4
  [17] .dynamic          DYNAMIC         08049490 000490 0000c8 08  WA  5   0  4
  [18] .ctors            PROGBITS        08049558 000558 000008 00  WA  0   0  4
  [19] .dtors            PROGBITS        08049560 000560 000008 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049568 000568 000004 00  WA  0   0  4
  [21] .got              PROGBITS        0804956c 00056c 000018 04  WA  0   0  4
  [22] .bss              NOBITS          08049584 <strong>000584</strong> 00000c 00  WA  0   0  4
  [23] .comment          PROGBITS        00000000 <strong>000584</strong> 000132 00      0   0  1
...</pre></blockquote>

重點的部份我用粗體字標示出來了：.bss section 與 .comment section 在檔案裡的 offset 是相同的。不過，用「他人」的工具來印可能會有一些盲點存在，比如說，我們可能不是很明白「Off」真正的意義；建議使用我們自行撰寫的 ELF 讀檔程式 loader-0.5.c（<a href="http://tw.jollen.org/elf-programming/">下載</a>）來做，因為這是自己寫的工具，能保證一些盲點都能得到證明。以下是用 loader-0.5.c 印出來的畫面：

<blockquote><pre># ./loader bss
ELF Identification
  Class:        32-bit objects
Machine:        Intel 80386
Name                Size FileOff
[00] .interp               19     244
[01] .note.ABI-tag         32     264
[02] .hash                 40     296
[03] .dynsym               80     336
[04] .dynstr               76     416
[05] .gnu.version          10     492
[06] .gnu.version_r        32     504
[07] .rel.dyn               8     536
[08] .rel.plt              16     544
[09] .init                 23     560
[10] .plt                  48     584
[11] .text                440     632
[12] .fini                 27    1072
[13] .rodata               49    1100
[14] .eh_frame              4    1152
[15] .data                 12    1156
[16] .dynamic             200    1168
[17] .ctors                 8    1368
[18] .dtors                 8    1376
[19] .jcr                   4    1384
[20] .got                  24    1388
[21] .bss                  12    1412
[22] .comment             306    1412</pre></blockquote>

了解 ELF 並自己撰寫工具，此過程讓我們了解到「Offset」指的是「確實是該 section 在檔案裡的啟始讀取位置」。這代表，無論程式裡有多少 uninitialized data，都是不佔用額外的檔案空間的。

<strong>畫面中的節區大小</strong>

「Size」代表該 section 的實體大小（in bytes），以 .bss section 來說，.bss section 的大小是 12 bytes。很不幸的是，這個大小並非表示 .bss section 佔用的「檔案大小」，而是「記憶體大小」；這可能會是一個使用工具時，因為畫面的「字義」所不小心產生的盲點。所以<strong>如果把 .bss section 的 Offset 加上他的 Size，並不會等於 .comment section 的 Offset</strong>。

所謂的「Size」，包含由 <em>objdump</em> 與 <em>readelf </em>所列印出來的畫面，或者說，「紀載在 section header entry」裡的 size 資訊，是表示「該 section 的實體記憶體大小」。

<strong>.bss section 的長度計算方式</strong>

.bss 的大小計算方式為（IA32 平臺）：

<blockquote>4 bytes + sizeof(所有的 uninitialized data)</blockquote>

這代表 .bss section 在記憶體所會佔用的長度。以<a href="http://www.jollen.org/blog/2006/12/bss_section_uninitialized_data.html#more">先前的例子</a>來說，計算式會是：

<blockquote>4 + sizeof(foo) + sizeof(bar) = 4 + 4 + 4 = 12 (bytes)</blockquote>

所以，.bss section 的「size」field 就是 12。

<strong>.bss section 的結構</strong>

.bss section 的空間結構類似於 stack，所以<a href="http://www.jollen.org/blog/2006/12/bss_section_uninitialized_data.html#more">前一則日記</a>講述的「foo 是第一個 uninitialized data，所以他的 virtual address，形同 .bss section 的開始位址（process virtual address）。」觀念，<strong>並非全然正確</strong>。

此部份留待後續再做說明。

<table border="0" cellpadding="0" cellspacing="0" width="100%">	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr></table><table border="0" cellpadding="2" cellspacing="0" width="100%">	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr></table><table border="0" cellpadding="2" cellspacing="0" width="100%">	<tr><td width="100%">		<ul><li>2006.12.14: <a href="http://www.jollen.org/blog/2006/12/enabling_dynamic_loader_1.html">理解 dynamic loader 內部原理的幾個先備知識（一）</a></li><li>2006.12.15: <a href="http://www.jollen.org/blog/2006/12/bss_section_uninitialized_data.html">.bss section 的觀念：uninitialized data section</a></li></ul></td></tr></table>]]></description>
         <link>https://www.jollen.org/blog/2006/12/bss_section_2.html</link>
         <guid>https://www.jollen.org/blog/2006/12/bss_section_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 15 Dec 2006 02:02:28 +0800</pubDate>
      </item>
            <item>
         <title>.bss section 的觀念：uninitialized data section</title>
         <description><![CDATA[.bss 節區存放「uninitialized data」<!--this article is copyright by www.jollen.org-->，由程式碼的角度來看，就是「未初始化的變數」。我們直接以一段 code 來說明，讓大家更清楚這樣的概念。

<blockquote><pre>#include &lt;stdio.h&gt;
<br />int foo;
int bar;
<br />int main(void)
{
   int *ptr;
<br />   printf(".bss section starts at %08p\n", &foo);
<br />   printf("foo is %d.\n", foo);
<br />   ptr = &foo;
   *ptr = 12345;<!--this article is copyright by www.jollen.org-->
<br />   printf("foo is %d.\n", foo);
   printf(".bss section starts at %08p\n", &foo);<!--this article is copyright by www.jollen.org-->
<br />   return 0;
}</pre></blockquote>

這段 code 相當簡單，但是隱含幾個重要的觀念，條列說明如下：

1. <em>foo</em> 是一個變數<!--this article is copyright by www.jollen.org-->，在程式碼裡沒有被初始化（uninitialized），所以程式執行時（process），<em>foo</em> 變數會被擺在「.bss section」。
2. 同理，<em>bar</em> <!--this article is copyright by www.jollen.org-->變數也是。
3. <em>foo</em> 是第一個 uninitialized data，所以他的 virtual address，形同 .bss section 的開始位址（process virtual address）。

程式要實驗的<!--this article is copyright by www.jollen.org-->項目如下：

1. 觀念 3. 的應用，我們印出 .bss section 的 start address。
2. <em>foo </em>是全域<!--this article is copyright by www.jollen.org-->變數，未初始化時的值是 0（zero）。
3. 用 '*ptr' 指向 .bss section 的 start address，此位址等於 <em>foo</em> 變數的值。
4. 把 .bss section 啟始位址處記憶體的值（value）改成 12345（透<!--this article is copyright by www.jollen.org-->過 <em>ptr </em>指標）。

<strong>沒搞錯的話，<em><!--this article is copyright by www.jollen.org-->foo</em> 變數的值就會變成 12345。</strong>

以下是執行結果：

<blockquote><pre># ./bss
.bss section starts at 0x8049588
foo is 0.
foo is 12345.
.bss section starts at 0x8049588</pre></blockquote>

很特別的一個 section，值得深入研究。]]></description>
         <link>https://www.jollen.org/blog/2006/12/bss_section_uninitialized_data.html</link>
         <guid>https://www.jollen.org/blog/2006/12/bss_section_uninitialized_data.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 15 Dec 2006 01:11:24 +0800</pubDate>
      </item>
            <item>
         <title>理解 dynamic loader 內部原理的幾個先備知識（上）：ELF 端的議題</title>
         <description><![CDATA[<p>接下來，即將啟動 ELF 格式的「execution view」介紹。本日記是進入「dynamic loader」前的<u>準備工作</u>「之一」。</p>
<p>果然是很大挑戰的研究。說明幾個注意事項如下。</p>
<p><b>必須了解 ELF 的格式觀念</b></p>
<p>「<a href="http://www.jollen.org/EmbeddedLinux/Executable_Linking_Format.html">Executable 
and Linking Format</a>」專欄已經把 ELF 格式的「linking view」做了初步的介紹，在此專欄裡，Jollen 針對 ELF 
格式的概念以 loader v0.1~v0.5 共五個範例做說明。想要了解 ELF 格式的朋友，可以參考此專欄。</p>
<p><b>Section 的用途摘要說明</b></p>
<p>每個 section 都有不同的用途，在 SysV ABI 裡所定義的特殊 section 與其用途整理如下：</p>
<table border="1" width="100%" id="table1">
	<tr>
		<td width="14%" bgcolor="#C0C0C0"><b>Section</b></td>
		<td width="84%" bgcolor="#C0C0C0"><b>用途</b></td>
	</tr>
	<tr>
		<td width="14%">.bss</td>
		<td width="84%">&nbsp;用來置放程式裡未初始化的資料（uninitialized 
		data），當程式執行時預設會將這裡的資料初始化為0（zero）。</td>
	</tr>
	<tr>
		<td width="14%">.comment</td>
		<td width="84%">&nbsp;置放版本控制資訊。</td>
	</tr>
	<tr>
		<td width="14%">.data</td>
		<td width="84%">&nbsp;置放已初始化的資料（initialized data）</td>
	</tr>
	<tr>
		<td width="14%">.data1</td>
		<td width="84%">&nbsp;置放已初始化的資料（initialized data）</td>
	</tr>
	<tr>
		<td width="14%">.debug</td>
		<td width="84%">&nbsp;置放除錯時所使用資訊。</td>
	</tr>
	<tr>
		<td width="14%">.dynamic</td>
		<td width="84%">&nbsp;置放dynamic linking（動態連結）使用資訊。</td>
	</tr>
	<tr>
		<td width="14%">.dynstr</td>
		<td width="84%">&nbsp;置放dynamic linking所需的字串。</td>
	</tr>
	<tr>
		<td width="14%">.dynsym</td>
		<td width="84%">&nbsp;置放dynamic linking symbol table（動態連結符號表)</td>
	</tr>
	<tr>
		<td width="14%">.fini</td>
		<td width="84%">&nbsp;當程式正常結束後，系統會執行此section裡的程式碼。</td>
	</tr>
	<tr>
		<td width="14%">.got</td>
		<td width="84%">&nbsp;Global Offset Table</td>
	</tr>
	<tr>
		<td width="14%">.hash</td>
		<td width="84%">&nbsp;Symbol Hash Table</td>
	</tr>
	<tr>
		<td width="14%">.init</td>
		<td width="84%">&nbsp;當程式開始執行時，系統會在進入程式進入點前（C的main() 函數）執行此section裡的程式碼。</td>
	</tr>
	<tr>
		<td width="14%">.interp</td>
		<td width="84%">&nbsp;置放program interpreter（程式直譯器）的path name（路徑名稱）。</td>
	</tr>
	<tr>
		<td width="14%">.line</td>
		<td width="84%">&nbsp;置放編譯後的機器碼與原始程式之間的對應資訊，利用gdb 的list命令可以列出object 
		file的原始程式碼，就是參考此 section的資訊。</td>
	</tr>
	<tr>
		<td width="14%">.note</td>
		<td width="84%">&nbsp;紀錄用section。</td>
	</tr>
	<tr>
		<td width="14%">.plt</td>
		<td width="84%">&nbsp;Procedure Linkage Table</td>
	</tr>
	<tr>
		<td width="14%">.relname</td>
		<td width="84%">&nbsp;置放relocation資訊。</td>
	</tr>
	<tr>
		<td width="14%">.relaname</td>
		<td width="84%">&nbsp;置放relocation資訊。</td>
	</tr>
	<tr>
		<td width="14%">.rodata</td>
		<td width="84%">&nbsp;置放read-only（唯讀）資料。</td>
	</tr>
	<tr>
		<td width="14%">.rodata1</td>
		<td width="84%">&nbsp;置放read-only（唯讀）資料。</td>
	</tr>
	<tr>
		<td width="14%">.shstrtab</td>
		<td width="84%">&nbsp;Section Name String 
		Table。置放section的名稱字串。請參考本章範例程式的說明。</td>
	</tr>
	<tr>
		<td width="14%">.strtab</td>
		<td width="84%">&nbsp;Symbol table的string table。</td>
	</tr>
	<tr>
		<td width="14%">.symtab</td>
		<td width="84%">&nbsp;Symbol Table</td>
	</tr>
	<tr>
		<td width="14%">.text</td>
		<td width="84%">&nbsp;置放“text”資料，即可執行的程式指令。</td>
	</tr>
</table>
<p>前陣子「<a href="http://blog.linux.org.tw/~jserv/">jserv</a>」兄的<u>深入淺出 Hello 
World</u>的演講把 ELF 格式「執行時期」的作業系統行為做了很不錯的「整體概念」呈現，對於執行時期 ELF object file 的概念呈現，jserv 兄的簡報會比我接下來要分享的專欄更為細膩而且連貫。</p>
<p><b>整體觀念：jserv 兄的「深入淺出 Hello World Part I/II」</b></p>
<p>很精采的 ELF 執行時期系統行為的分享。</p>
<p>雖然準備 ELF 與 loader 的專欄花了不少時間，但是還是覺得寫的不好。原因是，對於這種必須深入系統內部細節的技術討論，「整體概念」的一次呈現是相當重要的。對於 ELF 執行時期系統行為的討論，我要<u>推薦</u> jserv 兄的「<a href="http://blog.linux.org.tw/~jserv/archives/001844.html">深入淺出 Hello World</a>」系列演講；對於整體的概念呈現，jserv 的簡報做的比我的專欄還要好。</p>
<p>知識若能分享，就不必重造車輪，開放源碼分享的是知識，而不是程式碼。Jollen 自己的「ELF 執行時間」專欄，整體的呈現方式一直拿不定主意，還好，直接以 jserv 兄<u>分享</u>的簡報做為藍本，就容易多了！</p>
<p><b>需要了解 Linux 的 Memory Management 嗎？</b></p>
<p>需要懂一點概念，但不用深入 kernel code。</p>
<p>Linux 的 memory management 並不是容易撰寫的題目，這道題目的規劃走向可以是「kernel code 導向」，也可以是「概念式導向」，前者以 kernel code 來說明，後者以圖解觀念的方式進行；前者的優點是具體而且可以很細微，但是考驗作者的功力，後者的優點是概念能清楚呈現，但細膩度不足。</p>
<p>我認為，一開始選擇「概念式導向」的呈現方式會比較適當，這應該也是大家比較可以接受的方式。</p>
<p><b>.bss 節區</b></p>
<p>這是一個很特殊的節區，「linking 
view」上，他不佔檔案空間、他用來存放「未初始化的變數」。Process（程式執行時）會具備這個特殊的節區。因此，我們只討論 .bss section 
的「process virtual address」，而不討論「.bss section 所在的 file offset」。</p>
<p>之後再寫日記，以一段 code 來說明。</p>
<p><b>注記</b></p>
<ul>
<li>2006.12.29: 調整標題。（Edited by Jollen）</li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2006/12/enabling_dynamic_loader_1.html</link>
         <guid>https://www.jollen.org/blog/2006/12/enabling_dynamic_loader_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 14 Dec 2006 23:39:24 +0800</pubDate>
      </item>
            <item>
         <title>Linux 2.6.20 將正式加入 KVM 驅動程式</title>
         <description><![CDATA[方才在 slashdot 上撇見「<a href="http://linux.slashdot.org/article.pl?sid=06/12/12/0135240&from=rss">It looks like the newest version of the Linux kernel (2.6.20) will include KVM, the relatively new virtualization environment.</a>」這篇 blog，kernel 2.6.20 已正式加入了 KVM 驅動程式，這也宣告 Linux 已經正式踏入「虛擬化（virtualization）」這個熱門的伺服器技術領域了。

Linux 的 kernel-space 虛擬機器驅動程式稱為「KVM」，KVM 於今年的 10 月 19 日首次正式發佈於 linux-kernel mailing list，並且於 11 月份時被 Linus 正式加入 kernel tree（<a href="http://www.heise.de/english/newsticker/news/82344/from/rss09">根據此文</a>）。這應該會是個有趣的好東西。

<table border="0" cellpadding="0" cellspacing="0" width="100%">	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr></table><table border="0" cellpadding="2" cellspacing="0" width="100%">	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr></table><table border="0" cellpadding="2" cellspacing="0" width="100%">	<tr><td width="100%">		<ul><li>2006.10.19: <a href="http://www.jollen.org/blog/2006/10/kernel_26_kvm_kernel_virtual_m.html">Kernel 2.6 的 KVM (Kernel Virtual Machine) 驅動程式來了</a></li><li>2006.11.02: <a href="http://www.jollen.org/blog/2006/11/kvm_howto.html">KVM 驅動程式的 HOWTO</a></li></ul></td></tr></table>
]]></description>
         <link>https://www.jollen.org/blog/2006/12/kvm_driver_coming.html</link>
         <guid>https://www.jollen.org/blog/2006/12/kvm_driver_coming.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Tue, 12 Dec 2006 22:10:05 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式的 I/O, #1: 基本概念</title>
         <description><![CDATA[<p dir="ltr">由本篇日記開始，我們將進行「Linux Device Driver 入門：I/O 處理」的議題討論。這裡所提的 I/O 處理定義是：user process 與 physical device 的 I/O 存取。</p>
<p dir="ltr">在讀「Linux Device Driver 入門：I/O 處理」<!--this article is copyright by www.jollen.org-->專欄前，您必須熟悉 Linux 驅動程式的架構，因此「<a href="http://www.jollen.org/LinuxDeviceDriver/">Linux Device Driver 入門：架構層</a>」的專欄是 Jollen's Linux Device Driver 系列專欄的先備知識；此外，接下來的專欄使用的語法也必須對架構層有基本認識後才能看得懂。</p>
<p dir="ltr"><b>Linux 驅動程式 I/O 機制</b></p>
<p dir="ltr">Linux device driver 處理 I/O 的「基本款」是：</p>
<ul>
  <li dir="ltr">
  <p dir="ltr">fops-&gt;ioctl<!--this article is copyright by www.jollen.org--></li>
  <li dir="ltr">
  <p dir="ltr">fops-&gt;read</li>
  <li dir="ltr">
  <p dir="ltr">fops-&gt;write</li>
</ul>
<p dir="ltr">另外「典藏款」則是 mmap，未來在「Linux Device Driver 進階」專欄裡再來討論這個主題。</p>
<p dir="ltr"><b>fops-&gt;ioctl</b></p>
<p>ioctl 代表 input/output control 的意思，故名思義，ioctl system call 是用來控制 I/O 讀寫用的，並且是支援 user application 存取裝置的重要 system call。因此，在Linux驅動程式設計上，我們會實作ioctl system call以提供user application讀寫（input/output）裝置的功能。</p>
<p>依此觀念，回到架構篇所舉的 debug card 範例。當 user application 需要將數字顯示到 debug card 時，<!--this article is copyright by www.jollen.org-->範例 debug card 0.1.0 便需要實作 ioctl system call，然後在<i> fops-&gt;ioctl</i> 裡呼叫 <i>outb() </i>將 user application 所指定的數字輸出至 I/O port 80H。</p>
<p>User application 使用 GNU LIBC 的<i> ioctl() </i>函數呼叫device driver所提供的命令來「控制」裝置，因此驅動程式必須實作 <i>fops-&gt;ioctl</i> 以提供「命令」給使用者。 </p>
<p><b>fops-&gt;read &amp; fops-&gt;write</b></p>
<p>read/write 是 Linux 驅動程式最重要的 2 個 driver function，也是驅動程式最核心的觀念所在。對驅動程式而言，read/write 的目的是在實作並支援 user application 的 read() 與 write() 函數；user application 是否能正常由硬體讀寫資料，完全掌握在驅動程式的 read/write ethod。</p>
<p>User application 呼叫<i> read()</i>/<i>write() </i>函數後，<!--this article is copyright by www.jollen.org-->就會執行 <i>fops-&gt;read</i> 與 <i>fops-&gt;write</i>。read/write method 負責讀取使用者資料與進行裝置的I/O 存取。依照觸發資料傳輸的方式來區分，我們可以將 I/O 裝置分成以下 2 種（from hardware view）：</p>
<ul>
  <li>&nbsp;Polling：I/O裝置不具備中斷<!--this article is copyright by www.jollen.org-->。</li>
  <li>&nbsp;Interrupt：I/O裝置以中斷觸發方式進行I/O。</li>
</ul>
<p>根據I/O處理原理的不同（from software view），可以將 read/write method 的實作策略分成多種排<!--this article is copyright by www.jollen.org-->列組合來討論。為了簡化討論內容，未來的日記將鎖定「Interrupt 式的 I/O」來做探討。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/inux_device_driver_io_1.html</link>
         <guid>https://www.jollen.org/blog/2006/12/inux_device_driver_io_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Tue, 12 Dec 2006 12:02:30 +0800</pubDate>
      </item>
            <item>
         <title>LPIC-3 即將登場</title>
         <description><![CDATA[幾天前 LPI（Linux Professional Institute） <a href="http://www.jollen.org/blog/2006/12/lpi_certification_lpic.html">修改了認證規定</a>，自至再也沒有所謂的終生有效的 LPIC  證照了。前幾天 LPI 又發佈消息指出，明年一月將會開始發動 LPI 的最頂級認證「LPIC-3」。LPIC-3 的認證也籌畫好幾年了，終於在今天看到這則另人感動的消息。

由於 LPIC-3 是針對大型企業應用與 IT 專家、顧問等級的認證考試，能取得 LPIC-3 這種級數的認證，會是一種技術能力上的肯定；不過，LPIC-3 必須具備「有效」的 LPIC-1 與 LPIC-2 才能報考，所以尚未取得前二級的認證，或是證照過期的朋友，可要先做一下準備功課了。

原文報導在此：<a href="http://www.linux-watch.com/news/NS8820915301.html">http://www.linux-watch.com/news/NS8820915301.html</a>]]></description>
         <link>https://www.jollen.org/blog/2006/12/lpic3.html</link>
         <guid>https://www.jollen.org/blog/2006/12/lpic3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Mon, 11 Dec 2006 23:22:15 +0800</pubDate>
      </item>
            <item>
         <title>Linux 2.6.19 正式支援 Atmel AVR32 架構</title>
         <description><![CDATA[Linux 2.6.19 正式加入了 Atmel AVR32 architecture 的支援，這是由 Atmel 原廠所實作的 architecture-level porting。引用一段節錄至 KernelNewbies.org 的 changelog wiki 上的說明：

<blockquote>AVR32 is a new high-performance 32-bit RISC microprocessor core, designed for cost-sensitive embedded applications, with particular emphasis on low power consumption and high code density.</blockquote>

Linux for AVR32 架構的資訊可參考 <a href="http://avr32linux.org/twiki/bin/view">The AVR32 Linux project 網站</a>。AVR32 architecture 對於作業系統（operating system）的支援方面，除了必備的 MMU（Memory Management Unit）外，還有一個 MPU（Memory Protection Unit）單位。

MPU 允許 user 將記憶體空間切割成不同的「protection regions」，每個 region 的大小都是 user-defined 的，並且「starts at a user-defined address」。另外，節錄一小段 AVR32 architecture document 上對於 MPU 的說明如下：

<blockquote>The different regions can have different access privileges, cacheability attributes and bufferability attributes. The MPU does not perform any address translation.</blockquote>

因此，AVR32 架構文件提到，MPU 是簡單的 MMU，只是不做 address translation。]]></description>
         <link>https://www.jollen.org/blog/2006/12/avr32_linux.html</link>
         <guid>https://www.jollen.org/blog/2006/12/avr32_linux.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 09 Dec 2006 18:28:18 +0800</pubDate>
      </item>
            <item>
         <title>新書入荷：Embedded Linux Primer</title>
         <description><![CDATA[這本書在網路上「打書」打了很久，昨天跑去天瓏站著翻閱約一分鐘後就把它帶回來了。原因是這是一本對於 embedded Linux 的 domain issues 整理的很不錯的書，並且也具備優質原文書的特點，也就是對於概念與觀念的「文字陳述」都能不廢話的切入核心（有時看英文才能挑起一些 sense ）。這就是為什麼一定要閱讀原文書的原因。

<img alt="embedded_linux_primer.jpg" src="http://www.jollen.org/blog/2006/12/07/embedded_linux_primer.jpg" width="480" height="561" />

這本書沒有 step-by-step 的教學，也沒有類似「指令介紹」的條列說明。對於已經有「片斷實作經驗」的讀者來說，閱讀此書能快速得到「完整的領域知識」；會以「快速」來形容是因為這本書需要一些片斷的實作經驗，才能比較容易理解章節的關係與內文佈署，但是我特別喜歡這種風格的寫作，主要原因是能無痛且快速瀏覽完整的相關議題。

同時，這本書也能讓想學習 embedded Linux 的讀者清楚了解到 embedded Linux 所涉及的層面到底有多廣。]]></description>
         <link>https://www.jollen.org/blog/2006/12/got_embedded_linux_primer.html</link>
         <guid>https://www.jollen.org/blog/2006/12/got_embedded_linux_primer.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 07 Dec 2006 15:11:58 +0800</pubDate>
      </item>
            <item>
         <title>我要怎麼知道 kernel 更新了什麼東西？</title>
         <description><![CDATA[這是一個時常被問到的問題，由於 kernel 的變動快速，因此 Jollen 在日前便寫了一篇「<a href="http://www.jollen.org/blog/2006/11/reading_kernel_kernel_patch.html">讓 kernel 常在我心：探討如何與 kernel 的發展同步</a>」的日記，內容是大略介紹如何與 kernel 的發展同步（day-by-day）。

但是，如果並不是很需要每天去注意 kernel 的動態的話，只要在每個 kernel 穩定版（stable）釋出後去看 Changlog 就好了。特別是 kernel 2.6.1x 的更新項目（update）、臭蟲修正（big fix）、新的驅動程式與 filesystem 更是以可怕的「量」在 patch，特別是最近半年的 3 個 kernel 版本（2.6.17~19），變化量真是到了油門全開的狀態，所以每天看 kernel 會是沈重的負擔。

話說回來，改變是好事。期待的是，看著這麼多的改變與越來越多的新驅動程式加入，以及企業級（enterprise-class）功能的成熟，我們已經可以拿到越來越棒的「成熟」kernel 了。誠如 Linus 在 2.6.19 stable 釋出時的玩笑話「It's one of those rare 'perfect' kernels」。

言歸正傳，如果要 keep 新 kernel 做了什麼改變，或是了解「什麼功能在幾版的 kernel 才開始有」、「某些 bug 在幾版做修正」、「這個版本是否對理器架構面做修正」等，建議可以直接由 <a href="http://kernelnewbies.org">kernelnewbies.org</a> 做查詢，例如，我想知道 kernel 2.6.17 改了什麼東西，就可以輸入以下的 URL：

<a href="http://kernelnewbies.org/Linux_2_6_17">http://kernelnewbies.org/Linux_2_6_17</a>

同理，我想知道幾天前才丟出來的 kernel 2.6.19 加了什麼、改了什麼，就輸入以下的 URL：

<a href="http://kernelnewbies.org/Linux_2_6_19">http://kernelnewbies.org/Linux_2_6_19</a>

就如前面提到的，kernel 2.6.1x 的修改變化相當大，特別是在 kernel 2.6.16（大約）後，每每都有重大更新，修正範圍也「波及」到「Kernel Core」。如果工作場合與 kernel 有關係，確實有必要仔細閱讀每一個版本的 Changelog。]]></description>
         <link>https://www.jollen.org/blog/2006/12/linux_kernel_changelog_howto.html</link>
         <guid>https://www.jollen.org/blog/2006/12/linux_kernel_changelog_howto.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Wed, 06 Dec 2006 22:15:54 +0800</pubDate>
      </item>
            <item>
         <title>MIT Programming Tricks：HAKMEM</title>
         <description><![CDATA[今天在「苦牢之最後一年」的 blog 裡看到這篇：「<a href="http://blog.ijliao.info/archives/2006/12/04/2739/">開根號倒數 (InvSqrt(), 1 / sqrt(x)) 速算法</a>」，標題讓我大感興趣，原來是一篇 slashdot 上的新聞，他們在找「開根號倒數」這個利害技巧的原作者，詳細介紹可看苦牢兄的 blog。Slashdot 的新聞所提到的「找人」過程在這裡：<a href="http://www.beyond3d.com/articles/fastinvsqrt/">http://www.beyond3d.com/articles/fastinvsqrt/</a>。

仔細閱讀的話發現還相當有趣，作者是誰最後仍然不可考，被點名的高手都說不是他幹的！不過最大的收獲是知道了一份「<a href="http://home.pipeline.com/~hbaker1/hakmem/hakmem.html">HAKMEM</a>」的文件，這是在 60's 年代到 70's 年代初期，一群 MIT 的高手所記錄的程式寫作技巧（tricks）。

長久以來一直被「分層架構」與「抽象化設計」所教育著，便遺忘了這些原本才是「程式設計師」本質的數理應用，只能說 programmer 稱不上，充其量可能只是看文件寫 code 的 coding mahcine 吧。

另外，講到程式寫作技巧，就想到有趣的「解題」遊戲，又想到一位「冼鏡光」老師，冼老師的那本 C 名題精選著作是解題的必備好書。目前冼老師在 Michigan Technological University 教書，<a href="http://www.cs.mtu.edu/~shene/">老師有個人首頁</a>。]]></description>
         <link>https://www.jollen.org/blog/2006/12/mit_programming_hakmem.html</link>
         <guid>https://www.jollen.org/blog/2006/12/mit_programming_hakmem.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 06 Dec 2006 20:48:02 +0800</pubDate>
      </item>
            <item>
         <title> ELF（Executable and Linking Format）格式教學文件, #8: loader v0.5 與結果輸出（改善與小討論）</title>
         <description><![CDATA[<p>我們接續 loader v0.4 的工作，強化一下輸出結果的可讀性；先來比較一下 loader v0.4 與 loader v0.5 
的輸出畫面。</p>
<table border="1" cellpadding="0" cellspacing="5" style="border-collapse: collapse" bordercolor="#111111" width="100%">
  <tr>
    <td width="50%">$ ./loader-0.4 loader-0.4</td>
    <td width="50%">$ ./loader-0.5 loader-0.4 </td>
  </tr>
  <tr>
    <td width="50%" valign="top">
    <pre>ELF Identification
  Class:	32-bit objects
Machine:	Intel 80386
Num of secionts: 34
.interp
.note.ABI-tag
.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rel.dyn
.rel.plt
.init
.plt
.text
.fini
.rodata
.eh_frame
.data
.dynamic
.ctors
.dtors
.jcr
.got
.bss
.comment
.debug_aranges
.debug_pubnames
.debug_info
.debug_abbrev
.debug_line
.debug_frame
.debug_str
.shstrtab
.symtab
.strtab</pre>
    </td>
    <td width="50%" valign="top">
    <pre>ELF Identification
  Class:	32-bit objects
Machine:	Intel 80386
Name                Size FileOff
[01] .interp               19     244
[02] .note.ABI-tag         32     264
[03] .hash                 56     296
[04] .dynsym              144     352
[05] .dynstr               98     496
[06] .gnu.version          18     594
[07] .gnu.version_r        32     612
[08] .rel.dyn               8     644
[09] .rel.plt              48     652
[10] .init                 23     700
[11] .plt                 112     724
[12] .text               1292     836
[13] .fini                 27    2128
[14] .rodata              348    2156
[15] .eh_frame              4    2504
[16] .data                 12    2508
[17] .dynamic             200    2520
[18] .ctors                 8    2720
[19] .dtors                 8    2728
[20] .jcr                   4    2736
[21] .got                  40    2740
[22] .bss                   4    2780
[23] .comment             306    2780
[24] .debug_aranges       120    3088
[25] .debug_pubnames       37    3208
[26] .debug_info         2692    3245
[27] .debug_abbrev        312    5937
[28] .debug_line          636    6249
[29] .debug_frame          20    6888
[30] .debug_str          1722    6908
[31] .shstrtab            299    8630
[32] .symtab             1856   10292
[33] .strtab             1132   12148
</pre>
    </td>
  </tr>
</table>

<p>Cool！做出了 'objdump -x' 的部份功能。頗為有趣，那麼用 <i>objdump</i> 來互相比較一下看看。</p>
<p><b>loader v0.5 v.s. objdump v.s. readelf</b></p>
<table border="1" cellpadding="0" cellspacing="5" style="border-collapse: collapse" bordercolor="#111111" width="100%">
  <tr>
    <td width="50%">$ objdump -x loader-0.4（只擷取 section 輸出結果）</td>
    <td width="50%">$ ./loader-0.5 loader-0.4 </td>
  </tr>
  <tr>
    <td width="50%" valign="top">
    <pre>Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .interp       00000013  080480f4  080480f4  000000f4  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  08048108  08048108  00000108  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .hash         00000038  08048128  08048128  00000128  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .dynsym       00000090  08048160  08048160  00000160  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynstr       00000062  080481f0  080481f0  000001f0  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .gnu.version  00000012  08048252  08048252  00000252  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version_r 00000020  08048264  08048264  00000264  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .rel.dyn      00000008  08048284  08048284  00000284  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rel.plt      00000030  0804828c  0804828c  0000028c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .init         00000017  080482bc  080482bc  000002bc  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 10 .plt          00000070  080482d4  080482d4  000002d4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .text         0000050c  08048344  08048344  00000344  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .fini         0000001b  08048850  08048850  00000850  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .rodata       0000015c  0804886c  0804886c  0000086c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 14 .eh_frame     00000004  080489c8  080489c8  000009c8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 15 .data         0000000c  080499cc  080499cc  000009cc  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 16 .dynamic      000000c8  080499d8  080499d8  000009d8  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 17 .ctors        00000008  08049aa0  08049aa0  00000aa0  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 18 .dtors        00000008  08049aa8  08049aa8  00000aa8  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 19 .jcr          00000004  08049ab0  08049ab0  00000ab0  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 20 .got          00000028  08049ab4  08049ab4  00000ab4  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 21 .bss          00000004  08049adc  08049adc  00000adc  2**2
                  ALLOC
 22 .comment      00000132  00000000  00000000  00000adc  2**0
                  CONTENTS, READONLY
 23 .debug_aranges 00000078  00000000  00000000  00000c10  2**3
                  CONTENTS, READONLY, DEBUGGING
 24 .debug_pubnames 00000025  00000000  00000000  00000c88  2**0
                  CONTENTS, READONLY, DEBUGGING
 25 .debug_info   00000a84  00000000  00000000  00000cad  2**0
                  CONTENTS, READONLY, DEBUGGING
 26 .debug_abbrev 00000138  00000000  00000000  00001731  2**0
                  CONTENTS, READONLY, DEBUGGING
 27 .debug_line   0000027c  00000000  00000000  00001869  2**0
                  CONTENTS, READONLY, DEBUGGING
 28 .debug_frame  00000014  00000000  00000000  00001ae8  2**2
                  CONTENTS, READONLY, DEBUGGING
 29 .debug_str    000006ba  00000000  00000000  00001afc  2**0
                  CONTENTS, READONLY, DEBUGGING</pre>
    </td>
    <td width="50%" valign="top">
    <pre>ELF Identification
  Class:	32-bit objects
Machine:	Intel 80386
Name                Size FileOff
[00] .interp               19     244
[01] .note.ABI-tag         32     264
[02] .hash                 56     296
[03] .dynsym              144     352
[04] .dynstr               98     496
[05] .gnu.version          18     594
[06] .gnu.version_r        32     612
[07] .rel.dyn               8     644
[08] .rel.plt              48     652
[09] .init                 23     700
[10] .plt                 112     724
[11] .text               1292     836
[12] .fini                 27    2128
[13] .rodata              348    2156
[14] .eh_frame              4    2504
[15] .data                 12    2508
[16] .dynamic             200    2520
[17] .ctors                 8    2720
[18] .dtors                 8    2728
[19] .jcr                   4    2736
[20] .got                  40    2740
[21] .bss                   4    2780
[22] .comment             306    2780
[23] .debug_aranges       120    3088
[24] .debug_pubnames       37    3208
[25] .debug_info         2692    3245
[26] .debug_abbrev        312    5937
[27] .debug_line          636    6249
[28] .debug_frame          20    6888
[29] .debug_str          1722    6908
<font color="#FF0000">[30] .shstrtab            299    8630
[31] .symtab             1856   10292
[32] .strtab             1132   12148</font></pre>
    </td>
  </tr>
</table>

<p>不過事情好像有點怪異，我們的 loader v0.5 怎麼在最後多出 3 個 section 呢？被附身了。</p>
<p>不過，原來是 objdump 不會把最後這 3 個 section 印出來啦，這 3 個 section 分別是：</p>
<blockquote>
<p>1)<i> .shstrtab</i>：section header straing table</p>
<p>2)<i> .symtab</i>：symbol table</p>
<p>3) <i>.strtab</i>：string table</p>
</blockquote>
<p>哇塞！原來 symbol table 藏在這裡啊。但是，如果我們改用 <i>readelf </i>來讀 ELF，就可以看到完整的 ELF headers 
了，而且輸出的畫面也比較具可讀性：</p>
<blockquote>
  <p>$ readelf -S loader-0.4</p>
  <pre>There are 34 section headers, starting at offset 0x22e4:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        080480f4 0000f4 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048108 000108 000020 00   A  0   0  4
  [ 3] .hash             HASH            08048128 000128 000038 04   A  4   0  4
  [ 4] .dynsym           DYNSYM          08048160 000160 000090 10   A  5   1  4
  [ 5] .dynstr           STRTAB          080481f0 0001f0 000062 00   A  0   0  1
  [ 6] .gnu.version      VERSYM          08048252 000252 000012 02   A  4   0  2
  [ 7] .gnu.version_r    VERNEED         08048264 000264 000020 00   A  5   1  4
  [ 8] .rel.dyn          REL             08048284 000284 000008 08   A  4   0  4
  [ 9] .rel.plt          REL             0804828c 00028c 000030 08   A  4   b  4
  [10] .init             PROGBITS        080482bc 0002bc 000017 00  AX  0   0  4
  [11] .plt              PROGBITS        080482d4 0002d4 000070 04  AX  0   0  4
  [12] .text             PROGBITS        08048344 000344 00050c 00  AX  0   0  4
  [13] .fini             PROGBITS        08048850 000850 00001b 00  AX  0   0  4
  [14] .rodata           PROGBITS        0804886c 00086c 00015c 00   A  0   0  4
  [15] .eh_frame         PROGBITS        080489c8 0009c8 000004 00   A  0   0  4
  [16] .data             PROGBITS        080499cc 0009cc 00000c 00  WA  0   0  4
  [17] .dynamic          DYNAMIC         080499d8 0009d8 0000c8 08  WA  5   0  4
  [18] .ctors            PROGBITS        08049aa0 000aa0 000008 00  WA  0   0  4
  [19] .dtors            PROGBITS        08049aa8 000aa8 000008 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049ab0 000ab0 000004 00  WA  0   0  4
  [21] .got              PROGBITS        08049ab4 000ab4 000028 04  WA  0   0  4
  [22] .bss              NOBITS          08049adc 000adc 000004 00  WA  0   0  4
  [23] .comment          PROGBITS        00000000 000adc 000132 00      0   0  1
  [24] .debug_aranges    PROGBITS        00000000 000c10 000078 00      0   0  8
  [25] .debug_pubnames   PROGBITS        00000000 000c88 000025 00      0   0  1
  [26] .debug_info       PROGBITS        00000000 000cad 000a84 00      0   0  1
  [27] .debug_abbrev     PROGBITS        00000000 001731 000138 00      0   0  1
  [28] .debug_line       PROGBITS        00000000 001869 00027c 00      0   0  1
  [29] .debug_frame      PROGBITS        00000000 001ae8 000014 00      0   0  4
  [30] .debug_str        PROGBITS        00000000 001afc 0006ba 01  MS  0   0  1
  [31] .shstrtab         STRTAB          00000000 0021b6 00012b 00      0   0  1
  [32] .symtab           SYMTAB          00000000 002834 000740 10     33  54  4
  [33] .strtab           STRTAB          00000000 002f74 00046c 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)</pre>
</blockquote>
<p>原來這是工具的問題。嗯！好吧，以後都改用 <i>readelf</i> 來玩吧。</p>
<p><b>Index 0: undefined</b></p>
<p>還有一點就是，ELF headers index 0 在 SysV ABI 裡的定義是「undefined」，<i>readelf
</i>把它輸出成 NULL，我們的範例跟 <i>objdump </i>一樣，直接跳過！</p>
<p>readelf 輸出的其它 column 分述如下。</p>
<p><b>Type</b></p>
<p>「type」欄位的話就是在「<a href="http://www.jollen.org/blog/2006/12/elfexecutable_and_linking_form.html">ELF（Executable 
and Linking Format）格式教學文件, #7: 讀 ELF 的 Section Name（透過 strtab）</a>」裡介紹到的 <i>
sh_type</i>。</p>
<p><b>Addr</b></p>
<p>這個好玩，不過目前先跳過。（&gt;_&lt;）</p>
<p><b>Off、Size</b></p>
<p>跟我們的 loader v0.5 輸出結果一樣，分別代表 section 在 ELF object 裡的 file offset 
與其大小（bytes）。readelf 是用十六進位輸出，我們的範例是用十進位輸出。</p>
<p>範例程式一樣可以由「<a href="http://tw.jollen.org/elf-programming/">http://tw.jollen.org/elf-programming/</a>」下載。</p>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">

	<tr><td width="100%">
		<ul>
			<li>2006.11.19: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_1.html">ELF（Executable and Linking Format）格式教學文件, #1: ELF 簡介</a></li>

			<li>2006.11.22: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_2html.html">ELF（Executable and Linking Format）格式教學文件, #2: ELF header 與範例規劃</a></li>
			<li>2006.11.24: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_3.html">ELF（Executable and Linking Format）格式教學文件, #3: 第一個範例：loader v0.1（讀 ELF 檔頭）</a></li>

			<li>2006.11.26: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_4.html">ELF（Executable and Linking Format）格式教學文件, #4: 第一個範例：loader v0.2（ELF Identification）</a></li>
			<li>2006.11.28:
			<a href="http://www.jollen.org/blog/2006/11/elfexecutable_and_linking_form.html">
			ELF（Executable and Linking Format）格式教學文件, #5: 讀 ELF Section（說明）</a></li>
			<li>2006.12.04:
			<a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_6.html">
			ELF（Executable and Linking Format）格式教學文件, #6: 讀 ELF Section（程式列表）</a></li>
			<li>2006.12.04:
            <a href="http://www.jollen.org/blog/2006/12/elfexecutable_and_linking_form.html">
            ELF（Executable and Linking Format）格式教學文件, #7: 讀 ELF 的 Section 
            Name（透過 strtab）</a></li>
		</ul>

	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/12/_elfexecutable_and_linking_for.html</link>
         <guid>https://www.jollen.org/blog/2006/12/_elfexecutable_and_linking_for.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 06 Dec 2006 01:12:10 +0800</pubDate>
      </item>
            <item>
         <title>ELF（Executable and Linking Format）格式教學文件, #7: 讀 ELF 的 Section Name（透過 strtab）</title>
         <description><![CDATA[<p>根據 SysV ABI 的定義，若 section 的類型為 <i>SHT_STRTAB</i>，則該 section entry 即為 string 
table 的 section header。Section 的類型可由 section header 的 <i>sh_type</i> 欄位來判斷，SysV 
ABI 定義的 section 類型（<i>sh_type</i>）如下表所示。</p>
<p>表<i> sh_type</i> 欄位的定義</p>
<table border="1" width="100%" id="table17" style="border-collapse: collapse" cellspacing="3">
	<tr>
		<td width="377" bgcolor="#E6E6E6"><b>Name</b></td>
		<td bgcolor="#E6E6E6"><b>&nbsp;Value</b></td>
	</tr>
	<tr>
		<td width="377">SHT_NULL</td>
		<td>&nbsp;0</td>
	</tr>
	<tr>
		<td width="377">SHT_PROGBITS</td>
		<td>&nbsp;1</td>
	</tr>
	<tr>
		<td width="377">SHT_SYMTAB</td>
		<td>&nbsp;2</td>
	</tr>
	<tr>
		<td width="377">SHT_STRTAB</td>
		<td>&nbsp;3</td>
	</tr>
	<tr>
		<td width="377">SHT_RELA</td>
		<td>&nbsp;4</td>
	</tr>
	<tr>
		<td width="377">SHT_HASH</td>
		<td>&nbsp;5</td>
	</tr>
	<tr>
		<td width="377">SHT_DYNAMIC</td>
		<td>&nbsp;6</td>
	</tr>
	<tr>
		<td width="377">SHT_NOTE</td>
		<td>&nbsp;7</td>
	</tr>
	<tr>
		<td width="377">SHT_NOBITS</td>
		<td>&nbsp;8</td>
	</tr>
	<tr>
		<td width="377">SHT_REL</td>
		<td>&nbsp;9</td>
	</tr>
	<tr>
		<td width="377">SHT_SHLIB</td>
		<td>&nbsp;10</td>
	</tr>
	<tr>
		<td width="377">SHT_DYNSYM</td>
		<td>&nbsp;11</td>
	</tr>
	<tr>
		<td width="377">SHT_LOPROC</td>
		<td>&nbsp;0x70000000</td>
	</tr>
	<tr>
		<td width="377">SHT_HIPROC</td>
		<td>&nbsp;0x7fffffff</td>
	</tr>
	<tr>
		<td width="377">SHT_LOUSER</td>
		<td>&nbsp;0x80000000</td>
	</tr>
	<tr>
		<td width="377">SHT_HIUSER</td>
		<td>&nbsp;0xffffffff</td>
	</tr>
</table>
<p>我們在範例程式 <i>loader-0.3.c</i> 中，試著由一堆的 section 裡找出類型為 string table（<i>SHT_STRTAB</i>）的section。接下來程式 
<i>loader-0.4.c </i>將會試著實作讀取 ELF 的 string table，並將有 section 的名稱印出。</p>
<p><b>讀取 Section Name String Table</b></p>
<p>String table 是一個特殊的 section，此 section 紀錄所有 section 的名稱（ASCII 字串）。String table 
是一個字元型別的陣列，每一個 section 都會有一個索引值來索引自己的 section name 字串，<u>section header </u>的 <i>
sh_name</i> 欄位則是存放了此索引值，如圖所示。</p>
<img alt="elf_strtab.JPG" src="http://www.jollen.org/blog/2006/12/04/elf_strtab.JPG" width="485" height="333" />
<p>圖 ELF section name string table</p>
<p>接下來的範例程式將不再直接列表，請大家由 
<a href="http://tw.jollen.org/elf-programming">
http://tw.jollen.org/elf-programming</a> 下載。</p>
<p><b>程式說明</b></p>
<p>呼叫了 <em>parse_sections()</em> 函數來讀取section header table：</p>
<blockquote>
	<p>parse_sections(&amp;f_header, fd);</p>
</blockquote>
<p>相較於 <em>loader-0.3.c</em> 的 <em>parse_sections()</em> 函數，在 0.4 的版本裡，我們所做的改變如下：</p>
<p>1. 讀取 string table 的內容<br>
2. 列印所有 section 的名稱</p>
<p>首先，我們新增一個陣列來存放 string table 的內容：</p>
<blockquote>
	<p>char strtab[65535];</p>
</blockquote>
<p>接下來一樣讀取所有的 section entry，並且找出 string table：</p>
<pre>   for (i = 0; i < hdr->e_shnum; i++) {
      read(fd, &header_ent[i], sizeof(Elf32_Shdr));

      /* load section name string table */
      if (i == hdr->e_shstrndx) {
         sh_strtab = &header_ent[i];
      }
   }</pre>
<p>我們試著用另外一種方法來找出 string table 吧！根據 SysV ABI 的定義，string table 在 section header 
table 裡的 section entry index（索引值）紀錄在 ELF header 的 <i>e_shstrndx</i> 
欄位，因此，我們判斷目前的 section header table 索引值是否等於 <i>e_shstrndx</i> 來找出 string 
table。接下來再讀取string table　的內容：</p>
<pre>   /* read “String Table” */
   lseek(fd, sh_strtab->sh_offset, SEEK_SET);
   read(fd, strtab, sh_strtab-&gt;sh_size);</pre>
<p>程式裡利用 <i>lseek()</i> 函數將檔案讀寫指標移 string table 開始的地方，然後再將整個 string table 
讀出。要注意的是，section 的長度紀錄於 section header裡的 <i>sh_size</i> 
欄位。最後再逐一將每個section的名稱列印在螢幕上：</p>
<pre>   /* Index 0: undefined */
   for (i = 1; i < hdr->e_shnum; i++) {
      printf("%s\n", &strtab[header_ent[i].sh_name]);
   }</pre>
<p><b>小結</b></p>
<p>我們知道一個很重要的觀念了。ELF section 的字串名稱是由 string table 查表得知，section 名稱在 string table 
陣列裡的索引值則是紀錄在 section header 裡的 <i>sh_name</i> 欄位。</p>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
			<li>2006.11.19: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_1.html">ELF（Executable and Linking Format）格式教學文件, #1: ELF 簡介</a></li>

			<li>2006.11.22: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_2html.html">ELF（Executable and Linking Format）格式教學文件, #2: ELF header 與範例規劃</a></li>
			<li>2006.11.24: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_3.html">ELF（Executable and Linking Format）格式教學文件, #3: 第一個範例：loader v0.1（讀 ELF 檔頭）</a></li>
			<li>2006.11.26: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_4.html">ELF（Executable and Linking Format）格式教學文件, #4: 第一個範例：loader v0.2（ELF Identification）</a></li>
			<li>2006.11.28:
			<a href="http://www.jollen.org/blog/2006/11/elfexecutable_and_linking_form.html">
			ELF（Executable and Linking Format）格式教學文件, #5: 讀 ELF Section（說明）</a></li>
			<li>2006.12.04:
			<a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_6.html">
			ELF（Executable and Linking Format）格式教學文件, #6: 讀 ELF Section（程式列表）</a></li>
		</ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/12/elfexecutable_and_linking_form.html</link>
         <guid>https://www.jollen.org/blog/2006/12/elfexecutable_and_linking_form.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 04 Dec 2006 17:25:24 +0800</pubDate>
      </item>
            <item>
         <title>ELF（Executable and Linking Format）格式教學文件, #6: 讀 ELF Section（程式列表）</title>
         <description><![CDATA[<p><b>範例列表：loader v0.3</b></p>
<blockquote>
	<pre>/*
 * Copyright(c) 2003,2006 www.jollen.org
 *
 * ELF programming. ver 0.3
 *
 */
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
#include &lt;elf.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;

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");
   }
}

void parse_sections(Elf32_Ehdr *hdr, int fd)
{
   int i;
   Elf32_Shdr header[40];
   Elf32_Shdr *strtab;          /* point to string table */

   printf("Num of secionts: %d\n", hdr->e_shnum);
   /* file offset of section header table */
   lseek(fd, hdr->e_shoff, SEEK_SET);

   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];
   }
}

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);
      parse_sections(&f_header, fd);
   } else {
      printf("not a ELF binary file\n");
   }

   close(fd);
}　</pre>
</blockquote>
<p><b>執行結果</b></p>
<blockquote>
	<pre>$ ./loader-0.3 ./loader-0.3
ELF Identification
  Class:     32-bit objects
Machine:     Intel 80386
Num of secionts: 34</pre>
</blockquote>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
			<li>2006.11.19: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_1.html">ELF（Executable and Linking Format）格式教學文件, #1: ELF 簡介</a></li>

			<li>2006.11.22: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_2html.html">ELF（Executable and Linking Format）格式教學文件, #2: ELF header 與範例規劃</a></li>
			<li>2006.11.24: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_3.html">ELF（Executable and Linking Format）格式教學文件, #3: 第一個範例：loader v0.1（讀 ELF 檔頭）</a></li>
			<li>2006.11.26: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_4.html">ELF（Executable and Linking Format）格式教學文件, #4: 第一個範例：loader v0.2（ELF Identification）</a></li>
			<li>2006.11.28:
			<a href="http://www.jollen.org/blog/2006/11/elfexecutable_and_linking_form.html">
			ELF（Executable and Linking Format）格式教學文件, #5: 讀 ELF Section（說明）</a></li>
		</ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/12/executable_linking_format_elf_6.html</link>
         <guid>https://www.jollen.org/blog/2006/12/executable_linking_format_elf_6.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 04 Dec 2006 16:39:25 +0800</pubDate>
      </item>
            <item>
         <title>LPI 發佈新的 recertification 規定：未來 LPIC 證照將不再終生有效</title>
         <description><![CDATA[LPIC 證照要「重考」了，新的重新認證規章己經正式發佈了；由取得認證日起算，在二年後就要準備重新認證的程序。

LPI 會在取得認證二年後建議做重新認證（recertification）的動作，若要讓取得的 LPIC 認證有效（ACTIVE），就必須在五年內完成重新認證的工作。重新認證必須通過持有最高認證等級的所有最新考試，通過重新認證後，證書的狀態便會更新為 ACTIVE，有效期間為五年。

當取得較高等級的認證後，由取得較高等級之日起算，所有較低等級的認證狀態都會變更為 ACTIVE，期限一樣是五年。若沒有重新進行認證考試，並且讓認證失效的話，那麼就要重新取得所有認證，包含目前的最高等級與較低等級的認證。

LPI 由 2004 年 9 月 1 日開始，啟用了認證狀態資料庫，透過此資料庫能查詢證照的狀態（ACTIVE 或 INACTIVE），以往在此日期以前所取得的認證都被視為終生有效，不過在新的規章裡，LPI 將不再賦與該早期認證的終生有效權利。在 2004 年 9 月 1 日前取得認證的朋友，仍然要根據最新訂定的規章乖乖進行認證的工作。

因此，LPIC 認證將不再終生有效，原先的「終生版本」將由取得認證之日算起五年內有效。所以如果你的 LPIC 證照是在 2004 年 9 月 1 日之後取得的話，照這樣算來，就要在 2009 年 9 月 1 日前重新認證完畢；那如果是更早期取得的認證不就完蛋了嗎，很可能再過幾天就到期了啊！

不過，在 LPI 於 2006 年 12 月 1 日（美國時間）發表的新規章裡提到，在 2003 年 9 月 1 日之前取得的認證，只要在 2008 年 9 月 1 日前重新認證即可，這段期間內，證照仍視為有效（ACTIVE）。

目前已經有 LPIC 認證，或是準備要考取認證的朋友，可要留意了。「<a href="http://www.lpi.org/en/lpi/english/certification/policies">規章原文</a>」]]></description>
         <link>https://www.jollen.org/blog/2006/12/lpi_certification_lpic.html</link>
         <guid>https://www.jollen.org/blog/2006/12/lpi_certification_lpic.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Sun, 03 Dec 2006 23:09:26 +0800</pubDate>
      </item>
            <item>
         <title> System Call 專題討論, #6：unistd.h（x86）</title>
         <description><![CDATA[<p><i>unistd.h</i> 是一個重要的標頭檔，裡頭是 system call 編號的定義；另外，<i>linux/arch/i386/kernel/entry.S</i> 
則是每一個 system call 的進入點，也就是 system call table（位於 .data section）。</p>
<p><i>unistd.h</i>也定義了處理不同參數個數的 system call handler，在這個標頭檔裡可以看到處理 0~6個參數的 
handler（_syscall0~_syscall6）。例如以下是處理 1 個參數的handler：</p>
<blockquote>
	<pre>#define _syscall1(type,name,type1,arg1) \
type name(type1 arg1) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
	: "=a" (__res) \
	: "0" (__NR_##name),"b" ((long)(arg1))); \
__syscall_return(type,__res); \
}</pre>
</blockquote>
<p>type, name分別為 system call的傳回值型別與函數名稱，例如呼叫 <i>fork()</i>，則此巨集展開後會變：</p>
<blockquote>
	<pre>int fork(type 1 arg1)
{
…
}</pre>
</blockquote>
<p>　</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/system_call_6_unistd_h.html</link>
         <guid>https://www.jollen.org/blog/2006/12/system_call_6_unistd_h.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 01 Dec 2006 16:52:45 +0800</pubDate>
      </item>
            <item>
         <title> System Call 專題討論, #5：0x80 軟體中斷</title>
         <description><![CDATA[<p>System call 是透過中斷來呼叫，而在 x86 系統的架構中，32-255 是所謂的 maskable 
interrupts 即使用者定義的中斷。Linux 在 i386 上實作system call可透過以下 2 個機制：</p>
<p>˙ lcall7/lcall27（call gates，呼叫閘道）<br>
˙ int 0x80（software interrupt．軟體中斷）</p>
<p>Linux 應用程式使用 <i>int </i>指令來觸發 0x80 號軟體中斷，其它作業系統像是 Solaris 的應用程式，則是使用 lcall7。在開機時，IDT是由<i>arch/i386/kernel/traps.c:trap_init()
</i>做初始化的設定：</p>
<blockquote>
	<pre>void __init trap_init(void)
{
#ifdef CONFIG_EISA
if (isa_readl(0x0FFFD9) == 'E'+('I'&lt;&lt;8)+('S'&lt;&lt;16)+('A'&lt;&lt;24))
EISA_bus = 1;
#endif

#ifdef CONFIG_X86_LOCAL_APIC
init_apic_mappings();
#endif

set_trap_gate(0,&amp;divide_error);
set_trap_gate(1,&amp;debug);
set_intr_gate(2,&amp;nmi);
set_system_gate(3,&amp;int3); /* int3-5 can be called from all */
set_system_gate(4,&amp;overflow);
set_system_gate(5,&amp;bounds);
set_trap_gate(6,&amp;invalid_op);
set_trap_gate(7,&amp;device_not_available);
set_trap_gate(8,&amp;double_fault);
set_trap_gate(9,&amp;coprocessor_segment_overrun);
set_trap_gate(10,&amp;invalid_TSS);
set_trap_gate(11,&amp;segment_not_present);
set_trap_gate(12,&amp;stack_segment);
set_trap_gate(13,&amp;general_protection);
set_intr_gate(14,&amp;page_fault);
set_trap_gate(15,&amp;spurious_interrupt_bug);
set_trap_gate(16,&amp;coprocessor_error);
set_trap_gate(17,&amp;alignment_check);
set_trap_gate(18,&amp;machine_check);
set_trap_gate(19,&amp;simd_coprocessor_error);

set_system_gate(SYSCALL_VECTOR,&amp;system_call);

/*
* default LDT is a single-entry callgate to lcall7 for iBCS
* and a callgate to lcall27 for Solaris/x86 binaries
*/
set_call_gate(&amp;default_ldt[0],lcall7);
set_call_gate(&amp;default_ldt[4],lcall27);

/*
* Should be a barrier for any external CPU state.
*/
cpu_init();

#ifdef CONFIG_X86_VISWS_APIC
superio_init();
lithium_init();
cobalt_init();
#endif
}</pre>
</blockquote>
<p>由以上的程式碼可以看出，0x80 號中斷向量會指到 <i>system_call</i> 進入點的位址。<i>system_call</i> 位於 <i>
arch/i386/kernel/entry.S</i>：</p>
<blockquote>
	<pre>ENTRY(system_call)
	pushl %eax			# save orig_eax
	SAVE_ALL
	GET_CURRENT(%ebx)
	testb $0x02,tsk_ptrace(%ebx)	# PT_TRACESYS
	jne tracesys
	cmpl $(NR_syscalls),%eax
	jae badsys
	call *SYMBOL_NAME(sys_call_table)(,%eax,4)
	movl %eax,EAX(%esp)		# save the return value
ENTRY(ret_from_sys_call)
	cli				# need_resched and signals atomic test
	cmpl $0,need_resched(%ebx)
	jne reschedule
	cmpl $0,sigpending(%ebx)
	jne signal_return</pre>
</blockquote>
<p>0x80 號中斷裡有很多 system call service routine，每個 system call service routine 
的位址是經由 <i>sys_call_table </i>查表得知。要呼叫 system call 時，必須告訴 Linux kernel 該 system call 的編號。System call 的編號透過 <i>%eax</i> 暫存器來傳遞給 Linux kernel。</p>
<p>例如 <i>sys_open</i> 這個 system call 的編號為 5。因此當我們呼叫 <i>sys_open()</i> 時，就要指定 <i>
%eax</i> 暫存器的值為5。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/system_call_5.html</link>
         <guid>https://www.jollen.org/blog/2006/12/system_call_5.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 01 Dec 2006 16:44:32 +0800</pubDate>
      </item>
            <item>
         <title>System Call 專題討論, #4：x86 的 Interrupt</title>
         <description><![CDATA[<p>我們由之前的 hello.S 程式範例來做切入：</p>
<blockquote>
	<p>movl $len,%edx<br>
	movl $msg,%ecx<br>
	movl $1,%ebx<br>
	movl $4,%eax<br>
	int $0x80</p>
</blockquote>
<p>System call 編號 4（%eax = 4）為 <i>sys_write</i> 函數，其原型宣告如下：</p>
<p>ssize_t sys_write(unsigned int fd, const char * buf, size_t count)</p>
<p>根據 <i>sys_write</i> 的參數宣告，我們必須傳遞參數如下：</p>
<p>˙ 第 1 個參數為 <i>unsigned int fd</i>，透過 <i>%ebx</i> 暫存器傳值。<br>
˙ 第 2 個參數 <i>const char * buf </i>透過 <i>%ecx</i> 暫存器傳遞位址（address）。<br>
˙ 第 3 個參數 <i>size_t count</i> 透過 <i>%edx</i> 暫存器傳值。</p>
<p>這個部份的話嘛，大家不妨翻閱一下 Jollen 整理的 
<a href="http://www.jollen.org/EmbeddedLinux/Linux_System_Calls.html">LSCT</a>！</p>
<p><b>x86 的 Interrupt</b></p>
<p>x86的interrupt（中斷）可分別系統定義與使用者自訂：</p>
<p>˙ 中斷向量0~8、10~14、16~18：predefined interrupts and exceptions。<br>
˙ 中斷向量19-31：保留。<br>
˙ 中斷向量32-255：user-defined interrupts（maskable interrupts）。</p>
<p>每個中斷都有一個編號，稱為interrupt vector（中斷向量）；當中斷產生後，就會跳至相對應的interrupt 
handler執行程式。Interrupt handler程式的所在位址經由interrupt descriptor table（IDT）來得知。</p>
<p>Enternal interrupt、software interrupts與exception都是透過IDT來處理，IDT裡存放的是gate 
descriptor，gate descriptor的種類有：</p>
<p>˙ interrupt gate<br>
˙ trap gate<br>
˙ task gate</p>
<p>中斷的處理過程如下：</p>
<p>1. 中斷產生。</p>
<p>2. CPU根據interrupt vector來索引IDT裡的gate descriptor。</p>
<p>3. 如果 gate descriptor 是 interrupt gate，trap gate則以類似call gate方式呼叫handler 
procedure。如果是task gate，則透過task switch 來呼叫handler。</p>

<img alt="x86_gate.JPG" src="http://www.jollen.org/blog/2006/12/01/x86_gate.JPG" width="484" height="349" />

<p>Interrupt gate與task gate的主要差別在於interrupt gate會將EFLAG的IP位元清除，而task gate則不會去變更IP位元。Linux不使用task gate。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/system_call_4.html</link>
         <guid>https://www.jollen.org/blog/2006/12/system_call_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 01 Dec 2006 16:36:58 +0800</pubDate>
      </item>
            <item>
         <title>System Call 專題討論, #3：使用 Assembly 呼叫 System Call（x86）</title>
         <description><![CDATA[<p>Wrapper routine 透過 0x80 號中斷（軟體中斷）進入 kernel space，並且跳至 system call 
handler執行。由於透過軟體中斷即可達成呼叫 system call的目地，因此使用 assembly來寫程式時，我們也可以直接呼叫system call。</p>
<p>在產生軟體中斷前，我們必須告訴 Linux kernel 所要呼叫的 system call 編號，system call 的編號透過 <i>%eax</i> 
暫存器來指定；若要傳遞參數，則是透過其它暫存器來傳遞。</p>
<p><b>參數傳遞與傳回值</b></p>
<p>Linux system call最多可傳遞6個參數，參數的傳遞是透過以下的暫存器來完成：</p>
<ul>
	<li><i>%ebx</i>：第1個參數。</li>
	<li><i>%ecx</i>：第2個參數。</li>
	<li><i>%edx</i>：第3個參數。</li>
	<li><i>%esi</i>：第4個參數。</li>
	<li><i>%edi</i>：第5個參數。</li>
	<li><i>%ebp</i>：第6個參數（做臨時用途）。</li>
</ul>
<p>System call 的編號由 <i>%eax </i>暫存器指定。System call 的傳回值則是存放於 <i>%eax</i> 
暫存器。請看以下的範例程式 <i>hello.S</i>。</p>
<blockquote>
	<pre>.data

msg:        .ascii	    "Hello, World!\n"
            len = . – msg

.text

            .global _start

_start:

# sys_write
	movl	    $len,%edx
	movl	    $msg,%ecx
	movl	    $1,%ebx
	movl	    $4,%eax
	int      $0x80

# sys_exit
	movl	    	$0,%ebx
	movl	    	$1,%eax
	int      $0x80　</pre>
</blockquote>
<p>組譯與連結的方法為：</p>
<blockquote>
	<p>$ as -o hello.o hello.S<br>
	$ ld -s -o hello hello.o</p>
</blockquote>
<p>透過 assembly 程式，我們便能來初步了解 x86 上的 system call 架構。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/system_call_3_assembly_system.html</link>
         <guid>https://www.jollen.org/blog/2006/12/system_call_3_assembly_system.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 01 Dec 2006 16:30:40 +0800</pubDate>
      </item>
            <item>
         <title>System Call 專題討論, #2：使用 C 語言呼叫 System Call</title>
         <description><![CDATA[<p>在 Linux system 底下，必須透過 GLIBC 裡的 system call function 來取得 kernel 的 system call 服務。</p>
<p>由 GLIBC 提供用來呼叫 system call 的函數稱為 wrapper function，wrapper function會呼叫 Linux kernel的handler routine，即 <i>system_call()</i> 函數。</p>
<p>呼叫 <i>getpid()</i> system call 的範例：</p>
<blockquote>
	<pre>#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
   pid_t self, parent;

   self = getpid();
   parent = getppid();

   printf("PID: %d, Parent PID: %d\n", (int) self, (int) parent);
   return 0;
}</pre>
</blockquote>
<p>利用 strace 工具可以追蹤程式所使用到的 system call：</p>
<blockquote>
	<pre>$ <b>strace ./pid</b>
execve("./pid", ["./pid"], [/* 25 vars */]) = 0
uname({sys="Linux", node="jollen", ...}) = 0
brk(0)                                  = 0x80495ac
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40016000
open("/etc/ld.so.preload", O_RDONLY)    = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=114793, ...}) = 0
old_mmap(NULL, 114793, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000
close(3)                                = 0
open("/lib/tls/libc.so.6", O_RDONLY)    = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0`V\1B4\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1531064, ...}) = 0
old_mmap(0x42000000, 1257224, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x42000000
old_mmap(0x4212e000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x12e000) = 0x4212e000
old_mmap(0x42131000, 7944, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x42131000
close(3)                                = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0x400169e0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
munmap(0x40017000, 114793)              = 0
getpid()                                = 970
getppid()                               = 969
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40017000
write(1, "PID: 970, Parent PID: 969\n", 26) = 26
munmap(0x40017000, 4096)                = 0
exit_group(0)                           = ?
</pre>
</blockquote>
<p>由 strace 的輸出結果可以得知程式所呼叫的 system call，在這裡我們可以看到 <i>getpid()</i> 與 <i>getppid()</i>，並且也能得知 <i>printf()</i> 是基於 <i>write()</i> system call。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/system_call_2.html</link>
         <guid>https://www.jollen.org/blog/2006/12/system_call_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 01 Dec 2006 16:24:50 +0800</pubDate>
      </item>
            <item>
         <title>System Call 專題討論, #1：什麼是System Call</title>
         <description><![CDATA[<p>System call 是 process 與作業系統之間的介面。System call 是由 Linux kernel 
所實作並提供給使用者，user-space program 可透過 system call 與Linux kernel 溝通。以 C 語言來呼叫 system call 的話，則是透過 GLIBC（libc）來間接呼叫。</p>

<p>GLIBC 提供呼叫 system call 的介面稱為 wrapper routine，wrapper routine 會叫起 Linux 的 
system call handler，最後再由 system call handler 找到service routine 的所在的位址，並交由 
service routine 完成工作。整個流程如圖所示。</p>

<img alt="system_call_1.JPG" src="http://www.jollen.org/blog/2006/12/01/system_call_1.JPG" width="476" height="231" />

<p>User program 與 wrapper routine 是 user-space 的 code，system call handler 與 
service routine 則是屬於 kernel space。如圖，我們可以看到灰色的箭頭跨越一個鴻溝，代表著由 user space 進入 kernel space，在 i386 上這個動作是藉由中斷來達成。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/12/system_call_1.html</link>
         <guid>https://www.jollen.org/blog/2006/12/system_call_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 01 Dec 2006 16:10:04 +0800</pubDate>
      </item>
            <item>
         <title>TimeSys 十二月份的 Webinar</title>
         <description><![CDATA[TimeSys 是一家「供應現成 RFS」的服務公司，他們每個月都會有幾場 Webinars 的活動，本月份（１２月份）的 Webinars 一共三場：

1. Linux Boot-Up：了解由 bootloader 開機到 user-space 模式，並執行 user program 的過程。

2. Hello World, from an Embedded Perspective：怎麼把 "Hello World" 做 cross compilation 後放到 RFS。

3. Survey of Linux Filesystems：介紹 embedded system 使用的 filesystem，以及建立 RFS 的方法。

這三個主題都是給初學者的議題，有興趣的朋友一定要撥空上網參加喔！參加 TimeSys 的 Webinars 必須事前註冊，有興趣的朋友可至 TimeSys 的<a href="http://www.timesys.com/webinars/index.htm">註冊頁面</a>登記。

在註冊頁面有每一場 webinar 的時間與大綱。]]></description>
         <link>https://www.jollen.org/blog/2006/12/timesys_webinar.html</link>
         <guid>https://www.jollen.org/blog/2006/12/timesys_webinar.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Fri, 01 Dec 2006 15:48:37 +0800</pubDate>
      </item>
            <item>
         <title>PReP / CHRP / OpenFirmware</title>
         <description><![CDATA[最近有些朋友在問 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」。

<a href="http://www.openfirmware.org/">OpenFirmware 只是一個規格</a>，目前有 open source 的 OpenFirmware 專案，也就是知名的 <a href="http://www.openbios.info/Welcome_to_OpenBIOS">OpenBIOS</a>。當然也有商業性質的 OpenFirmware 實作，不過既然 IBM 也貢獻了 FCODE 給 OpenBIOS，那麼我想未來 OpenBIOS 絕對是首選的 OpenFirmware 實作了。

最後要說明的是，PReP 與 CHRP 都不是目前 Power Architecture 的 reference design 規格了。早先 Jollen 在「<a href="http://www.jollen.org/blog/2006/09/vga_video_graphics_array.html">RISC 嵌入式平臺 (PowerPC) 的 VGA 解決方案</a>」的日記裡也大略提到過 OpenBIOS 與 OpenFirmware（FCODE suite），大家也可以參考一下。

新一代的 Power Architecture 規格叫做 PAPR，這是由 Power.org 組織所制定的新規格，主要是針對 Power 架構的 workstation 與 server 所制定的（no PC...），目前的最新版本是 v2.0。]]></description>
         <link>https://www.jollen.org/blog/2006/11/prep_chrp_openfirmware.html</link>
         <guid>https://www.jollen.org/blog/2006/11/prep_chrp_openfirmware.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">U-Boot , ARM9 and Pre-OS 相關</category>
        
        
         <pubDate>Wed, 29 Nov 2006 15:57:57 +0800</pubDate>
      </item>
            <item>
         <title>Debian ARM 爆起：思考社群發展模式的關鍵二個因素</title>
         <description><![CDATA[Debian ARM 已成為 Debian 數十種平臺的 distribution 中的第三大。由 Debian 的  <a href="http://popcon.debian.org/">popularity-contest</a> 統計資料來看，Debian ARM 已經超越 PowerPC 成為第三大的 Debian 架構分支。詳細報導<a href="http://linuxdevices.com/news/NS3535328630.html">在此</a>。

讓 Debian ARM 成為第三熱門的 Debian 版本的功臣就是今年大大有名的 NSLU2 計畫，如果您還不曉得 NSLU2 的名號，可以到<a href="http://www.nslu2-linux.org/">NSLU2 的官方網站</a>瞧瞧。當初 NSLU2 計畫發起時，便立下這樣的志願（節錄自 LinuxDevices.com 的報導）：

<blockquote>to have the Linksys NSLU2 become a fully supported mainstream Linux distribution device.</blockquote>

這個目標達成了！Debian ARM 從第七名竄升到第三名，只花了九個月的時間；並且，有 90% 的 Debian ARM 使用者都是將 Debian ARM 安裝在 Linksys 的 NSLU2 device 上（節錄自 LinuxDevices.com 的報導）：

<blockquote>Ninety percent of those ARM installations are NSLU2 devices.</blockquote>

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 上的報導裡已經講出重點了：

<blockquote>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.</blockquote>

可見閉門造車並不是好事，因為除了要不停的「重造車輪」外，也無法讓不同的社群響應你的計畫，或是跟你互補。

]]></description>
         <link>https://www.jollen.org/blog/2006/11/debian_arm_nslu2_community.html</link>
         <guid>https://www.jollen.org/blog/2006/11/debian_arm_nslu2_community.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Wed, 29 Nov 2006 09:18:13 +0800</pubDate>
      </item>
            <item>
         <title>ELF（Executable and Linking Format）格式教學文件, #5: 讀 ELF Section（說明）</title>
         <description><![CDATA[<p>今天的內容「讀取 ELF section」是這系列 ELF 文章的重點戲。因為我們終於進入 ELF 的核心議題「節區的觀念」了。在 loader v0.5 以前的範例，都是屬於靜態的討論（linking view）；在 loader v0.6 開始的討論中，我們將會開始提到動態的執行行為（execution view）。</p>
<p><b>如何讀取 ELF Section</b></p>
<p>我們分 2 個步驟來讀取 ELF 的 section 資訊：</p>
<p>1. 如圖一，一開始先讀取 section header table 的資訊。section header table 是所有 section 的紀錄表格，所有的 section 都要透過 section header table 才能得知其在檔案中的偏移位置（offset），如此一來才能讀取 section 的內容。</p>
<p>2. 如圖二，接著再根據 section header table 讀取檔案裡的每一個 section。</p>
<p>section header table 裡的 section 個數（section entries）紀錄於 ELF header 裡的 <i>e_shnum</i> 欄位，每個 section entry 的長度則是紀錄在 ELF header 的 <i>e_shentsize</i> 欄位，單位是bytes。</p>

<p><img alt="elf_sht.jpg" src="http://www.jollen.org/blog/2006/11/28/elf_sht.jpg" width="455" height="410" /><br />
圖一：Section Header Table</p>

<p>ELF header 裡的 <i>e_shoff</i> 欄位，是 section header table 開始的檔案偏移位置 （file offset）。因此，我們只要由 <i>e_shoff</i> 偏移值開始讀取 <i>e_shnum</i> 個單位，每個單位為 <i>e_shentsize</i>（bytes），即可將整個 section 
header table 讀取出來。</p>

<p><img alt="elf_section_entry.jpg" src="http://www.jollen.org/blog/2006/11/28/elf_section_entry.jpg" width="455" height="410" /><br />
圖二：Section Entries</p>

<p>SysV ABI 定義 section entry 的資料結構如下：</p>
<blockquote>
	<pre>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;</pre>
</blockquote>
<p>Section header table 是 <i>Elf32_Shdr</i> data type 的陣列，其元素個數為 <i>e_shnum</i>，我們可以透過索引 <i>Elf32_Shdr</i> 陣列來讀取所有的 section，如圖二所示。</p>
<p>Section header（Elf32_Shdr）的欄位用途說明如下表。</p>
<table border="1" width="100%" id="table1" cellspacing="3" cellpadding="0" style="border-collapse: collapse">
	<tr>
		<td bgcolor="#E7E7E7"><b>Field</b></td>
		<td bgcolor="#E7E7E7"><b>Description</b></td>
	</tr>
	<tr>
		<td>sh_name</td>
		<td>&nbsp;section的名稱，需由string table查表取得。</td>
	</tr>
	<tr>
		<td>sh_type</td>
		<td>&nbsp;section的類型。</td>
	</tr>
	<tr>
		<td>sh_flags</td>
		<td>&nbsp;section的屬性。</td>
	</tr>
	<tr>
		<td>sh_addr</td>
		<td>&nbsp;section在記憶體裡的起始位址，但並非所有的section都會被載入至記憶體。</td>
	</tr>
	<tr>
		<td>sh_offset</td>
		<td>&nbsp;section在objct file裡的開始偏移值（offset），程式必須根據此欄位來讀取section的本文。</td>
	</tr>
	<tr>
		<td>sh_size</td>
		<td>&nbsp;section的長度（bytes）。</td>
	</tr>
	<tr>
		<td>sh_link</td>
		<td>&nbsp;可用來存放section header table的index link。</td>
	</tr>
	<tr>
		<td>sh_info</td>
		<td>&nbsp;可用來存放section的額外資訊。</td>
	</tr>
	<tr>
		<td>sh_addralign</td>
		<td>&nbsp;紀錄section的address alignment，例如有些section的 alignment為DWORD（dobuleword）。</td>
	</tr>
	<tr>
		<td>sh_entsize</td>
		<td>&nbsp;有些section的內容為entry長度固定的table，例如symbol 
		table。此欄用來紀錄entry的長度，單位是bytes。</td>
	</tr>
</table>
<p><b>範例程式：loader v0.3</b></p>
<p>接著說明 loader v0.2 需要改寫的功能。</p>
<p>在主程式新增一個 <i>parse_sections()</i> 函數來讀取 ELF object file 的 section header 
table：</p>
<blockquote>
	<p>parse_sections(&amp;f_header, fd);</p>
</blockquote>
<p><i>parse_sections()</i> 負責做2件工作：</p>
<p>1. 讀取 Section Header Table</p>
<p>2. 找出 Section Name String Table</p>
<p>同時，在主程式也要加入以下 2 個變數：</p>
<blockquote>
	<p>Elf32_Shdr header[40];<br>
	Elf32_Shdr *strtab; /* point to string table */</p>
</blockquote>
<p><i>header[] </i>用來存放由 section header table 所讀取出來的所有 section entry，其型別為 <i>Elf32_Shdr</i>；<i>strtab</i> 指標用來指向 section name string table，其型別為 <i>Elf32_Shdr</i>。Section name string table 是一個特殊的 section，下一個範例再來處理這個 section。</p>
<p>將檔案讀寫指標移到 <i>e_shoff</i> 的地方，準備開始讀取 section header table：</p>
<blockquote>
	<p>lseek(fd, hdr-&gt;e_shoff, SEEK_SET);</p>
</blockquote>
<p>然後再利用最簡單的方式，一次一個將所有的section entry讀取出來，並且判斷該section entry是否為string table：</p>
<pre>   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];
   }</pre>
<p>最後，section 用途是根據他的類型來區分的，這個部份留待下次再做說明。</p>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
			<li>2006.11.19: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_1.html">ELF（Executable and Linking Format）格式教學文件, #1: ELF 簡介</a></li>
			<li>2006.11.22: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_2html.html">ELF（Executable and Linking Format）格式教學文件, #2: ELF header 與範例規劃</a></li>
			<li>2006.11.24: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_3.html">ELF（Executable and Linking Format）格式教學文件, #3: 第一個範例：loader v0.1（讀 ELF 檔頭）</a></li>
			<li>2006.11.26: <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_4.html">ELF（Executable and Linking Format）格式教學文件, #4: 第一個範例：loader v0.2（ELF Identification）</a></li>
		</ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/11/elfexecutable_and_linking_form.html</link>
         <guid>https://www.jollen.org/blog/2006/11/elfexecutable_and_linking_form.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Tue, 28 Nov 2006 17:14:49 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 與王建民</title>
         <description><![CDATA[椅子坐太久除了手痛、腰酸與腳麻外，頭腦也會變的極度渾沌。所以在計畫讓屁股離開椅子前，就來看了一下「王建民」。有趣的是，我找了一下「王建民」跟「embedded linux」在 Google 的搜尋量比較，結果還挺有趣的。先來看一下「embedded linux」跟「wince」、「embedded system」關鍵字的搜尋關係：

<img src="http://www2.jollen.org/images/dot1.gif" border="0" alt="" /><font color="#4684ee"> embedded linux</font>&nbsp;&nbsp;&nbsp;<img src="http://www2.jollen.org/images/dot2.gif" border="0" alt="" /><font color="#dc3912"> wince</font>&nbsp;&nbsp;&nbsp;<img src="http://www2.jollen.org/images/dot3.gif" border="0" alt="" /><font color="#ff9900"> embedded system</font>&nbsp;&nbsp;&nbsp;</b></font></p>
<img alt="wang_40_1.png" src="http://www.jollen.org/blog/2006/11/28/wang_40_1.png" width="580" height="260" />

結果 embedded linux 跟 embedded system 的關係相當密切，幾乎是貼在一起走的。至於跟 wince 做比較的話，則是保持一定的差距，看起來跟幾年前相差不遠。

再來是「embedded linux」PK「王建民」：

<p><font size="-1"><b><img src="http://www2.jollen.org/images/dot1.gif" border="0" alt="" /><font color="#4684ee"> embedded linux </font><img src="http://www2.jollen.org/images/dot2.gif" border="0"alt="" /><font color="#dc3912">王建民</font>&nbsp;&nbsp;&nbsp;</b></font></p>
<img alt="wang_40_2.png" src="http://www.jollen.org/blog/2006/11/28/wang_40_2.png" width="580" height="260" />

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 找「王建民」，會發現右半部很「台灣味」：

<img alt="wang_40_3.PNG" src="http://www.jollen.org/blog/2006/11/28/wang_40_3.PNG" width="582" height="422" />

王建民，加油！





]]></description>
         <link>https://www.jollen.org/blog/2006/11/embedded_linux_3.html</link>
         <guid>https://www.jollen.org/blog/2006/11/embedded_linux_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Tue, 28 Nov 2006 14:00:27 +0800</pubDate>
      </item>
            <item>
         <title>ELF（Executable and Linking Format）格式教學文件, #4: 第一個範例：loader v0.2（ELF Identification）</title>
         <description><![CDATA[<p><b>ELF Identification</b></p>
<p dir="ltr">ELF header 資料結構裡的 <i>e_ident </i>欄位用來判斷檔案是否為ELF格式的欄位，<i>e_ident</i> 
欄位是一個陣列資料結構，其長度由 SysV ABI所定義的常數 EI_NIDENT指定。</p>
<p dir="ltr"><i>EI_NIDENT</i> 的值為 16 代表 e_ident 欄<!--this article is copyright by and from www.jollen.org-->位有 16 個元素，SysV ABI 定義了 8 個常數來索引<i> e_ident</i> 
陣列的元素。</p>
<p dir="ltr">表 <i>e_ident[] </i>索引值定義</p>

<table border="1" cellpadding="0" style="border-collapse: collapse" bordercolor="#111111" width="100%" cellspacing="3">
  <tr>
    <td bgcolor="#E4E4E4"><b>Name</b></td>
    <td bgcolor="#E4E4E4"><b>&nbsp;Value</b></td>
    <td bgcolor="#E4E4E4"><b>&nbsp;說明</b></td>
  </tr>
  <tr>
    <td>EI_MAG0</td>
    <td>&nbsp;0</td>
    <td>&nbsp;ELF 識別字元</td>
  </tr>
  <tr>
    <td>EI_MAG1</td>
    <td>&nbsp;1</td>
    <td>&nbsp;ELF 識別字元<!--this article is copyright by and from www.jollen.org--></td>
  </tr>
  <tr>
    <td>EI_MAG2</td>
    <td>&nbsp;2</td>
    <td>&nbsp;ELF 識別字元</td>
  </tr>
  <tr>
    <td>EI_MAG3</td>
    <td>&nbsp;3</td>
    <td>&nbsp;ELF 識別字元</td>
  </tr>
  <tr>
    <td>EI_CLASS</td>
    <td>&nbsp;4</td>
    <td>&nbsp;檔案類別 (class)</td>
  </tr>
  <tr>
    <td>EI_DATA</td>
    <td>&nbsp;5</td>
    <td>&nbsp;資料編碼方式 (Data encoding)</td>
  </tr>
  <tr>
    <td>EI_VERSION</td>
    <td>&nbsp;6</td>
    <td>&nbsp;檔案版本</td>
  </tr>
  <tr>
    <td>EI_PAD</td>
    <td>&nbsp;7</td>
    <td>&nbsp;padding bytes 的開頭</td>
  </tr>
</table>

<p dir="ltr">loader-0.2.c主要的改變在於加入了判斷檔案是否為 ELF 格式的程式碼。要判斷檔案是否為 ELF 格式，必須根據<i> e_ident[]</i> 裡的識別字元來做判斷：</p>

<ul>
  <li dir="ltr">

<p dir="ltr"><i>e_ident[EI_MAG0]</i> 必須等於 ELFMAG0</p>

  </li>
  <li dir="ltr">

<p dir="ltr"><i>e_ident[EI_MAG1] </i>必須等於 ELFMAG1</p>

  </li>
  <li dir="ltr">

<p dir="ltr"><i>e_ident[EI_MAG2]</i> 必須等於 ELFMAG2</p>

  </li>
  <li dir="ltr">

<p dir="ltr"><i>e_ident[EI_MAG3] </i>必須等於 ELFMAG3</p>

  </li>
</ul>

<p dir="ltr">表 ELF識別字元（magic number）定義</p>

<table border="1" cellpadding="0" cellspacing="3" style="border-collapse: collapse" bordercolor="#111111" width="100%">
  <tr>
    <td bgcolor="#E4E4E4">

<p dir="ltr"><b>Name</b></p>

    </td>
    <td bgcolor="#E4E4E4">

<p dir="ltr"><b>&nbsp;Value</b></p>

    </td>
    <td bgcolor="#E4E4E4">

<p dir="ltr"><b>&nbsp;說明</b></p>

    </td>
  </tr>
  <tr>
    <td>

<p dir="ltr">ELFMAG0</p>

    </td>
    <td>

<p dir="ltr">&nbsp;0x7f</p>

    </td>
    <td>

<p dir="ltr">&nbsp;e_ident[EI_MAG0] 之值</p>

    </td>
  </tr>
  <tr>
    <td>

<p dir="ltr">ELFMAG1</p>

    </td>
    <td>

<p dir="ltr">&nbsp;‘E’</p>

    </td>
    <td>

<p dir="ltr">&nbsp;e_ident[EI_MAG1] 之值</p>

    </td>
  </tr>
  <tr>
    <td>

<p dir="ltr">ELFMAG2</p>

    </td>
    <td>

<p dir="ltr">&nbsp;‘L’</p>

    </td>
    <td>

<p dir="ltr">&nbsp;e_ident[EI_MAG2] 之值</p>

    </td>
  </tr>
  <tr>
    <td>

<p dir="ltr">ELFMAG3</p>

    </td>
    <td>

<p dir="ltr">&nbsp;‘F’<!--this article is copyright by and from www.jollen.org--></p>

    </td>
    <td>

<p dir="ltr">&nbsp;e_ident[EI_MAG3] 之值</p>

    </td>
  </tr>
</table>

<p dir="ltr"><b>判斷是否為 ELF 格式</b></p>

<p dir="ltr">設計一個 elf_ident() 函數來判斷檔案是否為 ELF 格式，其程式碼如下：</p>

<blockquote>

<pre><i>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;
}</i></pre>

</blockquote>

<p dir="ltr">e_ident[] 裡還存放許多 ELF 的資訊。以下再舉一例，我們新增一個函數 <i>parse_ident()</i> 來分析 
<i>e_ident[] </i>的 &quot;CLASS&quot; 資訊：</p>

<blockquote>

<pre><i>void parse_ident(char *ident)
{
   printf("ELF Identification\n");

   printf("  Class:	");
   switch (*(ident+EI_CLASS)) </i><!--this article is copyright by and from www.jollen.org--><i>{
      case ELFCLASSNONE: printf("Invalid class\n");
             break;
      case ELFCLASS32: printf("32-bit objects\n");
            break;
      case ELFCLASS64: printf("64-bit objects\n");
            break;
   }
}</i></pre>

</blockquote>

<p dir="ltr"><b>程式列表：loader-0.2.c</b></p>

<blockquote>

<pre>/*
 * Copyright(c) 2003,2006 www.jollen.org
 *
 * ELF programming. ver 0.2
 *
 */
#include <stdio.h>
#include <unistd.h>
#include <elf.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

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;
   }<!--this article is copyright by and from www.jollen.org-->

   /* 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");
   }
}</pre>

</blockquote>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FF5959"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td>
	</tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li>2006.11.19:&nbsp;<a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_1.html">ELF（Executable and Linking Format）格式教學文件, #1: ELF 簡介</a></li>
			<li>2006.11.22:
            <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_2html.html">ELF（Executable and Linking Format）格式教學文件, #2: ELF header 與範例規劃</a></li>
			<li>2006.11.24:
            <a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_3.html">
            ELF（Executable and Linking Format）格式教學文件, #3: 第一個範例：loader v0.1（讀 
            ELF 檔頭）</a></li>
		</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/11/executable_linking_format_elf_4.html</link>
         <guid>https://www.jollen.org/blog/2006/11/executable_linking_format_elf_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Sun, 26 Nov 2006 12:27:02 +0800</pubDate>
      </item>
            <item>
         <title>ELF（Executable and Linking Format）格式教學文件, #3: 第一個範例：loader v0.1（讀 ELF 檔頭）</title>
         <description><![CDATA[<p><b>ELF 的第一個</b><!--This article is copyright by www.jollen.org (c) 2006--><b>範例：loader v0.1</b></p>
<p>ELF header 儲存執行檔的重要資訊，我們必須先知道如何讀取 ELF 檔案的檔頭資訊（header），才能處理個別節區（section）。在 GNU/Linux 
系統下，程式可以直接引用 <i>elf.h</i> 標頭檔。完整的程式範例<!--This article is copyright by www.jollen.org (c) 2006-->
			<i>loader-0.1.c</i> 列表如下。</p>
<blockquote>
	<pre>/*
 * Copyright (C) 2003 www.jollen.org
 *
 * ELF programming. <!--This article is copyright by www.jollen.org (c) 2006-->ver 0.1
 *
 */
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
#include &lt;elf.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
<!--This article is copyright by www.jollen.org (c) 2006-->
void <b>parse_machine</b>(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, <!--This article is copyright by www.jollen.org (c) 2006-->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;
   }<!--This article is copyright by www.jollen.org (c) 2006-->

   /* Read ELF Header */
   read(fd, &f_header, sizeof(Elf32_Ehdr));

   /* Parse header information */
   parse_machine(f_header.e_machine);
}</pre>
</blockquote>

<p><!--This article is copyright by www.jollen.org (c) 2006-->
			<b>範例說明</b></p>
<p>程式 <i>loader-0.1.c</i> 首先宣告變數 <i>f_header</i> 用存放所讀取的檔頭資料：</p>
<blockquote>
	<p><i>Elf32_Ehdr f_header;</i></p>
</blockquote>
<p><i>Elf32_Ehdr</i> 的宣告在 <i>elf.h</i> 裡，這個 data type 是 SysV ABI 裡的 ELF object 
files 檔頭的標準資料結構。讀者的方式如同傳統的 C 語言一般，只要利用低階 I/O 函數將檔頭的部份讀出即可：</p>
<blockquote>
	<p><i>read(fd, &amp;f_header, </i><!--This article is copyright by www.jollen.org (c) 2006-->
			<i>sizeof(Elf32_Ehdr));</i></p>
</blockquote>
<p>目前我們<!--This article is copyright by www.jollen.org (c) 2006-->只讀取 ELF header 的部份。ELF header 裡存放許多重要的 object file 資訊，其中一項為 <i>e_machine</i> 
成員，因此程式接著來判斷此欄位的內容，並且列印出 object file 所支援的硬體平臺名稱。</p>
<p>在 <i>parse_machine() </i>裡頭，我們判斷 <i>e_machine</i> 欄位的值，並且列印出相對應的硬體平臺名稱，例如：若 
<i>e_mahcine</i> 的值為 <i>EM_386</i>，則印出 &quot;Intel 80386&quot; 字串。<i>EM_386</i> 
與其它相關的常數都定義在 SysV ABI 標準裡<!--This article is copyright by www.jollen.org (c) 2006-->，可在 <i>elf.h</i> 裡看到。這部份就留給大家當功課了。</p>
<p><b>執行</b><!--This article is copyright by www.jollen.org (c) 2006--><b>結果</b></p>
<p>先將上面的程式碼 copy 下來存成 <i>loader-0.1.c</i> 後編譯成執行檔：</p>
<blockquote>
	<p><i>$ gcc -o loader-0.1 loader-0.1.c</i></p>
</blockquote>
<p>接著再用<i> loader-0.1</i> 來分<!--This article is copyright by www.jollen.org (c) 2006-->析 ELF object file：</p>
<blockquote>
	<pre><i># ./loader-0.1 /bin/vi
<b>Machine:        Intel 80386</b></i></pre>
</blockquote>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FF5959"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td>
	</tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li>2006.11.19:&nbsp;<a href="/blog/2006/11/executable_linking_format_elf_1.html">ELF（Executable and Linking Format）格式教學文件, #1: ELF 簡介</a></li>
			<li>2006.11.22:
			<a href="/blog/2006/11/executable_linking_format_elf_2html.html">
			ELF（Executable and Linking Format）格式教學文件, #2: ELF header 與範例規劃</a></li>
		</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/11/executable_linking_format_elf_3.html</link>
         <guid>https://www.jollen.org/blog/2006/11/executable_linking_format_elf_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Fri, 24 Nov 2006 00:06:02 +0800</pubDate>
      </item>
            <item>
         <title>我用來建立 Linux System Calls&apos; Table 的程式</title>
         <description><![CDATA[以 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 說明。下載檔案 [<a href="http://www.jollen.org/EmbeddedLinux/lsct_gen-0.1.tar.bz2">lsct_gen-0.1.tar.bz2</a>（10 KBytes）]。另外，這個 script 也會把 system call 所在的檔案印出來。

這個版本只適合用來處理 2.6 系列的程式碼，無法處理 2.4 系列的 kernel source code。]]></description>
         <link>https://www.jollen.org/blog/2006/11/lsct_gen.html</link>
         <guid>https://www.jollen.org/blog/2006/11/lsct_gen.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Thu, 23 Nov 2006 19:12:24 +0800</pubDate>
      </item>
            <item>
         <title>ELF（Executable and Linking Format）格式教學文件, #2: ELF header 與範例規劃</title>
         <description><![CDATA[<p>本文接續之前的日記「ELF（Executable and Linking Format）格式教學文件, #1: ELF 簡介」，在了解 ELF 的用途後，再來我們先由「ELF Header」的部<!--This article is copyright by www.jollen.org (c) 2006-->份開始看起，我們的目標是寫一個可以將 ELF 執行檔裡所有 
section 讀取出來的程式。未來將繼續朝 user-space 的 ELF 處理前進。</p>
<p>如果您是 Jollen 
的讀者，或許曾經在書上讀到這個章節，<!--This article is copyright by www.jollen.org (c) 2006-->本系統的內容與書上內容大致相仿，但仍有一些小差異，建議您可再讀一次。我希望能以其它的出版形式，將這些內容都與大家分享。</p>
<h3>ELF header：<!--This article is copyright by www.jollen.org (c) 2006-->檔頭格式</h3>
<p>ELF 於 SysV ABI 標準中定義，其中 ELF header 的結構如下表：</p>
<table border="1" width="100%" id="table1">
	<tr>
		<td bgcolor="#CCCCCC"><b>Field</b></td>
		<td width="689" bgcolor="#CCCCCC"><b>Description</b></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_ident</span></i></td>
		<td width="689">
		<p class="-"><span style="font-family: 華康中明體\(P\)">用來辨別檔案是否為</span><span lang="EN-US">ELF</span><span style="font-family: 華康中明體\(P\)">，並包含一些</span><span lang="EN-US">machine 
		independent </span><span style="font-family: 華康中明體\(P\)">的資料。</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_type</span></i></td>
		<td width="689">
		<p class="-"><span style="font-family: 華康中明體\(P\)">檔案的類型</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_machine</span></i></td>
		<td width="689">
		<p class="-"><span style="font-family: 華康中明體\(P\)">檔案的</span><!--This article is copyright by www.jollen.org (c) 2006--><span style="font-family: 華康中明體\(P\)">平臺</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_version</span></i></td>
		<td width="689">
		<p class="-"><span style="font-family: 華康中明體\(P\)">版本資訊</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_entry</span></i></td>
		<td width="689">
		<p class="-"><span style="font-family: 華康中明體\(P\)">程式的起始位址（</span><span lang="EN-US">process 
		virtual address</span><span style="font-family: 華康中明體\(P\)">）</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_phoff</span></i></td>
		<td width="689">
		<p class="-"><span lang="EN-US">program header table</span><span style="font-family: 華康中明體\(P\)">的檔案偏移值（</span><span lang="EN-US">offset</span><span style="font-family: 華康中明體\(P\)">），單位是</span><span lang="EN-US">bytes</span><span style="font-family: 華康中明體\(P\)">。如果沒有</span><span lang="EN-US">program 
		header table</span><span style="font-family: 華康中明體\(P\)">則此值為</span><span lang="EN-US">0</span><span style="font-family: 華康中明體\(P\)">。</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_shoff</span></i></td>
		<td width="689">
		<p class="-"><span lang="EN-US">section header table</span><span style="font-family: 華康中明體\(P\)">的檔案偏移值（</span><span lang="EN-US">offset</span><span style="font-family: 華康中明體\(P\)">），單位是</span><span lang="EN-US">bytes</span><span style="font-family: 華康中明體\(P\)">。如果沒有</span><span lang="EN-US">section 
		header table</span><span style="font-family: 華康中明體\(P\)">則此值為</span><span lang="EN-US">0</span><span style="font-family: 華康中明體\(P\)">。</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_flags</span></i><!--This article is copyright by www.jollen.org (c) 2006--></td>
		<td width="689">
		<p class="-"><span style="font-family: 華康中明體\(P\)">與</span><span lang="EN-US">processor</span><span style="font-family: 華康中明體\(P\)">有關的旗標值</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_ehsize</span></i></td>
		<td width="689">
		<p class="-"><span lang="EN-US">ELF header</span><span style="font-family: 華康中明體\(P\)">的長度，單位是</span><span lang="EN-US">bytes</span><span style="font-family: 華康中明體\(P\)">。</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_phentsize</span></i></td>
		<td width="689">
		<p class="-"><span lang="EN-US">program header table</span><span style="font-family: 華康中明體\(P\)">每個</span><span lang="EN-US">entry</span><span style="font-family: 華康中明體\(P\)">的長度（</span><span lang="EN-US">bytes</span><span style="font-family: 華康中明體\(P\)">），每個</span><span lang="EN-US">entry</span><span style="font-family: 華康中明體\(P\)">的長度都相等。</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_phnum</span></i></td>
		<td width="689">
		<p class="-"><span lang="EN-US">program header table</span><span style="font-family: 華康中明體\(P\)">的</span><span lang="EN-US">entry</span><span style="font-family: 華康中明體\(P\)">個數，若無</span><span lang="EN-US">program 
		header table </span><span style="font-family: 華康中明體\(P\)">則此欄的值為</span><span lang="EN-US">0</span><span style="font-family: 華康中明體\(P\)">。</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_shentsize</span></i></td>
		<td width="689">
		<p class="-"><span lang="EN-US">section header table</span><span style="font-family: 華康中明體\(P\)">每個</span><span lang="EN-US">entry</span><span style="font-family: 華康中明體\(P\)">的長度（</span><span lang="EN-US">bytes</span><span style="font-family: 華康中明體\(P\)">），每個</span><span lang="EN-US">entry</span><span style="font-family: 華康中明體\(P\)">的長度都相等。</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_shnum</span></i></td>
		<td width="689">
		<p class="-"><span lang="EN-US">section header table</span><span style="font-family: 華康中明體\(P\)">的</span><span lang="EN-US">entry</span><span style="font-family: 華康中明體\(P\)">個數，若</span><!--This article is copyright by www.jollen.org (c) 2006--><span style="font-family: 華康中明體\(P\)">無</span><span lang="EN-US">program 
		header table</span><span style="font-family: 華康中明體\(P\)">則此欄的值為</span><span lang="EN-US">0</span><span style="font-family: 華康中明體\(P\)">。</span></td>
	</tr>
	<tr>
		<td>
		<p class="-"><i><span lang="EN-US">e_shstrndx</span></i></td>
		<td width="689">
		<p class="-"><span lang="EN-US">section header table</span><span style="font-family: 華康中明體\(P\)">的</span><span lang="EN-US">index</span><span style="font-family: 華康中明體\(P\)">值，索引至</span><span lang="EN-US">section 
		name string table entry</span><span style="font-family: 華康中明體\(P\)">。如果檔案沒有</span><span lang="EN-US">section 
		name string table</span><span style="font-family: 華康中明體\(P\)">，則此欄的值為</span><i><span lang="EN-US">SHN_UNDEF</span></i><span style="font-family: 華康中明體\(P\)">。</span></td>
	</tr>
</table>
<p>ELF 檔案格式最基本的就是它的檔頭（header）部份，ELF header 儲存 object file 的各種資訊。ELF header 
的讀取方式非常簡單，我們將會實作讀取 ELF header 的程式，以強化我們所讀到的觀念。</p>
<p>ELF header 的資料結構<!--This article is copyright by www.jollen.org (c) 2006-->定義在 <i>elf.h</i> 裡，如下：</p>
<blockquote>
	<pre><i>/* 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 </i><!--This article is copyright by www.jollen.org (c) 2006--><i>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;</i></pre>
</blockquote>
<p>Section 與 section header table 的讀取與處理方式，會在介紹完 ELF header 後再做說明。</p>
<h3>ELF 
<!--This article is copyright by www.jollen.org (c) 2006-->
的範例程式</h3>
<p>本系列日記共有 5 個範例程式，其功能差異整理如下表。</p>
<table border="1" width="100%" id="table17">
	<tr>
		<td bgcolor="#CCCCCC">　</td>
		<td bgcolor="#CCCCCC">&nbsp;讀取檔頭</td>
		<td bgcolor="#CCCCCC">&nbsp;分析檔頭</td>
		<td bgcolor="#CCCCCC">&nbsp;判斷ELF</td>
		<td bgcolor="#CCCCCC">&nbsp;處理Section</td>
		<td bgcolor="#CCCCCC">&nbsp;讀取Strtab</td>
		<td bgcolor="#CCCCCC">&nbsp;列印節區名稱</td>
	</tr>
	<tr>
		<td><i>loader-0.1.c</i></td>
		<td>&nbsp;●<!--This article is copyright by www.jollen.org (c) 2006--></td>
		<td>&nbsp;●</td>
		<td>&nbsp;　</td>
		<td>&nbsp;　</td>
		<td>&nbsp;　</td>
		<td>&nbsp;　</td>
	</tr>
	<tr>
		<td><i>loader-0.2.c</i></td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;　</td>
		<td>&nbsp;　</td>
		<td>&nbsp;　</td>
	</tr>
	<tr>
		<td><i>loader-0.3.c</i></td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;　</td>
		<td>&nbsp;　</td>
	</tr>
	<tr>
		<td><i>loader-0.4.c</i></td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
	</tr>
	<tr>
		<td><i>loader-0.5.c</i></td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
		<td>&nbsp;●<!--This article is copyright by www.jollen.org (c) 2006--></td>
		<td>&nbsp;●</td>
		<td>&nbsp;●</td>
	</tr>
</table>
<p>為了能了解ELF格式，我們將會實作 6 大項功能如下：</p>
<blockquote>
	<p>1. 讀取檔頭：讀取 ELF 檔案的檔頭資訊。</p>
	<p>2. 分析檔頭：分析讀取的檔頭資<!--This article is copyright by www.jollen.org (c) 2006-->訊，例如分析 ELF 執行檔的編碼平臺。</p>
	<p>3. 判斷 ELF：判斷所讀取的檔案是否為標準 ELF 格式的檔案。</p>
	<p>4. 處理 Section：<!--This article is copyright by www.jollen.org (c) 2006-->可以讀取所有 section 的資訊，並做簡單處理，例如找出是 string table 的 section。</p>
	<p>5. 讀取 <i>StrTab</i>：讀取 section name string table 裡的資訊。</p>
	<p>6. 列印節區名稱：處理 section name string table，可以根據 section name string table 來列印所有 
section的名稱（ASCII string）。</p>
</blockquote>
<p>我們將會以漸進式的<!--This article is copyright by www.jollen.org (c) 2006-->方式來慢慢完成所有的功能，不同版本範例程式間的主要差異將會特別做說明，並詳細解釋新功能的程式實作。</p>
<p>另外，<i>loader-0.4.c</i> 與 <i>loader-0.5.c </i>看似功能相同，但 <i>loader-0.5.c</i> 
主要是為了討論<i> 
loader-0.4.c</i> 裡幾個奇怪的地方。我們會在講解<i>loader-0.5.c</i> 程式時再做更詳細的說明。</p>
<p>Loader 在載入 object file 前，必須先由 ELF header 取得 object file 的資訊並且判斷 object file 是否為 ELF 格式的執行檔，然後才能將 object file 載至記憶體。</p>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FF5959"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<!--This article is copyright by www.jollen.org (c) 2006-->
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td>
	</tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul><!--This article is copyright by www.jollen.org (c) 2006-->
			<li>2006.11.19:&nbsp;
			<a href="http://www.jollen.org/blog/2006/11/executable_linking_format_elf_1.html">
			ELF（Executable and Linking Format）格式教學文件, #1: ELF 簡介</a></li>
		</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/11/executable_linking_format_elf_2html.html</link>
         <guid>https://www.jollen.org/blog/2006/11/executable_linking_format_elf_2html.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 22 Nov 2006 23:39:19 +0800</pubDate>
      </item>
            <item>
         <title>讓 kernel 常在我心：探討如何與 kernel 的發展同步</title>
         <description><![CDATA[<p>從事 Embedded Linux（GNU/Linux systems on 
devices）工作的朋友，除了日常的讀書功課外，另外一個重要的工作就是「隨時注意 Linux kernel 的發展狀況」。要能與 Linux kernel 
的發展同步，嚴格來說，已經是一件吃重的工作了，不過還是可以列出幾個基本的工作原則如下：</p>
<blockquote>
	<p>1. 每天閱讀 linux-kernel 郵遞論壇（mailing-list）的「標題」。</p>
	<p>2. 隨時上 kernel.org 看看最新釋出的 kernel 版本（或留意 linux-kernel-announce mailing-list）。</p>
	<p>3. 閱讀釋出版本的 Changelog。</p>
	<p>4. 不要與 git 系統的距離太遠，定時看看 git 系統，保持一定的「短距離」。</p>
</blockquote>
<p><b>Mailing List</b></p>
<p>linux-kernel 上的 posts 每天的量大約在 200~350 
篇左右，數量並不算少，要把每一篇都看過是不太可能的，因此以我自己的閱讀心態來說，我會建議以下的閱讀方式：</p>
<blockquote>
	<p>1. 看標題，如果是自己有興趣或正在留意的更新，我就會標記下來持續追蹤。</p>
	<p>2. 如果有 Bugfix 的 patch 出現，我會看看這項修正的起因與原理，但為了不讓自己花費太多時間，如果我對這項 patch 
的修正「原理」不甚熟悉，我便會跳過此 post。</p>
</blockquote>
<p>以下是閱讀 linux-kernel list 必須知道的幾件事：</p>
<p>1. 如果有新的修正，第一時間都會發佈在此 list 上，並且標題的起頭一定是 &quot;<b>[PATCH <i>n</i>/<i>m</i>] <i>
subject</i></b>&quot; 這樣的格式。<u>PATCH</u> 表示這是一個 patch 的發佈，由於一個 patch 會以多篇 post 
發佈，因此就用「n/m」來表示「這是第幾篇 patch，總共有幾篇。」，例如：</p>
<blockquote>
	<p>[PATCH 0/7] KVM: Kernel-based Virtual Machine<br>
	[PATCH 1/7] KVM: userspace interface<br>
	[PATCH 2/7] KVM: Intel virtual mode extensions definitions<br>
	[PATCH 3/7] KVM: kvm data structures<br>
	[PATCH 5/7] KVM: mmu virtualization<br>
	[PATCH 6/7] KVM: x86 emulator<br>
	[PATCH 7/7] KVM: plumbing</p>
</blockquote>
<p>2. 不能在這裡詢問與 kernel 
發展無關的問題，例如：工具的使用、系統設定、詢問是否有XXX驅動程式、請求協助測試等等，這些都是不能張貼的文章。另外，原本就該留意的非成文禮節一定要注意，像是 
FAQ 能找到的東西，就不要去麻煩人家。</p>
<p>3. list 裡大部份都是 device driver 
的討論，並且很多都是架構面或觀念面的討論與修正建議，所以當您參與討論時，千萬不要用「個人的主觀看法，或是沒有事實與理論根據」的角度發表意見；由於「觀念」的修正是 
kernel 2.6 驅動程式的大討論方向，所以必須先把主題相關的東西先看懂看熟後再參與討論。</p>
<p><b>kernel.org</b></p>
<p>我會不定時來看看最新發佈的 kernel 版本，因此我們必須知道目前仍在維護的 kernel 版本與其分支狀況，大致說明如下：</p>
<blockquote>
	<p>1. 2.6 與 2.4 都是目前仍持續積極維護中的版本。以本文寫作的時間為例（2006/11/20），目前最新的版本分別是 <u>
	2.6.18.3</u>（2006/11/19 釋出）與 <u>2.4.33.4</u>（2006/11/19 釋出）。</p>
	<p>2. 2.4 的釋出版本有「stable」與「prepatch」二個分支。</p>
	<p>3. 2.6 的釋出版本有「stable」、「prepatch」、「snapshot」與「-mm patch」四個分支。</p>
</blockquote>
<p>其中 '-mm' 系列的 2.6 kernel 是由 Andrew Morton 所釋出的 Linux 分支，主要性質以「實驗」與「新功能」為主
，所以常常可以在這個分支的 kernel 裡找到不久前才剛發佈在 mailing-list 上的 patch。如果您不太知道怎麼處理 mailing-list 
上的 patch 發佈，也可以等 -mm patch 的發佈。</p>
<p>另外，prepatch 就是所謂的 '-rc' 發佈，所以檔名會接 '-rc?' 字串，例如：linux-2.6.19-rc6。</p>
<p><b>Changlog</b></p>
<p>每個在 kernel.org 上所釋出的 kernel 都會有一份變更紀錄（change logs）的檔案，可以了解這個版本與分支的釋出做了什麼變更。</p>
<p>上到 kernel.org 後，每個釋出版本的後面，會有 4 個選項（視版本不同）：</p>
<ul>
	<li><b>F</b> = full source</li>
	<li><b>V</b> = view patch </li>
	<li><b>VI</b> = view incremental</li>
	<li><b>C</b> = current changesets</li>
</ul>
<p>把「C」給按下去就會進到所謂的「git 系統」。</p>
<p><b>git 系統</b></p>
<p>git 是 kernel 用來維護發展中版本的系統，也就是所謂的「snapshot 」版本，snapshot 版本都是當天新鮮送達的 
kernel，釋出時會在後面加上「-git?」字串。</p>
<p>我會不定時點「C」到 kernel 的 git 去看看 kernel 的最新（最近）動態，說不定有我正要找的 patch。git 系統裡也能看到 
kernel 的 &quot;commit&quot; 訊息，包含 commit 的作者、時間與差異比較（diff），而且都可以很方便地瀏覽。</p>
<p><b>結語</b></p>
<p>隨時與 kernel 的發展同步，有幾個動機：</p>
<blockquote>
	<p>1. 學習，kernel developers 會討論與多觀念面的議題，很好的學習機會。</p>
	<p>2. 新的 kernel 加入了哪些新驅動程式、修正掉哪些臭蟲與增強了哪些驅動程式的功能？</p>
	<p>3. 留意我所使用的平臺（architecture）是否有重要的修正（eg. for i386, for ARM...etc）。</p>
	<p>4. 如果新 kernel 有加入重要的 feature 或 Bugfix，我會把自己玩耍的 code patch 到新 kernel。</p>
</blockquote>
<p>測試這些有趣的新版本，建議使用 <a href="http://www.jollen.org/blog/2006/09/qemu.html">QEMU</a> 
或是 <a href="http://user-mode-linux.sourceforge.net/">User-Mode Linux</a> 來進行。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/11/reading_kernel_kernel_patch.html</link>
         <guid>https://www.jollen.org/blog/2006/11/reading_kernel_kernel_patch.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Mon, 20 Nov 2006 19:53:07 +0800</pubDate>
      </item>
            <item>
         <title>ELF（Executable and Linking Format）格式教學文件, #1: ELF 簡介</title>
         <description><![CDATA[ELF（Executable and Linking Format）是 object file 的檔案格式，其主要結構是以 section（節區）為主，我們可以利用 GNU binutils 套件的 objdump 工具來列<!--this article is copyrighted by and from www.jollen.org-->出執行檔的 section 與其內容。例如，我想把 ls 命令的 ELF section 列印出來：

<blockquote><pre># 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
...</pre></blockquote>

如果要印出指定 section 的內容，例如 .data section：

<blockquote><pre># objdump -j .data -s /bin/ls
<br />/bin/ls:     file format elf32-i386
<br />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                           ....</pre></blockquote>

Object file 主要有 3 種類別：

1. relocatable file
2. executable file<!--this article is copyrighted by and from www.jollen.org-->
3. shared object file

relocatable file 即副檔<!--this article is copyrighted by and from www.jollen.org-->名為 .o 的檔案、executable file 為一般的執行檔、shared object file 則是 *.so（shared libraries）檔案。ELF 是 object file 的檔案格式，其檔案格式如下圖。

<img alt="jollen_blog-ELF_View.jpg" src="http://www.jollen.org/blog/2006/11/19/jollen_blog-ELF_View.jpg" width="455" height="410" />
圖：ELF View（Execution View v.s. Linking View）

Linking view 指的是經由 assembler 或 linkage editor 編譯過，可被 CPU 執行的檔案格式，也就是儲存在儲存裝置上的程式格式（stored <!--this article is copyrighted by and from www.jollen.org-->programs）。Execution view 指的是由 loader 載入後，程式執行時的格式，也就是存在於記憶體上的程<!--this article is copyrighted by and from www.jollen.org-->式格式（process）。

當載入器（loader）將程式<!--this article is copyrighted by and from www.jollen.org-->載入記憶體後，object file 就會是 execution view 的格式。了解 ELF 格式與學會如何讀取 ELF 內容，對於深入研究 Linux 內部的程式實作相當有幫助。例如，<!--this article is copyrighted by and from www.jollen.org-->未來我們將會討論的 sys_init_module 實作，便需要對「如何讀取 ELF object」有一定程度的了解。
]]></description>
         <link>https://www.jollen.org/blog/2006/11/executable_linking_format_elf_1.html</link>
         <guid>https://www.jollen.org/blog/2006/11/executable_linking_format_elf_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Sun, 19 Nov 2006 22:12:09 +0800</pubDate>
      </item>
            <item>
         <title>MontaVista 推出 Dev Rock 5 嵌入式 Linux 開發工具之《殺手級 IDE 快快出現！》</title>
         <description><![CDATA[Embedded Linux 的開發工具一日千里，主意不錯而且發展速度快，顯現的是未來「bring Linux to devices」的工作將更快速（快但是不見得輕鬆），這也將會構成 embedded Linux 將<!--this article is copyrighted by and came from  www.jollen.org-->來市佔率（使用 Linux 做為 device OS 解決方案的比率）能大幅並快速提升的要素之一：「快速平臺與軟硬解決方案」 （rapid prototyping plat<!--this article is copyrighted by and came from  www.jollen.org-->form and SW/HW solutions）。

由此可大膽推測，embedded Linux 後期的生態環境正在快速形成當中，或許再過 2~3 年，就會有殺手<!--this article is copyrighted by and came from  www.jollen.org-->級的 embedded Linux 的開發工具（IDE）出現。倘若如此，developer 現在一個很重要的工作就是僅快去學習研究 embedded Linux 的基礎原理；未來才能善用工具，而不是只會用工具。<!--this article is copyrighted by and came from  www.jollen.org-->

embedded Linux 的開發工具逐漸成形了，會這麼說是<!--this article is copyrighted by and came from  www.jollen.org-->因為今天看到一則新聞：

「<a href="http://www.linuxdevices.com/news/NS4278425311.html">MontaVista launches Dev Rocket 5 beta</a>」

試想一個情境：怎麼<!--this article is copyrighted by and came from  www.jollen.org-->讓「自己寫好的 code」透過 IDE 環境「咚！」一聲就被整合到 target device 的 image 檔（root filesysetm）裡？（像是 TimeSys 的服務，很快，但仍要自己「整」一堆東西到他提供的 RFS<!--this article is copyrighted by and came from  www.jollen.org--> 裡。）

推敲一下未來可能的 killer application 身影。

首先引用報導裡關於 Dev Rocket 5 的說明如下：

<blockquote>MontaVista Software has invited current customers to join an open beta program for its n<!--this article is copyrighted by and came from  www.jollen.org-->ext-generation Eclipse-based embedded Linux development toolset.</blockquote>

很好，這是一個 Eclipse-based 的 IDE（如果您不知道 Eclipse 的重要性，網路上有很多前輩發表許多關於他的看法，非常精采）。

根據報導說明，我的想法是，Dev Rocket 能支援「MontaVista Linux Edition Management」，但是如果能<!--this article is copyrighted by and came from  www.jollen.org-->把這個地方做成是比較萬用（一般化）的一個 feature，那麼會是比較有彈性而且有殺氣的。要產生 root filesystem image 檔，就要有一個好用的 Linux distribution "build" 工具；目前為特定 target device 建立 Linux distribution 的一套重量級工具是 OpenEmbedded。

Open<!--this article is copyrighted by and came from  www.jollen.org-->Embedded 是一個舉足輕重的工具，他能方便地建立 embedded Linux distribution。例如之前講到的 Neo1973 Linux mobile phone 就<!--this article is copyrighted by and came from  www.jollen.org-->用到 OpenEmbedded。現在好多人在玩的 Linksys NSLU-2 也都是 OpenEmbedded 的產物。

Dev Rocket 5 <!--this article is copyrighted by and came from  www.jollen.org-->也支援「One-click Debugging」與「Platform Image Creation and Configuration」的功能；Dev Rocket 5 也有 plug-ins 的功能，能整入像是「UML modeling」這樣的模組進來。

MontaVista 對 embedded Linux 的貢獻良多，未來若是能將這些 IDE open 出來，發散一陣子再<!--this article is copyrighted by and came from  www.jollen.org-->收斂起來，這所謂的 killer application 已經不遠了<!--this article is copyrighted by and came from  www.jollen.org-->。

<table id="table11" border="0" cellpadding="0" cellspacing="0" width="100%"><tr><td bgcolor="#3366cc"><img alt="" height="1" width="1"></td></tr></table><table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td bgcolor="#e5ecf9"><font size="-1"><b>Also See</b></font></td></tr></table><table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td width="100%"><ul><li>2006.11.08: <a href="http://www.jollen.org/blog/2006/11/mobile_20_openmoko_linux_smart.html">「Mobile 2.0 的思考」與第一隻採用 OpenMoko 的 Linux Smartphone</a> - 使用 OpenEmbeded 的 &quot;open&quot; 
Linux smartphone。</li><!--this article is copyrighted by and came from  www.jollen.org--><li>2006.09.22: <a href="http://www.jollen.org/blog/2006/09/embedded_linux.html">
Embedded Linux 系統性的教學看法</a> - 提到 OpenEmbedded（使用 bitbake 系統）。</li></ul></td></tr></table>]]></description>
         <link>https://www.jollen.org/blog/2006/11/montavista_dev_rock_5_linux_id.html</link>
         <guid>https://www.jollen.org/blog/2006/11/montavista_dev_rock_5_linux_id.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Thu, 16 Nov 2006 23:08:38 +0800</pubDate>
      </item>
            <item>
         <title>深入淺出 insmod, #1</title>
         <description><![CDATA[<!--p>深入淺出 insmod, #1</p-->
<h4>作者／陳俊宏<br>
http://www.jollen.org</h4>
<!--This article is copyright by www.jollen.org (c) 2006--><p>這篇文章的目的是為了解釋 Linux 驅動程式課程中，經常被詢問的部份，我把這個地方以紅色來標示：</p>
<blockquote>
	<pre><i>struct file_operations {
	<b><font color="#FF0000">struct module *owner;</font></b>
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, struct dentry *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
	ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};</i></pre>
</blockquote>
<p>是的，就是 <i>*owner </i>這個 field 常被問起。<i>*owner</i> 的用途倒底是什麼？（以下以 kernel 2.4.x 為主做說明）</p>
<h3><b>insmod 基本觀念</b></h3>
<p>我用流程的方式來說明。不過在這之前，先請第 128 號系統呼叫 <i>sys_init_module</i> 出場。<i>sys_init_module</i> 
system call 的主要用途是載入 kernel module，以下是他的原型宣告<!--This article is copyright by www.jollen.org (c) 2006-->：</p>
<blockquote>
	<p><i>long sys_init_module(const char *name_user, struct module <b>*mod_user</b>);</i></p>
</blockquote>
<p>在這裡我們看到第 2 個參數 <i>*mod_user</i> 就是此行的重點 <i>struct module</i>。另外，<i>sys_init_module
</i>系統呼叫透過 
glibc 的 <i>init_module()</i> system call 來叫用。以下是 INIT_MODULE(2)：</p>
<blockquote>
	<pre>NAME
       init_module - initialize a loadable module entry

SYNOPSIS
       #include &lt;linux/module.h&gt;

       int init_module(const char *name, struct module *image);

DESCRIPTION
       init_module loads the relocated module image into kernel space and runs
       the module's init function.

       The module image begins with a module structure and is followed by code
       and data as appropriate.  The module structure is defined as follows:</pre>
</blockquote>
<p>另外，INIT_MODULE(2) 提到「 This system call is only open to the superuser.」，因此 
<i>insmod</i> 只能以 root 身份執行。<i>insmod</i> 的原始碼包含在 <i>modutils</i> 套件裡，<i>insmod 
</i>實作呼叫 <i>sys_init_module</i> 
system call 的程式位於 <i>util/sys_nim.c</i>，函數名稱為 <i>sys_init_module</i>，程式碼內容簡單易懂，<!--This article is copyright by www.jollen.org (c) 2006-->節錄如下：</p>
<blockquote>
	<pre><i>#ifndef CONFIG_USE_SYSCALL

extern int init_module(const char *name, const struct module *info);

int
sys_init_module(const char *name, const <b>struct module *info</b>)
{
  return init_module(name, <b>info</b>);
}

#else

#define __NR_sys_init_module  __NR_init_module
_syscall2(int, sys_init_module, const char *, name,
          const struct module *, info)

#endif</i></pre>
</blockquote>
<p>請了解這裡的<i> sys_init_module</i> 是一個 <i>modutils</i> 自行實作的函數，用來提供給 <i>insmod</i> 
指令呼叫，與 kernel 的 <i>sys_init_module</i> system call 是二個不同的東西。insmod 指令的原始碼位於 
modutils 套件裡的 <i>insmod/insmod.c</i>，我們已經了解到，<i>insmod.c</i> 會呼叫 <i>util/sys_nim.c:sys_init_module()</i> 
來載入 kernel module。</p><!--This article is copyright by www.jollen.org (c) 2006-->
<p><i>insmod </i>的程式碼中，載入 kernel module 的工作主要是由<i> init_module()</i> 來執行，我們透過<i> 
init_module() </i>的原始碼來說明幾件事件：</p>
<blockquote>
	<pre>static int init_module(const char *m_name, <font color="#000080"><b>struct obj_file *f</b></font>,
		       unsigned long m_size, const char *blob_name,
		       unsigned int noload, unsigned int flag_load_map)
{
	<b><font color="#000080">struct module *module;</font></b>
	struct obj_section *sec;
	void *image;
	int ret = 0;
	tgt_long m_addr;

	sec = obj_find_section(f, ".this");
	<font color="#000080"><b>module = (struct module *) sec->contents;</b></font>
	m_addr = sec->header.sh_addr;

<b><font color="#000080">	module->size_of_struct = sizeof(*module);
	module->size = m_size;
	module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0;</font></b>

	sec = obj_find_section(f, "__ksymtab");
	if (sec && sec->header.sh_size) {
		module->syms = sec->header.sh_addr;
		module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p);
	}
	if (n_ext_modules_used) {
		sec = obj_find_section(f, ".kmodtab");
		module->deps = sec->header.sh_addr;
		module->ndeps = n_ext_modules_used;
	}
	<font color="#000080"><b>module->init =</b></font> obj_symbol_final_value(f, obj_find_symbol(f, "init_module"));
	<font color="#000080"><b>module->cleanup =</b></font> obj_symbol_final_value(f,
		obj_find_symbol(f, "cleanup_module"));

	<b><font color="#FF0000">sec = obj_find_section(f, "__ex_table");</font></b>
	if (sec) {
		<font color="#000080"><b>module->ex_table_start = </b></font>sec->header.sh_addr;
		<font color="#000080"><b>module->ex_table_end =</b></font> sec->header.sh_addr + sec->header.sh_size;
	}
	<font color="#FF0000"><b>sec = obj_find_section(f, ".text.init");</b></font>
	if (sec) {
		module->runsize = sec->header.sh_addr - m_addr;
	}
	<font color="#FF0000"><b>sec = obj_find_section(f, ".data.init");</b></font>
	if (sec) {
		if (!module->runsize ||
		    module->runsize > sec->header.sh_addr - m_addr)
			<font color="#000080"><b>module->runsize =</b></font> sec->header.sh_addr - m_addr;
	}
	<font color="#FF0000"><b>sec = obj_find_section(f, ARCHDATA_SEC_NAME);</b></font>
	if (sec && sec->header.sh_size) {
		<font color="#000080"><b>module->archdata_start =</b></font> sec->header.sh_addr;
		<font color="#000080"><b>module->archdata_end =</b></font> module->archdata_start + sec->header.sh_size;
	}
	<font color="#FF0000"><b>sec = obj_find_section(f, KALLSYMS_SEC_NAME);</b></font>
	if (sec && sec->header.sh_size) {
		<font color="#000080"><b>module->kallsyms_start =</b></font> sec->header.sh_addr;
		<font color="#000080"><b>module->kallsyms_end =</b></font> module->kallsyms_start + sec->header.sh_size;
	}
	if (!arch_init_module(f, module))
		return 0;

	/*
	 * Whew!  All of the initialization is complete.
	 * Collect the final module image and give it to the kernel.
	 */
	image = xmalloc(m_size);
	obj_create_image(f, image);

	if (flag_load_map)
		print_load_map(f);

	if (blob_name) {
		int fd, l;
		fd = open(blob_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
		if (fd < 0) {
			error("open %s failed %m", blob_name);
			ret = -1;
		}
		else {
			if ((l = write(fd, image, m_size)) != m_size) {
				error("write %s failed %m", blob_name);
				ret = -1;
			}
			close(fd);
		}
	}

	if (ret == 0 && !noload) {
		fflush(stdout);		/* Flush any debugging output */
		ret = <b><font color="#008000">sys_init_module(m_name, (struct module *) image);</font></b>
		if (ret) {
			error("init_module: %m");
			lprintf("Hint: insmod errors can be caused by incorrect module parameters, "
				"including invalid IO or IRQ parameters.\n"
			        "      You may find more information in syslog or the output from dmesg");
		}
	}

	free(image);

	return ret == 0;
}</pre>
</blockquote>
<p>重要的觀念如下：</p>
<p>1.<i> struct module </i>是「module descriptor」，kernel 的 module manager 以 module 
descriptor 來維護 kernel module。<u>我們把 struct module 稱為 <b>module object</b>。</u></p>
<p>2. 看藍色的部份，運算子的左邊。init_module() 負責「<u>setup</u> module object」。</p>
<p>3. 看紅色的部份，module object 的內容主要是讀取自 kernel module object file 裡的特定 section。</p>
<p>4. 最後是綠色的部份，讀完 object file 裡的特定 section，並 setup 好 module object 後，就呼叫 <i>
sys_init_moddule()</i> 來載入 module object 至 kernel-space。</p>
<p>了解主要的觀念與流程後，再來就是重要的小結了！</p>
<h3><b>insmod 整體觀念</b></h3>
<p><i>insmod</i> 指令使用 <i>sys_init_module </i>系統服務，將&nbsp; kernel module 載入到 kernel 的 address 
space。例如，載入 hello.o 的命令為：</p>
<blockquote>
	<p><i># insmod hello.o</i></p>
</blockquote>
<p>要了解 kernel module 的載入過程，必須由 insmod 的原始碼開始討論。
這裡只是一開始，所以沒有對程式碼的細節做太多介紹，當然這是後面才需要做的功課。</p>
<h3><b>關於 <i>*owner</i> 的小結</b></h3>
<p>透過以上的說明，我們就可以來回答「<i>*owner</i> 的用途倒底是什麼？」的問題了。</p>
<p><i>*owner</i> 的 data type 是 <i>struct module</i>，即 module object 的資料結構。在整個 
kernel module 的載入過程中，module object 是由 <i>insmod</i> 指令所設置，並且是代表 kernel module 
object file。</p>
<p>所以，<i>*owner </i>是 kernel module 的觀念，與「Linux 驅動程式」沒有關係。<b>fops</b> 才是Linux 
驅動程式的觀念。</p>
<p>最後，為什麼 fops 裡要有 <i>*owner</i> 呢？答案就很明顯了：<u>Linux 驅動以 fops-&gt;owner 
來表示「我這個驅動程式是屬於（is owned）那一個 kernel module」</u>。</p>
<p>關於更多 <i>*owner</i>，我們可以想到的是，驅動程式的 usage count 是紀錄在 module object 裡，而不是 file 
operation（思考<!--This article is copyright by www.jollen.org (c) 2006-->為什麼），所以當我們對 <b>usage count </b>做 increment/decrement 時，就需要 <!--This article is copyright by www.jollen.org (c) 2006-->module object。</p>
<p>另外，關於寫 Linux 驅動程式時，「要不要管 <i>*owner</i>」的答案也是很清楚了，基本上，我們可以<u>先</u>不用理會這個 field，但是如果要給 
<i>*owner<!--This article is copyright by www.jollen.org (c) 2006--></i> 
欄位值的話，根據 Linux kernel 的說明，我們只要這麼寫即可：</p>
<pre>static struct file_operations xxx_ops = {
	owner:		<b>THIS_MODULE</b>,
	...
};</pre>
<p>THIS_MODULE 的定義是（<i>include/linux/module.h</i>）：</p>
<blockquote>
	<p><i>#define THIS_MODULE (&amp;__this_module)</i></p>
</blockquote>
<p>__this_module 是一個外部符號，用來表示「這個驅動程式所屬的 module object」。如果是分層的驅動程式，那麼這個 field <!--This article is copyright by www.jollen.org (c) 2006-->
就要填。</p>
<p>課堂上我們再以 <i>busmouse.c</i> / <i>logibusmouse.c</i> 來說明這個部份。</p>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FF5959"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<!--This article is copyright by www.jollen.org (c) 2006-->
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td>
	</tr>

</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul><!--This article is copyright by www.jollen.org (c) 2006-->
			<li>2006.10.07:
			<a href="http://www.jollen.org/blog/2006/10/linux_2429_system_calls_table_1.html">
			Linux 2.4.29 System Calls Table (LSCT)</a></li>
		</ul>
		</td>
	</tr>

</table>
<p>　</p>]]></description>
         <link>https://www.jollen.org/blog/2006/11/hello_world_insmod_1.html</link>
         <guid>https://www.jollen.org/blog/2006/11/hello_world_insmod_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Wed, 15 Nov 2006 20:52:32 +0800</pubDate>
      </item>
            <item>
         <title>Sun Microsystem 釋出 GPLv2 Java 實作：與我的有感而發</title>
         <description><![CDATA[Sun Microsystems 在今天宣佈，將要以 GPLv2 釋出他的 Java 實作。

這二年可以看到非常多的廠商或是專案加入 open source community 的陣營，而 Sun 的 Java 實作超過 10 年的發展，過程真是非常的精采，最終也提出 GPLv2 的版本，有點小意外，但我想可能也不出大家所預期的可能結果。

Sun 的 Java 實作最終還是選擇以 GPLv2 釋出新版本，並且也不意外地引發「<a href="http://www.linux-watch.com/news/NS4045253737.html">Is Java's move to GPL too late?</a>」的討論。在這 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, <a href="http://www.linux-watch.com/news/NS4348361333.html">http://www.linux-watch.com/news/NS4348361333.html</a>.
Sun GPLs Java, targets mobile phones, <a href="http://www.linuxdevices.com/news/NS6857451192.html">http://www.linuxdevices.com/news/NS6857451192.html</a>.]]></description>
         <link>https://www.jollen.org/blog/2006/11/sun_microsystem_gplv2_java_1.html</link>
         <guid>https://www.jollen.org/blog/2006/11/sun_microsystem_gplv2_java_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 14 Nov 2006 21:07:05 +0800</pubDate>
      </item>
            <item>
         <title>Novell 宣佈釋出 Mono 1.2：Linux 執行 .NET 程式的解決方案</title>
         <description><![CDATA[前幾天大家或許都看到 Microsoft/Novell 宣佈要作作開發 Linux 軟體的新聞了。就在 2 天前，Novell 在 Microsoft 的 TechEd Developers Conference & Expo（Barcelona Spain）宣佈推出 Mono 1.2。

Mono 是一套可以讓 Linux 與 Unix 使用者執行 Microsoft .NET 程式的軟體，在 <a href="http://www.linux-watch.com/news/NS9711352426.html">LinuxWatch</a> 的報導中提出 1.2 版是一個重要的里程碑，節錄原始報導如下：

<blockquote>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. </blockquote>

Mono 1.2 的新突破是可以讓 Microsoft 的 UI（user interface）更容易轉移到 Linux 平臺上。Mono Project 的官方網站是：<a href="http://www.mono-project.com/Main_Page">http://www.mono-project.com/Main_Page</a>，現在已經可以下載到 Mono 1.2 了。

對 Linux 應用軟體開發的朋友，可以了解一下這個重要的專案計畫。]]></description>
         <link>https://www.jollen.org/blog/2006/11/novell_mono_12linux_net.html</link>
         <guid>https://www.jollen.org/blog/2006/11/novell_mono_12linux_net.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 13 Nov 2006 00:04:52 +0800</pubDate>
      </item>
            <item>
         <title>Linux Link TEch Show 的訪談：理查史都曼談 GPLv3</title>
         <description><![CDATA[我們知道前陣子、這陣子，有關 GPLv3 的議題真的是「有點火」熱。大家對 GPLv3 都有不同的看法與意見，甚致連 Linus 也都跑出來表示意見。

日前 Linux Link Tech Show 對理查史都曼（Richard Stallman）做了一場訪談，主題是談論有關 GPLv3 的修改目的。這場訪談是以聲音檔的方式提供下載，有 <a href="http://tllts.org/mirror.php?fname=tllts_165-11-01-06.ogg">ogg</a> 與 <a href="http://tllts.org/mirror.php?fname=tllts_165-11-01-06.mp3">MP3</a> 二種格式可取得。

找個時間一定要仔細聽聽這個訪談，畢竟 GPLv3 很可能會決定開放源碼解決方案的大未來。

]]></description>
         <link>https://www.jollen.org/blog/2006/11/linux_link_tech_show_gplv3.html</link>
         <guid>https://www.jollen.org/blog/2006/11/linux_link_tech_show_gplv3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Sun, 12 Nov 2006 23:43:59 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Linux 核心分享包,#3: fork_init()《講義6》</title>
         <description><![CDATA[<p>我們知道，在 start_kernel() 的開機過程中，會呼叫許多初始化核心的常式。其中 fork_init() 是用來初始化 kernel 的&quot;fork&quot; 環境的。&quot;fork()&quot; 是用來產生 child process 的 system call，當 parent process 想要執行外部程式時，會先 fork child process，接著 child process 再利用 exec system call 自己的空間取代為外部程式。</p>

<img alt="fork_init.jpg" src="http://www.jollen.org/blog/2006/11/09/fork_init.jpg" width="640" height="452" border="1" />

<p dir="ltr">從講義第 5 頁開始，我們把問題做分割，找出與 Scheduling 有關的初始化流程來做研讀，因為我們想要以 Scheduling 做為深入 Linux kernel 的第一門課。</p>

<p dir="ltr"><b>講解 fork_init()</b></p>
<p>1. Kernel 能 fork 的 process 數是依據 physical memory 的大小來決定的，我們看到 <i>fork_init(unsigned long mempages)</i> 的<i> mempages</i> 即是用來計算 <i>max_threads</i> 的參數。</p>
<p>2. 看到 <i>mempages</i> 參數，這個參數是由 <i>start_kernel() </i>呼叫<i> fork_init() </i>時傳入的：</p>
<blockquote>
<pre><i>asmlinkage void __init start_kernel(void)
{
   ...
   fork_init(<b>num_physpages</b>);
   ...
}</i></pre>
</blockquote>
<p>3. 找一下 <i>num_physpages</i>，原來是 kernel 的 global symbol 啊，這個地方在 <i>mm/memory.c</i>：</p>

<blockquote>

<pre><i>unsigned long <b>num_physpages</b>;
...
<b>EXPORT_SYMBOL(num_physpages);</b></i></pre>

</blockquote>

<p>我們的想法是，目前還沒讀到有關 Memory Manager 與 mm 初始化的主題，所以還不如先暫時把這個地方當黑盒子，再做一次分割。因此，<i>num_physpages</i> 是怎麼來的就先放到「TODO」清單裡了。</p>

<p>4. 計算 <i>max_threads</i> 需要以下二個常數：</p>

<p>a. <i>THREAD_SIZE</i>：dependent on architecture</p>

<p>b. <i>PAGE_SIZE</i>：dependent on architecture</p>

<p>這二個常數都是平臺相依的，我把他們都標示在投影片上。這是在 processor-level 的 porting 就要處理掉的。</p>

<p>5. 再來，如同程式碼所述：至少要能產生 20 個 thread  系統才能開機。所以限定 <i>max_threads</i> 的最小值是 20。</p>

<p>另外，我們會發現有些與 &quot;memory&quot; 或 &quot;signal&quot; 有關的 code，目前比較好的做法是先放到「TODO」。希望我們這裡的思考邏輯與陳述方式對大家「閱讀核心」能有一點幫助。</p>

<p>  </p>
<table id="table11" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#3366cc"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#e5ecf9"><font size="-1"><b>跟上我們的腳步：請先行閱讀以下的文章，再看這篇日記！</b></font></td>
	</tr>
</table>
<table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
<ul>
  <li class="archive-list-item"><font size="2">2006.10.22: <a href="http://www.jollen.org/blog/2006/10/jollen_linux_1_1.html"> Jollen 的 Linux 核心分享包,#1: 《開工篇》</a></font></li>
  <li class="archive-list-item"><font size="2">2006.10.24: <a href="http://www.jollen.org/blog/2006/10/jollen_linux_1_15.html"> Jollen 的 Linux 核心分享包,#2: 《講義1~5》</a></font></li>
</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/11/jollen_linux_3_fork_init.html</link>
         <guid>https://www.jollen.org/blog/2006/11/jollen_linux_3_fork_init.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 09 Nov 2006 23:41:50 +0800</pubDate>
      </item>
            <item>
         <title>「Mobile 2.0 的思考」與第一隻採用 OpenMoko 的 Linux Smartphone</title>
         <description><![CDATA[<p>這隻手機是由台灣的大眾電腦所開發（真是一個好消息），原始的報導在 LinuxDevices.com 上，詳情可點閱這篇文章「<a href="http://www.linuxdevices.com/news/NS2986976174.html">Cheap, 
hackable Linux smartphone due soon</a>」。</p>
<p>Neo1973（代號：FIC-GTA001） 是「completely open, Linux-based, GPS-equipped, quad-band GSM/GPRS phone」，沒錯，這是一隻「PHS 手機」。預計上市的時間是今年（2006）的 12 月中，並在明年的 1 月份銷售。</p>
<p>Neo1973 採用 Samsung S3C2410 平臺，並且是第一隻採用「OpenMoko」平臺的 Linux 智慧型手機，此外，更採用「apt-get-lke」的 software manager。不管正著看還是反著看，都很符合 Linux 玩家的習慣（與期望）。</p>
<p>非常期待他的問世，到時一定要去帶一隻回家「hack hack」一番。</p>
<table border="1" width="100%" cellspacing="0" bordercolor="#000080" cellpadding="4" style="border-collapse: collapse">
	<tr>
		<td bgcolor="#CACAFF">Sean Moss-Pultz（Neo1973 的 PM，大眾電腦），在 Amsterdam（阿姆斯特丹）所舉行的「Open Source in Mobile」 conference 上發表了一篇演說，內容是有關於「OpenMoKo」這個 Linux smartphone 專用的 application framework 與即將推出的 Neo1973 Linux 智慧型手機。這篇演說的後半段提到一個 Linux 智慧型手機的 ecosystem（生態）觀念，他稱之為「Mobile 2.0」。</p>
		<p>這場演講提出一個讓人充滿無限想像空間的「Mobile 2.0」概念，假使以使用者與互動為主的 WWW 是 Web 2.0，那麼將來以使用者與互動為主的 mobile phone 不就是「Mobile 2.0」。</p>
		<p>如果 Linux 是自由開放的國度，使用者是 Mobile 2.0 的核心價值，那麼未來 Linux Smartphone 哪需要與微軟較勁？</p>
		<p>最近這一年，可以陸續看到許多 Linux 智慧型手機出場在大眾市場，而當大家只看到 Linux 智慧型手機聲斯力竭地與微軟較勁時，Mobile 2.0 的觀點著實另人興奮。</p></td>
	</tr>
</table>
<p>突然發現到，我好像已經加入 Moss-Pultz 的 ecosystem 一員了，顯然我是這個系統的 end user。這個互動來自於「open hacking」的思維邏輯，近年來似乎被不少的國際大廠巧妙地應用著。（突然一個唸頭閃過，昨天參加的一場 roadshow 不就也是如此！）</p>
<p>　</p>
<p>（圖片來源：http://www.linuxdevices.com/news/NS2986976174.html）</p>
<p>Neo1973 其實是採取「dual-OS」的產品策略，因此她也能執行 Microsoft Windows Mobile。不過大家最期待的「Open Source」節錄一小段原始報導的說明如下：</p>
<blockquote>
	<p><i>Because OpenMoKo consists exclusively of 	open-source software, the Neo1973 will ship with a limited feature set, including a dialer (image at left), unified SyncML-enabled email/text 	messaging client, phonebook, (image at right), and media player, according 	to Moss-Pultz. However, many additional open source applications will be 	available through &quot;feeds,&quot; including &quot;certified&quot; ones from FIC, as well as 	those from commercial and community sources, he adds.</i></p>
</blockquote>
<p>還有更棒，Neo1973 採用 OpenEmbedded 來讓使用者增加、移除與更新套件，OpenEmbedded 裡頭已經包含了數以「千」計的套件，透過 bitbake 來建立 Linux distribution，讓愛好者與玩家都能透過簡單的指令來實做「Embedded Linux to Devices」。</p>
<p>Neo1973 的 hardware 規格也是很棒的，整理如下：</p>
<p>1. Samsung S3C2410 SoC</p>
<p>2. 128MB RAM</p>
<p>3. 64M flash</p>
<p>4. 64MB MicroSD</p>
<p>5. 2.8&quot; touchscreen</p>
<p>6. Assisted GPS</p>
<p>7. quad-band GSM/GPRS module</p>
<p>Moss-Pultz 的 presentation 可以在這裡找到：<a href="http://www.linuxdevices.com/files/article072/sld002.html">http://www.linuxdevices.com/files/article072/sld002.html</a>
</p>
<p>OpenMoko 的「official site」是：<a href="http://www.openmoko.com/press/index.html">http://www.openmoko.com/press/index.html</a>。在這上面有完整的新聞稿與簡報 PDF 檔可以下載。</p>
<p>Revision, 2006/11/09, 01:13PM.]]></description>
         <link>https://www.jollen.org/blog/2006/11/mobile_20_openmoko_linux_smart.html</link>
         <guid>https://www.jollen.org/blog/2006/11/mobile_20_openmoko_linux_smart.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Wed, 08 Nov 2006 20:37:08 +0800</pubDate>
      </item>
            <item>
         <title>Linux System Calls&apos; Forum, #7：（第157號系統服務）sys_sched_getscheduler</title>
         <description><![CDATA[<p>「作業系統的排程器（scheduler）提供好幾種排程演算法，並在不同的應用場合，或是不同的 process 採取適合的排程策略（policy）。」</p>
<p>Linux 的 process descriptor 資料結構為 <i>struct task_struct</i>，我們最早開始談論與 process 有關的 system call 時，就看過這個用來描述 process 的資料結構。<i>struct task_struct </i>裡頭的 <i>policy </i>欄位便是用來描述該 process 的排程策略。</p>
<p><b>與排程有關的系統呼叫</b></p>
<p dir="ltr">整理與 scheduling 有關的幾個 system call 如下（可參考「<a href="http://www.jollen.org/blog/2006/10/linux_26_system_call12.html">Linux 2.6 的 System Call：12 大類</a>」），另外 Jollen 也把到目前為止已介紹過的 system call 加上紅色註記。</p>
<table border="1" cellpadding="2" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="100%">
	<tr>
		<td width="47"><b>號碼</b></td>
		<td width="219"><b>System Call 名稱</b></td>
		<td width="71%"><b>Manipulation</b></td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">34 </td>
		<td width="219"><b><font color="#FF0000">sys_nice</font></b></td>
		<td width="71%">關於 struct task_struct-&gt;nice 欄位。</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">154 </td>
		<td width="219">sys_sched_setparam</td>
		<td width="71%">關於 struct task_struct-&gt;rt_priority 欄位。</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">155 </td>
		<td width="219">sys_sched_getparam</td>
		<td width="71%">關於 struct task_struct-&gt;rt_priority 欄位。</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">156 </td>
		<td width="219"><font color="#0000FF"><b>sys_sched_setscheduler</b></font></td>
		<td width="71%">關於 struct task_struct-&gt;policy 欄位。（這是下一個要讀的）</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">157 </td>
		<td width="219"><font color="#FF0000"><b>sys_sched_getscheduler</b></font></td>
		<td width="71%">關於 struct task_struct-&gt;policy 欄位。</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">158 </td>
		<td width="219">sys_sched_yield</td>
		<td width="71%">暫不討論。</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">159 </td>
		<td width="219">sys_sched_get_priority_max</td>
		<td width="71%">根據 'policy' 傳回 'rt_priority' 所允許的最大 priority 值。</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">160 </td>
		<td width="219">sys_sched_get_priority_min</td>
		<td width="71%">根據 'policy' 傳回 'rt_priority' 所允許的最小 priority 值。</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">161 </td>
		<td width="219">sys_sched_rr_get_interval</td>
		<td width="71%">若 policy 為 SCHED_RR（Round Robin policy），則傳回 time 
		quantum（time slice）值。</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">241 </td>
		<td width="219">sys_sched_setaffinity</td>
		<td width="71%">這是 Linux 2.6 新增加的 system call。</td>
	</tr>
	<tr>
		<td width="47">
		<p dir="ltr">242 </td>
		<td width="219">sys_sched_getaffinity</td>
		<td width="71%">這是 Linux 2.6 新增加的 system call。</td>
	</tr>
</table>
<p dir="ltr">「'sys_sched_getscheduler' 便是用來取得 process 排程策略（policy）的 system 
call。」</p>
<p dir="ltr">「'struct task_struct-&gt;policy' 是今天要了解的主題。」</p>
<p dir="ltr">我們想要思考一個問題「作業系統的教科書提到的排程演算法中，Round Robin 是我們知道現在的分時作業系統（eg. Linux）所採用的策略，所以我想了解 Linux 的 process descriptor 裡，是哪一個 field 在紀錄這件事情。」</p>
<p dir="ltr"><b>struct task_struct 的 policy 欄位</b></p>
<p dir="ltr">這個問題還挺簡單的，因為直接看 struct task_struct 幾乎就能猜到是哪一個 field，再用谷歌找資料來印證我們的設假；另外，看「Understanding the Linux Kernel」也寫的很清楚。</p>
<pre>
<i>struct task_struct {
	...
	unsigned long <b>policy</b>;
	...
};</i></pre>
<p>接著再回頭來看今天的主題，<i>sys_sched_getscheduler</i> 的實作（Linux 2.6.11+）：</p>
<pre><i>/**
 * 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;
	<b>task_t *p</b>;  // jollen: 'typedef struct task_struct task_t;'

	if (pid < 0)
		goto out_nounlock;

	retval = -ESRCH;
	read_lock(&tasklist_lock);
	<b>p = find_process_by_pid(pid)</b>; // 參照說明 1.
	if (p) {
		retval = security_task_getscheduler(p);
		if (!retval)
			retval = <b>p->policy</b>; // 參照說明 2.
	}
	read_unlock(&tasklist_lock);

out_nounlock:
	return retval;
}</i></pre>
<p>取出重點部位來看：</p>
<ol>
	<li>取得指定 PID 的 process descriptor。常常在一些說明文件看到「若 PID 為 0，則取得 <i>current</i> 
	的...」，不過這裡的實作好像看的不太清楚，沒關係等一下再來往下看 code。</li>
	<li>傳回 <i>policy</i> 欄位。</li>
</ol>
<p>很簡單的將 <i>sys_sched_getscheduler</i> 看了一下，並且了解到 <i>policy</i> 欄位的用途。</p>
<p><b>find_process_by_pid</b></p>
<p>把 find_process_by_pid() 的實作拿出來看：</p>
<pre><i>/**
 * 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;
}</i></pre>
<p>發現一件很重要的是，也是之前都沒提過的。</p>
<p>「若 PID 為 0，則傳回 <i>current</i>，否則傳回 PID 為 pid 的 process 之 process descriptor。」</p>
<p>別忘了，current 的 data type 也是 'struct task_struct'。</p>
<p><b>Linux 排程策略：policy 欄位的值</b></p>
<p>Linux 2.6 提供的排程策略如下：</p>
<pre><i>/*
 * Scheduling policies
 */
#define SCHED_NORMAL		0
#define SCHED_FIFO			1
#define SCHED_RR			2
#define SCHED_BATCH		3</i></pre>
<p>定義在 include/linux/sched.h。順帶把 Linux 2.4 提供的排程策略也節錄一下：</p>
<pre><i>/*
 * Scheduling policies
 */
#define SCHED_OTHER		0
#define SCHED_FIFO			1
#define SCHED_RR			2</i></pre>]]></description>
         <link>https://www.jollen.org/blog/2006/11/linux_system_calls_forum_7157s.html</link>
         <guid>https://www.jollen.org/blog/2006/11/linux_system_calls_forum_7157s.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 05 Nov 2006 00:03:08 +0800</pubDate>
      </item>
            <item>
         <title>目前的「Linux System Calls&apos; Forum」與「Jollen 的 Linux 核心分享包」專欄進度報告。</title>
         <description><![CDATA[<p>「Linux System Calls' Forum」與「Jollen 的 Linux 核心分享包」是二個相互配合的專欄，因此相關的日記彼此之間是具備相依性與次序的。建議的閱讀順序是「依照日記的時間（即發佈順序）」來閱讀「Linux System Calls' Forum」與「Jollen 的 Linux 核心分享包」。這是這二個系統日記的閱讀建議。</p>
<p><b>目前的狀況報告</b></p>
<p>對於 Linux system call 的討論，我們已經來到了 scheduler 的門口了，要深入了解 Linux scheduler 的設計原理與相關理論前，Jollen 還必須多做一點功課才行。到目前為止，我們所討論過的幾個 system call 如下。</p>
<table id="table11" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#3366cc"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#e5ecf9"><font size="-1"><b>跟上我們的腳步：請讀以下的文章，再看這篇日記！</b></font></td>
	</tr>
</table>
<table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li>
			2006.10.11:
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_120_s.html">
			Linux System Calls' Forum, #1：(第20號系統服務) sys_getpid</a></li>
			<li>
			2006.10.12:
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_21992.html">
			Linux System Calls' Forum, #2：(第199,201,200,202,224號系統服務) sys_getuid, 
			sys_geteuid, sys_getgid, sys_getegid, sys_gettid</a></li>
			<li>
			2006.10.14:
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_364_s.html">
			Linux System Calls' Forum, #3：(第64號系統服務) sys_getppid</a></li>
			<li>
			2006.10.15:
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_434_s.html">
			Linux System Calls' Forum, #4：(第34號系統服務) sys_nice</a></li>
			<li>
			2006.10.19:
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_596_s.html">
			Linux System Calls' Forum, #5：(第96號系統服務) sys_getpriority</a></li>
			<li>
			2006.10.20:
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_697_s.html">
			Linux System Calls' Forum, #6：(第97號系統服務) sys_setpriority</a></li>
		</ul>
		</td>
	</tr>
</table>

<p>「Jollen 的 Linux 核心分享包」已發佈二則日記如下。</p>
<table id="table11" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#3366cc"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#e5ecf9"><font size="-1"><b>跟上我們的腳步：請讀以下的文章，再看這篇日記！</b></font></td>
	</tr>
</table>
<table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li>2006.10.22:
			<a href="http://www.jollen.org/blog/2006/10/jollen_linux_1_1.html">
			Jollen 的 Linux 核心分享包,#1: 《開工篇》</a></li>
			<li>2006.10.24:
			<a href="http://www.jollen.org/blog/2006/10/jollen_linux_1_15.html">
			Jollen 的 Linux 核心分享包,#2: 《講義1~5》</a></li>
		</ul>
		</td>
	</tr>
</table>
<p>另外，Jollen 在每一個 system call 的討論都點出幾個基本的主題，希望大家能大致了解這目前「Linux System Calls' Forum」裡出現的幾個主題後，再繼續往下看「Linux System Calls' Forum」論壇。</p>
<p><b>後續進度</b></p>
<p>這篇日記完成後，緊接著的就是「Linux System Calls' Forum, #7：（第157號系統服務）sys_sched_getscheduler」，後然緊接的是「Jollen 的 Linux 核心分享包,#3」了，請大家多多指正！</p>]]></description>
         <link>https://www.jollen.org/blog/2006/11/linux_system_calls_forumjollen.html</link>
         <guid>https://www.jollen.org/blog/2006/11/linux_system_calls_forumjollen.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 04 Nov 2006 21:34:02 +0800</pubDate>
      </item>
            <item>
         <title>KVM 驅動程式的 HOWTO</title>
         <description><![CDATA[<a href="http://www.jollen.org/blog/2006/10/kernel_26_kvm_kernel_virtual_m.html">之前提到</a>過 Kernel 2.6 的 KVM 驅動程式現身了，今天看到 KVM 驅動程式的 HOWTO 已經上線了。KVM 的官方網站是：<a href="http://kvm.sourceforge.net/">http://kvm.sourceforge.net/</a>

裡頭除了有 HOWTO 外，也有 mailing list 可以加入了。KVM 的驅動程式除了有 kernel-space 的 driver 外，也包含一個 user-space 的 library，以及給 <a href="http://www.jollen.org/blog/2006/09/qemu.html">QEMU</a> 的 patch。]]></description>
         <link>https://www.jollen.org/blog/2006/11/kvm_howto.html</link>
         <guid>https://www.jollen.org/blog/2006/11/kvm_howto.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 02 Nov 2006 22:03:10 +0800</pubDate>
      </item>
            <item>
         <title>Preemptive Process Scheduling 的觀念</title>
         <description><![CDATA[先前我們在介紹 <a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_364_s.html">sys_getppid</a> 時，跳掉了一段 code 如下：

<pre>
#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
{
   ...
}
#endif
</pre>

這段 code 在判斷 kernel 是否有 'CONFIG_SMP'（多處理器）與 'CONFIG_PREEMPT'（可搶先的 process 排程）。目前 kernel 2.6 在各種處理器平臺已經支援 preemptive process scheduler，當然也包含了 i386 處理器。

這個 kernel 的設定位於 arch/i386/Kconfig，我們節錄 Kconfig 內容如下：

<blockquote><pre>config PREEMPT
        bool "Preemptible Kernel"
        help
          This option reduces the latency of the kernel when reacting to
          real-time or interactive events by <strong>allowing a low priority process to
          be preempted even if it is in kernel mode executing a system call.</strong>
          This allows applications to run more reliably even when the system is
          under load.<br />
          Say Y here if you are building a kernel for a desktop, embedded
          or real-time system.  Say N if you are unsure.</pre></blockquote>

這段 code 與 CONFIG_PREEMPT 的用意非常清楚：提供 preemptive process scheduling 的能力，我們對 Linux kernel preemptive process scheduling 的處理方法相當感興趣。

在進一步研究與 scheduling 有關的 system call 與 'CONFIG_PREEMPT' 前，有必要先來複習一下什麼是 "preemptvie"。

<strong>Preemption</strong>

一個重要的工作是了解「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 的核心裡已經有不錯的支援了。

<strong>更多 Linux Preemptive Scheduling</strong>

1. Linux 的 scheduler 包含 preemptive 的實作。
2. Linux kernel 本身是 nonpreemptvie。我們在這裡指的 "preemptive" 是指 "process preemptive"。所以當 process 是在 User Mode 時才能被搶先。
3. Process preemptvie（可被搶先因此中斷）：process 除了在 time-slice 用完時被中斷外（典型案例），在某些狀況下也是可以被中斷的。例如，前面提到的優先序問題便是一例。
4. 被中斷的 process 仍然是 running state（因為只是被搶先而已）。

Linux/Unix 作業系統核心是 nonpreemptive 的實作，這樣的作業系統設計較簡單，並且可免除許多核心同步（kernel synchronization）的問題。

Preemptive Scheduling

以下是節錄自作業系統教科書裡，對於可搶先班程的說明[1]：

<blockquote>CPU scheduling decisions may take place when a process -

1.	Switches from running to waiting state.
2.	Switches from running to ready state.
3.	Switches from waiting to ready.
4.	Terminates.

Scheduling under 1 and 4 is nonpreemptive.</blockquote>


這裡有一份 preemption 的名詞解釋：<a href="http://www.netrino.com/Publications/Glossary/Preemption.html">http://www.netrino.com/Publications/Glossary/Preemption.html</a>

[1] A. Silberschatz, P. Galvin, and G. Gagne, <i>Applied Operating System Concepts</i>, First Edition, John Wiley & Sons, Inc. (2000).]]></description>
         <link>https://www.jollen.org/blog/2006/11/_scheduler_running_process.html</link>
         <guid>https://www.jollen.org/blog/2006/11/_scheduler_running_process.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 02 Nov 2006 14:55:15 +0800</pubDate>
      </item>
            <item>
         <title>Ragel：狀態機編譯器</title>
         <description>Ragel 是一個 &quot;State Machine Compiler&quot;，Ragel 可以把狀態機（regular expression）編譯成 C/C++/Objective-C/D 程式碼。Ragel 除了可以用來快速撰寫分析文件的程式外，用來學習 Automata 也是很好的工具。

Ragel 的官方網站：http://www.cs.queensu.ca/~thurston/ragel/</description>
         <link>https://www.jollen.org/blog/2006/11/ragel.html</link>
         <guid>https://www.jollen.org/blog/2006/11/ragel.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 01 Nov 2006 20:48:08 +0800</pubDate>
      </item>
            <item>
         <title>玩 FreeDOS 1.0</title>
         <description><![CDATA[<a href="http://www.freedos.org/">FreeDOS</a> 在今年 9 月時釋出了重大的里程（big milestone）版本 "1.0"。FreeDOS 是一個 "complete", "free", "100% MS-DOS compatible" 的 operating system，今天心血來潮，把她裝起來玩了一下，其實還真有點懷念呢。


<img alt="fdos2.jpg" src="http://www.jollen.org/blog/2006/11/01/fdos2.jpg" width="732" height="425" />
懷念的滋味：西冒號斜線大於。

<img alt="fdos1.jpg" src="http://www.jollen.org/blog/2006/11/01/fdos1.jpg" width="652" height="425" />
DOOM！]]></description>
         <link>https://www.jollen.org/blog/2006/11/_freedos_10.html</link>
         <guid>https://www.jollen.org/blog/2006/11/_freedos_10.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Wed, 01 Nov 2006 12:41:55 +0800</pubDate>
      </item>
            <item>
         <title>在 Red Hat Linux 9 下安裝 bitbake 1.6 的注意事項</title>
         <description><![CDATA[最近有朋友問到，在 Red Hat Linux 9 底下要如何安裝 bitbake 1.6。如果您是使用 Red Hat Linux 9（我們的課程學員），想要在 Red Hat Linux 9 底下安裝 bitbake 1.6 的話，需要注意以下事項（已實測成功）：

1. Python 要更新至 2.3（rh9 提供的是 2.2），至於其它更新版本的 Python 經測試，則是不能使用的。我們測試過  Python 2.5，結果是無法運作。我們實測可運作的版本是 Python 2.3.5 ；另外請務必使用以下的 configure 設定：

<blockquote># ./configure --enable-unicode=ucs4 --prefix=/usr</blockquote>

2. 安裝 <a href="http://psyco.sourceforge.net/">Psyco</a>（Python JIT Compiler）：這是必要的，以快加 bitbake 建立套件的速度。我們測試的版本是 2006/10/23 的 snapshot，實測後可順利運作，不過更新一點的 snapshot 我想應該也不致於有太大問題。

3. 安裝 <a href="http://www.mathematik.uni-kl.de/~obachman/Texi2html/">text2html</a> library，我們實測的版本是 texi2html 1.64。

4. 安裝 <a href="http://cyberelk.net/tim/xmlto/">xmlto</a>，我們使用的版本是 xmlto 0.0.18。

以下是 bitbake 1.6 在我們的 Red Hat Linux 9 下運作的畫面。

<img alt="bitbake.jpg" src="http://www.jollen.org/blog/2006/11/01/bitbake.jpg" width="640" height="385" />


]]></description>
         <link>https://www.jollen.org/blog/2006/10/_red_hat_linux_9_bitbake_16.html</link>
         <guid>https://www.jollen.org/blog/2006/10/_red_hat_linux_9_bitbake_16.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Tue, 31 Oct 2006 23:43:55 +0800</pubDate>
      </item>
            <item>
         <title>PowerPC 的無硬碟刀峰伺服器</title>
         <description><![CDATA[一個有趣的應用。這是利用 IBM PowerPC 750GX 處理器，搭載 Tundra Tsi108 北橋，製作出無硬體的 server board，掛載（mount）在一個背板（backbone）後，就成為一台小型的分散式計算伺服器。以下是這台「diskless-blade server」的照片。

<img alt="gx_108_1.jpg" src="http://www.jollen.org/blog/2006/10/31/gx_108_1.jpg" width="480" height="360" />

<img alt="gx_108_2.jpg" src="http://www.jollen.org/blog/2006/10/31/gx_108_2.jpg" width="480" height="360" />

基本上這是一個 <strong>computation platform</strong>，因此需要軟體「分散式計算技術」的支援。不過不拿這個可怕的題目來討論，基本上 Linux 要有一個無硬碟的計算環境，最簡單的方法是「NFS」。

<img alt="gx_108_3.jpg" src="http://www.jollen.org/blog/2006/10/31/gx_108_3.jpg" width="480" height="360" />
(一台機器可安插 4 片 diskless server board，可做為小型的伺服器或計算平臺。)

NFS 果然是萬用解決方案，像是這種異質性平臺（host v.s. target、client v.s. server），利用 NFS 來掛載（mount）host（或server）端的 Linux distribution 到 target（或 client）端來做計算，是最簡單的方法。

唯一需要具備的能力是：

1. NFS 的設定：/etc/exports。
2. 製作 target 的 NFS bootstrap root filesystem。
3. 灌一套支援 target 端的 Linux distribution 在 host 端。

以下是這個「server board」的 block diagram。

<img alt="gx_108_4.JPG" src="http://www.jollen.org/blog/2006/10/31/gx_108_4.JPG" width="471" height="257" />

diskless-blade server 的幾個好處是：

1. 資料存放於 remote data server。
2. 將計算（computation）與資料儲存（storage）分開：separate computation from storage。
3. 計算端（computation）的損壞不影響資料安全性。
4. 提供較佳的備援。
5. 使用 Giga lan 互連（connectivity），佈署成本（deployment）較低（see the block diagram）。

至於軟體端的解決方案，我想大家應該也都猜得到，這片伺服器板是使用 U-Boot + Linux 2.6.x 做為解決方案。

<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td bgcolor="#e5ecf9"><font size="-1"><b>Also See</b></font></td></tr></table><table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td width="100%"><ul><li><a href="http://www.jollen.org/blog/2006/09/_powerpc_powerpc.html">PowerPC 衝吧！</a>：關於 PowerPC 的 NFS root filesystem。</li></ul></td></tr></table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/ibm_powerpc_1.html</link>
         <guid>https://www.jollen.org/blog/2006/10/ibm_powerpc_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 31 Oct 2006 13:29:28 +0800</pubDate>
      </item>
            <item>
         <title>Kconfig 與 &quot;-mregparm=num&quot; 參數</title>
         <description><![CDATA[gcc 在 i386 / x86_64 的 machine dependent 參數 (-m) 中，有 1 個參數與參數的傳遞相關的：-mregparm=num，這個參數的用途與先前提到的 "__attribute((regparm(?))__" 作用相同<sup>*2</sup>。

-mregparm 用來指定有多少個 integer 的參數可以透過暫存器（register）來傳遞， 以下是 -mregparm 的 man（GCC(1))：

<blockquote><pre>Intel 386 and AMD x86-64 Options
...
-mregparam
           Use a different function-calling convention where the first two
           arguments are passed in registers.</pre></blockquote>

kernel 的編譯設定中（menuconfig），也提供一個選項來設定「暫存器參數的傳遞」。這個設定位於 menuconfig 的 「Processor type and features|Use register arguments (EXPERIMENTAL) (NEW)」，Jollen 所使用的 kernel 版本是 Linux v2.6.11 (for i386)，如果您的 kernel 不是 i386 或是版本不同，您可能無法在這個地方找到此設定項。

<strong>Kconfig 與 -mregparam</strong>

以下是節錄自 arch/i386/Kconfig 的內容：

<blockquote><pre>config REGPARM
        bool "Use register arguments (EXPERIMENTAL)"
        depends on EXPERIMENTAL
        default n
        help
        Compile the kernel with -mregparm=3. This uses a different ABI
        and passes the first three arguments of a function call in registers.
        This will probably break binary only modules.
<br />        This feature is only enabled for gcc-3.0 and later - earlier compilers
        generate incorrect output with certain kernel constructs when
        -mregparm=3 is used.</pre></blockquote>

當我們把 REGPARM 設定為 'y' 後，Makefile 裡的 CONFIG_REGPARM 值也會是 'y'。當 CONFIG_REGPARM=y 時，cflags-y 便加 "-mregparm=3" 的參數，請注意 Kernel 2.6.x 必須以 3.2.x 以上的 gcc 來編譯<sup>*1</sup>。相關的 Makefile 節錄如下（arch/i386/Makefile）：

<blockquote><pre># -mregparm=3 works ok on gcc-3.0 and later
#
GCC_VERSION                     := $(call cc-version)
cflags-$(CONFIG_REGPARM)        += $(shell if [ $(GCC_VERSION) -ge 0300 ] ; then echo "-mregparm=3"; fi ;)
<br /># Disable unit-at-a-time mode, it makes gcc use a lot more stack
# due to the lack of sharing of stacklots.
CFLAGS += $(call cc-option,-fno-unit-at-a-time)
<br />CFLAGS += $(cflags-y)</pre></blockquote>

最後，CFLAGS 的值會加上 '$(cflgs-y)'，我們知道 CFLAGS 是 gcc 的編譯參數設定變數，所以當 gcc 在編譯 kernel 時，便會套用 "-mregparm=3" 的參數。

<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td bgcolor="#e5ecf9"><font size="-1"><b>Also See</b></font></td></tr></table><table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td width="100%"><ul><li><a href="http://www.jollen.org/blog/2006/10/jollen_linux_1_15.html">Jollen 的 Linux 核心分享包,#2: 《講義1~5》</a><sup>*1</sup></li><li><a href="http://www.jollen.org/blog/2006/10/_gcc___attribute.html">關於 gcc 的 __attribute__ 用法</a><sup>*2</sup></li></ul></td></tr></table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/kconfig_mregparmnum.html</link>
         <guid>https://www.jollen.org/blog/2006/10/kconfig_mregparmnum.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 30 Oct 2006 15:27:43 +0800</pubDate>
      </item>
            <item>
         <title>關於 gcc 的 __attribute__ 用法</title>
         <description><![CDATA[gcc 提供一種「定義函數屬性（attribute）」的語法，也就是前一篇日記我們所提到的 <em>__attribute__</em> 標籤。<em>__attribute__</em> 用來讓我們定義函數的行為，以便告訴 gcc 在編譯時期對此函數做一些特殊的處理或檢查動作。

以 <em>asmlinkage</em> 的例子來說，<em>asmlinkage</em> 的定義是（<em>/usr/include/linux/linkage.h</em>）：

<blockquote><pre>#if defined __i386__
#define asmlinkage CPP_ASMLINKAGE <strong>__attribute__((regparm(0)))</strong>
#elif defined __ia64__
#define asmlinkage CPP_ASMLINKAGE __attribute__((syscall_linkage))
#else
#define asmlinkage CPP_ASMLINKAGE
#endif<pre></blockquote>

由此可知，以 <em>sys_nice()</em> 的原型宣告來說，以下的二行程式碼是等價的：

<blockquote><pre>asmlinkage sys_nice(...);
__attribute__((regparm(0))) sys_nice();</pre></blockquote>

"__attribute__" 寫在函數宣告之前或之後都可以（左括號前或右括號後），並接著使用一對「雙括號」來註明此函數的屬性。語法：

<blockquote>__attribute__((keywords)) functon_name(...);</blockquote>

屬性的關鍵字如下（節錄自 gcc 3.4.6 manual）：

<blockquote>noreturn,noinline, always_inline, pure, const, nothrow, format, format_arg, no_instrument_function, section, constructor, destructor, used, unused, deprecated, weak, malloc,alias, warn_unused_result, nonnull.</blockquote>

<strong>regparm 的用法與用途</strong>

regparm 的語法是：regparm(number)，regparm 屬性只在 Intel 386 平臺上有作用，用來指定最多可以有多少個（"number" ）參數（arguments）能以暫存器來傳遞，regparm(0) 表示參數都不能透過暫存器來傳遞，因此所有參數都會透過堆疊來傳遞。

<strong>更多關於__attribute__</strong>

又如，在 ARM 平臺上宣告 "__attribute((interrupt))" 表示此函數是一個 interrupt handler。

其它的屬性說明可參閱 gcc 的手冊。

<em>__attribute__</em> 是重要的 gcc 用法，對系統程式（system software）的開發尤其重要，應仔細閱讀 gcc 手冊。

<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td bgcolor="#e5ecf9"><font size="-1"><b>Also See</b></font></td></tr></table><table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%"><tr><td width="100%"><ul><li><a href="http://www.jollen.org/blog/2006/10/_asmlinkage.html">什麼是 "asmlinkage"？</a></li></ul></td></tr></table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/_gcc___attribute.html</link>
         <guid>https://www.jollen.org/blog/2006/10/_gcc___attribute.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Fri, 27 Oct 2006 00:02:15 +0800</pubDate>
      </item>
            <item>
         <title>什麼是 &quot;asmlinkage&quot;？</title>
         <description><![CDATA[有網友來信問到，kernel 裡的 system call 實作函數中（C 函數），為什麼每一個函數原型宣告的前面都有一個 "asmlinkage" 的字串？例如：

<strong>asmlinkage</strong> long sys_nice(int increment)

"asmlinkage" 是在 i386 system call 實作中相當重要的一個 gcc 標籤（tag）。

當 system call handler 要呼叫相對應的 system call routine 時，便將一般用途暫存器的值 push 到 stack 裡，因此 system call routine 就要由 stack 來讀取 system call handler 傳遞的參數。這就是 asmlinkage 標籤的用意。

system call handler 是 assembly code，system call routine（例如：sys_nice）是 C code，當 assembly code 呼叫 C function，並且是以 stack 方式傳參數（parameter）時，在 C function 的 prototype 前面就要加上 "asmlinkage"。

加上 "asmlinkage" 後，C function 就會由 stack 取參數，而不是從 register 取參數（可能發生在程式碼最佳化後）。

<strong>更進一步的說明...</strong>

80x86 的 assembly 有 2 種傳遞參數的方法：

1. register method
2. stack method

Register method 大多使用一般用途（general-purpose）暫存器來傳遞參數，這種方法的好處是簡單且快速。另外一種傳遞參數的做法是使用 stack（堆疊），assembly code 的模式如下：

push number1
push number2
push number3
call sum

在 'sum' procedure 裡取值的方法，最簡單的做法是：

pop ax       
pop ax
pop bx
pop cx

Stack Top 是放 IP，我們傳給 sum procedure 的參數由 stack 的後一個 entry 開始讀取。

<strong>其它有關 asmlinkage</strong>

1. asmlinkage 是一個定義
2. "asmlinkage" 被定義在 <em>/usr/include/linux/linkage.h</em>
3. 如果您看了 <em>linkage.h</em>，會發現 "__attribute__" 這個語法，這是 gcc 用來定義 function attribute 的語法。]]></description>
         <link>https://www.jollen.org/blog/2006/10/_asmlinkage.html</link>
         <guid>https://www.jollen.org/blog/2006/10/_asmlinkage.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 26 Oct 2006 16:15:38 +0800</pubDate>
      </item>
            <item>
         <title>kernel 2.6 的 battery class 驅動程式</title>
         <description><![CDATA["class driver" 是 kernel 2.6 driver model 的新觀念，從系統管理面的角度來看，就是 /sysfs。現在 kernel 2.6 已經出現專門針對電池電源管理的驅動程式了：battery class。這是一個有別於以往對於電源管理的實作，雖然 "battery class" 仍只是一個初步實作的驅動程式，但未來 "battery" 與 "AC powe" 在 Linux 驅動程式新觀念底下，將會是一個獨立的 "device"。

新的 battery class 實作中，我們也看到了 OLPC（百元美金電腦）專用的電池驅動程式。程式碼目前可由 kernel 的 git 下載，並可追磫此討論串：<a href="http://forum.jollen.org/index.php?showtopic=6899">http://forum.jollen.org/index.php?showtopic=6899</a>]]></description>
         <link>https://www.jollen.org/blog/2006/10/kernel_26_battery_class.html</link>
         <guid>https://www.jollen.org/blog/2006/10/kernel_26_battery_class.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Wed, 25 Oct 2006 10:35:36 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Linux 核心分享包,#2: 《講義1~5》</title>
         <description><![CDATA[<img alt="process_sched_intro_01.JPG" src="http://www.jollen.org/blog/2006/10/24/process_sched_intro_01.JPG" width="472" height="334" />

<p>講義第1頁到第5頁的說明。</p>

<strong>課前說明</strong>

<p>作業系統（operating system）理論的教材書，與 process 和 scheduling（排程）相關的主題都是最先被討論的議題。因此，對於 Linux kernel 原始碼的研究，我們也從 process 與 scheduling 有關的部份開始講起。</p>
<p>  </p>
<img alt="process_sched_intro_02.JPG" src="http://www.jollen.org/blog/2006/10/24/process_sched_intro_02.JPG" width="472" height="334" />
<p>  </p>
<p>Process 是執行中的程式，因此「程式如何被執行」是第一個重點，這個部份的觀念當然也包含「程式如何由儲存裝置載入」。緊接著的是，「程式載入記憶體後的佈局（layout）」，這是第二個要研究的重點。當程式被載到記憶體後，作業系統的 scheduler（排程器）便負責排程的工作與執行本文切換（context switch），這是第三個所要討論的重點。</p>

<p>因此，我們將會研究 Linux kernel 的 3 個「process and scheduling」議題：</p>

<p>1. Shell 如何載入執行檔（ELF executables）至記憶體。<br />
2. Process 的 memory 資枓結構。<br />
3. Linux scheduling 與 x86 context switch。</p>

<p>在開始進入主題前，我們先來了解一下 Linux kernel 的開機流程與 scheduler 初始化的關係。然後我們會接著討論「Process Creation」，並一併說明「 Shell 如何載入執行檔（ELF executables）至記憶體」。</p>

<strong>編譯 Kernel 2.6 的注意事項</strong>
<p>  </p>
<img alt="process_sched_intro_03.JPG" src="http://www.jollen.org/blog/2006/10/24/process_sched_intro_03.JPG" width="472" height="334" />
<p>  </p>
<p>Kernel 2.6 的編譯必須使用 gcc 3.2 版以上，這張講義的 code 位於 <em>init/main.c</em>，由這段 code 也能知道，在編譯時期（compile time）就會做 gcc 版本的檢查。</p>

<strong>Operating System Entry Point</strong>

<p><em>init/main.c:start_kernel()</em> 是「 architecture-independent booting process」的起點（entry point），到這裡表示 kernel 的開機已經進入作業系統核心的部份（OS booting），並且完成了機器平臺（architectural）的初始化與設定工作。</p>
<p>  </p>
<img alt="process_sched_intro_04.JPG" src="http://www.jollen.org/blog/2006/10/24/process_sched_intro_04.JPG" width="472" height="334" />
<p>  </p>
<p>用一句簡單的話來解釋：我們已經脫離 arch/ 目錄啦！<em>start_kernel()</em> 也被解釋為作業系統（operating system、linux kernel）的進入點。</p>

<strong>Initialization Sequence</strong>

<p>在 <em>main.c</em> 裡的 <em>start_kernel()</em> 函數實作中，我們能看到一大串的「initialization」函數呼叫。這些初始化函數的呼叫是有順序關係的，因此是「initialization sequence」。</p>

<p>在整個初始化程序中，與 scheduler 有關的初始化函數說明如下。</p>

<strong>Scheduler Initialization Sequence</strong>
<p>  </p>
<img alt="process_sched_intro_05.JPG" src="http://www.jollen.org/blog/2006/10/24/process_sched_intro_05.JPG" width="472" height="334" />
<p>  </p>
<p>我們先來說明一下這 3 個初始化函數的主要目的，再深入討論其內部實作：</p>

<p>1. <em>sched_init()</em>：初始化排程器，包含：初始化 run queue、初始化 “idle thread”。<br />
2. <em>pidhash_init()</em>：初始化 “PID” 的 hash table。<br />
3. <em>fork_init()</em>：計算 kernel 能 “fork” 的最大 process 數量。</p>

<p>  </p>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FF5959"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FFD8CA"><font size="-1"><b>Keywords</b></font></td>
	</tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
<p>operating system, process, scheduling, Linux kernel, scheduler, context switch, ELF, process creation, run queue, idle thread, PID, </p>
		</td>
	</tr>
</table>

<p>  </p>
<table id="table11" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#3366cc"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#e5ecf9"><font size="-1"><b>跟上我們的腳步：請先行閱讀以下的文章，再看這篇日記！</b></font></td>
	</tr>
</table>
<table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
<ul>
  <li class="archive-list-item"><font size="2">2006.10.22:
  <a href="http://www.jollen.org/blog/2006/10/jollen_linux_1_1.html">
  Jollen 的 Linux 核心分享包,#1: 《開工篇》</a></font></li>
</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/jollen_linux_1_15.html</link>
         <guid>https://www.jollen.org/blog/2006/10/jollen_linux_1_15.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Tue, 24 Oct 2006 22:33:10 +0800</pubDate>
      </item>
            <item>
         <title>ReiserFS 日誌式檔案系統</title>
         <description><![CDATA[自 kernel 2.4.1 開始，Linux 已經支援一種稱為 ReiserFS 的日誌式檔案系統。日誌式檔案系統可以提供更安全的檔案保護機制，特別是可以運用在伺服器或是商業環境的應用上。

目前有 4 種較廣為人知的日誌式檔案系統：

□ XFS

□ JFS

□ ext3 / ext4

□ ReiserFS

其中 JFS 是由 IBM 所發展，有興趣的讀者可以參考 IBM 的說明：

<a href="http://www-128.ibm.com/developerworks/linux/library/l-jfs.html">http://www-128.ibm.com/developerworks/linux/library/l-jfs.html</a>

另外 XFS 是由 SGI 所發展。我們選擇目前較受觀迎的 ReiserFS 來做介紹。

<strong>ReiserFS 的特點</strong>

ReiserFS 官方網站：<a href="http://www.namesys.com/">http://www.namesys.com/</a>。

日誌式檔案系統被認為相當適合應用在大型的商業伺服器環境中。在這類的環境裡，資料完整性相當的重要。

日誌式檔案系統有著資料庫管理系統 (DBMS) 的交易機制 (Transaction) 特性，一連串的動作如果中間發生錯誤而中斷，資料可以被還原成原來的狀態，可確保資料不會因為未完成，而發生資料不完整的情形。

日誌式檔案會在分割區記錄使用資訊，檔案的寫入動作會先被記錄到記錄檔裡，進行寫入動作時如果中途發生中斷 (例如當機)，而重新啟動電腦時，日誌式的檔案系統會根據記錄檔將先前未完成的動作做回覆 (roll back)，因此可以保證資料不會因為寫入時發生的中斷，而產生不完整的資料。

<strong>安裝 ＲeiserFS V4</strong>

ReiserFS 目前搭載於 kernel 2.4 的是 V3 版，搭載於 kernel 2.6 的是 V4 版。Kernel 的 patch、設定與安裝可參考：

<a href="http://www.namesys.com/install_v4.html">http://www.namesys.com/install_v4.html</a>

另外，也需要用到 reiser4progs 工具程式，下載位置：

ftp://ftp.namesys.com/pub/reiser4progs/libaal-1.0.5.tar.gz
ftp://ftp.namesys.com/pub/reiser4progs/reiser4progs-1.0.5.tar.gz

要製作 reiser4 的分割區的話只要使用 mkfs.reiser4 工具即可：

linux# mkfs.reiser4 /dev/hda5  (範例)

要 mount reiserfs 分割區時，必須加上 -t reiser4 的參數:

linux# mount -t reiser4 /dev/hda5 /home2

如果要對 reiserfs 分割區做檢查，只要使用 fsck.reiser4 指令即可。]]></description>
         <link>https://www.jollen.org/blog/2006/10/reiserfs_1.html</link>
         <guid>https://www.jollen.org/blog/2006/10/reiserfs_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Tue, 24 Oct 2006 11:22:48 +0800</pubDate>
      </item>
            <item>
         <title>GNU gv 設定與使用心得</title>
         <description><![CDATA[GNU gv 是用來讀取 PostScript 與 PDF 格式文件的利器，GNU gv 其實只是一個前端界面 (front-end)，實際上 GNU gv 是由 ghostview 1.5 衍生而來的，並且使用 ghostscript 來直譯文件。

GNU gv 的官方網站：<a href="http://www.gnu.org/software/gv/">http://www.gnu.org/software/gv/</a>

<strong>gv 的使用</strong>

我們可以在 X Window System 底下直接下命令，例如：

linux# gv Trans.PS.gz

這是利用 gv 觀看 Trans.PS.gz 文件的範例，特別的是，經由 gzip 壓縮過的 PostScript 或 PDF 文件不需解壓即可直接利用 gv 閱讀，非常的方便。

利用 gv 也可以閱讀 PDF 文件，例如：

linux# gv imug.pdf

gv 命令列的參數：

□ -ad <resource file>

讀取 <resource file> 資源檔。-ad <resource file> 參數的優先權較 -style <resource file> 來得高。

□ -antialias，-noantialias

是否要使用 antialiasing 功能。

□ -arguments <arguments>

gv 是 ghostscript 的 front-end，在這裡的 <arguments> 是要傳給 ghostscript 的參數。

□ -center，-nocenter

是否要使用自動置中功能。

□ -dsc，-nodsc

是否要使用 DSC (document structuring convention)。如果不使用 DSC (-nodsc)，gv 就不會檢查文件的結構，並且直接將整個檔案傳給 ghostscript。在 -nodsc 模式下，閱讀文件時不會有頁數顯示，而且無法在文件裡移動。

□ -eof，-noeof

使用 -eof 時，當遇到結束的註解時，即表示文件的結束。有些文件，例如將兩個文件合併，雖然有結束的註解，但不表示檔案的結束，這時加上 -noeof 參數才能閱讀全部內容。

□ -pixmap，-nopixmap

如果使用 -pixmap 參數，gv 會以 pixmap 的方式將顯示過的內容保存下來，反之，-nopixmap 參數表示是由 X Server 負責這項工作，而不是 gv。

□ -v

查詢版本編號。

□ -h，-?

顯示求助畫面 (參數用法)。

□ -help

顯示較清楚的求助畫面。

□ -scale <n>

<n> 是一個整數，指定文件的放大倍數。

□ -scalebase <n>

設定預設的放大倍數。

□ -monochrome, -grayscale, -color

設定調色盤，即文件的顏色樣式。

□ -media <media>

設定紙張大小。

□ -page <label>

指定文件的標名。

□ -portrait，-landscape，-seascape，-upsidedown

設定文件的顯示位置。

□ -quiet，-noquiet

是否要以 -dQUIET 參數執行 ghostscript。

□ -resize，-noresize

設定gv是否可以自動依視窗大小調整文件的大小。

□ -safer，-nosafer

是否要以 -dSAFER 參數執行 ghostscript。

□ -spartan

和 -style gv_spartan.dat 同義。

□ -style <resource file>

指定設定檔 (resource file)。這個參數的優先權比 -ad <resource file> 高。

□ -swap，-noswap

是否要交換 landscape 與 seascape 的顯示方式。

□ -watch，-nowatch

使用 -watch 參數的話，預設是 gv 每一秒會檢查一次文件內容是否有改變，如果有的話，gv 即顯示新的內容。

<strong>gv 的設定</strong>

如果嫌每次都要打一堆煩人的參數，其實我們可以把這些常用的參數編輯在一個檔案裡 (resource file)。

gv 會先使用由 XFILESEARCHPATH 環境變數所指定的 resource file，如果沒有 XFILESEARCHPATH 環境變數，則使用 /usr/X11R6/lib/X11/app-defaults/GV，系統資源檔可以設定 gv 的預設執行環境。

我們可以依我們平時閱讀的習慣來設定自己的 gv 環境，個人化的 resource file 位於 ~/.gv，gv 會優先使用 ~/.gv 這個 resource file。

gv 也有提供我們 resource file 的設定範例，請由 /usr/X11R6/lib/X11/gv/ 將 gv_user.ad 複製到 ~/.gv：

linux# cp /usr/X11R6/lib/X11/gv/gv_user.ad ~/.gv

GNU gv 提供以下 3 個設定範例：

1. /usr/X11R6/lib/X11/gv/gv_class.ad
2. /usr/X11R6/lib/X11/gv/gv_system.ad
3. /usr/X11R6/lib/X11/gv/gv_user.ad

gv_user.ad 是 gv 個人 resource file 的設定範例，gv_system.ad 是系統 resource file 的設定範例，但新裝好的 gv，實際上這兩個檔案內容是一樣的。

gv 的 resource file (gv_user.ad)：
<blockquote>
!
!  gv_user.ad
!  User specific application defaults for gv
!  Copyright (C) 1995, 1996, 1997  Johannes Plass
!

!########## gv_user_res.dat

!##### Application specific Resources

GV.pageMedia:           automatic
GV.orientation:         automatic
GV.fallbackOrientation: portrait
GV.swapLandscape:       False
GV.autoCenter:          True
GV.antialias:           True
GV.respectDSC:          True
GV.ignoreEOF:           True
GV.confirmPrint:        True
GV.reverseScrolling:    False
GV.scrollingEyeGuide:   True
GV.autoResize:          True
GV.maximumWidth:        screen-20
GV.maximumHeight:       screen-44
GV.minimumWidth:        400
GV.minimumHeight:       430
GV.confirmQuit:         1
GV.watchFile:           False
GV.watchFileFrequency:  1000
GV.showTitle:           True
GV.miscMenuEntries:     redisplay       \n\
                        # update        \n\
                        stop            \n\
                        line            \n\
                        toggle_current  \n\
                        toggle_even     \n\
                        toggle_odd      \n\
                        unmark          \n\
                        line            \n\
                        print_all       \n\
                        print_marked    \n\
                        save_all        \n\
                        save_marked
GV.scale:               0
GV.scaleBase:           1
GV.scales:              Natural size,   1.000,  screen  \n\
                        Pixel based,    1.000,  pixel   \n\
                        0.100,          0.100           \n\
                        0.125,          0.125           \n\
                        0.250,          0.250           \n\
                        0.500,          0.500           \n\
                        0.707,          0.707           \n\
                        1.000,          1.000           \n\
                        1.414,          1.414           \n\
                        2.000,          2.000           \n\
                        4.000,          4.000           \n\
                        8.000,          8.000           \n\
                        10.00,          10.00
GV.medias:              Letter,         612     792     \n\
                        # LetterSmall,  612     792     \n\
                        Legal,          612     1008    \n\
                        Statement,      396     612     \n\
                        Tabloid,                792     1224    \n\
                        Ledger,         1224    792     \n\
                        Folio,          612     936     \n\
                        Quarto,         610     780     \n\
                        # 7x9,          504     648     \n\
                        # 9x11,         648     792     \n\
                        # 9x12,         648     864     \n\
                        # 10x13,                720     936     \n\
                        10x14,          720     1008    \n\
                        Executive,      540     720     \n\
                        # A0,           2384    3370    \n\
                        # A1,           1684    2384    \n\
                        # A2,           1191    1684    \n\
                        A3,             842     1191    \n\
                        A4,             595     842     \n\
                        # A4Small,      595     842     \n\
                        A5,             420     595     \n\
                        # A6,           297     420     \n\
                        # A7,           210     297     \n\
                        # A8,           148     210     \n\
                        # A9,           105     148     \n\
                        # A10,          73      105     \n\
                        # B0,           2920    4127    \n\
                        # B1,           2064    2920    \n\
                        # B2,           1460    2064    \n\
                        # B3,           1032    1460    \n\
                        B4,             729     1032    \n\
                        B5,             516     729     \n\
                        # B6,           363     516     \n\
                        # B7,           258     363     \n\
                        # B8,           181     258     \n\
                        # B9,           127     181     \n\
                        # B10,          91      127     \n\
                        # ISOB0,                2835    4008    \n\
                        # ISOB1,                2004    2835    \n\
                        # ISOB2,                1417    2004    \n\
                        # ISOB3,                1001    1417    \n\
                        # ISOB4,                709     1001    \n\
                        # ISOB5,                499     709     \n\
                        # ISOB6,                354     499     \n\
                        # ISOB7,                249     354     \n\
                        # ISOB8,                176     249     \n\
                        # ISOB9,                125     176     \n\
                        # ISOB10,       88      125     \n\
                        # C0,           2599    3676    \n\
                        # C1,           1837    2599    \n\
                        # C2,           1298    1837    \n\
                        # C3,           918     1296    \n\
                        # C4,           649     918     \n\
                        # C5,           459     649     \n\
                        # C6,           323     459     \n\
                        # C7,           230     323     \n\
                        # DL,           312     624
GV.magMenu:             2,      2       \n\
                        4,      4       \n\
                        8,      8       \n\
                        16,     16      \n\
                        32,     32      \n\
                        64,     64

!##### Ghostview Widget

GV*Ghostview.background:                white
GV*Ghostview.foreground:                black

!########## gv_intern_res.dat (generated by makefile)

GV.gsInterpreter:       gs
GV.gsCmdScanPDF:        gs -dNODISPLAY -dQUIET -sPDFname=%s -sDSCname=%s pdf2dsc.ps -c quit
GV.gsCmdConvPDF:        gs -dNODISPLAY -dQUIET  -dNOPAUSE -sPSFile=%s %s -c quit
GV.gsX11Device:         -sDEVICE=x11
GV.gsX11AlphaDevice:    -dNOPLATFONTS -sDEVICE=x11alpha
GV.gsSafer:             True
GV.gsQuiet:             True
GV.gsArguments:
GV.uncompressCommand:   gzip -d -c %s > %s
GV.printCommand:        lpr

!########## gv_make_res.dat (generated by makefile)

GV.scratchDir:          ~/
GV.defaultSaveDir:      ~/
GV.fallbackPageMedia:   letter
GV.useBackingPixmap:    True
GV*dirs:                Home\n\
                        Tmp\n\

                        /usr/doc\n\
                        /usr/local/doc
GV*filter:              no .*
GV*filters:             None\n\
                        *.*ps* *.pdf*  no  .*\n\
                        *.*ps*   no  .*\n\
                        *.pdf*  no  .*\n\
                        no  .*</blockquote>

<strong>設定說明</strong>

□ antialias 

是否使用 antialias 功能 (True/False)。

□ autoCenter

是否使用自動置中功能 (True/False)。

□ autoResize

是否依照目前文件的頁面大小來調整視窗 (True/False)。

□ confirmPrint

列印時是否需要額外的確認 (True/False)。

□ confirmQuit

離開時是否要確認 (0/1/2)。預設值是 1 表示文件內容有變更時 (例如轉換格式) 才確認，0 表示永遠做不確認，2 表示每次離開時都做確認。

□ scrollingEyeGuide

利用鍵盤捲動文件時是否提示上一頁的部份，使用這個參數，在捲動文件時才能分辨的出那些內容是上一頁的舊內容 (True/False)。

□ ignoreEOF

是否忽略 EOF，當 gv 遇到結束的註解時，並不表示檔案的結束，可能文件中間來雜了 EOF，設定 ignoreEOF=True 時才能完全閱讀這樣的文件 (True/False)。

□ respectDSC

是否要使用 DSC (document structuring convention)。如果不使用 DSC，gv 就不會檢查文件的結構，並且直接將整個檔案傳給 ghostscript (True/False)。在 respectDSC=False 時，閱讀文件時不會有頁數顯示，而且無法在文件裡移動。

□ swapLandscape

landscape 與 seacape 的義意是否要互相交換 (True/False)。

□ scratchDir

設定一個暫存目錄。

□ defaultSaveDir

設定一個預設的存檔目錄。

□ useBackingPixmap

False 表示由 X Server 負責暫存顯示過的內容。True 則是由 gv 以 pixmap 方式暫存顯示過的內容。

□ watchFile

是否要自動檢查文件的改變 (True/False)。

□ watchFileFrequency

設定檢查文件的時間間隔，單位為 milliseconds，這個值必須大於 500 (milliseconds)。當 WathFile 為 True 時這個設定才有效。

□ printCommand

設定列印文件的指令，%s 巨集可用在這個指令的參數上，表示要列印的文件檔名。

□ gsInterpreter

啟動 ghostscript interpreter 的命令。

□ gsCmdScanPDF

由 PDF 格式的檔案中讀取 DSC 的命令。

□ gsCmdConvPDF

將 PDF 的格式轉換成 PostScript 的命令。

□ gsX11Device

啟動 X11 的命令。

□ gsX11AlphaDevice

當 antialiasing 啟動時，執行 X11 的命令。

□ gsSafer

確認 ghostscript 是否應該以 -dSAFER 的參數啟動。

□ gsQuiet

確認 ghostscript 是否應該以 -dQUIET 的參數啟動。

□ gsArguments

設定要傳給 ghostscript 的參數。

□ dirs

設定在目錄列表時可以選擇的目錄。

□ filter

設定 gv 的目錄列表方式，語法：

&lt;filespecs> [no &lt;filespecs>]

例如：

GV*filter: *.ps *.pdf no .*

表示只列出 *.ps 與 *.pdf 的檔案，但不列出 "." 開頭的檔案 (隱藏檔)。

□ filters

設定 Fliters 選單中可用的目錄列表方式。

□ miscMenuEntries

設定在文件上按滑鼠右鍵的彈跳視窗內容(update/redisplay/toggle_current/toggle_even/toggle_odd/unmark/stop/print_all/print_marked/save_all/save_marked/line)。

□ showTitle

是否在 title 上顯示目前所閱讀的文件名稱。

□ maximumWidth, maximumHeight

設定主視窗的最小寬度與高度，必須是正整數，最大當然是螢幕的解析度設定。

□ minimumWidth, minimumHeight

設定主視窗的最小寬度與高度，必須是大於 200 的正整數。

□ scale

設定放大倍數。

□ scaleBase

設定預設的放大倍數，必須為正整數。

□ scales

設定在 Scale 選單中的可用的閱讀放大倍數。

□ orientation

設定預設的文件顯示位置 (portrait/landscape/seascape/upside-down/automatic)。automatic 表示由 DSC 取得。

□ fallbackOrientation

設定自動偵測失敗時的預設紙張大小，value 有 portrait/landscape/seascape/upside-down。

□ medias

設定紙張大小，在 gv 的 Media 選單中可以選擇，如果是以 "!" 或 "#" 開始，表示不列入 Media 的選單中，但仍可以被自動偵測並使用。

□ pagemedia

設定紙張大小，value 為在 paper-sizes 中的設定。value 為 automatic 的話，gv 會試著由 DSC 自動偵測大小。

□ fallbackPageMedia

設定紙張大小，value 為在 paper-sizes 中的設定。當自動偵測失敗時就使用這裡設定的大小。

<strong>gv 的操作</strong>

底下只是基本常用的按鍵，其它 gv 的操作熱鍵請參考 man gv。

□ O            開啟新檔案
□ Q            離開 gv
□ shift-ctrl-P 列印目檔的文件
□ ctrl-L       重新顯示目前這一頁
□ .            重新顯示目前這一頁

□ V            把目前文件置中
□ Home         跳到第一頁
□ End          跳到最後一頁

□ shift-up     把文件往上捲，或移動 -1 頁
□ shift-left   把文件往左移，或移動 -1 頁
□ shift-down   把文件往下捲，或移動  1 頁
□ shift-right  把文件往右移，或移動  1 頁

□ shift-up     移動 -1 頁
□ shift-down   移動  1 頁

□ c-Enter      移動 -1 頁
□ s-Enter      移動 -1 頁

□ Enter        移動  1 頁
□ B            移動 -1 頁
□ F            移動  1 頁

□ ctrl-left    移動 -5 頁
□ ctrl-right   移動  5 頁
□Insert       移動 -5 頁
□ Delete       移動  5 頁]]></description>
         <link>https://www.jollen.org/blog/2006/10/gnu_gv.html</link>
         <guid>https://www.jollen.org/blog/2006/10/gnu_gv.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 23 Oct 2006 16:23:55 +0800</pubDate>
      </item>
            <item>
         <title>網路上的 ext4 filesystem benchmark</title>
         <description><![CDATA[<p>網路上已經有人發表初步的 ext4 filesystem benchmark 數據了，該測試報告的作者將 ext4 的 "extents" 模式開啟，I/O scheduler 為 CFQ。<a href="http://linux.inet.hr/cfq_to_become_the_default_i_o_scheduler.html">CFQ 是 kernel 2.6.18 之後預設的 I/O 排程法</a>。</p>

<p>由這篇測試報告來看，ext4 在「寫入」的效能表現上，大幅領先 ext3 與 raiser4；不過在其它測試項，ext4 則是與 ext3/raiser4 互有領先。但是整體來看，ext4 的整體效能表現確實比 ext3 來得優秀。</p>

<p>該測試報告也提到，由於 ext4 仍在開發測試階段，一切都要等穩定版出現後，再來做更進一步的評估了。測試報告原文：</p>

<p><a href="http://linux.inet.hr/first_benchmarks_of_the_ext4_file_system.html">http://linux.inet.hr/first_benchmarks_of_the_ext4_file_system.html</a></p>

<p>Linux-kernel mailing list 也有討論串：</p>

<p><a href="http://forum.jollen.org/index.php?showtopic=6705">http://forum.jollen.org/index.php?showtopic=6705</a></p>

<p>  </p>
<table id="table11" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#3366cc"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#e5ecf9"><font size="-1"><b>Also See</b></font></td>
	</tr>
</table>
<table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
<ul>
  <li class="archive-list-item"><font size="2">2006.10.13:
  <a href="http://www.jollen.org/blog/2006/10/ext4.html">
  ext4 檔案系統現身了！</a></font></li>
</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/_ext4_filesystem_benchmark_1.html</link>
         <guid>https://www.jollen.org/blog/2006/10/_ext4_filesystem_benchmark_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Mon, 23 Oct 2006 11:15:53 +0800</pubDate>
      </item>
            <item>
         <title>Jollen 的 Linux 核心分享包,#1: 《開工篇》</title>
         <description><![CDATA[<p>Jollen 計畫在自己的 Blog 陸續與大與分享一些 Linux kernel 的研究心得，我們的寫作方向是以重點式的心得整理為主，不過希望加入一些教學性的風格，希望對大家有幫助。到目前為止，我們與大家分享了 Linux system call service 的幾篇日記，接下來仍會再討論幾個系統服務。</p>

<strong>故事是這樣開始的</strong>

<p>「Linux System Calls' Forum（LSCT）」旨在討論重要的系統服務（system service），以便將來我們能用最有效率的方式研究 kernel；「Jollen 的 Linux 核心分享包」則是在討論 Linux kernel 與作業系統有關的主題，方向是「討論 kernel 實作」。</p>

<p>Jollen 打算以「講義配合 Blog 開講」的方式跟大家一起玩核心！「一份講義」會以多篇日記方式跟大家討論，這裡的講義是從以前的筆記、演講、內訓課程或是討論會節錄並整理而成（需要時當然也會重新編製），Jollen 打算以講義的形式來整理，因此可能並不適合當做「教材」來使用；因此，在與朋友分享這份講義的同時，也要請大家一同分享 Jollen's Blog 網站。</p>

<p>第一份講義請由本文最後的網址下載，在開始看 kernel 前，以下的準備工作是很重要的：</p>

<p>1. 準備一份 Linux 2.6.11 以上的 kernel source code。請參考以下的幾張投影片，大略了解 kernel code 的分佈與原始碼結構。<br />
2. 準備 Source Insight。這個工具非常熱門，所以我想大家都己經有了！<br />
3. 先備知識（enabling technology）：作業系統原理是一定要啦。大家在學校都修過這門必修課，不過日記偶而也會提到參考章節，所以可以的話，也能準備一份在手邊。</p>

<strong>發佈計畫</strong>

<p>以目前的手稿整理狀況來看，大約會以一星期 1~2 篇日記的進度刊出，第一份講義計畫以 10 篇的篇幅來講解。</p>

<strong>Kernel Source 結構</strong>
<p>  </p>
<img alt="kernel_tree%20%281%29.JPG" src="http://www.jollen.org/blog/2006/10/22/kernel_tree%20%281%29.JPG" width="476" height="357" />
<p>  </p>
<img alt="kernel_tree%20%283%29.JPG" src="http://www.jollen.org/blog/2006/10/22/kernel_tree%20%283%29.JPG" width="476" height="357" />
<p>重要目錄：</p>
<ul>
<li>System Call Interfaces: 實作程式碼散落於各目錄</li>
<li>Network System: net/ 目錄</li>
<li>File System: fs/ 目錄</li>
<li>Memory Management: mm/ 目錄, 一些 helper-routine 實作於 arch/ 目錄</li>
<li>Process Management: 大部份實作於 kernel/ 目錄</li>
<li>Interprocess Communication: ipc/ 目錄</li>
<li>Device Drivers: drivers/ 目錄</li>
<li>SELinux (Security Enhanced Linux): security/ 目錄 (only kernel 2.6 or kernel 2.4 for mobile phone)</li>
</ul>
<p>  </p>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FF5959"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FFD8CA"><font size="-1"><b>下載講義</b></font></td>
	</tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li><a href="http://www.jollen.org/blog/2006/10/22/Jollen%20Blog_Linux%20Kernel_01_Process%20Scheduling%20Introduction.pdf">Jollen Blog_Linux Kernel_01_Process Scheduling Introduction.pdf</a> (522 KB)
</li>
		</ul>
		</td>
	</tr>
</table>
<p>  </p>
<table id="table11" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#3366cc"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#e5ecf9"><font size="-1"><b>本系列專欄旨在銜接以下的文章</b></font></td>
	</tr>
</table>
<table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
<ul>
  <li class="archive-list-item"><font size="2">2006.10.20:
  <a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_697_s.html">
  Linux System Calls' Forum, #6：(第97號系統服務) sys_setpriority</a></font></li>
  <li class="archive-list-item"><font size="2">2006.10.19:
  <a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_596_s.html">
  Linux System Calls' Forum, #5：(第96號系統服務) sys_getpriority</a></font></li>
  <li class="archive-list-item"><font size="2">2006.10.15: </font>
  <a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_434_s.html">
  <font size="2">Linux System Calls' Forum, #4：(第34號系統服務) sys_nice</font></a></li>
  <li class="archive-list-item"><font size="2">2006.10.14: </font>
  <a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_364_s.html">
  <font size="2">Linux System Calls' Forum, #3：(第64號系統服務) sys_getppid</font></a></li>
  <li class="archive-list-item"><font size="2">2006.10.12: </font>
  <a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_21992.html">
  <font size="2">Linux System Calls' Forum, #2：(第199,201,200,202,224號系統服務) 
  sys_getuid, sys_geteuid, sys_getgid, sys_getegid, sys_gettid</font></a></li>
  <li class="archive-list-item"><font size="2">2006.10.11: </font>
  <a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_120_s.html">
  <font size="2">Linux System Calls' Forum, #1：(第20號系統服務) sys_getpid</font></a></li>
</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/jollen_linux_1_1.html</link>
         <guid>https://www.jollen.org/blog/2006/10/jollen_linux_1_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 22 Oct 2006 21:04:21 +0800</pubDate>
      </item>
            <item>
         <title>Linux System Calls&apos; Forum, #6：(第97號系統服務) sys_setpriority</title>
         <description><![CDATA[<p>了解 <i>sys_getpriority()</i> 後，要看懂 <i>sys_setpriority() </i>就不是問題了。</p>
<table border="1" cellpadding="3" width="95%" id="table1" cellspacing="0" bordercolor="#3366cc" style="border-collapse: collapse">
	<tr>
		<td width="9%" bgcolor="#e5ecf9"><b>97</b></td>
		<td width="37%" bgcolor="#e5ecf9"><b>sys_setpriority</b></td>
		<td width="50%" bgcolor="#e5ecf9"><b>linux/kernel/sys.c</b></td>
	</tr>
	<tr>
		<td colspan="3">類別：Systems<br>
		原型宣告：long <b>sys_setpriority</b>(int which, int who, int niceval);<br>
		用途說明：變更 process 的排程優先序（Priority）。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：<pre>
asmlinkage long sys_setpriority(int which, int who, int niceval)
{
	struct task_struct *g, *p;
	struct user_struct *user;
	int error = -EINVAL;

	if (which > 2 || which < 0)
		goto out;

	/* normalize: avoid signed division (rounding problems) */
	error = -ESRCH;
	if (niceval < -20)
		niceval = -20;
	if (niceval > 19)
		niceval = 19;

	read_lock(&tasklist_lock);
	switch (which) {
		case PRIO_PROCESS:
			if (!who)
				who = current->pid;
			p = find_task_by_pid(who);
			if (p)
				error = set_one_prio(p, niceval, error);
			break;
		case PRIO_PGRP:
			if (!who)
				who = process_group(current);
			do_each_task_pid(who, PIDTYPE_PGID, p) {
				error = set_one_prio(p, niceval, error);
			} while_each_task_pid(who, PIDTYPE_PGID, p);
			break;
		case PRIO_USER:
			user = current->user;
			if (!who)
				who = current->uid;
			else
				if ((who != current->uid) && !(user = find_user(who)))
					goto out_unlock;	/* No processes for this user */

			do_each_thread(g, p)
				if (p->uid == who)
					error = set_one_prio(p, niceval, error);
			while_each_thread(g, p);
			if (who != current->uid)
				free_uid(user);		/* For find_user() */
			break;
	}
out_unlock:
	read_unlock(&tasklist_lock);
out:
	return error;
}</pre>
		</td>
	</tr>
</table>
<p><b>Jollen 的說明</b></p>
<p>程式一開始先判斷 <i>which</i> 參數的正確性，並且將 user process 所指定的 nice 值限定在 [-20..19] 的範圍內：</p>
<pre>
	if (which > 2 || which < 0)
		goto out;

	/* normalize: avoid signed division (rounding problems) */
	error = -ESRCH;
	if (niceval < -20)
		niceval = -20;
	if (niceval > 19)
		niceval = 19;</pre>
<p>底下我們來討論 <i>PRIO_PROCESS</i> 與 <i>PRIO_PGRP</i> 
參數的實作，強烈建議您先行閱讀本文最後所整理的文章，再回頭來看以下的說明，才能得到最佳學習效果。</p>
<p><b>變更 priority：PRIO_PROCESS</b></p>
<p>主要的程式片斷如下：</p>
<pre>	case <b>PRIO_PROCESS</b>:   /* 請看 1. */
		if (!who)
			who = current->pid;   /* 請看 1. */
		p = find_task_by_pid(who);   /* 請看 2. */
		if (p)   /* 請看 3. */
			error = <b><font color="#FF0000">set_one_prio</font></b>(p, niceval, error);   /* 請看 3. */
		break;</pre>
<p>說明：</p>
<ol>
	<li>若 <i>which = PRIO_PROCESS</i>，則 <i>who </i>的值就是 process ID。因為<i> who </i>
	不能為 0，所以如果 <i>who</i> 是 0 的話，就指定為 <i>current </i>的 PID。</li>
	<li><i>find_task_by_pid()</i> 是 kernel API，用來取得 PID 的 process descriptor。<b>我們已經學過了！</b></li>
	<li>如果 <i>p</i> 不是 NULL，就呼叫 <i>set_one_prio()</i>，修改 <i>p</i> 的「nice 值」為 <i>
	niceval</i>。</li>
</ol>
<p><b>變更 priority：PRIO_PGRP</b></p>
<p>主要的程式片斷如下：</p>
<pre>	case <b>PRIO_PGRP</b>:
		if (!who)
			who = process_group(current);
		do_each_task_pid(who, PIDTYPE_PGID, p) {   /* 請看 1. */
			error = <b><font color="#FF0000">set_one_prio</font></b>(p, niceval, error);   /* 請看 1. */
		} while_each_task_pid(who, PIDTYPE_PGID, p);   /* 請看 1. */
		break;</pre>
<p>說明：</p>
<ol>
	<li>在迴圈裡，逐一修改 process group 裡的 process，將其優先序變更為 <i>niceval</i>。這樣的迴圈，在 
	<i>sys_getpriority()</i> 也看過一次！</li>
</ol>
<p>二段 kernel code 都呼叫到此 kernel API：</p>
<ul>
	<li>kernel API - <i>set_one_prio()</i>：傳入 process descriptor 與新的 nice 值，將 
	process 的 priorioty 做變更。</li>
</ul>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FF5959"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FFD8CA"><font size="-1"><b>TIP</b></font></td>
	</tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li>&quot;<i>*_PGRP</i>&quot;（process group）是 kernel 2.4 的 sematics，「<i>current-&gt;pgrp</i>」是 
			kernel 2.4 的寫法。我們未來有機會的話再做說明。</li>
		</ul>
		</td>
	</tr>
</table>
<p>　</p>
<table id="table11" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#3366cc"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#e5ecf9"><font size="-1"><b>跟上我們的腳步：請讀以下的文章，再看這篇日記！</b></font></td>
	</tr>
</table>
<table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li>
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_596_s.html">
			Linux System Calls' Forum, #5：(第96號系統服務) sys_getpriority</a></li>
		</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_697_s.html</link>
         <guid>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_697_s.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 20 Oct 2006 19:38:13 +0800</pubDate>
      </item>
            <item>
         <title>再探 sys_getpriority()</title>
         <description><![CDATA[<em>getpriority()</em> 若指定 <em>which</em> 是 <em>PRIO_PGRP</em> 的話，那麼便能取得 process group 裡的「最大 priority 值」。先節錄相關 kernel code 如下：

<pre>
asmlinkage long sys_getpriority(int which, int who)
{
		...
		case PRIO_PGRP:
			if (!who)
				who = process_group(current);
			do_each_task_pid(who, PIDTYPE_PGID, p) {
				niceval = 20 - task_nice(p);
				if (niceval > retval)
					retval = niceval;
			} while_each_task_pid(who, PIDTYPE_PGID, p);
			break;
		...
}
</pre>

<p>接續昨天的 <em>sys_getpriority()</em>，讓我們再來看一下 <strong>PRIO_PGRP</strong> 參數的實作：</p>

<p>1. 如果<em> who</em> 是 0 的話，先呼叫 <em>process_group()</em> 取得 group ID。<br />
2. 使用 <em>do_each_task_pid</em> 與 <em>while_each_task_pid</em> 巨集做迴圈。<br />
3. 在迴圈裡面，呼叫 task_nice() 取得 process 的 priority，然後判斷目前的 nice 值是否大於先前的 nice 值。這麼做的用意是「找到 process group 裡最大的 nice 值 (lowest priority) 後回傳」。

<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#e5ecf9"><font size="-1"><b>跟上我們的腳步：請讀以下的文章，再看這篇日記！</b></font></td>
	</tr>
</table>
<table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li>
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_596_s.html">
			Linux System Calls' Forum, #5：(第96號系統服務) sys_getpriority</a></li>
		</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/_sys_getpriority.html</link>
         <guid>https://www.jollen.org/blog/2006/10/_sys_getpriority.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 20 Oct 2006 16:13:25 +0800</pubDate>
      </item>
            <item>
         <title>Kernel 2.6 的 KVM (Kernel Virtual Machine) 驅動程式來了</title>
         <description><![CDATA[在伺服器（server）技術的發展藍圖中（roadmap），虛擬伺服器（virtual server）是 Intel 近來所著墨的重點。由以往單一硬體對單一作業系統（operating system）的伺服器架構（1 physical server v.s. 1 OS），未來將會演進成為單一硬體對多個作業系統的架構（1 physical server v.s. multiple OS）。

Intel 的 Virtualization Technology 就是一個這樣的新技術。Intel 的 Virtualization Technology 可以讓處理器支援多個 OS，不過這其實是基於我們所熟悉的虛擬機器軟體（例如：VMware、QEMU、Xen等）才能達到的，所以這是一個由「processor + chipsets + BIOS + 虛擬機器軟體」互相運作所實現的技術。

<strong>Virtual Machine Monitor (VMM)</strong>

軟體實作的虛擬機器在此技術領域中被統稱為「VMM - Virtual Machine Monitor」，現在已經有支援 Intel Virtualization Technology 的 VMM 了，那就是知名的 Xen。相關的參考網址如下：

Extending Xen with Intel Virtualization Technology - <a href="http://www.intel.com/technology/itj/2006/v10i3/3-xen/1-abstract.htm">http://www.intel.com/technology/itj/2006/v10i3/3-xen/1-abstract.htm</a>
Xen - <a href="http://www.xensource.com/index.html">http://www.xensource.com/index.html</a>

接下來，就是我們今天在網路上所看到的重點了！

<strong>KVM: Kernel-based Virtual Machine</strong>

今天，看到了 2.6 kernel 的 KVM patch。KVM（Kernel Virtual Machine）是 Intel's virtualization technology 的驅動程式！KVM 驅動程式目前支援 i386 與 x86_64 "host"，且「All combinations are allowed except x86_64 guest on i386 host.」。

比較特殊的是 Linux 2.6 的 KVM 驅動程式支援三種模式：kernel mode、user mode 與 guest mode。KVM 驅動程式的 user-space 介面是 '/dev/kvm'，因此 process 可以執行自己的 virtual machine，在一台電腦上也能執行多個 virtual machine。Guest mode：

<blockquote>「Guest mode has its own address
space mapping guest physical memory (which is accessible to user mode by
mmap()ing /dev/kvm). Guest mode has no access to any I/O devices; any such
access is intercepted and directed to user mode for emulation. 」。</blockquote>

取得 Patch 與追踪主題

<a href="http://forum.jollen.org/index.php?showtopic=6315">[PATCH 0/7] KVM: Kernel-based Virtual Machine</a>
<a href="http://forum.jollen.org/index.php?showtopic=6316">[PATCH 1/7] KVM: userspace interface</a>
<a href="http://forum.jollen.org/index.php?showtopic=6317">[PATCH 2/7] KVM: Intel virtual mode extensions definitions</a>
<a href="http://forum.jollen.org/index.php?showtopic=6318">[PATCH 3/7] KVM: kvm data structures</a>
<a href="http://forum.jollen.org/index.php?showtopic=6319">[PATCH 5/7] KVM: mmu virtualization</a>
<a href="http://forum.jollen.org/index.php?showtopic=6320">[PATCH 6/7] KVM: x86 emulator</a>
<a href="http://forum.jollen.org/index.php?showtopic=6321">[PATCH 7/7] KVM: plumbing</a>

<strong>關於更多 Intel 的 Virtualization Technology</strong>

可以參考的網址如下：

Make virtualization a reality - <a href="http://www.intel.com/cd/channel/reseller/asmo-na/eng/products/server/processors/250640.htm">http://www.intel.com/cd/channel/reseller/asmo-na/eng/products/server/processors/250640.htm</a>
Server Virtualization Technology - <a href="http://www.intel.com/business/bss/products/server/virtualization_wp.pdf">http://www.intel.com/business/bss/products/server/virtualization_wp.pdf</a>
]]></description>
         <link>https://www.jollen.org/blog/2006/10/kernel_26_kvm_kernel_virtual_m.html</link>
         <guid>https://www.jollen.org/blog/2006/10/kernel_26_kvm_kernel_virtual_m.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 19 Oct 2006 23:27:51 +0800</pubDate>
      </item>
            <item>
         <title>Linux System Calls&apos; Forum, #5：(第96號系統服務) sys_getpriority</title>
         <description><![CDATA[<p><i>nice() </i>是用來變更 process 優先序（priority）的 system call 也是最古老的一個，<i>sys_getpriority()</i> 
與 <i>sys_setpriority()</i> 則是取代 <i>sys_nice()</i> 的新實作，今天我們先討論一下 'sys_getpriority'。</p>
<table border="1" cellpadding="3" width="95%" id="table1" cellspacing="0" bordercolor="#3366cc" style="border-collapse: collapse">
	<tr>
		<td width="9%" bgcolor="#e5ecf9"><b>96</b></td>
		<td width="37%" bgcolor="#e5ecf9"><b>sys_getpriority</b></td>
		<td width="50%" bgcolor="#e5ecf9"><b>linux/kernel/sys.c</b></td>
	</tr>
	<tr>
		<td colspan="3">類別：Systems<br>
		原型宣告：long <b>sys_getpriority</b>(int which, int who);<br>
		用途說明：取得 process 的排程優先序（Priority）。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：<pre>
/*
 * Ugh. To avoid negative return values, "getpriority()" will
 * not return the normal nice-value, but a negated value that
 * has been offset by 20 (ie it returns 40..1 instead of -20..19)
 * to stay compatible.
 */
asmlinkage long sys_getpriority(int which, int who)
{
	struct task_struct *g, *p;
	struct user_struct *user;
	long niceval, retval = -ESRCH;

	if (which > 2 || which < 0)
		return -EINVAL;

	read_lock(&tasklist_lock);
	switch (which) {
		case PRIO_PROCESS:
			if (!who)
				who = current->pid;
			p = find_task_by_pid(who);
			if (p) {
				niceval = 20 - task_nice(p);
				if (niceval > retval)
					retval = niceval;
			}
			break;
		case PRIO_PGRP:
			if (!who)
				who = process_group(current);
			do_each_task_pid(who, PIDTYPE_PGID, p) {
				niceval = 20 - task_nice(p);
				if (niceval > retval)
					retval = niceval;
			} while_each_task_pid(who, PIDTYPE_PGID, p);
			break;
		case PRIO_USER:
			user = current->user;
			if (!who)
				who = current->uid;
			else
				if ((who != current->uid) && !(user = find_user(who)))
					goto out_unlock;	/* No processes for this user */

			do_each_thread(g, p)
				if (p->uid == who) {
					niceval = 20 - task_nice(p);
					if (niceval > retval)
						retval = niceval;
				}
			while_each_thread(g, p);
			if (who != current->uid)
				free_uid(user);		/* for find_user() */
			break;
	}
out_unlock:
	read_unlock(&tasklist_lock);

	return retval;
}</pre>
		</td>
	</tr>
</table>
<p><b>Jollen 的說明</b></p>
<p>讓我們來看一下 system call service - 'sys_getpriority' 的程式實作。首先是 <i>sys_getpriority()</i> 
的參數：<i>which</i> 與 <i>who</i>，這二個參數對應到 <i>getpriority() </i>的 <i>which</i> 與 <i>
who</i> 參數；如果您不是很清楚 <i>which</i>/<i>who</i> 的話，請參閱<a href="http://www.jollen.org/blog/2006/10/getpriority_setpriority.html">前幾天的日記</a>。</p>
<p>我們拿底下的程式片斷來討論：</p>
<pre>
	switch (<b>which</b>) { /* 請看 1. */
		case <b>PRIO_PROCESS</b>: /* 請看 2. */
			if (!who) /* 請看 2. */
				who = current->pid;
			p = <font color="#FF0000">find_task_by_pid</font>(who); /* 請看 3. */
			if (p) { /* 請看 4. */
				niceval = 20 - <font color="#FF0000">task_nice</font>(p); /* 請看 5. */
				if (niceval > retval)
					retval = niceval;
			}
			break;
		...
	}</pre>
<p>說明如下：</p>
<ol>
	<li>判斷 <i>which</i> 參數，假設我們想要取得 process 的 priority，那麼 <i>which</i> 的值就是 <i>
	PRIO_PROCESS</i>。</li>
	<li>若 <i>which = PRIO_PROCESS</i>，則 <i>who </i>的值就是 process ID。因為<i> who </i>
	不應該為 0，所以如果 <i>who</i> 是 0 的話，就指定為 <i>current </i>的 PID。</li>
	<li><i>find_task_by_pid()</i> 是 kernel API，用來取得 PID 的 process descriptor。</li>
	<li>如果 <i>p</i> 不是 NULL，就呼叫 <i>task_nice()</i>，取得 process 的「nice 值」，然後傳回 
	nice 值。</li>
	<li>kernel 的 nice 值範圍為 [-20..19]，但 user-space 的 nice 值是 
	[40,1]，所以要做換算：[20-(-20)..20-19] = [40..1]。</li>
</ol>
<p>喔！要取得 process priority 的 kernel 實作看起來還挺簡單的。我們又學到了：</p>
<ul>
	<li>kernel API - <i>find_task_by_pid()</i>：傳入 PID，取得該 process 的 process 
	descriptor。</li>
	<li>kernel API - <i>task_nice()</i>：傳入 process descriptor，取得該 process 的 
	priority（nice 值）。</li>
</ul>
<table id="table16" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FF5959"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table14" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#FFD8CA"><font size="-1"><b>TIP</b></font></td>
	</tr>
</table>
<table id="table15" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li>所謂的「nice 值」就是 process 的優先序（優先等級、priority）。</li>
		</ul>
		</td>
	</tr>
</table>
<p>　</p>
<table id="table11" border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#3366cc"><img alt="" height="1" width="1"></td>
	</tr>
</table>
<table id="table12" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td bgcolor="#e5ecf9"><font size="-1"><b>跟上我們的腳步：請讀以下的文章，再看這篇日記！</b></font></td>
	</tr>
</table>
<table id="table13" border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr>
		<td width="100%">
		<ul>
			<li>
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_120_s.html">
			Linux System Calls' Forum, #1：(第20號系統服務) sys_getpid</a></li>
			<li>
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_21992.html">
			Linux System Calls' Forum, #2：(第199,201,200,202,224號系統服務) sys_getuid, 
			sys_geteuid, sys_getgid, sys_getegid, sys_gettid</a></li>
			<li>
			<a href="http://www.jollen.org/blog/2006/10/linux_system_calls_forum_434_s.html">
			Linux System Calls' Forum, #4：(第34號系統服務) sys_nice</a></li>
			<li>
			<a href="http://www.jollen.org/blog/2006/10/getpriority_setpriority.html">
			getpriority() 與 setpriority() 程式設計</a></li>
		</ul>
		</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_596_s.html</link>
         <guid>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_596_s.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 19 Oct 2006 21:07:56 +0800</pubDate>
      </item>
            <item>
         <title>座談演講投影片下載</title>
         <description><![CDATA[跟學弟妹的座談演講，分享自己的工作經驗，並且簡單介紹該如何進入嵌入式 Linux 領域。<a href="http://www.jollen.org/blog/2006/10/19/2006-10-19%20%E8%AB%87%E8%A9%B1%E6%BC%94%E8%AC%9B.pdf">請按我下載</a>]]></description>
         <link>https://www.jollen.org/blog/2006/10/post_3.html</link>
         <guid>https://www.jollen.org/blog/2006/10/post_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">其它</category>
        
        
         <pubDate>Thu, 19 Oct 2006 16:35:19 +0800</pubDate>
      </item>
            <item>
         <title>是蟲啦，不是警告。</title>
         <description><![CDATA[<p>是蟲啦，不是警告。在 Linux kernel 的 mailing list 看到一則有意思的張貼，<a href="http://forum.jollen.org/index.php?showtopic=6002">全文</a>如下，Jollen 把重點用紅色標示出來：</p>

<blockquote><pre>
* Jeremy Fitzhardinge <jeremy@goop.org> wrote:

> A warning is a warning, not a BUG.

> - printk("BUG: warning at %s:%d/%s()n", __FILE__,
> + printk("WARNING at %s:%d %s()n", __FILE__,

<font color="#ff0000">i'm not really happy about this change.</font>

Firstly, 
<font color="#ff0000">most WARN_ON()s are /bugs/, not warnings</font> ... 
<font color="#ff0000">If it's a real
warning, a KERN_INFO printk should be done.</font>

Secondly, the reason i changed it to the 'BUG: ...' format is that i
tried to make it easier for automated tools (and for users) to figure
out that a kernel bug happened.

Ingo
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/ 
</pre></blockquote>

<p>很重要，一定要看的概念。在 Linux kernel 裡，WARN_ON() 所秀出來的訊息格式為「BUG: xxx」，表示這是一個臭蟲。如果是警告訊息，則是用 loglevel 'KERN_INFO' 來列印訊息。</p>

<p>所以，這位老兄解釋的很清楚，這類的 "warning" 訊息是 kernel 的 "bug"，並不是字面上所表示的「警告」訊息！</p>
]]></description>
         <link>https://www.jollen.org/blog/2006/10/post_2.html</link>
         <guid>https://www.jollen.org/blog/2006/10/post_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Wed, 18 Oct 2006 21:20:09 +0800</pubDate>
      </item>
            <item>
         <title>getpriority() 與 setpriority() 程式設計</title>
         <description><![CDATA[<p>前幾天介紹到 Linux 的一個排程（Scheduling）系統服務叫做 <em>sys_nice()</em>，與 <em>sys_nice()</em> 相關的 system call service 是 <em>sys_getpriority()</em> 與 <em>sys_setpriority()</em>，不過在看 kernel code 前，我們應該先試著了解 <em>getpriority()</em> 與 <em>setpriority() </em>二個 system call 的用法。</p>

<p>先來了解一下 <em>getpriority()</em> 的用途，GETPRIORITY(2)：</p>
<pre><blockquote>
SYNOPSIS
       #include &lt;sys/time.h>
       #include &lt;sys/resource.h>

       int getpriority(int which, int who);
</blockquote></pre>

<p><em>getpriority()</em> 的 <em>which</em> 參數用來指定要取得 priority 對象：process、process group 或 user ID。</p>

<p>最容易學習的方式就是讀範例，所以我們將會設計 2 個範例來執行。</p>

<p><em>which</em> 參數：</p>

<ul>
<li><strong>PRIO_PROCESS</strong>：<em>who</em> 參數指定 process ID，傳回 process 的 priority。
<li><strong>PRIO_PGRP</strong>：<em>who</em> 參數指定 process group ID，傳回 process group 的 priority。
<li><strong>PRIO_USER</strong>：<em>who</em> 參數指定 user ID，傳回 user 的 priority 。
</ul>

Jollen 寫了二個程式：<em>setpriority.c</em> 與 <em>getpriority.c</em>。先來測試一下。

<p><strong>實測結果</strong></p>

<p>隨便找一個對象下手：</p>
<pre><blockquote>
# ps ax
...
17349 ?        S      0:02 [httpd]
...
</blockquote></pre>

<p>把 PID 17349 的 scheduling priority 往上提升一級：</p>
<pre><blockquote>
# ./getpriority 17349
Process (17349) <strong>Priority is 0</strong>.
# ./setpriority
Usage: ./setpriority [pid] [priority (-20~19)]
# ./setpriority 17349 -1
OK.
# ./getpriority 17349
Process (17349) <strong>Priority is -1</strong>.
</blockquote></pre>

另外，系統指令 nice 可以用來設定執行命令的 process priority：
<pre><blockquote>
NICE(1)                               FSF                              NICE(1)

NAME
       nice - run a program with modified scheduling priority

SYNOPSIS
       nice [OPTION] [COMMAND [ARG]...]

DESCRIPTION
       Run  COMMAND  with  an  adjusted scheduling priority.  With no COMMAND,
       print the current scheduling priority.  ADJUST is 10 by default.  Range
       goes from -20 (highest priority) to 19 (lowest).

       -n, --adjustment=ADJUST
              increment priority by ADJUST first

       --help display this help and exit

       --version
              output version information and exit
</blockquote></pre>

<p><strong>程式碼</strong></p>

<pre>
/* Program: <strong>getpriority.c </strong>*/
#include &lt;stdio.h>
#include &lt;unistd.h>
#include &lt;sys/types.h>
#include &lt;sys/time.h>
#include &lt;sys/resource.h>

int main(int argc, char *argv[])
{
   /* getpriority() 傳回的類別是 int */
   int prio_process, prio_pgroup, prio_user;
   pid_t pid;

   if (argc != 2)
      return -1;

   pid = atoi(argv[1]);
   prio_process = getpriority(PRIO_PROCESS, pid);

   printf("Process (%d) Priority is %d.\n", pid, prio_process);
   return 0;
}
</pre>

<pre>
/* Program: <strong>setpriority.c </strong>*/
#include &lt;stdio.h>
#include &lt;unistd.h>
#include &lt;sys/types.h>
#include &lt;sys/time.h>
#include &lt;sys/resource.h>

int main(int argc, char *argv[])
{
   int errno, prio;
   pid_t pid;

   if (argc != 3) {
      printf("Usage: %s [pid] [priority (-20~20)]\n", argv[0]);
      return -1;
   }

   pid = atoi(argv[1]);
   prio = atoi(argv[2]);
   errno = setpriority(PRIO_PROCESS, pid, prio);

   printf("OK.\n");

   return 0;
}
</pre>

了解 s<em>etpriority()</em> 與 <em>getpriority()</em> 後，就可以開始研究 system call service - 'sys_setpriority' 與 'sys_getpriority' 了。]]></description>
         <link>https://www.jollen.org/blog/2006/10/getpriority_setpriority.html</link>
         <guid>https://www.jollen.org/blog/2006/10/getpriority_setpriority.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Tue, 17 Oct 2006 22:40:20 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 的系統管理,#5: &apos;./configure&apos; 與 Software Stack</title>
         <description><![CDATA['./configure' 的設定決定 root filesystem 的架構（architecture），即 software stack 的設計。以我們「Embedded Linux 的系統管理 (x86)」課程舉出的 Nano-X project 案例來說，我們可以規劃出 2 種架構：

1. 典型的 Desktop (PC) 使用的架構 - ffplay + SDL + x11。
2. 讓 ffplay 在 Nano-X 介面裡撥放影片 - ffplay + SDL + Nano-X。

其中第 2 點是最有趣的架構，可以讓同學練習 software stack 有關的 './configure' 設定。

首先，同學要先把 FFmpeg 在 x86 上編譯完成，然後我們透過不同的 SDL 管理觀念，讓 ffplay  能夠在 X11 與 Nano-X 的介面上撥放。

不管是在 X11 上撥放影片，或是在 Nano-X 上撥放，都不必動到 FFmpeg（ffplay）。(why?)

<strong>嵌入式 Linux 系統管理 - Software Stack 的設定</strong>

如果使用 Red Hat Linux 9 (or Fedora) 預設的 SDL，ffplay 的執行畫面如下圖。

<img alt="ffplay_x11.jpg" src="http://www.jollen.org/blog/2006/10/15/ffplay_x11.jpg" width="252" height="345" />
圖一：在 X11 上執行的 ffplay

這時 SDL 的編譯設定 (configure) 應該是類似這樣的：

<blockquote># ./configure --prefix=/usr 
<strong>--enable-video-x11 </strong>
--disable-video-fbcon 
<strong>--disable-nano-direct-fb </strong>
--disable-video-nanox 
--disable-nanox-share-memory 
--disable-alsatest 
--with-nanox-pixel-type=0888</blockquote>

我們修改 configure 參數如下:

<blockquote># ./configure --prefix=/usr 
<strong>--disable-video-x11 </strong>
--disable-video-fbcon 
<strong>--enable-nano-direct-fb </strong>
--enable-video-nanox 
--enable-nanox-share-memory 
--disable-alsatest
--with-nanox-pixel-type=0888</blockquote>

重新編譯並安裝 SDL 後，ffplay 就能在 Nano-X 的介面下撥放影片。如果 Nano-X 的設定是支援 X11 的話，執行後的結果會是下圖的畫面。

<img alt="ffplay_nanox.jpg" src="http://www.jollen.org/blog/2006/10/15/ffplay_nanox.jpg" width="652" height="505" />
圖二：在 Nano-X 上執行的 ffplay (Nano-X with X11)

我們怎麼讓 ffplay 可以在開機後，直接透過 VGA framebuffer 來撥放影片，而不用再進入 X11 呢？做法是：把 Nano-X 設定成支援 VGA framebuufer 即可（Nano-X with VGA FB），至於 SDL 的話，是不用再做任何調整的了！(why?) 當然 ffplay 也不用變動。所以，關於第 2 點的架構做法，可以再細分成：

2.1 ffplay + SDL + Nano-X + X11
2.2 ffplay + SDL + Nano-X + VGA fb

很值得用來練習編譯設定與觀念討論的例子。

<strong>Embedded Linux 的考量</strong>

「ffplay + SDL + Nano-X」這個架構是適合 Embedded Linux 使用的，只要讓 Nano-X 能完整支援 target device，並把 SDL 與 FFmpeg 移植到 target device，便能在 target device 上撥放影片檔。]]></description>
         <link>https://www.jollen.org/blog/2006/10/embedded_linux_5_configure_sof.html</link>
         <guid>https://www.jollen.org/blog/2006/10/embedded_linux_5_configure_sof.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Sun, 15 Oct 2006 12:01:46 +0800</pubDate>
      </item>
            <item>
         <title>Linux System Calls&apos; Forum, #4：(第34號系統服務) sys_nice</title>
         <description><![CDATA[<p><i>nice() </i>是用來變更 process 優先序（priority）的 system call，<i>nice() </i>system 
call 叫用（invoke）的 system call 實作常式為<i> sys_nice</i>。<i>sys_nice</i> 服務常式（service 
routine）是 Linux 排程（scheduling）相關的服務常式中最基本（也是最古老）的一個。</p>
<table border="1" width="95%" cellpadding="2">
	<tr>
		<td width="9%">34</td>
		<td width="37%">sys_nice</td>
		<td width="50%">linux/kernel/sched.c</td>
	</tr>
	<tr>
		<td colspan="3">類別：Scheduling<br>
		原型宣告：long <b>sys_nice</b>(int increment);<br>
		用途說明：變更 process 的優先序。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：<pre>
/*
 * sys_nice - change the priority of the current process.
 * @increment: priority increment
 *
 * sys_setpriority is a more generic, but much slower function that
 * does similar things.
 */
asmlinkage long sys_nice(int increment)
{
	int retval;
	long nice;

	/*
	 * Setpriority might change our priority at the same moment.
	 * We don't have to worry. Conceptually one call occurs first
	 * and we have a single winner.
	 */
	if (increment < -40)
		increment = -40;
	if (increment > 40)
		increment = 40;

	nice = PRIO_TO_NICE(current->static_prio) + increment;
	if (nice < -20)
		nice = -20;
	if (nice > 19)
		nice = 19;

	if (increment < 0 && !can_nice(current, nice))
		return -EPERM;

	retval = security_task_setnice(current, nice);
	if (retval)
		return retval;

	set_user_nice(current, nice);
	return 0;
}</pre></td>
	</tr>
</table>
<p><b>Jollen 的說明</b></p>
<p>首先，讓我們先看一下 NICE(2) 的內容：</p>
<blockquote>
	<pre>NAME
       nice - change process priority

SYNOPSIS
       #include <unistd.h>

       int nice(int inc);

DESCRIPTION
       nice  adds  inc  to  the nice value for the calling pid.  (A large nice
       value means a low priority.)  Only the superuser may specify a negative
       increment, or priority increase.</pre>
</blockquote>
<p>參數<i> inc</i> 是所要改變的 priority 數量，<i>inc </i>可以是正數也可以是負數。正數表示 priority 變多（多加上 
inc，表示&nbsp; low 
priority），若要提升 priority，<i>inc </i>只要指定為負數即可；根據 NICE(2) 的說明，只有 superuser 
才能指定負數，也就是把提高優先序。</p>
<p><i>nice() </i>會把 process 的 priority 變更為 &quot;priority+<i> inc</i>&quot;。</p>
<p><b>sys_nice() 的實作</b></p>
<p>以下是 <i>sys_nice() </i>service routine 的原型：</p>
<blockquote>
	<p>long sys_nice(int increment)</p>
</blockquote>
<p><i>increment</i> 就是前面所提到的 <i>inc</i> 參數（透過 system call 機制傳遞，並非函數呼叫傳值）。increment 的範圍被 kernel 
限定在 [40,-40] 的範圍內：</p>
<pre>	if (increment < -40)
		increment = -40;
	if (increment > 40)
		increment = 40;</pre>
<p>所以如果呼叫 nice() 時，指定的優先改變量超過 [40,-40] 時，是得不到預期效果的。<i>sys_nice</i> 接著先把 process 
的 &quot;priority&quot; 數值轉算成 &quot;nice&quot; 值，然後再加上 increment（因為 increment 是一個 &quot;nice&quot; 值，不是 
&quot;priority&quot; 值）：</p>
<pre>
	nice = PRIO_TO_NICE(current->static_prio) + increment;</pre>
<p>得到的 &quot;nice&quot; 值，必須限定在 [19,-20] 的範圍內：</p>
<pre>
	if (nice < -20)
		nice = -20;
	if (nice > 19)
		nice = 19;</pre>
<p>再來，如果 <i>increment </i>小於 0，也就是 user 的 &quot;nice&quot; 值是負的，那就要檢查是不是可以變更此 process 的 
&quot;priority&quot;：</p>
<pre>
	if (increment < 0 && !<b>can_nice</b>(current, nice))
		return -EPERM;　</pre>
<p>不過這裡的重點是 <i>can_nice()</i> 所呼叫的 kernel API - <font color="#FF0000"><i>
capable()</i></font>，並非 <i>can_nice()</i> 本身，再追一下<i> can_nice()</i> 的原始碼可以找到這段：</p>
<blockquote>
	<p>capable(CAP_SYS_NICE)</p>
</blockquote>
<p><i>capable()</i> 用來檢查 process 的 &quot;capability&quot;，不過我們先在這裡略過這個主題。</p>
<p>Process capability 若允許變更 priority，就修改 process 的 priority，否則 return <i>-EPERM</i>。</p>
<p><b>更多關於 sys_nice()</b></p>
<p>sys_nice() 目前大概是為了要向後相容而持續存在於 Linux 原始碼裡，目前已經推薦改用 sys_setpriority() 服務常式了。因此在 
Linux programming 上，我們也要改用 setpriority() system call 來取代 nice()。以下是 
GETPRIORITY(2)：</p>
<blockquote>
	<pre>NAME
       getpriority, setpriority - get/set program scheduling priority

SYNOPSIS
       #include <sys/time.h>
       #include <sys/resource.h>

       int getpriority(int which, int who);
       int setpriority(int which, int who, int prio);</pre>
</blockquote>
<table border="1" width="100%" id="table1">
	<tr>
		<td><b>TIP</b><p>　　&quot;priority&quot; 表示 process 的優先序，數字越大表示優先序越低；&quot;nice&quot; 指的是要改變的 
		priority 量，例如 -10 表示要把 priority 減少 10，即提高優先序。</td>
	</tr>
</table>
<p>--jollen</p>
<p>作者／陳俊宏<br>
<a href="http://www.jollen.org">http://www.jollen.org</a> </p>]]></description>
         <link>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_434_s.html</link>
         <guid>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_434_s.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sun, 15 Oct 2006 00:55:24 +0800</pubDate>
      </item>
            <item>
         <title>Linux System Calls&apos; Forum, #3：(第64號系統服務) sys_getppid</title>
         <description><![CDATA[<p><i>sys_getppid</i> 是很有趣的一個實作常式。</p>
<p>當 process fork 新的 process 後，便成為該 process 的 parent process，當然，新的 process 便成為 
child process。在 UNIX 的 multithreaded 程式設計的理論中，process 之間的關係是相當重要的，比如說，只有 related 
process (e.g. parent process v.s. child process) 可以透過 pipe 的機制來交換資料。</p>
<p>process 呼叫 <i>fork() </i>函數（更正確的說法是 fork system call 的 wrapper function）後，<i>fork()</i> 便傳回 
child process 的 PID；child process 則可以透過 <i>getppid()</i> system call 來取得 parent process 
的 PID。</p>
<table border="1" width="95%" cellpadding="2">
	<tr>
		<td width="9%">64</td>
		<td width="37%">sys_getppid</td>
		<td width="50%">linux/kernel/timer.c</td>
	</tr>
	<tr>
		<td colspan="3">類別：Kernel Timer &amp; Process<br>
		原型宣告：long <b>sys_getppid</b>(void);<br>
		用途說明：取得 parent process 的 PID。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：<pre>
/*
 * Accessing ->group_leader->real_parent is not SMP-safe, it could
 * change from under us. However, rather than getting any lock
 * we can use an optimistic algorithm: get the parent
 * pid, and go back and check that the parent is still
 * the same. If it has changed (which is extremely unlikely
 * indeed), we just try again..
 *
 * NOTE! This depends on the fact that even if we _do_
 * get an old value of "parent", we can happily dereference
 * the pointer (it was and remains a dereferencable kernel pointer
 * no matter what): we just can't necessarily trust the result
 * until we know that the parent pointer is valid.
 *
 * NOTE2: ->group_leader never changes from under us.
 */
asmlinkage long sys_getppid(void)
{
	int pid;
	struct task_struct *me = current;
	struct task_struct *parent;

	parent = me->group_leader->real_parent;
	for (;;) {
		pid = parent->tgid;
#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
{
		struct task_struct *old = parent;

		/*
		 * Make sure we read the pid before re-reading the
		 * parent pointer:
		 */
		smp_rmb();
		parent = me->group_leader->real_parent;
		if (old != parent)
			continue;
}
#endif
		break;
	}
	return pid;
}		
</pre></td>
	</tr>
</table>
<p><b>Jollen 的說明</b></p>
<p>Linux kernel 內部的 <i>getppid</i> 實作常式 (routine) -<i> sys_getppid</i> 的程式碼並不難懂，
在這裡我們先忽略 SMP 與 preemptive scheduling 的議題，因此有一段落的程式碼我們故意視而不見，讓我們來討論剩下的程式碼：</p>
<table border="0" width="100%" cellpadding="3">
	<tr>
		<td width="100%">
<blockquote>
	<pre>
asmlinkage long sys_getppid(void)
{
	int pid;
	struct task_struct *me = current;
	struct task_struct *parent;

	<font color="#0000FF"><b>parent = me->group_leader->real_parent;</b></font>
	for (;;) {
		pid = parent->tgid;
<font color="#808080">#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
{
 	...
}
#endif</font>
		break;
	}
	return pid;
}	
</pre>
</blockquote>
      </td>
   </tr>
</table>
<i>struct task_struct</i> 的 field - '<i>group_leader</i>' 是一個 pointer，指向 thread 
(process) group 的 leader；<i>struct task_struct </i>的 field - '<i>real_parent</i>' 
是一個 pointer，指向 task 的 &quot;real parent&quot;：
<blockquote>
	<pre>struct task_struct {
	...
	/* real parent process (when being debugged) */
	struct task_struct *<b>real_parent</b>;
	struct task_struct *parent; /* parent process */
	...
	/* threadgroup leader */
	struct task_struct *<b>group_leader</b>;
	...
}	
</pre>
</blockquote>
<p><b>real_parent 與 parent</b></p>
<p>在 <i>struct task_struct</i> 裡，我們發現一件有趣的事：怎麼會有 <i>real_parent </i>與 <i>parent</i> 
二個 field 呢？</p>
<p>我們姑且不去說明 <i>parent </i>的用義，<i>real_parent</i> 指的是建立（create）此 process（task）的 
process，當建立該 process 的 process（<i>real_parent</i>）已經不存在時（eg. terminate），<i>real_parent
</i>便指向<i> init </i>process。</p>
<p>好囉，我們知道一件重要的事情了，要找到「我的爸爸」 kernel code 寫法是：</p>
<blockquote>
	<p>struct task_struct *me = <b>current</b>;&nbsp;&nbsp; /* 我自己 */</p>
	<p>struct task_struct *parent;&nbsp;&nbsp; /* 我爸 */</p>
	<p>parent = me-&gt;real_parent;&nbsp;&nbsp; /* 我真正的爸（生下我的）*/</p>
</blockquote>
<p>但這裡的寫法並不正確。讓我們繼續往下討論。</p>
<p><b>Group Leader</b></p>
<p>每一個 process 都是某些 process group 的成員，所有的 process group 都有一個帶班的 process（group 
leader）。比如說，當 GNU/Linux 系統開機時，<i>init</i> process 就是所有 process 的 group leader。</p>
<p>因此 process 的 parent process 應該是「group leader 的 real parent」，所以我們要先索引到 process 
descriptor 的 <i>group_leader </i>欄位，然後再索引到 group leader 的 <i>real_parent</i>。</p>
<table border="1" width="100%" id="table1">
	<tr>
		<td><b>TIP</b><p>　　　借用一下作業系統的說法：<i>struct task_struct </i>就是 process 的 
		process descriptor。</td>
	</tr>
</table>
<p>所以，正確的 kernel code 寫法是：</p>
<blockquote>
	<p>struct task_struct *me = <b>current</b>;&nbsp;&nbsp; /* 我自己 */</p>
	<p>struct task_struct *parent;&nbsp;&nbsp; /* 我爸 */</p>
	<p>parent = me-&gt;group_leader-&gt;real_parent;&nbsp;&nbsp; /* 領班的真正爸，才是我的爸 = = &quot; 
	*/</p>
</blockquote>
<p>這樣就能理解 Linux 2.6 的 <i>sys_getppid</i> 實作了，雖然有一點不太直覺，不過看起來是頗有學問的設計。知道怎麼找到 
parent process 後，再由 process descriptor 裡把 PID 拿出來就行了：</p>
<blockquote>
	<p>parent-&gt;tgid;</p>
</blockquote>
<p>作業系統還真是有趣。</p>
<p><i>--jollen</i></p>
<p>作者／陳俊宏<br>
<a href="http://www.jollen.org">http://www.jollen.org</a></p>]]></description>
         <link>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_364_s.html</link>
         <guid>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_364_s.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 14 Oct 2006 22:49:22 +0800</pubDate>
      </item>
            <item>
         <title>ext4 檔案系統現身了！</title>
         <description><![CDATA[新一代的檔案儲存 (storage) 檔案系統 (filesystem) - ext4 已經出現在Linux 2.16.19rc1-mm1 的核心原始碼裡了！ext4 是更先進的檔案系統，他前身就是知名的 ext3 檔案系統；ext3 是目前 Linux 普遍使用的知名檔案系統。

ext4 加入了 "extent file writing" 技術的支援，這是一個能減少 "fragmentation" 並增進效能的技術。有興趣的朋友可以閱讀 Linux-watch 上的官方新聞：

<a href="http://www.linux-watch.com/news/NS3183866977.html ">http://www.linux-watch.com/news/NS3183866977.html </a>

'-mm' 系列的 2.6 kernel 是由 Andrew Morton 所釋出的 Linux 分支，主要性質以「實驗」與「新功能」為主；'ext4' 一睹為快，搶鮮下載 (mm patch 下載官網)：

<a href="ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.19-rc1/2.6.19-rc1-mm1/">ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.19-rc1/2.6.19-rc1-mm1/</a>

並且也要下載新的 e2fsprogs 套件：

<a href="ftp://ftp.kernel.org/pub/linux/kernel/people/tytso/e2fsprogs-interim/">ftp://ftp.kernel.org/pub/linux/kernel/people/tytso/e2fsprogs-interim/</a>

使用方法 (<a href="ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.19-rc1/2.6.19-rc1-mm1/announce.txt">ftp：//ftp.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.19-rc1/2.6.19-rc1-mm1/announce.txt</a>)：

# mke2fs -j /dev/hda1   (格式化)
# mount /dev/hda1 /wherever -t ext4dev   (filesystem type 'ext4dev')

如果要使用 'extent' 功能：

# mount /dev/hda1 /wherever -t ext4dev <strong>-o extents
</strong>]]></description>
         <link>https://www.jollen.org/blog/2006/10/ext4.html</link>
         <guid>https://www.jollen.org/blog/2006/10/ext4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Fri, 13 Oct 2006 09:29:19 +0800</pubDate>
      </item>
            <item>
         <title>Linux System Calls&apos; Forum, #2：(第199,201,200,202,224號系統服務) sys_getuid, sys_geteuid, sys_getgid, sys_getegid, sys_gettid</title>
         <description><![CDATA[<br />
<!--system call: sys_getuid-->
<table border="1" width="95%" cellpadding="2">
	<tr>
		<td width="9%">199</td>
		<td width="37%">sys_getuid</td>
		<td width="50%">linux/kernel/timer.c</td>
	</tr>
	<tr>
		<td colspan="3">類別：Kernel Timer &amp; Process<br>
		原型宣告：long <b>sys_getuid</b>(void);<br>
		用途說明：取得目前 process 的 real user ID (UID)。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：
<pre>
asmlinkage long sys_getuid(void)
{
        /* Only we change this so SMP safe */
        return current->uid;
}
</pre></td>
	</tr>
</table>
<br />
<!--system call: sys_geteuid-->
<table border="1" width="95%" cellpadding="2">
	<tr>
		<td width="9%">201</td>
		<td width="37%">sys_geteuid</td>
		<td width="50%">linux/kernel/timer.c</td>
	</tr>
	<tr>
		<td colspan="3">類別：Kernel Timer &amp; Process<br>
		原型宣告：long <b>sys_geteuid</b>(void);<br>
		用途說明：取得目前 process 的 effective real user ID (file ID bit)。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：
<pre>
asmlinkage long sys_geteuid(void)
{
        /* Only we change this so SMP safe */
        return current->euid;
}
</pre></td>
	</tr>
</table>
<br />
<strong>Jollen 的說明</strong>

<p>在 <em>struct task_struct</em> 裡紀錄了 task (process) 的所有資訊，其中我們最為熟悉的 process ID 與 user ID (and group ID) 都可以由這個資料結構裡取得。讓我們來看看 wrapper function 'getuid' 的用法 (from man)：</p>

<blockquote><pre>
NAME
       getuid, geteuid - get user identity
                                                                                
SYNOPSIS
       #include <unistd.h>
       #include <sys/types.h>
 
       uid_t getuid(void);
       uid_t geteuid(void);
 
DESCRIPTION
       getuid returns the real user ID of the current process.
 
       geteuid returns the effective user ID of the current process.
 
       The  real  ID corresponds to the ID of the calling process.  The effec-
       tive ID corresponds to the set ID bit on the file being executed.
</pre></blockquote>

<p>'getuid' 所對應的 system call service 為 sys_getuid，讓我們來看一下 Linux 的實作：</p>

<blockquote><pre>
asmlinkage long sys_getuid(void)
{
        /* Only we change this so SMP safe */
        return <strong>current->uid</strong>;
}
</pre></blockquote>

<p>嗯，果然是非常的好懂，只要懂 <em>current</em> 巨集的用途即可！另外，要提醒的是，'current->uid' 並非直接 return 給 user-space 的 wrapper function (<em>getuid</em> in our example)。(那是 return 給誰?)</p>

<p>在 Linux 裡有許多類似 <em>getuid</em> 的 system call 實作，比如 <em>sys_geteuid</em> 的程式碼也是直接回傳 <em>struct task_struct</em> 裡的 field，並不難懂。</p>

<p>以下是 <em>sys_getgid</em> 與 <em>sys_getegid</em> 的整理。</p>

<!--system call: sys_getgid-->
<table border="1" width="95%" cellpadding="2">
	<tr>
		<td width="9%">200</td>
		<td width="37%">sys_getgid</td>
		<td width="50%">linux/kernel/timer.c</td>
	</tr>
	<tr>
		<td colspan="3">類別：Kernel Timer &amp; Process<br>
		原型宣告：long <b>sys_getgid</b>(void);<br>
		用途說明：取得目前 process 的 real group ID (GID)。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：
<pre>
asmlinkage long sys_getgid(void)
{
        /* Only we change this so SMP safe */
        return current->gid;
}
</pre></td>
	</tr>
</table>
<br />
<!--system call: sys_getegid-->
<table border="1" width="95%" cellpadding="2">
	<tr>
		<td width="9%">202</td>
		<td width="37%">sys_getegid</td>
		<td width="50%">linux/kernel/timer.c</td>
	</tr>
	<tr>
		<td colspan="3">類別：Kernel Timer &amp; Process<br>
		原型宣告：long <b>sys_getegid</b>(void);<br>
		用途說明：取得目前 process 的 effective group ID (EGID)。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：
<pre>
asmlinkage long sys_getegid(void)
{
        /* Only we change this so SMP safe */
        return  current->egid;
}
</pre></td>
	</tr>
</table>

<p>這 4 個 Linux system call 都非常的容易了解，由 Linux System Calls' Forum #1,#2 我們了解到 2 個重要的 Kernel 議題：</p>

<ul>
<li>1. <em>struct task_struct</em>;
<li>2. The <em>current</em> macro.
</ul>

<p>最後還有一個 <em>sys_gettid</em> 的實作。</p>

<!--system call: sys_gettid-->
<table border="1" width="95%" cellpadding="2">
	<tr>
		<td width="9%">224</td>
		<td width="37%">sys_gettid</td>
		<td width="50%">linux/kernel/timer.c</td>
	</tr>
	<tr>
		<td colspan="3">類別：Kernel Timer &amp; Process<br>
		原型宣告：long <b>sys_gettid</b>(void);<br>
		用途說明：取得 task 的 "pid"。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：
<pre>
/* Thread ID - the internal kernel "pid" */
asmlinkage long sys_gettid(void)
{
        return current->pid;
}
</pre>
</td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_21992.html</link>
         <guid>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_21992.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 12 Oct 2006 12:57:37 +0800</pubDate>
      </item>
            <item>
         <title>Linux System Calls&apos; Forum, #1：(第20號系統服務) sys_getpid</title>
         <description><![CDATA[<p>對於 Linux system call 的研究，我們採取的策略是「依分類」來做討論，而不是依 system call 編號依序討論。透過 system 
call (kernel-space 端) 的研究，我們除了可以更深入了解作業系統外，更能一探 Linux kernel 的奧妙。</p>
<table border="1" width="95%" cellpadding="2">
	<tr>
		<td width="9%">20</td>
		<td width="37%">sys_getpid</td>
		<td width="50%">linux/kernel/timer.c</td>
	</tr>
	<tr>
		<td colspan="3">類別：Kernel Timer &amp; Process<br>
		原型宣告：long <b>sys_getpid</b>(void);<br>
		用途說明：取得目前 process 的 thread ID (process ID)。</td>
	</tr>
	<tr>
		<td colspan="3">Kernel (2.6.11 or above) 實作：<pre>
/**
 * sys_getpid - return the thread group id of the current process
 *
 * Note, despite the name, this returns the tgid not the pid.  The tgid and
 * the pid are identical unless CLONE_THREAD was specified on clone() in
 * which case the tgid is the same in all threads of the same group.
 *
 * This is SMP safe as current->tgid does not change.
 */
asmlinkage long sys_getpid(void)
{
	return current-><b><font color="#0000FF">tgid</font></b>;
}</pre></td>
	</tr>
</table>
<p><b>Jollen 的說明</b></p>
<p>「Linux system calls 討論」系列，我們略過關於 user-space 端的說明，因此大家必須先了解以下主題：</p>
<ul>
	<li>system call 的 wrapper function (glibc)</li>
	<li>0x80 號軟體中斷 (i386)</li>
</ul>
<p><i>sys_getpid</i> 透過 <i>current</i> 巨集取得目前 process 的 ID，kernel code 都可以存取 <i>current</i> 巨集 
(我們把此巨集當做 kernel 的 global symbol 來看) 來存取目前 process 的狀態。</p>
<p><i>current</i> 的 data structure 為 <i>struct task_struct</i>：</p>
<table border="0" width="100%" cellpadding="3">
	<tr>
		<td width="100%">
<blockquote>
	<p>// include/linux/sched.h</p>
<pre>
struct task_struct {
	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
	struct thread_info *thread_info;
	atomic_t usage;
	unsigned long flags;	/* per process flags, defined below */
	unsigned long ptrace;
	int lock_depth;		/* BKL lock depth */
#if defined(CONFIG_SMP) && defined(__ARCH_WANT_UNLOCKED_CTXSW)
	int oncpu;
#endif
	int prio, static_prio;
	struct list_head run_list;
	prio_array_t *array;
	unsigned short ioprio;
	unsigned int btrace_seq;
	unsigned long sleep_avg;
	unsigned long long timestamp, last_ran;
	unsigned long long sched_time; /* sched_clock time spent running */
	enum sleep_type sleep_type;
	unsigned long policy;
	cpumask_t cpus_allowed;
	unsigned int time_slice, first_time_slice;
#ifdef CONFIG_SCHEDSTATS
	struct sched_info sched_info;
#endif
	struct list_head tasks;
	/*
	 * ptrace_list/ptrace_children forms the list of my children
	 * that were stolen by a ptracer.
	 */
	struct list_head ptrace_children;
	struct list_head ptrace_list;
	struct mm_struct *mm, *active_mm;
/* task state */
	struct linux_binfmt *binfmt;
	long exit_state;
	int exit_code, exit_signal;
	int pdeath_signal;  /*  The signal sent when the parent dies  */
	/* ??? */
	unsigned long personality;
	unsigned did_exec:1;
	pid_t pid;
	pid_t tgid;
	/* 
	 * pointers to (original) parent process, youngest child, younger sibling,
	 * older sibling, respectively.  (p->father can be replaced with 
	 * p->parent->pid)
	 */
	struct task_struct *real_parent; /* real parent process (when being debugged) */
	struct task_struct *parent;	/* parent process */
	/*
	 * children/sibling forms the list of my children plus the
	 * tasks I'm ptracing.
	 */
	struct list_head children;	/* list of my children */
	struct list_head sibling;	/* linkage in my parent's children list */
	struct task_struct *group_leader;	/* threadgroup leader */
	/* PID/PID hash table linkage. */
	struct pid_link pids[PIDTYPE_MAX];
	struct list_head thread_group;
	struct completion *vfork_done;		/* for vfork() */
	int __user *set_child_tid;		/* CLONE_CHILD_SETTID */
	int __user *clear_child_tid;		/* CLONE_CHILD_CLEARTID */
	unsigned long rt_priority;
	cputime_t utime, stime;
	unsigned long nvcsw, nivcsw; /* context switch counts */
	struct timespec start_time;
/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
	unsigned long min_flt, maj_flt;
  	cputime_t it_prof_expires, it_virt_expires;
	unsigned long long it_sched_expires;
	struct list_head cpu_timers[3];
/* process credentials */
	uid_t uid,euid,suid,fsuid;
	gid_t gid,egid,sgid,fsgid;
	struct group_info *group_info;
	kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
	unsigned keep_capabilities:1;
	struct user_struct *user;
#ifdef CONFIG_KEYS
	struct key *request_key_auth;	/* assumed request_key authority */
	struct key *thread_keyring;	/* keyring private to this thread */
	unsigned char jit_keyring;	/* default keyring to attach requested keys to */
#endif
	int oomkilladj; /* OOM kill score adjustment (bit shift). */
	char comm[TASK_COMM_LEN]; /* executable name excluding path
				     - access with [gs]et_task_comm (which lock
				       it with task_lock())
				     - initialized normally by flush_old_exec */
/* file system info */
	int link_count, total_link_count;
/* ipc stuff */
	struct sysv_sem sysvsem;
/* CPU-specific state of this task */
	struct thread_struct thread;
/* filesystem information */
	struct fs_struct *fs;
/* open file information */
	struct files_struct *files;
/* namespace */
	struct namespace *namespace;
/* signal handlers */
	struct signal_struct *signal;
	struct sighand_struct *sighand;
	sigset_t blocked, real_blocked;
	sigset_t saved_sigmask;		/* To be restored with TIF_RESTORE_SIGMASK */
	struct sigpending pending;
	unsigned long sas_ss_sp;
	size_t sas_ss_size;
	int (*notifier)(void *priv);
	void *notifier_data;
	sigset_t *notifier_mask;
	void *security;
	struct audit_context *audit_context;
	seccomp_t seccomp;
/* Thread group tracking */
   	u32 parent_exec_id;
   	u32 self_exec_id;
/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */
	spinlock_t alloc_lock;
/* Protection of proc_dentry: nesting proc_lock, dcache_lock, write_lock_irq(&tasklist_lock); */
	spinlock_t proc_lock;
#ifdef CONFIG_DEBUG_MUTEXES
	/* mutex deadlock detection */
	struct mutex_waiter *blocked_on;
#endif
/* journalling filesystem info */
	void *journal_info;
/* VM state */
	struct reclaim_state *reclaim_state;
	struct dentry *proc_dentry;
	struct backing_dev_info *backing_dev_info;
	struct io_context *io_context;
	unsigned long ptrace_message;
	siginfo_t *last_siginfo; /* For ptrace use.  */
/*
 * current io wait handle: wait queue entry to use for io waits
 * If this thread is processing aio, this points at the waitqueue
 * inside the currently handled kiocb. It may be NULL (i.e. default
 * to a stack based synchronous wait) if its doing sync IO.
 */
	wait_queue_t *io_wait;
/* i/o counters(bytes read/written, #syscalls */
	u64 rchar, wchar, syscr, syscw;
#if defined(CONFIG_BSD_PROCESS_ACCT)
	u64 acct_rss_mem1;	/* accumulated rss usage */
	u64 acct_vm_mem1;	/* accumulated virtual memory usage */
	clock_t acct_stimexpd;	/* clock_t-converted stime since last update */
#endif
#ifdef CONFIG_NUMA
  	struct mempolicy *mempolicy;
	short il_next;
#endif
#ifdef CONFIG_CPUSETS
	struct cpuset *cpuset;
	nodemask_t mems_allowed;
	int cpuset_mems_generation;
	int cpuset_mem_spread_rotor;
#endif
	struct robust_list_head __user *robust_list;
#ifdef CONFIG_COMPAT
	struct compat_robust_list_head __user *compat_robust_list;
#endif
	atomic_t fs_excl;	/* holding fs exclusive resources */
	struct rcu_head rcu;
	/*
	 * cache last used pipe for splice
	 */
	struct pipe_inode_info *splice_pipe;
};
</pre>
</blockquote>
      </td>
   </tr>
</table>
<i>struct task_struct</i> 是 Linux kernel 表示 task 的標準資料結構。<i>sys_getpid</i> 傳回 <i>current-&gt;tgid</i> 即完成工作，其中 <i>tgid </i>表示 thread group ID 即 PID。
<p>--作者／陳俊宏 (www.jollen.org)</p>]]></description>
         <link>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_120_s.html</link>
         <guid>https://www.jollen.org/blog/2006/10/linux_system_calls_forum_120_s.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Wed, 11 Oct 2006 17:01:32 +0800</pubDate>
      </item>
            <item>
         <title>Linux 2.6 的 System Call：12 大類</title>
         <description><![CDATA[把 Linux 提供的 sytsem call service 依分類做整理，並提供實作檔案。本表使用於 Jollen 的「2. GNU Toolchains 與 Embedded Linux Programming」課程中，在此提供給大家做參考。目前依據 Linux 2.6.11 原始碼製成，請搭配 2.6.11 以上的版本做研究。

<table border="1" cellpadding="3" width="100%" id="table1" cellspacing="1">
	<tr>
		<td align="center" width="5%" bgcolor="#e4e4cb"><b>no</b></td>
		<td align="center" width="15%" bgcolor="#e4e4cb"><b>Syscall name</b></td>
		<td align="center" width="80%" bgcolor="#e4e4cb"><b>Implementation file 
		in Linux 2.6.11 (or above)</b></td>
	</tr>
	<tr>
		<td align="center" width="100%" colspan="3">
		<p align="left">目前大致把 Linux 2.6.11 的 system call 分成以下幾個類別：</p>
		<ul>
			<li>
			<p align="left"><a href="#c1">Machine-dependent (i386)</a></li>
			<li>
			<p align="left"><a href="#c2">Filesystem</a></li>
			<li>
			<p align="left"><a href="#c3">Linux kernel</a></li>
			<li>
			<p align="left"><a href="#c4">Scheduling</a></li>
			<li>
			<p align="left"><a href="#c5">Signals</a></li>
			<li>
			<p align="left"><a href="#c6">Systems</a></li>
			<li>
			<p align="left"><a href="#c7">Time</a></li>
			<li>
			<p align="left"><a href="#c8">Kernel Timer &amp; Process</a></li>
			<li>
			<p align="left"><a href="#c9">16-bit uid</a></li>
			<li>
			<p align="left"><a href="#c10">Memory Management</a></li>
			<li>
			<p align="left"><a href="#c11">Swapfile</a></li>
			<li>
			<p align="left"><a href="#c12">Socket</a></li>
		</ul>
		<p align="left">最後一個欄位列出該 system call 實作的檔案 (以 Linux 2.6.11 為主)。<font color="#000080">請注意，Blog 
		裡並沒有該檔案的 hyperlink，您可以複制本 blog，並準備一份 Linux 2.6.11 (or above) 的原始碼，以方便 
		link 到檔案做查詢。</font></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c1"></a>Machine-dependent 
		(i386)</b></td>
	</tr>
	<tr>
		<td width="5%">101</td>
		<td width="15%">sys_ioperm</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/ioport.c">
		linux/arch/i386/kernel/ioport.c</a></td>
	</tr>
	<tr>
		<td width="5%">110</td>
		<td width="15%">sys_iopl</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/ioport.c">
		linux/arch/i386/kernel/ioport.c</a></td>
	</tr>
	<tr>
		<td width="5%">123</td>
		<td width="15%">sys_modify_ldt</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/ldt.c">
		linux/arch/i386/kernel/ldt.c</a></td>
	</tr>
	<tr>
		<td width="5%">2</td>
		<td width="15%">sys_fork</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/process.c">
		linux/arch/i386/kernel/process.c</a></td>
	</tr>
	<tr>
		<td width="5%">11</td>
		<td width="15%">sys_execve</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/process.c">
		linux/arch/i386/kernel/process.c</a></td>
	</tr>
	<tr>
		<td width="5%">120</td>
		<td width="15%">sys_clone</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/process.c">
		linux/arch/i386/kernel/process.c</a></td>
	</tr>
	<tr>
		<td width="5%">190</td>
		<td width="15%">sys_vfork</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/process.c">
		linux/arch/i386/kernel/process.c</a></td>
	</tr>
	<tr>
		<td width="5%">243</td>
		<td width="15%">sys_set_thread_area</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/process.c">
		linux/arch/i386/kernel/process.c</a></td>
	</tr>
	<tr>
		<td width="5%">244</td>
		<td width="15%">sys_get_thread_area</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/process.c">
		linux/arch/i386/kernel/process.c</a></td>
	</tr>
	<tr>
		<td width="5%">26</td>
		<td width="15%">sys_ptrace</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/ptrace.c">
		linux/arch/i386/kernel/ptrace.c</a></td>
	</tr>
	<tr>
		<td width="5%">67</td>
		<td width="15%">sys_sigaction</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/signal.c">
		linux/arch/i386/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">72</td>
		<td width="15%">sys_sigsuspend</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/signal.c">
		linux/arch/i386/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">119</td>
		<td width="15%">sys_sigreturn</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/signal.c">
		linux/arch/i386/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">173</td>
		<td width="15%">sys_rt_sigreturn</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/signal.c">
		linux/arch/i386/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">179</td>
		<td width="15%">sys_rt_sigsuspend</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/signal.c">
		linux/arch/i386/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">186</td>
		<td width="15%">sys_sigaltstack</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/signal.c">
		linux/arch/i386/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">42</td>
		<td width="15%">sys_pipe</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/sys_i386.c">
		linux/arch/i386/kernel/sys_i386.c</a></td>
	</tr>
	<tr>
		<td width="5%">59</td>
		<td width="15%">sys_olduname</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/sys_i386.c">
		linux/arch/i386/kernel/sys_i386.c</a></td>
	</tr>
	<tr>
		<td width="5%">82</td>
		<td width="15%">old_select</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/sys_i386.c">
		linux/arch/i386/kernel/sys_i386.c</a></td>
	</tr>
	<tr>
		<td width="5%">90</td>
		<td width="15%">old_mmap</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/sys_i386.c">
		linux/arch/i386/kernel/sys_i386.c</a></td>
	</tr>
	<tr>
		<td width="5%">109</td>
		<td width="15%">sys_uname</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/sys_i386.c">
		linux/arch/i386/kernel/sys_i386.c</a></td>
	</tr>
	<tr>
		<td width="5%">117</td>
		<td width="15%">sys_ipc</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/sys_i386.c">
		linux/arch/i386/kernel/sys_i386.c</a></td>
	</tr>
	<tr>
		<td width="5%">192</td>
		<td width="15%">sys_mmap2</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/sys_i386.c">
		linux/arch/i386/kernel/sys_i386.c</a></td>
	</tr>
	<tr>
		<td width="5%">113</td>
		<td width="15%">sys_vm86old</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/vm86.c">
		linux/arch/i386/kernel/vm86.c</a></td>
	</tr>
	<tr>
		<td width="5%">166</td>
		<td width="15%">sys_vm86</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/arch/i386/kernel/vm86.c">
		linux/arch/i386/kernel/vm86.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c2"></a>Filesystem</b></td>
	</tr>
	<tr>
		<td width="5%">245</td>
		<td width="15%">sys_io_setup</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/aio.c">
		linux/fs/aio.c</a></td>
	</tr>
	<tr>
		<td width="5%">246</td>
		<td width="15%">sys_io_destroy</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/aio.c">
		linux/fs/aio.c</a></td>
	</tr>
	<tr>
		<td width="5%">247</td>
		<td width="15%">sys_io_getevents</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/aio.c">
		linux/fs/aio.c</a></td>
	</tr>
	<tr>
		<td width="5%">248</td>
		<td width="15%">sys_io_submit</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/aio.c">
		linux/fs/aio.c</a></td>
	</tr>
	<tr>
		<td width="5%">249</td>
		<td width="15%">sys_io_cancel</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/aio.c">
		linux/fs/aio.c</a></td>
	</tr>
	<tr>
		<td width="5%">36</td>
		<td width="15%">sys_sync</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/buffer.c">
		linux/fs/buffer.c</a></td>
	</tr>
	<tr>
		<td width="5%">118</td>
		<td width="15%">sys_fsync</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/buffer.c">
		linux/fs/buffer.c</a></td>
	</tr>
	<tr>
		<td width="5%">134</td>
		<td width="15%">sys_bdflush</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/buffer.c">
		linux/fs/buffer.c</a></td>
	</tr>
	<tr>
		<td width="5%">148</td>
		<td width="15%">sys_fdatasync</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/buffer.c">
		linux/fs/buffer.c</a></td>
	</tr>
	<tr>
		<td width="5%">183</td>
		<td width="15%">sys_getcwd</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/dcache.c">
		linux/fs/dcache.c</a></td>
	</tr>
	<tr>
		<td width="5%">253</td>
		<td width="15%">sys_lookup_dcookie</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/dcookies.c">
		linux/fs/dcookies.c</a></td>
	</tr>
	<tr>
		<td width="5%">254</td>
		<td width="15%">sys_epoll_create</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/eventpoll.c">
		linux/fs/eventpoll.c</a></td>
	</tr>
	<tr>
		<td width="5%">255</td>
		<td width="15%">sys_epoll_ctl</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/eventpoll.c">
		linux/fs/eventpoll.c</a></td>
	</tr>
	<tr>
		<td width="5%">256</td>
		<td width="15%">sys_epoll_wait</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/eventpoll.c">
		linux/fs/eventpoll.c</a></td>
	</tr>
	<tr>
		<td width="5%">86</td>
		<td width="15%">sys_uselib</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/exec.c">
		linux/fs/exec.c</a></td>
	</tr>
	<tr>
		<td width="5%">41</td>
		<td width="15%">sys_dup</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/fcntl.c">
		linux/fs/fcntl.c</a></td>
	</tr>
	<tr>
		<td width="5%">55</td>
		<td width="15%">sys_fcntl</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/fcntl.c">
		linux/fs/fcntl.c</a></td>
	</tr>
	<tr>
		<td width="5%">63</td>
		<td width="15%">sys_dup2</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/fcntl.c">
		linux/fs/fcntl.c</a></td>
	</tr>
	<tr>
		<td width="5%">221</td>
		<td width="15%">sys_fcntl64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/fcntl.c">
		linux/fs/fcntl.c</a></td>
	</tr>
	<tr>
		<td width="5%">135</td>
		<td width="15%">sys_sysfs</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/filesystems.c">
		linux/fs/filesystems.c</a></td>
	</tr>
	<tr>
		<td width="5%">54</td>
		<td width="15%">sys_ioctl</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/ioctl.c">
		linux/fs/ioctl.c</a></td>
	</tr>
	<tr>
		<td width="5%">143</td>
		<td width="15%">sys_flock</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/locks.c">
		linux/fs/locks.c</a></td>
	</tr>
	<tr>
		<td width="5%">9</td>
		<td width="15%">sys_link</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namei.c">
		linux/fs/namei.c</a></td>
	</tr>
	<tr>
		<td width="5%">10</td>
		<td width="15%">sys_unlink</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namei.c">
		linux/fs/namei.c</a></td>
	</tr>
	<tr>
		<td width="5%">14</td>
		<td width="15%">sys_mknod</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namei.c">
		linux/fs/namei.c</a></td>
	</tr>
	<tr>
		<td width="5%">38</td>
		<td width="15%">sys_rename</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namei.c">
		linux/fs/namei.c</a></td>
	</tr>
	<tr>
		<td width="5%">39</td>
		<td width="15%">sys_mkdir</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namei.c">
		linux/fs/namei.c</a></td>
	</tr>
	<tr>
		<td width="5%">40</td>
		<td width="15%">sys_rmdir</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namei.c">
		linux/fs/namei.c</a></td>
	</tr>
	<tr>
		<td width="5%">83</td>
		<td width="15%">sys_symlink</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namei.c">
		linux/fs/namei.c</a></td>
	</tr>
	<tr>
		<td width="5%">21</td>
		<td width="15%">sys_mount</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namespace.c">
		linux/fs/namespace.c</a></td>
	</tr>
	<tr>
		<td width="5%">22</td>
		<td width="15%">sys_oldumount</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namespace.c">
		linux/fs/namespace.c</a></td>
	</tr>
	<tr>
		<td width="5%">52</td>
		<td width="15%">sys_umount</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namespace.c">
		linux/fs/namespace.c</a></td>
	</tr>
	<tr>
		<td width="5%">217</td>
		<td width="15%">sys_pivot_root</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/namespace.c">
		linux/fs/namespace.c</a></td>
	</tr>
	<tr>
		<td width="5%">169</td>
		<td width="15%">sys_nfsservctl</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/nfsctl.c">
		linux/fs/nfsctl.c</a></td>
	</tr>
	<tr>
		<td width="5%">5</td>
		<td width="15%">sys_open</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">6</td>
		<td width="15%">sys_close</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">8</td>
		<td width="15%">sys_creat</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">12</td>
		<td width="15%">sys_chdir</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">15</td>
		<td width="15%">sys_chmod</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">30</td>
		<td width="15%">sys_utime</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">33</td>
		<td width="15%">sys_access</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">61</td>
		<td width="15%">sys_chroot</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">92</td>
		<td width="15%">sys_truncate</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">93</td>
		<td width="15%">sys_ftruncate</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">94</td>
		<td width="15%">sys_fchmod</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">99</td>
		<td width="15%">sys_statfs</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">100</td>
		<td width="15%">sys_fstatfs</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">111</td>
		<td width="15%">sys_vhangup</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">133</td>
		<td width="15%">sys_fchdir</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">193</td>
		<td width="15%">sys_truncate64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">194</td>
		<td width="15%">sys_ftruncate64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">198</td>
		<td width="15%">sys_lchown</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">207</td>
		<td width="15%">sys_fchown</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">212</td>
		<td width="15%">sys_chown</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">268</td>
		<td width="15%">sys_statfs64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">269</td>
		<td width="15%">sys_fstatfs64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/open.c">
		linux/fs/open.c</a></td>
	</tr>
	<tr>
		<td width="5%">131</td>
		<td width="15%">sys_quotactl</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/quota.c">
		linux/fs/quota.c</a></td>
	</tr>
	<tr>
		<td width="5%">89</td>
		<td width="15%">old_readdir</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/readdir.c">
		linux/fs/readdir.c</a></td>
	</tr>
	<tr>
		<td width="5%">141</td>
		<td width="15%">sys_getdents</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/readdir.c">
		linux/fs/readdir.c</a></td>
	</tr>
	<tr>
		<td width="5%">220</td>
		<td width="15%">sys_getdents64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/readdir.c">
		linux/fs/readdir.c</a></td>
	</tr>
	<tr>
		<td width="5%">3</td>
		<td width="15%">sys_read</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">4</td>
		<td width="15%">sys_write</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">19</td>
		<td width="15%">sys_lseek</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">140</td>
		<td width="15%">sys_llseek</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">145</td>
		<td width="15%">sys_readv</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">146</td>
		<td width="15%">sys_writev</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">180</td>
		<td width="15%">sys_pread64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">181</td>
		<td width="15%">sys_pwrite64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">187</td>
		<td width="15%">sys_sendfile</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">239</td>
		<td width="15%">sys_sendfile64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/read_write.c">
		linux/fs/read_write.c</a></td>
	</tr>
	<tr>
		<td width="5%">142</td>
		<td width="15%">sys_select</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/select.c">
		linux/fs/select.c</a></td>
	</tr>
	<tr>
		<td width="5%">168</td>
		<td width="15%">sys_poll</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/select.c">
		linux/fs/select.c</a></td>
	</tr>
	<tr>
		<td width="5%">18</td>
		<td width="15%">sys_stat</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">28</td>
		<td width="15%">sys_fstat</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">84</td>
		<td width="15%">sys_lstat</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">85</td>
		<td width="15%">sys_readlink</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">106</td>
		<td width="15%">sys_newstat</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">107</td>
		<td width="15%">sys_newlstat</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">108</td>
		<td width="15%">sys_newfstat</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">195</td>
		<td width="15%">sys_stat64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">196</td>
		<td width="15%">sys_lstat64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">197</td>
		<td width="15%">sys_fstat64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/stat.c">
		linux/fs/stat.c</a></td>
	</tr>
	<tr>
		<td width="5%">62</td>
		<td width="15%">sys_ustat</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/super.c">
		linux/fs/super.c</a></td>
	</tr>
	<tr>
		<td width="5%">226</td>
		<td width="15%">sys_setxattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">227</td>
		<td width="15%">sys_lsetxattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">228</td>
		<td width="15%">sys_fsetxattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">229</td>
		<td width="15%">sys_getxattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">230</td>
		<td width="15%">sys_lgetxattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">231</td>
		<td width="15%">sys_fgetxattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">232</td>
		<td width="15%">sys_listxattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">233</td>
		<td width="15%">sys_llistxattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">234</td>
		<td width="15%">sys_flistxattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">235</td>
		<td width="15%">sys_removexattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">236</td>
		<td width="15%">sys_lremovexattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="5%">237</td>
		<td width="15%">sys_fremovexattr</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/fs/xattr.c">
		linux/fs/xattr.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c3"></a>Linux Kernel</b></td>
	</tr>
	<tr>
		<td width="5%">51</td>
		<td width="15%">sys_acct</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/acct.c">
		linux/kernel/acct.c</a></td>
	</tr>
	<tr>
		<td width="5%">184</td>
		<td width="15%">sys_capget</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/capability.c">
		linux/kernel/capability.c</a></td>
	</tr>
	<tr>
		<td width="5%">185</td>
		<td width="15%">sys_capset</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/capability.c">
		linux/kernel/capability.c</a></td>
	</tr>
	<tr>
		<td width="5%">136</td>
		<td width="15%">sys_personality</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/exec_domain.c">
		linux/kernel/exec_domain.c</a></td>
	</tr>
	<tr>
		<td width="5%">1</td>
		<td width="15%">sys_exit</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/exit.c">
		linux/kernel/exit.c</a></td>
	</tr>
	<tr>
		<td width="5%">7</td>
		<td width="15%">sys_waitpid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/exit.c">
		linux/kernel/exit.c</a></td>
	</tr>
	<tr>
		<td width="5%">114</td>
		<td width="15%">sys_wait4</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/exit.c">
		linux/kernel/exit.c</a></td>
	</tr>
	<tr>
		<td width="5%">252</td>
		<td width="15%">sys_exit_group</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/exit.c">
		linux/kernel/exit.c</a></td>
	</tr>
	<tr>
		<td width="5%">258</td>
		<td width="15%">sys_set_tid_address</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/fork.c">
		linux/kernel/fork.c</a></td>
	</tr>
	<tr>
		<td width="5%">240</td>
		<td width="15%">sys_futex</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/futex.c">
		linux/kernel/futex.c</a></td>
	</tr>
	<tr>
		<td width="5%">104</td>
		<td width="15%">sys_setitimer</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/itimer.c">
		linux/kernel/itimer.c</a></td>
	</tr>
	<tr>
		<td width="5%">105</td>
		<td width="15%">sys_getitimer</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/itimer.c">
		linux/kernel/itimer.c</a></td>
	</tr>
	<tr>
		<td width="5%">128</td>
		<td width="15%">sys_init_module</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/module.c">
		linux/kernel/module.c</a></td>
	</tr>
	<tr>
		<td width="5%">129</td>
		<td width="15%">sys_delete_module</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/module.c">
		linux/kernel/module.c</a></td>
	</tr>
	<tr>
		<td width="5%">162</td>
		<td width="15%">sys_nanosleep</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">259</td>
		<td width="15%">sys_timer_create</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">260</td>
		<td width="15%">sys_timer_settime</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">261</td>
		<td width="15%">sys_timer_gettime</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">262</td>
		<td width="15%">sys_timer_getoverrun</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">263</td>
		<td width="15%">sys_timer_delete</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">264</td>
		<td width="15%">sys_clock_settime</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">265</td>
		<td width="15%">sys_clock_gettime</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">266</td>
		<td width="15%">sys_clock_getres</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">267</td>
		<td width="15%">sys_clock_nanosleep</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/posix-timers.c">
		linux/kernel/posix-timers.c</a></td>
	</tr>
	<tr>
		<td width="5%">103</td>
		<td width="15%">sys_syslog</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/printk.c">
		linux/kernel/printk.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c4"></a>Scheduling</b></td>
	</tr>
	<tr>
		<td width="5%">34</td>
		<td width="15%">sys_nice</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">154</td>
		<td width="15%">sys_sched_setparam</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">155</td>
		<td width="15%">sys_sched_getparam</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">156</td>
		<td width="15%">sys_sched_setscheduler</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">157</td>
		<td width="15%">sys_sched_getscheduler</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">158</td>
		<td width="15%">sys_sched_yield</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">159</td>
		<td width="15%">sys_sched_get_priority_max</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">160</td>
		<td width="15%">sys_sched_get_priority_min</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">161</td>
		<td width="15%">sys_sched_rr_get_interval</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">241</td>
		<td width="15%">sys_sched_setaffinity</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="5%">242</td>
		<td width="15%">sys_sched_getaffinity</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sched.c">
		linux/kernel/sched.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c5"></a>Signals</b></td>
	</tr>
	<tr>
		<td width="5%">0</td>
		<td width="15%">sys_restart_syscall</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">29</td>
		<td width="15%">sys_pause</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">37</td>
		<td width="15%">sys_kill</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">48</td>
		<td width="15%">sys_signal</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">68</td>
		<td width="15%">sys_sgetmask</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">69</td>
		<td width="15%">sys_ssetmask</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">73</td>
		<td width="15%">sys_sigpending</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">126</td>
		<td width="15%">sys_sigprocmask</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">174</td>
		<td width="15%">sys_rt_sigaction</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">175</td>
		<td width="15%">sys_rt_sigprocmask</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">176</td>
		<td width="15%">sys_rt_sigpending</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">177</td>
		<td width="15%">sys_rt_sigtimedwait</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">178</td>
		<td width="15%">sys_rt_sigqueueinfo</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">238</td>
		<td width="15%">sys_tkill</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="5%">270</td>
		<td width="15%">sys_tgkill</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/signal.c">
		linux/kernel/signal.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c6"></a>Systems</b></td>
	</tr>
	<tr>
		<td width="5%">43</td>
		<td width="15%">sys_times</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">57</td>
		<td width="15%">sys_setpgid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">60</td>
		<td width="15%">sys_umask</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">65</td>
		<td width="15%">sys_getpgrp</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">66</td>
		<td width="15%">sys_setsid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">74</td>
		<td width="15%">sys_sethostname</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">75</td>
		<td width="15%">sys_setrlimit</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">76</td>
		<td width="15%">sys_old_getrlimit</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">77</td>
		<td width="15%">sys_getrusage</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">88</td>
		<td width="15%">sys_reboot</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">96</td>
		<td width="15%">sys_getpriority</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">97</td>
		<td width="15%">sys_setpriority</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">121</td>
		<td width="15%">sys_setdomainname</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">122</td>
		<td width="15%">sys_newuname</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">132</td>
		<td width="15%">sys_getpgid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">147</td>
		<td width="15%">sys_getsid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">172</td>
		<td width="15%">sys_prctl</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">191</td>
		<td width="15%">sys_getrlimit</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">203</td>
		<td width="15%">sys_setreuid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">204</td>
		<td width="15%">sys_setregid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">205</td>
		<td width="15%">sys_getgroups</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">206</td>
		<td width="15%">sys_setgroups</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">208</td>
		<td width="15%">sys_setresuid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">209</td>
		<td width="15%">sys_getresuid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">210</td>
		<td width="15%">sys_setresgid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">211</td>
		<td width="15%">sys_getresgid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">213</td>
		<td width="15%">sys_setuid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">214</td>
		<td width="15%">sys_setgid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">215</td>
		<td width="15%">sys_setfsuid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">216</td>
		<td width="15%">sys_setfsgid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sys.c">
		linux/kernel/sys.c</a></td>
	</tr>
	<tr>
		<td width="5%">149</td>
		<td width="15%">sys_sysctl</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/sysctl.c">
		linux/kernel/sysctl.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c7"></a>Time</b></td>
	</tr>
	<tr>
		<td width="5%">13</td>
		<td width="15%">sys_time</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/time.c">
		linux/kernel/time.c</a></td>
	</tr>
	<tr>
		<td width="5%">25</td>
		<td width="15%">sys_stime</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/time.c">
		linux/kernel/time.c</a></td>
	</tr>
	<tr>
		<td width="5%">78</td>
		<td width="15%">sys_gettimeofday</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/time.c">
		linux/kernel/time.c</a></td>
	</tr>
	<tr>
		<td width="5%">79</td>
		<td width="15%">sys_settimeofday</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/time.c">
		linux/kernel/time.c</a></td>
	</tr>
	<tr>
		<td width="5%">124</td>
		<td width="15%">sys_adjtimex</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/time.c">
		linux/kernel/time.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c8"></a>
		Kernel Timer &amp; Process</b></td>
	</tr>	
	<tr>
		<td width="5%">20</td>
		<td width="15%">sys_getpid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/timer.c">
		linux/kernel/timer.c</a></td>
	</tr>
	<tr>
		<td width="5%">27</td>
		<td width="15%">sys_alarm</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/timer.c">
		linux/kernel/timer.c</a></td>
	</tr>
	<tr>
		<td width="5%">64</td>
		<td width="15%">sys_getppid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/timer.c">
		linux/kernel/timer.c</a></td>
	</tr>
	<tr>
		<td width="5%">116</td>
		<td width="15%">sys_sysinfo</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/timer.c">
		linux/kernel/timer.c</a></td>
	</tr>
	<tr>
		<td width="5%">199</td>
		<td width="15%">sys_getuid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/timer.c">
		linux/kernel/timer.c</a></td>
	</tr>
	<tr>
		<td width="5%">200</td>
		<td width="15%">sys_getgid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/timer.c">
		linux/kernel/timer.c</a></td>
	</tr>
	<tr>
		<td width="5%">201</td>
		<td width="15%">sys_geteuid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/timer.c">
		linux/kernel/timer.c</a></td>
	</tr>
	<tr>
		<td width="5%">202</td>
		<td width="15%">sys_getegid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/timer.c">
		linux/kernel/timer.c</a></td>
	</tr>
	<tr>
		<td width="5%">224</td>
		<td width="15%">sys_gettid</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/timer.c">
		linux/kernel/timer.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c9"></a>16-bit uid (wrapper 
		functions)</b></td>
	</tr>
	<tr>
		<td width="5%">16</td>
		<td width="15%">sys_lchown16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">23</td>
		<td width="15%">sys_setuid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">24</td>
		<td width="15%">sys_getuid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">46</td>
		<td width="15%">sys_setgid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">47</td>
		<td width="15%">sys_getgid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">49</td>
		<td width="15%">sys_geteuid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">50</td>
		<td width="15%">sys_getegid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">70</td>
		<td width="15%">sys_setreuid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">71</td>
		<td width="15%">sys_setregid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">80</td>
		<td width="15%">sys_getgroups16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">81</td>
		<td width="15%">sys_setgroups16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">95</td>
		<td width="15%">sys_fchown16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">138</td>
		<td width="15%">sys_setfsuid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">139</td>
		<td width="15%">sys_setfsgid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">164</td>
		<td width="15%">sys_setresuid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">165</td>
		<td width="15%">sys_getresuid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">170</td>
		<td width="15%">sys_setresgid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">171</td>
		<td width="15%">sys_getresgid16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="5%">182</td>
		<td width="15%">sys_chown16</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/kernel/uid16.c">
		linux/kernel/uid16.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c10"></a>Memory Management</b></td>
	</tr>
	<tr>
		<td width="5%">250</td>
		<td width="15%">sys_fadvise64</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/fadvise.c">
		linux/mm/fadvise.c</a></td>
	</tr>
	<tr>
		<td width="5%">225</td>
		<td width="15%">sys_readahead</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/filemap.c">
		linux/mm/filemap.c</a></td>
	</tr>
	<tr>
		<td width="5%">257</td>
		<td width="15%">sys_remap_file_pages</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/fremap.c">
		linux/mm/fremap.c</a></td>
	</tr>
	<tr>
		<td width="5%">219</td>
		<td width="15%">sys_madvise</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/madvise.c">
		linux/mm/madvise.c</a></td>
	</tr>
	<tr>
		<td width="5%">218</td>
		<td width="15%">sys_mincore</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/mincore.c">
		linux/mm/mincore.c</a></td>
	</tr>
	<tr>
		<td width="5%">150</td>
		<td width="15%">sys_mlock</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/mlock.c">
		linux/mm/mlock.c</a></td>
	</tr>
	<tr>
		<td width="5%">151</td>
		<td width="15%">sys_munlock</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/mlock.c">
		linux/mm/mlock.c</a></td>
	</tr>
	<tr>
		<td width="5%">152</td>
		<td width="15%">sys_mlockall</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/mlock.c">
		linux/mm/mlock.c</a></td>
	</tr>
	<tr>
		<td width="5%">153</td>
		<td width="15%">sys_munlockall</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/mlock.c">
		linux/mm/mlock.c</a></td>
	</tr>
	<tr>
		<td width="5%">45</td>
		<td width="15%">sys_brk</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/mmap.c">
		linux/mm/mmap.c</a></td>
	</tr>
	<tr>
		<td width="5%">91</td>
		<td width="15%">sys_munmap</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/mmap.c">
		linux/mm/mmap.c</a></td>
	</tr>
	<tr>
		<td width="5%">125</td>
		<td width="15%">sys_mprotect</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/mprotect.c">
		linux/mm/mprotect.c</a></td>
	</tr>
	<tr>
		<td width="5%">163</td>
		<td width="15%">sys_mremap</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/mremap.c">
		linux/mm/mremap.c</a></td>
	</tr>
	<tr>
		<td width="5%">144</td>
		<td width="15%">sys_msync</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/msync.c">
		linux/mm/msync.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c11"></a>Swapfile</b></td>
	</tr>	
	<tr>
		<td width="5%">87</td>
		<td width="15%">sys_swapon</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/swapfile.c">
		linux/mm/swapfile.c</a></td>
	</tr>
	<tr>
		<td width="5%">115</td>
		<td width="15%">sys_swapoff</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/mm/swapfile.c">
		linux/mm/swapfile.c</a></td>
	</tr>
	<tr>
		<td width="100%" colspan="3" bgcolor="#C0C0C0"><b><a name="c12"></a>Socket</b></td>
	</tr>
	<tr>
		<td width="5%">102</td>
		<td width="15%">sys_socketcall</td>
		<td width="80%">
		<a target="_blank" href="http://www2.jollen.org/EmbeddedLinux/lsct/v2.6/linux/net/socket.c">
		linux/net/socket.c</a></td>
	</tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/linux_26_system_call12.html</link>
         <guid>https://www.jollen.org/blog/2006/10/linux_26_system_call12.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Wed, 11 Oct 2006 14:58:32 +0800</pubDate>
      </item>
            <item>
         <title>Linux 2.6.11 System Calls Table (LSCT)</title>
         <description><![CDATA[以下是 Linux 2.6.11 的 system call 整理表格，提供給「Embedded Linux 嵌入式系統實作演練」的讀者您做參考。這是一張很方便的表格，可以取代 unistd.h 與 "man"。

這張工具表格可以幫助我們：

1. 最主要的目的：當然是研究作業系統，了解 Linux 提供的 sytsem call service。
2. Trace kernel.
3. Writing shellcode.

供您參考 :)

<strong>Linux (kernel 2.6.11) System Call Table</strong>

1. sys_ni_syscall 為保留號碼
2. 更新日期: 2006/10/07
3. 第一次發佈 2.6 kernel 的 LSCT

<table border="1" cellspacing="2" cellpadding="5" width="100%">
  <tr>
    <td width="5%" align="center" bgcolor="#E4E4CB"><b>no</b></td>
    <td width="15%" align="center" bgcolor="#E4E4CB"><b>Syscall</b></td>
    <td width="80%" align="center" bgcolor="#E4E4CB"><b>Prototype decl.</b></td>
  </tr>
  <tr>
    <td width="5%">0</td>
    <td width="15%">sys_restart_syscall</td>
    <td width="80%">long sys_restart_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">1</td>
    <td width="15%">sys_exit</td>
    <td width="80%">long sys_exit(int error_code)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">2</td>
    <td width="15%">sys_fork</td>
    <td width="80%">int sys_fork(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">3</td>
    <td width="15%">sys_read</td>
    <td width="80%">ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">4</td>
    <td width="15%">sys_write</td>
    <td width="80%">ssize_t sys_write(unsigned int fd, const char __user * buf, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">5</td>
    <td width="15%">sys_open</td>
    <td width="80%">long sys_open(const char __user * filename, int flags, int mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">6</td>
    <td width="15%">sys_close</td>
    <td width="80%">long sys_close(unsigned int fd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">7</td>
    <td width="15%">sys_waitpid</td>
    <td width="80%">long sys_waitpid(pid_t pid, int __user * stat_addr, int options)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">8</td>
    <td width="15%">sys_creat</td>
    <td width="80%">long sys_creat(const char __user * pathname, int mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">9</td>
    <td width="15%">sys_link</td>
    <td width="80%">long sys_link(const char __user * oldname, const char __user * newname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">10</td>
    <td width="15%">sys_unlink</td>
    <td width="80%">long sys_unlink(const char __user * pathname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">11</td>
    <td width="15%">sys_execve</td>
    <td width="80%">int sys_execve(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">12</td>
    <td width="15%">sys_chdir</td>
    <td width="80%">long sys_chdir(const char __user * filename)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">13</td>
    <td width="15%">sys_time</td>
    <td width="80%">long sys_time(time_t __user * tloc)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">14</td>
    <td width="15%">sys_mknod</td>
    <td width="80%">long sys_mknod(const char __user * filename, int mode, unsigned dev)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">15</td>
    <td width="15%">sys_chmod</td>
    <td width="80%">long sys_chmod(const char __user * filename, mode_t mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">16</td>
    <td width="15%">sys_lchown16</td>
    <td width="80%">long sys_lchown16(const char __user * filename, old_uid_t user, old_gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">17</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">18</td>
    <td width="15%">sys_stat</td>
    <td width="80%">long sys_stat(char __user * filename, struct __old_kernel_stat __user * statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">19</td>
    <td width="15%">sys_lseek</td>
    <td width="80%">off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">20</td>
    <td width="15%">sys_getpid</td>
    <td width="80%">long sys_getpid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">21</td>
    <td width="15%">sys_mount</td>
    <td width="80%">long sys_mount(char __user * dev_name, char __user * dir_name, char __user * type, unsigned long flags, void __user * data)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">22</td>
    <td width="15%">sys_oldumount</td>
    <td width="80%">long sys_oldumount(char __user * name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">23</td>
    <td width="15%">sys_setuid16</td>
    <td width="80%">long sys_setuid16(old_uid_t uid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">24</td>
    <td width="15%">sys_getuid16</td>
    <td width="80%">long sys_getuid16(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">25</td>
    <td width="15%">sys_stime</td>
    <td width="80%">long sys_stime(time_t __user * tptr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">26</td>
    <td width="15%">sys_ptrace</td>
    <td width="80%">int sys_ptrace(long request, long pid, long addr, long data)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">27</td>
    <td width="15%">sys_alarm</td>
    <td width="80%">unsigned long sys_alarm(unsigned int seconds)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">28</td>
    <td width="15%">sys_fstat</td>
    <td width="80%">long sys_fstat(unsigned int fd, struct __old_kernel_stat __user * statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">29</td>
    <td width="15%">sys_pause</td>
    <td width="80%">long sys_pause(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">30</td>
    <td width="15%">sys_utime</td>
    <td width="80%">long sys_utime(char __user * filename, struct utimbuf __user * times)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">31</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">32</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">33</td>
    <td width="15%">sys_access</td>
    <td width="80%">long sys_access(const char __user * filename, int mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">34</td>
    <td width="15%">sys_nice</td>
    <td width="80%">long sys_nice(int increment)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">35</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">36</td>
    <td width="15%">sys_sync</td>
    <td width="80%">long sys_sync(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">37</td>
    <td width="15%">sys_kill</td>
    <td width="80%">long sys_kill(int pid, int sig)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">38</td>
    <td width="15%">sys_rename</td>
    <td width="80%">long sys_rename(const char __user * oldname, const char __user * newname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">39</td>
    <td width="15%">sys_mkdir</td>
    <td width="80%">long sys_mkdir(const char __user * pathname, int mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">40</td>
    <td width="15%">sys_rmdir</td>
    <td width="80%">long sys_rmdir(const char __user * pathname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">41</td>
    <td width="15%">sys_dup</td>
    <td width="80%">long sys_dup(unsigned int fildes)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">42</td>
    <td width="15%">sys_pipe</td>
    <td width="80%">int sys_pipe(unsigned long __user * fildes)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">43</td>
    <td width="15%">sys_times</td>
    <td width="80%">long sys_times(struct tms __user * tbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">44</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">45</td>
    <td width="15%">sys_brk</td>
    <td width="80%">unsigned long sys_brk(unsigned long brk)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">46</td>
    <td width="15%">sys_setgid16</td>
    <td width="80%">long sys_setgid16(old_gid_t gid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">47</td>
    <td width="15%">sys_getgid16</td>
    <td width="80%">long sys_getgid16(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">48</td>
    <td width="15%">sys_signal</td>
    <td width="80%">unsigned long sys_signal(int sig, __sighandler_t handler)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">49</td>
    <td width="15%">sys_geteuid16</td>
    <td width="80%">long sys_geteuid16(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">50</td>
    <td width="15%">sys_getegid16</td>
    <td width="80%">long sys_getegid16(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">51</td>
    <td width="15%">sys_acct</td>
    <td width="80%">long sys_acct(const char __user * name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">52</td>
    <td width="15%">sys_umount</td>
    <td width="80%">long sys_umount(char __user * name, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">53</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">54</td>
    <td width="15%">sys_ioctl</td>
    <td width="80%">long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">55</td>
    <td width="15%">sys_fcntl</td>
    <td width="80%">long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">56</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">57</td>
    <td width="15%">sys_setpgid</td>
    <td width="80%">long sys_setpgid(pid_t pid, pid_t pgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">58</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">59</td>
    <td width="15%">sys_olduname</td>
    <td width="80%">int sys_olduname(struct oldold_utsname __user * name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">60</td>
    <td width="15%">sys_umask</td>
    <td width="80%">long sys_umask(int mask)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">61</td>
    <td width="15%">sys_chroot</td>
    <td width="80%">long sys_chroot(const char __user * filename)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">62</td>
    <td width="15%">sys_ustat</td>
    <td width="80%">long sys_ustat(unsigned dev, struct ustat __user * ubuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">63</td>
    <td width="15%">sys_dup2</td>
    <td width="80%">long sys_dup2(unsigned int oldfd, unsigned int newfd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">64</td>
    <td width="15%">sys_getppid</td>
    <td width="80%">long sys_getppid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">65</td>
    <td width="15%">sys_getpgrp</td>
    <td width="80%">long sys_getpgrp(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">66</td>
    <td width="15%">sys_setsid</td>
    <td width="80%">long sys_setsid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">67</td>
    <td width="15%">sys_sigaction</td>
    <td width="80%">int sys_sigaction(int sig, const struct old_sigaction __user * act, struct old_sigaction __user * oact)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">68</td>
    <td width="15%">sys_sgetmask</td>
    <td width="80%">long sys_sgetmask(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">69</td>
    <td width="15%">sys_ssetmask</td>
    <td width="80%">long sys_ssetmask(int newmask)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">70</td>
    <td width="15%">sys_setreuid16</td>
    <td width="80%">long sys_setreuid16(old_uid_t ruid, old_uid_t euid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">71</td>
    <td width="15%">sys_setregid16</td>
    <td width="80%">long sys_setregid16(old_gid_t rgid, old_gid_t egid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">72</td>
    <td width="15%">sys_sigsuspend</td>
    <td width="80%">int sys_sigsuspend(int history0, int history1, old_sigset_t mask)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">73</td>
    <td width="15%">sys_sigpending</td>
    <td width="80%">long sys_sigpending(old_sigset_t __user * set)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">74</td>
    <td width="15%">sys_sethostname</td>
    <td width="80%">long sys_sethostname(char __user * name, int len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">75</td>
    <td width="15%">sys_setrlimit</td>
    <td width="80%">long sys_setrlimit(unsigned int resource, struct rlimit __user * rlim)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">76</td>
    <td width="15%">sys_old_getrlimit</td>
    <td width="80%">long sys_old_getrlimit(unsigned int resource, struct rlimit __user * rlim)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">77</td>
    <td width="15%">sys_getrusage</td>
    <td width="80%">long sys_getrusage(int who, struct rusage __user * ru)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">78</td>
    <td width="15%">sys_gettimeofday</td>
    <td width="80%">long sys_gettimeofday(struct timeval __user * tv, struct timezone __user * tz)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">79</td>
    <td width="15%">sys_settimeofday</td>
    <td width="80%">long sys_settimeofday(struct timeval __user * tv, struct timezone __user * tz)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">80</td>
    <td width="15%">sys_getgroups16</td>
    <td width="80%">long sys_getgroups16(int gidsetsize, old_gid_t __user * grouplist)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">81</td>
    <td width="15%">sys_setgroups16</td>
    <td width="80%">long sys_setgroups16(int gidsetsize, old_gid_t __user * grouplist)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">82</td>
    <td width="15%">old_select</td>
    <td width="80%">int old_select(struct sel_arg_struct __user * arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">83</td>
    <td width="15%">sys_symlink</td>
    <td width="80%">long sys_symlink(const char __user * oldname, const char __user * newname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">84</td>
    <td width="15%">sys_lstat</td>
    <td width="80%">long sys_lstat(char __user * filename, struct __old_kernel_stat __user * statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">85</td>
    <td width="15%">sys_readlink</td>
    <td width="80%">long sys_readlink(const char __user * path, char __user * buf, int bufsiz)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">86</td>
    <td width="15%">sys_uselib</td>
    <td width="80%">long sys_uselib(const char __user * library)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">87</td>
    <td width="15%">sys_swapon</td>
    <td width="80%">long sys_swapon(const char __user * specialfile, int swap_flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">88</td>
    <td width="15%">sys_reboot</td>
    <td width="80%">long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user * arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">89</td>
    <td width="15%">old_readdir</td>
    <td width="80%">long old_readdir(unsigned int fd, struct old_linux_dirent __user * dirent, unsigned int count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">90</td>
    <td width="15%">old_mmap</td>
    <td width="80%">int old_mmap(struct mmap_arg_struct __user * arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">91</td>
    <td width="15%">sys_munmap</td>
    <td width="80%">long sys_munmap(unsigned long addr, size_t len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">92</td>
    <td width="15%">sys_truncate</td>
    <td width="80%">long sys_truncate(const char __user * path, unsigned long length)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">93</td>
    <td width="15%">sys_ftruncate</td>
    <td width="80%">long sys_ftruncate(unsigned int fd, unsigned long length)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">94</td>
    <td width="15%">sys_fchmod</td>
    <td width="80%">long sys_fchmod(unsigned int fd, mode_t mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">95</td>
    <td width="15%">sys_fchown16</td>
    <td width="80%">long sys_fchown16(unsigned int fd, old_uid_t user, old_gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">96</td>
    <td width="15%">sys_getpriority</td>
    <td width="80%">long sys_getpriority(int which, int who)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">97</td>
    <td width="15%">sys_setpriority</td>
    <td width="80%">long sys_setpriority(int which, int who, int niceval)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">98</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">99</td>
    <td width="15%">sys_statfs</td>
    <td width="80%">long sys_statfs(const char __user * path, struct statfs __user * buf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">100</td>
    <td width="15%">sys_fstatfs</td>
    <td width="80%">long sys_fstatfs(unsigned int fd, struct statfs __user * buf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">101</td>
    <td width="15%">sys_ioperm</td>
    <td width="80%">long sys_ioperm(unsigned long from, unsigned long num, int turn_on)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">102</td>
    <td width="15%">sys_socketcall</td>
    <td width="80%">long sys_socketcall(int call, unsigned long __user * args)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">103</td>
    <td width="15%">sys_syslog</td>
    <td width="80%">long sys_syslog(int type, char __user * buf, int len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">104</td>
    <td width="15%">sys_setitimer</td>
    <td width="80%">long sys_setitimer(int which, struct itimerval __user * value, struct itimerval __user * ovalue)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">105</td>
    <td width="15%">sys_getitimer</td>
    <td width="80%">long sys_getitimer(int which, struct itimerval __user * value)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">106</td>
    <td width="15%">sys_newstat</td>
    <td width="80%">long sys_newstat(char __user * filename, struct stat __user * statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">107</td>
    <td width="15%">sys_newlstat</td>
    <td width="80%">long sys_newlstat(char __user * filename, struct stat __user * statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">108</td>
    <td width="15%">sys_newfstat</td>
    <td width="80%">long sys_newfstat(unsigned int fd, struct stat __user * statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">109</td>
    <td width="15%">sys_uname</td>
    <td width="80%">int sys_uname(struct old_utsname __user * name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">110</td>
    <td width="15%">sys_iopl</td>
    <td width="80%">long sys_iopl(unsigned long unused)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">111</td>
    <td width="15%">sys_vhangup</td>
    <td width="80%">long sys_vhangup(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">112</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">113</td>
    <td width="15%">sys_vm86old</td>
    <td width="80%">int sys_vm86old(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">114</td>
    <td width="15%">sys_wait4</td>
    <td width="80%">long sys_wait4(pid_t pid, int __user * stat_addr, int options, struct rusage __user * ru)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">115</td>
    <td width="15%">sys_swapoff</td>
    <td width="80%">long sys_swapoff(const char __user * specialfile)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">116</td>
    <td width="15%">sys_sysinfo</td>
    <td width="80%">long sys_sysinfo(struct sysinfo __user * info)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">117</td>
    <td width="15%">sys_ipc</td>
    <td width="80%">int sys_ipc(uint call, int first, int second, int third, void __user * ptr, long fifth)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">118</td>
    <td width="15%">sys_fsync</td>
    <td width="80%">long sys_fsync(unsigned int fd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">119</td>
    <td width="15%">sys_sigreturn</td>
    <td width="80%">int sys_sigreturn(unsigned long __unused)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">120</td>
    <td width="15%">sys_clone</td>
    <td width="80%">int sys_clone(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">121</td>
    <td width="15%">sys_setdomainname</td>
    <td width="80%">long sys_setdomainname(char __user * name, int len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">122</td>
    <td width="15%">sys_newuname</td>
    <td width="80%">long sys_newuname(struct new_utsname __user * name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">123</td>
    <td width="15%">sys_modify_ldt</td>
    <td width="80%">int sys_modify_ldt(int func, void __user * ptr, unsigned long bytecount)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">124</td>
    <td width="15%">sys_adjtimex</td>
    <td width="80%">long sys_adjtimex(struct timex __user * txc_p)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">125</td>
    <td width="15%">sys_mprotect</td>
    <td width="80%">long sys_mprotect(unsigned long start, size_t len, unsigned long prot)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">126</td>
    <td width="15%">sys_sigprocmask</td>
    <td width="80%">long sys_sigprocmask(int how, old_sigset_t __user * set, old_sigset_t __user * oset)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">127</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">128</td>
    <td width="15%">sys_init_module</td>
    <td width="80%">long sys_init_module(void __user * umod, unsigned long len, const char __user * uargs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">129</td>
    <td width="15%">sys_delete_module</td>
    <td width="80%">long sys_delete_module(const char __user * name_user, unsigned int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">130</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">131</td>
    <td width="15%">sys_quotactl</td>
    <td width="80%">long sys_quotactl(unsigned int cmd, const char __user * special, qid_t id, void __user * addr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">132</td>
    <td width="15%">sys_getpgid</td>
    <td width="80%">long sys_getpgid(pid_t pid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">133</td>
    <td width="15%">sys_fchdir</td>
    <td width="80%">long sys_fchdir(unsigned int fd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">134</td>
    <td width="15%">sys_bdflush</td>
    <td width="80%">long sys_bdflush(int func, long data)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">135</td>
    <td width="15%">sys_sysfs</td>
    <td width="80%">long sys_sysfs(int option, unsigned long arg1, unsigned long arg2)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">136</td>
    <td width="15%">sys_personality</td>
    <td width="80%">long sys_personality(u_long personality)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">137</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">138</td>
    <td width="15%">sys_setfsuid16</td>
    <td width="80%">long sys_setfsuid16(old_uid_t uid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">139</td>
    <td width="15%">sys_setfsgid16</td>
    <td width="80%">long sys_setfsgid16(old_gid_t gid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">140</td>
    <td width="15%">sys_llseek</td>
    <td width="80%">long sys_llseek(unsigned int fd, unsigned long offset_high, unsigned long offset_low, loff_t __user * result, unsigned int origin)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">141</td>
    <td width="15%">sys_getdents</td>
    <td width="80%">long sys_getdents(unsigned int fd, struct linux_dirent __user * dirent, unsigned int count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">142</td>
    <td width="15%">sys_select</td>
    <td width="80%">long sys_select(int n, fd_set __user * inp, fd_set __user * outp, fd_set __user * exp, struct timeval __user * tvp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">143</td>
    <td width="15%">sys_flock</td>
    <td width="80%">long sys_flock(unsigned int fd, unsigned int cmd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">144</td>
    <td width="15%">sys_msync</td>
    <td width="80%">long sys_msync(unsigned long start, size_t len, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">145</td>
    <td width="15%">sys_readv</td>
    <td width="80%">ssize_t sys_readv(unsigned long fd, const struct iovec __user * vec, unsigned long vlen)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">146</td>
    <td width="15%">sys_writev</td>
    <td width="80%">ssize_t sys_writev(unsigned long fd, const struct iovec __user * vec, unsigned long vlen)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">147</td>
    <td width="15%">sys_getsid</td>
    <td width="80%">long sys_getsid(pid_t pid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">148</td>
    <td width="15%">sys_fdatasync</td>
    <td width="80%">long sys_fdatasync(unsigned int fd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">149</td>
    <td width="15%">sys_sysctl</td>
    <td width="80%">long sys_sysctl(struct __sysctl_args __user * args)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">150</td>
    <td width="15%">sys_mlock</td>
    <td width="80%">long sys_mlock(unsigned long start, size_t len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">151</td>
    <td width="15%">sys_munlock</td>
    <td width="80%">long sys_munlock(unsigned long start, size_t len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">152</td>
    <td width="15%">sys_mlockall</td>
    <td width="80%">long sys_mlockall(int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">153</td>
    <td width="15%">sys_munlockall</td>
    <td width="80%">long sys_munlockall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">154</td>
    <td width="15%">sys_sched_setparam</td>
    <td width="80%">long sys_sched_setparam(pid_t pid, struct sched_param __user * param)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">155</td>
    <td width="15%">sys_sched_getparam</td>
    <td width="80%">long sys_sched_getparam(pid_t pid, struct sched_param __user * param)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">156</td>
    <td width="15%">sys_sched_setscheduler</td>
    <td width="80%">long sys_sched_setscheduler(pid_t pid, int policy, struct sched_param __user * param)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">157</td>
    <td width="15%">sys_sched_getscheduler</td>
    <td width="80%">long sys_sched_getscheduler(pid_t pid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">158</td>
    <td width="15%">sys_sched_yield</td>
    <td width="80%">long sys_sched_yield(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">159</td>
    <td width="15%">sys_sched_get_priority_max</td>
    <td width="80%">long sys_sched_get_priority_max(int policy)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">160</td>
    <td width="15%">sys_sched_get_priority_min</td>
    <td width="80%">long sys_sched_get_priority_min(int policy)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">161</td>
    <td width="15%">sys_sched_rr_get_interval</td>
    <td width="80%">long sys_sched_rr_get_interval(pid_t pid, struct timespec __user * interval)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">162</td>
    <td width="15%">sys_nanosleep</td>
    <td width="80%">long sys_nanosleep(struct timespec __user * rqtp, struct timespec __user * rmtp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">163</td>
    <td width="15%">sys_mremap</td>
    <td width="80%">unsigned long sys_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">164</td>
    <td width="15%">sys_setresuid16</td>
    <td width="80%">long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">165</td>
    <td width="15%">sys_getresuid16</td>
    <td width="80%">long sys_getresuid16(old_uid_t __user * ruid, old_uid_t __user * euid, old_uid_t __user * suid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">166</td>
    <td width="15%">sys_vm86</td>
    <td width="80%">int sys_vm86(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">167</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">168</td>
    <td width="15%">sys_poll</td>
    <td width="80%">long sys_poll(struct pollfd __user * ufds, unsigned int nfds, long timeout)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">169</td>
    <td width="15%">sys_nfsservctl</td>
    <td width="80%">&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">170</td>
    <td width="15%">sys_setresgid16</td>
    <td width="80%">long sys_setresgid16(old_gid_t rgid, old_gid_t egid, old_gid_t sgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">171</td>
    <td width="15%">sys_getresgid16</td>
    <td width="80%">long sys_getresgid16(old_gid_t __user * rgid, old_gid_t __user * egid, old_gid_t __user * sgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">172</td>
    <td width="15%">sys_prctl</td>
    <td width="80%">long sys_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">173</td>
    <td width="15%">sys_rt_sigreturn</td>
    <td width="80%">int sys_rt_sigreturn(unsigned long __unused)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">174</td>
    <td width="15%">sys_rt_sigaction</td>
    <td width="80%">long sys_rt_sigaction(int sig, const struct sigaction __user * act, struct sigaction __user * oact, size_t sigsetsize)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">175</td>
    <td width="15%">sys_rt_sigprocmask</td>
    <td width="80%">long sys_rt_sigprocmask(int how, sigset_t __user * set, sigset_t __user * oset, size_t sigsetsize)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">176</td>
    <td width="15%">sys_rt_sigpending</td>
    <td width="80%">long sys_rt_sigpending(sigset_t __user * set, size_t sigsetsize)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">177</td>
    <td width="15%">sys_rt_sigtimedwait</td>
    <td width="80%">long sys_rt_sigtimedwait(const sigset_t __user * uthese, siginfo_t __user * uinfo, const struct timespec __user * uts, size_t sigsetsize)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">178</td>
    <td width="15%">sys_rt_sigqueueinfo</td>
    <td width="80%">long sys_rt_sigqueueinfo(int pid, int sig, siginfo_t __user * uinfo)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">179</td>
    <td width="15%">sys_rt_sigsuspend</td>
    <td width="80%">int sys_rt_sigsuspend(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">180</td>
    <td width="15%">sys_pread64</td>
    <td width="80%">ssize_t sys_pread64(unsigned int fd, char __user * buf, size_t count, loff_t pos)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">181</td>
    <td width="15%">sys_pwrite64</td>
    <td width="80%">ssize_t sys_pwrite64(unsigned int fd, const char __user * buf, size_t count, loff_t pos)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">182</td>
    <td width="15%">sys_chown16</td>
    <td width="80%">long sys_chown16(const char __user * filename, old_uid_t user, old_gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">183</td>
    <td width="15%">sys_getcwd</td>
    <td width="80%">long sys_getcwd(char __user * buf, unsigned long size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">184</td>
    <td width="15%">sys_capget</td>
    <td width="80%">long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">185</td>
    <td width="15%">sys_capset</td>
    <td width="80%">long sys_capset(cap_user_header_t header, const cap_user_data_t data)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">186</td>
    <td width="15%">sys_sigaltstack</td>
    <td width="80%">int sys_sigaltstack(unsigned long ebx)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">187</td>
    <td width="15%">sys_sendfile</td>
    <td width="80%">ssize_t sys_sendfile(int out_fd, int in_fd, off_t __user * offset, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">188</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">189</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">190</td>
    <td width="15%">sys_vfork</td>
    <td width="80%">int sys_vfork(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">191</td>
    <td width="15%">sys_getrlimit</td>
    <td width="80%">long sys_getrlimit(unsigned int resource, struct rlimit __user * rlim)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">192</td>
    <td width="15%">sys_mmap2</td>
    <td width="80%">long sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">193</td>
    <td width="15%">sys_truncate64</td>
    <td width="80%">long sys_truncate64(const char __user * path, loff_t length)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">194</td>
    <td width="15%">sys_ftruncate64</td>
    <td width="80%">long sys_ftruncate64(unsigned int fd, loff_t length)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">195</td>
    <td width="15%">sys_stat64</td>
    <td width="80%">long sys_stat64(char __user * filename, struct stat64 __user * statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">196</td>
    <td width="15%">sys_lstat64</td>
    <td width="80%">long sys_lstat64(char __user * filename, struct stat64 __user * statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">197</td>
    <td width="15%">sys_fstat64</td>
    <td width="80%">long sys_fstat64(unsigned long fd, struct stat64 __user * statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">198</td>
    <td width="15%">sys_lchown</td>
    <td width="80%">long sys_lchown(const char __user * filename, uid_t user, gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">199</td>
    <td width="15%">sys_getuid</td>
    <td width="80%">long sys_getuid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">200</td>
    <td width="15%">sys_getgid</td>
    <td width="80%">long sys_getgid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">201</td>
    <td width="15%">sys_geteuid</td>
    <td width="80%">long sys_geteuid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">202</td>
    <td width="15%">sys_getegid</td>
    <td width="80%">long sys_getegid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">203</td>
    <td width="15%">sys_setreuid</td>
    <td width="80%">long sys_setreuid(uid_t ruid, uid_t euid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">204</td>
    <td width="15%">sys_setregid</td>
    <td width="80%">long sys_setregid(gid_t rgid, gid_t egid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">205</td>
    <td width="15%">sys_getgroups</td>
    <td width="80%">long sys_getgroups(int gidsetsize, gid_t __user * grouplist)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">206</td>
    <td width="15%">sys_setgroups</td>
    <td width="80%">long sys_setgroups(int gidsetsize, gid_t __user * grouplist)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">207</td>
    <td width="15%">sys_fchown</td>
    <td width="80%">long sys_fchown(unsigned int fd, uid_t user, gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">208</td>
    <td width="15%">sys_setresuid</td>
    <td width="80%">long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">209</td>
    <td width="15%">sys_getresuid</td>
    <td width="80%">long sys_getresuid(uid_t __user * ruid, uid_t __user * euid, uid_t __user * suid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">210</td>
    <td width="15%">sys_setresgid</td>
    <td width="80%">long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">211</td>
    <td width="15%">sys_getresgid</td>
    <td width="80%">long sys_getresgid(gid_t __user * rgid, gid_t __user * egid, gid_t __user * sgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">212</td>
    <td width="15%">sys_chown</td>
    <td width="80%">long sys_chown(const char __user * filename, uid_t user, gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">213</td>
    <td width="15%">sys_setuid</td>
    <td width="80%">long sys_setuid(uid_t uid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">214</td>
    <td width="15%">sys_setgid</td>
    <td width="80%">long sys_setgid(gid_t gid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">215</td>
    <td width="15%">sys_setfsuid</td>
    <td width="80%">long sys_setfsuid(uid_t uid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">216</td>
    <td width="15%">sys_setfsgid</td>
    <td width="80%">long sys_setfsgid(gid_t gid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">217</td>
    <td width="15%">sys_pivot_root</td>
    <td width="80%">long sys_pivot_root(const char __user * new_root, const char __user * put_old)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">218</td>
    <td width="15%">sys_mincore</td>
    <td width="80%">long sys_mincore(unsigned long start, size_t len, unsigned char __user * vec)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">219</td>
    <td width="15%">sys_madvise</td>
    <td width="80%">long sys_madvise(unsigned long start, size_t len_in, int behavior)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">220</td>
    <td width="15%">sys_getdents64</td>
    <td width="80%">long sys_getdents64(unsigned int fd, struct linux_dirent64 __user * dirent, unsigned int count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">221</td>
    <td width="15%">sys_fcntl64</td>
    <td width="80%">long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">222</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">223</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">224</td>
    <td width="15%">sys_gettid</td>
    <td width="80%">long sys_gettid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">225</td>
    <td width="15%">sys_readahead</td>
    <td width="80%">ssize_t sys_readahead(int fd, loff_t offset, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">226</td>
    <td width="15%">sys_setxattr</td>
    <td width="80%">long sys_setxattr(char __user * path, char __user * name, void __user * value, size_t size, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">227</td>
    <td width="15%">sys_lsetxattr</td>
    <td width="80%">long sys_lsetxattr(char __user * path, char __user * name, void __user * value, size_t size, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">228</td>
    <td width="15%">sys_fsetxattr</td>
    <td width="80%">long sys_fsetxattr(int fd, char __user * name, void __user * value, size_t size, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">229</td>
    <td width="15%">sys_getxattr</td>
    <td width="80%">ssize_t sys_getxattr(char __user * path, char __user * name, void __user * value, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">230</td>
    <td width="15%">sys_lgetxattr</td>
    <td width="80%">ssize_t sys_lgetxattr(char __user * path, char __user * name, void __user * value, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">231</td>
    <td width="15%">sys_fgetxattr</td>
    <td width="80%">ssize_t sys_fgetxattr(int fd, char __user * name, void __user * value, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">232</td>
    <td width="15%">sys_listxattr</td>
    <td width="80%">ssize_t sys_listxattr(char __user * path, char __user * list, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">233</td>
    <td width="15%">sys_llistxattr</td>
    <td width="80%">ssize_t sys_llistxattr(char __user * path, char __user * list, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">234</td>
    <td width="15%">sys_flistxattr</td>
    <td width="80%">ssize_t sys_flistxattr(int fd, char __user * list, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">235</td>
    <td width="15%">sys_removexattr</td>
    <td width="80%">long sys_removexattr(char __user * path, char __user * name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">236</td>
    <td width="15%">sys_lremovexattr</td>
    <td width="80%">long sys_lremovexattr(char __user * path, char __user * name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">237</td>
    <td width="15%">sys_fremovexattr</td>
    <td width="80%">long sys_fremovexattr(int fd, char __user * name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">238</td>
    <td width="15%">sys_tkill</td>
    <td width="80%">long sys_tkill(int pid, int sig)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">239</td>
    <td width="15%">sys_sendfile64</td>
    <td width="80%">ssize_t sys_sendfile64(int out_fd, int in_fd, loff_t __user * offset, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">240</td>
    <td width="15%">sys_futex</td>
    <td width="80%">long sys_futex(u32 __user * uaddr, int op, int val, struct timespec __user * utime, u32 __user * uaddr2, int val3)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">241</td>
    <td width="15%">sys_sched_setaffinity</td>
    <td width="80%">long sys_sched_setaffinity(pid_t pid, unsigned int len, unsigned long __user * user_mask_ptr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">242</td>
    <td width="15%">sys_sched_getaffinity</td>
    <td width="80%">long sys_sched_getaffinity(pid_t pid, unsigned int len, unsigned long __user * user_mask_ptr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">243</td>
    <td width="15%">sys_set_thread_area</td>
    <td width="80%">int sys_set_thread_area(struct user_desc __user * u_info)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">244</td>
    <td width="15%">sys_get_thread_area</td>
    <td width="80%">int sys_get_thread_area(struct user_desc __user * u_info)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">245</td>
    <td width="15%">sys_io_setup</td>
    <td width="80%">long sys_io_setup(unsigned nr_events, aio_context_t __user * ctxp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">246</td>
    <td width="15%">sys_io_destroy</td>
    <td width="80%">long sys_io_destroy(aio_context_t ctx)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">247</td>
    <td width="15%">sys_io_getevents</td>
    <td width="80%">long sys_io_getevents(aio_context_t ctx_id, long min_nr, long nr, struct io_event __user * events, struct timespec __user * timeout)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">248</td>
    <td width="15%">sys_io_submit</td>
    <td width="80%">long sys_io_submit(aio_context_t ctx_id, long nr, struct iocb __user * __user * iocbpp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">249</td>
    <td width="15%">sys_io_cancel</td>
    <td width="80%">long sys_io_cancel(aio_context_t ctx_id, struct iocb __user * iocb, struct io_event __user * result)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">250</td>
    <td width="15%">sys_fadvise64</td>
    <td width="80%">long sys_fadvise64(int fd, loff_t offset, size_t len, int advice)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">251</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">252</td>
    <td width="15%">sys_exit_group</td>
    <td width="80%">void sys_exit_group(int error_code)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">253</td>
    <td width="15%">sys_lookup_dcookie</td>
    <td width="80%">long sys_lookup_dcookie(u64 cookie64, char __user * buf, size_t len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">254</td>
    <td width="15%">sys_epoll_create</td>
    <td width="80%">long sys_epoll_create(int size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">255</td>
    <td width="15%">sys_epoll_ctl</td>
    <td width="80%">long sys_epoll_ctl(int epfd, int op, int fd, struct epoll_event __user * event)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">256</td>
    <td width="15%">sys_epoll_wait</td>
    <td width="80%">long sys_epoll_wait(int epfd, struct epoll_event __user * events, int maxevents, int timeout)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">257</td>
    <td width="15%">sys_remap_file_pages</td>
    <td width="80%">long sys_remap_file_pages(unsigned long start, unsigned long size, unsigned long __prot, unsigned long pgoff, unsigned long flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">258</td>
    <td width="15%">sys_set_tid_address</td>
    <td width="80%">long sys_set_tid_address(int __user * tidptr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">259</td>
    <td width="15%">sys_timer_create</td>
    <td width="80%">long sys_timer_create(clockid_t which_clock, struct sigevent __user * timer_event_spec, timer_t __user * created_timer_id)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">260</td>
    <td width="15%">sys_timer_settime</td>
    <td width="80%">long sys_timer_settime(timer_t timer_id, int flags, const struct itimerspec __user * new_setting, struct itimerspec __user * old_setting)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">261</td>
    <td width="15%">sys_timer_gettime</td>
    <td width="80%">long sys_timer_gettime(timer_t timer_id, struct itimerspec __user * setting)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">262</td>
    <td width="15%">sys_timer_getoverrun</td>
    <td width="80%">long sys_timer_getoverrun(timer_t timer_id)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">263</td>
    <td width="15%">sys_timer_delete</td>
    <td width="80%">long sys_timer_delete(timer_t timer_id)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">264</td>
    <td width="15%">sys_clock_settime</td>
    <td width="80%">long sys_clock_settime(clockid_t which_clock, const struct timespec __user * tp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">265</td>
    <td width="15%">sys_clock_gettime</td>
    <td width="80%">long sys_clock_gettime(clockid_t which_clock, struct timespec __user * tp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">266</td>
    <td width="15%">sys_clock_getres</td>
    <td width="80%">long sys_clock_getres(clockid_t which_clock, struct timespec __user * tp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">267</td>
    <td width="15%">sys_clock_nanosleep</td>
    <td width="80%">long sys_clock_nanosleep(clockid_t which_clock, int flags, const struct timespec __user * rqtp, struct timespec __user * rmtp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">268</td>
    <td width="15%">sys_statfs64</td>
    <td width="80%">long sys_statfs64(const char __user * path, size_t sz, struct statfs64 __user * buf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">269</td>
    <td width="15%">sys_fstatfs64</td>
    <td width="80%">long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user * buf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">270</td>
    <td width="15%">sys_tgkill</td>
    <td width="80%">long sys_tgkill(int tgid, int pid, int sig)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">271</td>
    <td width="15%">sys_utimes</td>
    <td width="80%">long sys_utimes(char __user * filename, struct timeval __user * utimes)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">272</td>
    <td width="15%">sys_fadvise64_64</td>
    <td width="80%">long sys_fadvise64_64(int fd, loff_t offset, loff_t len, int advice)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">273</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">274</td>
    <td width="15%">sys_mbind</td>
    <td width="80%">long sys_mbind(unsigned long start, unsigned long len, unsigned long mode, unsigned long __user * nmask, unsigned long maxnode, unsigned flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">275</td>
    <td width="15%">sys_get_mempolicy</td>
    <td width="80%">long sys_get_mempolicy(int __user * policy, unsigned long __user * nmask, unsigned long maxnode, unsigned long addr, unsigned long flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">276</td>
    <td width="15%">sys_set_mempolicy</td>
    <td width="80%">long sys_set_mempolicy(int mode, unsigned long __user * nmask, unsigned long maxnode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">277</td>
    <td width="15%">sys_mq_open</td>
    <td width="80%">long sys_mq_open(const char __user * u_name, int oflag, mode_t mode, struct mq_attr __user * u_attr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">278</td>
    <td width="15%">sys_mq_unlink</td>
    <td width="80%">long sys_mq_unlink(const char __user * u_name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">279</td>
    <td width="15%">sys_mq_timedsend</td>
    <td width="80%">long sys_mq_timedsend(mqd_t mqdes, const char __user * u_msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec __user * u_abs_timeout)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">280</td>
    <td width="15%">sys_mq_timedreceive</td>
    <td width="80%">ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user * u_msg_ptr, size_t msg_len, unsigned int __user * u_msg_prio, const struct timespec __user * u_abs_timeout)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">281</td>
    <td width="15%">sys_mq_notify</td>
    <td width="80%">long sys_mq_notify(mqd_t mqdes, const struct sigevent __user * u_notification)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">282</td>
    <td width="15%">sys_mq_getsetattr</td>
    <td width="80%">long sys_mq_getsetattr(mqd_t mqdes, const struct mq_attr __user * u_mqstat, struct mq_attr __user * u_omqstat)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">283</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">284</td>
    <td width="15%">sys_waitid</td>
    <td width="80%">long sys_waitid(int which, pid_t pid, struct siginfo __user * infop, int options, struct rusage __user * ru)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">285</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">286</td>
    <td width="15%">sys_add_key</td>
    <td width="80%">long sys_add_key(const char __user * _type, const char __user * _description, const void __user * _payload, size_t plen, key_serial_t ringid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">287</td>
    <td width="15%">sys_request_key</td>
    <td width="80%">long sys_request_key(const char __user * _type, const char __user * _description, const char __user * _callout_info, key_serial_t destringid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">288</td>
    <td width="15%">sys_keyctl</td>
    <td width="80%">long sys_keyctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)&nbsp;</td>
  </tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/linux_2611_system_calls_table.html</link>
         <guid>https://www.jollen.org/blog/2006/10/linux_2611_system_calls_table.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 07 Oct 2006 23:41:39 +0800</pubDate>
      </item>
            <item>
         <title>Linux 2.4.29 System Calls Table (LSCT)</title>
         <description><![CDATA[以下是 Linux 2.4.29 的 system call 整理表格，提供給「Embedded Linux 嵌入式系統實作演練」的讀者您做參考。這是一張很方便的表格，可以取代 unistd.h 與 "man"。

這張工具表格可以幫助我們：

1. 最主要的目的：當然是研究作業系統，了解 Linux 提供的 sytsem call service。
2. Trace kernel.
3. Writing shellcode.

供您參考 :)
<strong>Linux (kernel 2.4.29) System Call Table</strong>

1. sys_ni_syscall 為保留號碼
2. 更新日期: 2006/10/07
3. 其實與 linux 2.4.22 是相同的，因此亦可參考我們之前 (3 年前) 整理的 LSCT。

<table border="1" cellspacing="2" cellpadding="5" width="100%">
<tr>
    <td width="5%" align="center" bgcolor="#E4E4CB"><b>no</b></td>
    <td width="15%" align="center" bgcolor="#E4E4CB"><b>Syscall</b></td>
    <td width="80%" align="center" bgcolor="#E4E4CB"><b>Prototype decl.</b></td>
  </tr>
  <tr>
    <td width="5%">0</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">1</td>
    <td width="15%">sys_exit</td>
    <td width="80%">long sys_exit(int error_code)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">2</td>
    <td width="15%">sys_fork</td>
    <td width="80%">int sys_fork(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">3</td>
    <td width="15%">sys_read</td>
    <td width="80%">ssize_t sys_read(unsigned int fd, char *buf, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">4</td>
    <td width="15%">sys_write</td>
    <td width="80%">ssize_t sys_write(unsigned int fd, const char *buf, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">5</td>
    <td width="15%">sys_open</td>
    <td width="80%">long sys_open(const char *filename, int flags, int mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">6</td>
    <td width="15%">sys_close</td>
    <td width="80%">long sys_close(unsigned int fd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">7</td>
    <td width="15%">sys_waitpid</td>
    <td width="80%">long sys_waitpid(pid_t pid, unsigned int *stat_addr, int options)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">8</td>
    <td width="15%">sys_creat</td>
    <td width="80%">long sys_creat(const char *pathname, int mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">9</td>
    <td width="15%">sys_link</td>
    <td width="80%">long sys_link(const char *oldname, const char *newname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">10</td>
    <td width="15%">sys_unlink</td>
    <td width="80%">long sys_unlink(const char *pathname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">11</td>
    <td width="15%">sys_execve</td>
    <td width="80%">int sys_execve(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">12</td>
    <td width="15%">sys_chdir</td>
    <td width="80%">long sys_chdir(const char *filename)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">13</td>
    <td width="15%">sys_time</td>
    <td width="80%">long sys_time(int *tloc)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">14</td>
    <td width="15%">sys_mknod</td>
    <td width="80%">long sys_mknod(const char *filename, int mode, dev_t dev)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">15</td>
    <td width="15%">sys_chmod</td>
    <td width="80%">long sys_chmod(const char *filename, mode_t mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">16</td>
    <td width="15%">sys_lchown16</td>
    <td width="80%">long sys_lchown16(const char *filename, old_uid_t user, old_gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">17</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">18</td>
    <td width="15%">sys_stat</td>
    <td width="80%">long sys_stat(char *filename, struct __old_kernel_stat *statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">19</td>
    <td width="15%">sys_lseek</td>
    <td width="80%">off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">20</td>
    <td width="15%">sys_getpid</td>
    <td width="80%">long sys_getpid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">21</td>
    <td width="15%">sys_mount</td>
    <td width="80%">long sys_mount(char *dev_name, char *dir_name, char *type, unsigned long flags, void *data)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">22</td>
    <td width="15%">sys_oldumount</td>
    <td width="80%">long sys_oldumount(char *name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">23</td>
    <td width="15%">sys_setuid16</td>
    <td width="80%">long sys_setuid16(old_uid_t uid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">24</td>
    <td width="15%">sys_getuid16</td>
    <td width="80%">long sys_getuid16(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">25</td>
    <td width="15%">sys_stime</td>
    <td width="80%">long sys_stime(int *tptr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">26</td>
    <td width="15%">sys_ptrace</td>
    <td width="80%">int sys_ptrace(long request, long pid, long addr, long data)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">27</td>
    <td width="15%">sys_alarm</td>
    <td width="80%">unsigned long sys_alarm(unsigned int seconds)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">28</td>
    <td width="15%">sys_fstat</td>
    <td width="80%">long sys_fstat(unsigned int fd, struct __old_kernel_stat *statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">29</td>
    <td width="15%">sys_pause</td>
    <td width="80%">int sys_pause(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">30</td>
    <td width="15%">sys_utime</td>
    <td width="80%">long sys_utime(char *filename, struct utimbuf *times)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">31</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">32</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">33</td>
    <td width="15%">sys_access</td>
    <td width="80%">long sys_access(const char *filename, int mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">34</td>
    <td width="15%">sys_nice</td>
    <td width="80%">long sys_nice(int increment)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">35</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">36</td>
    <td width="15%">sys_sync</td>
    <td width="80%">long sys_sync(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">37</td>
    <td width="15%">sys_kill</td>
    <td width="80%">long sys_kill(int pid, int sig)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">38</td>
    <td width="15%">sys_rename</td>
    <td width="80%">long sys_rename(const char *oldname, const char *newname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">39</td>
    <td width="15%">sys_mkdir</td>
    <td width="80%">long sys_mkdir(const char *pathname, int mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">40</td>
    <td width="15%">sys_rmdir</td>
    <td width="80%">long sys_rmdir(const char *pathname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">41</td>
    <td width="15%">sys_dup</td>
    <td width="80%">long sys_dup(unsigned int fildes)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">42</td>
    <td width="15%">sys_pipe</td>
    <td width="80%">int sys_pipe(unsigned long *fildes)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">43</td>
    <td width="15%">sys_times</td>
    <td width="80%">long sys_times(struct tms *tbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">44</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">45</td>
    <td width="15%">sys_brk</td>
    <td width="80%">unsigned long sys_brk(unsigned long brk)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">46</td>
    <td width="15%">sys_setgid16</td>
    <td width="80%">long sys_setgid16(old_gid_t gid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">47</td>
    <td width="15%">sys_getgid16</td>
    <td width="80%">long sys_getgid16(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">48</td>
    <td width="15%">sys_signal</td>
    <td width="80%">unsigned long sys_signal(int sig, __sighandler_t handler)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">49</td>
    <td width="15%">sys_geteuid16</td>
    <td width="80%">long sys_geteuid16(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">50</td>
    <td width="15%">sys_getegid16</td>
    <td width="80%">long sys_getegid16(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">51</td>
    <td width="15%">sys_acct</td>
    <td width="80%">long sys_acct(const char *name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">52</td>
    <td width="15%">sys_umount</td>
    <td width="80%">long sys_umount(char *name, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">53</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">54</td>
    <td width="15%">sys_ioctl</td>
    <td width="80%">long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">55</td>
    <td width="15%">sys_fcntl</td>
    <td width="80%">long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">56</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">57</td>
    <td width="15%">sys_setpgid</td>
    <td width="80%">long sys_setpgid(pid_t pid, pid_t pgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">58</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">59</td>
    <td width="15%">sys_olduname</td>
    <td width="80%">int sys_olduname(struct oldold_utsname *name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">60</td>
    <td width="15%">sys_umask</td>
    <td width="80%">long sys_umask(int mask)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">61</td>
    <td width="15%">sys_chroot</td>
    <td width="80%">long sys_chroot(const char *filename)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">62</td>
    <td width="15%">sys_ustat</td>
    <td width="80%">long sys_ustat(dev_t dev, struct ustat *ubuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">63</td>
    <td width="15%">sys_dup2</td>
    <td width="80%">long sys_dup2(unsigned int oldfd, unsigned int newfd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">64</td>
    <td width="15%">sys_getppid</td>
    <td width="80%">long sys_getppid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">65</td>
    <td width="15%">sys_getpgrp</td>
    <td width="80%">long sys_getpgrp(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">66</td>
    <td width="15%">sys_setsid</td>
    <td width="80%">long sys_setsid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">67</td>
    <td width="15%">sys_sigaction</td>
    <td width="80%">int sys_sigaction(int sig, const struct old_sigaction *act, struct old_sigaction *oact)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">68</td>
    <td width="15%">sys_sgetmask</td>
    <td width="80%">long sys_sgetmask(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">69</td>
    <td width="15%">sys_ssetmask</td>
    <td width="80%">long sys_ssetmask(int newmask)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">70</td>
    <td width="15%">sys_setreuid16</td>
    <td width="80%">long sys_setreuid16(old_uid_t ruid, old_uid_t euid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">71</td>
    <td width="15%">sys_setregid16</td>
    <td width="80%">long sys_setregid16(old_gid_t rgid, old_gid_t egid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">72</td>
    <td width="15%">sys_sigsuspend</td>
    <td width="80%">int sys_sigsuspend(int history0, int history1, old_sigset_t mask)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">73</td>
    <td width="15%">sys_sigpending</td>
    <td width="80%">long sys_sigpending(old_sigset_t * set)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">74</td>
    <td width="15%">sys_sethostname</td>
    <td width="80%">long sys_sethostname(char *name, int len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">75</td>
    <td width="15%">sys_setrlimit</td>
    <td width="80%">long sys_setrlimit(unsigned int resource, struct rlimit *rlim)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">76</td>
    <td width="15%">sys_old_getrlimit</td>
    <td width="80%">long sys_old_getrlimit(unsigned int resource, struct rlimit *rlim)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">77</td>
    <td width="15%">sys_getrusage</td>
    <td width="80%">long sys_getrusage(int who, struct rusage *ru)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">78</td>
    <td width="15%">sys_gettimeofday</td>
    <td width="80%">long sys_gettimeofday(struct timeval *tv, struct timezone *tz)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">79</td>
    <td width="15%">sys_settimeofday</td>
    <td width="80%">long sys_settimeofday(struct timeval *tv, struct timezone *tz)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">80</td>
    <td width="15%">sys_getgroups16</td>
    <td width="80%">long sys_getgroups16(int gidsetsize, old_gid_t * grouplist)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">81</td>
    <td width="15%">sys_setgroups16</td>
    <td width="80%">long sys_setgroups16(int gidsetsize, old_gid_t * grouplist)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">82</td>
    <td width="15%">old_select</td>
    <td width="80%">int old_select(struct sel_arg_struct *arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">83</td>
    <td width="15%">sys_symlink</td>
    <td width="80%">long sys_symlink(const char *oldname, const char *newname)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">84</td>
    <td width="15%">sys_lstat</td>
    <td width="80%">long sys_lstat(char *filename, struct __old_kernel_stat *statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">85</td>
    <td width="15%">sys_readlink</td>
    <td width="80%">long sys_readlink(const char *path, char *buf, int bufsiz)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">86</td>
    <td width="15%">sys_uselib</td>
    <td width="80%">long sys_uselib(const char *library)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">87</td>
    <td width="15%">sys_swapon</td>
    <td width="80%">long sys_swapon(const char *specialfile, int swap_flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">88</td>
    <td width="15%">sys_reboot</td>
    <td width="80%">long sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">89</td>
    <td width="15%">old_readdir</td>
    <td width="80%">int old_readdir(unsigned int fd, void *dirent, unsigned int count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">90</td>
    <td width="15%">old_mmap</td>
    <td width="80%">int old_mmap(struct mmap_arg_struct *arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">91</td>
    <td width="15%">sys_munmap</td>
    <td width="80%">long sys_munmap(unsigned long addr, size_t len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">92</td>
    <td width="15%">sys_truncate</td>
    <td width="80%">long sys_truncate(const char *path, unsigned long length)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">93</td>
    <td width="15%">sys_ftruncate</td>
    <td width="80%">long sys_ftruncate(unsigned int fd, unsigned long length)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">94</td>
    <td width="15%">sys_fchmod</td>
    <td width="80%">long sys_fchmod(unsigned int fd, mode_t mode)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">95</td>
    <td width="15%">sys_fchown16</td>
    <td width="80%">long sys_fchown16(unsigned int fd, old_uid_t user, old_gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">96</td>
    <td width="15%">sys_getpriority</td>
    <td width="80%">long sys_getpriority(int which, int who)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">97</td>
    <td width="15%">sys_setpriority</td>
    <td width="80%">long sys_setpriority(int which, int who, int niceval)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">98</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">99</td>
    <td width="15%">sys_statfs</td>
    <td width="80%">long sys_statfs(const char *path, struct statfs *buf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">100</td>
    <td width="15%">sys_fstatfs</td>
    <td width="80%">long sys_fstatfs(unsigned int fd, struct statfs *buf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">101</td>
    <td width="15%">sys_ioperm</td>
    <td width="80%">int sys_ioperm(unsigned long from, unsigned long num, int turn_on)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">102</td>
    <td width="15%">sys_socketcall</td>
    <td width="80%">long sys_socketcall(int call, unsigned long *args)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">103</td>
    <td width="15%">sys_syslog</td>
    <td width="80%">long sys_syslog(int type, char *buf, int len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">104</td>
    <td width="15%">sys_setitimer</td>
    <td width="80%">long sys_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">105</td>
    <td width="15%">sys_getitimer</td>
    <td width="80%">long sys_getitimer(int which, struct itimerval *value)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">106</td>
    <td width="15%">sys_newstat</td>
    <td width="80%">long sys_newstat(char *filename, struct stat *statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">107</td>
    <td width="15%">sys_newlstat</td>
    <td width="80%">long sys_newlstat(char *filename, struct stat *statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">108</td>
    <td width="15%">sys_newfstat</td>
    <td width="80%">long sys_newfstat(unsigned int fd, struct stat *statbuf)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">109</td>
    <td width="15%">sys_uname</td>
    <td width="80%">int sys_uname(struct old_utsname *name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">110</td>
    <td width="15%">sys_iopl</td>
    <td width="80%">int sys_iopl(unsigned long unused)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">111</td>
    <td width="15%">sys_vhangup</td>
    <td width="80%">long sys_vhangup(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">112</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">113</td>
    <td width="15%">sys_vm86old</td>
    <td width="80%">int sys_vm86old(struct vm86_struct *v86)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">114</td>
    <td width="15%">sys_wait4</td>
    <td width="80%">long sys_wait4(pid_t pid, unsigned int *stat_addr, int options, struct rusage *ru)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">115</td>
    <td width="15%">sys_swapoff</td>
    <td width="80%">long sys_swapoff(const char *specialfile)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">116</td>
    <td width="15%">sys_sysinfo</td>
    <td width="80%">long sys_sysinfo(struct sysinfo *info)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">117</td>
    <td width="15%">sys_ipc</td>
    <td width="80%">int sys_ipc(uint call, int first, int second, int third, void *ptr, long fifth)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">118</td>
    <td width="15%">sys_fsync</td>
    <td width="80%">long sys_fsync(unsigned int fd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">119</td>
    <td width="15%">sys_sigreturn</td>
    <td width="80%">int sys_sigreturn(unsigned long __unused)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">120</td>
    <td width="15%">sys_clone</td>
    <td width="80%">int sys_clone(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">121</td>
    <td width="15%">sys_setdomainname</td>
    <td width="80%">long sys_setdomainname(char *name, int len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">122</td>
    <td width="15%">sys_newuname</td>
    <td width="80%">long sys_newuname(struct new_utsname *name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">123</td>
    <td width="15%">sys_modify_ldt</td>
    <td width="80%">int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">124</td>
    <td width="15%">sys_adjtimex</td>
    <td width="80%">long sys_adjtimex(struct timex *txc_p)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">125</td>
    <td width="15%">sys_mprotect</td>
    <td width="80%">long sys_mprotect(unsigned long start, size_t len, unsigned long prot)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">126</td>
    <td width="15%">sys_sigprocmask</td>
    <td width="80%">long sys_sigprocmask(int how, old_sigset_t * set, old_sigset_t * oset)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">127</td>
    <td width="15%">sys_create_module</td>
    <td width="80%">unsigned long sys_create_module(const char *name_user, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">128</td>
    <td width="15%">sys_init_module</td>
    <td width="80%">long sys_init_module(const char *name_user, struct module *mod_user)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">129</td>
    <td width="15%">sys_delete_module</td>
    <td width="80%">long sys_delete_module(const char *name_user)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">130</td>
    <td width="15%">sys_get_kernel_syms</td>
    <td width="80%">long sys_get_kernel_syms(struct kernel_sym *table)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">131</td>
    <td width="15%">sys_quotactl</td>
    <td width="80%">long sys_quotactl(unsigned int cmd, const char *special, qid_t id, caddr_t addr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">132</td>
    <td width="15%">sys_getpgid</td>
    <td width="80%">long sys_getpgid(pid_t pid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">133</td>
    <td width="15%">sys_fchdir</td>
    <td width="80%">long sys_fchdir(unsigned int fd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">134</td>
    <td width="15%">sys_bdflush</td>
    <td width="80%">long sys_bdflush(int func, long data)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">135</td>
    <td width="15%">sys_sysfs</td>
    <td width="80%">long sys_sysfs(int option, unsigned long arg1, unsigned long arg2)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">136</td>
    <td width="15%">sys_personality</td>
    <td width="80%">long sys_personality(u_long personality)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">137</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">138</td>
    <td width="15%">sys_setfsuid16</td>
    <td width="80%">long sys_setfsuid16(old_uid_t uid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">139</td>
    <td width="15%">sys_setfsgid16</td>
    <td width="80%">long sys_setfsgid16(old_gid_t gid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">140</td>
    <td width="15%">sys_llseek</td>
    <td width="80%">long sys_llseek(unsigned int fd, unsigned long offset_high, unsigned long offset_low, loff_t * result, unsigned int origin)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">141</td>
    <td width="15%">sys_getdents</td>
    <td width="80%">long sys_getdents(unsigned int fd, void *dirent, unsigned int count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">142</td>
    <td width="15%">sys_select</td>
    <td width="80%">long sys_select(int n, fd_set * inp, fd_set * outp, fd_set * exp, struct timeval *tvp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">143</td>
    <td width="15%">sys_flock</td>
    <td width="80%">long sys_flock(unsigned int fd, unsigned int cmd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">144</td>
    <td width="15%">sys_msync</td>
    <td width="80%">long sys_msync(unsigned long start, size_t len, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">145</td>
    <td width="15%">sys_readv</td>
    <td width="80%">ssize_t sys_readv(unsigned long fd, const struct iovec * vector, unsigned long count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">146</td>
    <td width="15%">sys_writev</td>
    <td width="80%">ssize_t sys_writev(unsigned long fd, const struct iovec * vector, unsigned long count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">147</td>
    <td width="15%">sys_getsid</td>
    <td width="80%">long sys_getsid(pid_t pid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">148</td>
    <td width="15%">sys_fdatasync</td>
    <td width="80%">long sys_fdatasync(unsigned int fd)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">149</td>
    <td width="15%">sys_sysctl</td>
    <td width="80%">long sys_sysctl(struct __sysctl_args *args)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">150</td>
    <td width="15%">sys_mlock</td>
    <td width="80%">long sys_mlock(unsigned long start, size_t len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">151</td>
    <td width="15%">sys_munlock</td>
    <td width="80%">long sys_munlock(unsigned long start, size_t len)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">152</td>
    <td width="15%">sys_mlockall</td>
    <td width="80%">long sys_mlockall(int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">153</td>
    <td width="15%">sys_munlockall</td>
    <td width="80%">long sys_munlockall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">154</td>
    <td width="15%">sys_sched_setparam</td>
    <td width="80%">long sys_sched_setparam(pid_t pid, struct sched_param *param)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">155</td>
    <td width="15%">sys_sched_getparam</td>
    <td width="80%">long sys_sched_getparam(pid_t pid, struct sched_param *param)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">156</td>
    <td width="15%">sys_sched_setscheduler</td>
    <td width="80%">long sys_sched_setscheduler(pid_t pid, int policy, struct sched_param *param)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">157</td>
    <td width="15%">sys_sched_getscheduler</td>
    <td width="80%">long sys_sched_getscheduler(pid_t pid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">158</td>
    <td width="15%">sys_sched_yield</td>
    <td width="80%">long sys_sched_yield(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">159</td>
    <td width="15%">sys_sched_get_priority_max</td>
    <td width="80%">long sys_sched_get_priority_max(int policy)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">160</td>
    <td width="15%">sys_sched_get_priority_min</td>
    <td width="80%">long sys_sched_get_priority_min(int policy)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">161</td>
    <td width="15%">sys_sched_rr_get_interval</td>
    <td width="80%">long sys_sched_rr_get_interval(pid_t pid, struct timespec *interval)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">162</td>
    <td width="15%">sys_nanosleep</td>
    <td width="80%">long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">163</td>
    <td width="15%">sys_mremap</td>
    <td width="80%">unsigned long sys_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">164</td>
    <td width="15%">sys_setresuid16</td>
    <td width="80%">long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">165</td>
    <td width="15%">sys_getresuid16</td>
    <td width="80%">long sys_getresuid16(old_uid_t * ruid, old_uid_t * euid, old_uid_t * suid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">166</td>
    <td width="15%">sys_vm86</td>
    <td width="80%">int sys_vm86(unsigned long subfunction, struct vm86plus_struct *v86)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">167</td>
    <td width="15%">sys_query_module</td>
    <td width="80%">long sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, size_t * ret)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">168</td>
    <td width="15%">sys_poll</td>
    <td width="80%">long sys_poll(struct pollfd *ufds, unsigned int nfds, long timeout)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">169</td>
    <td width="15%">sys_nfsservctl</td>
    <td width="80%">&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">170</td>
    <td width="15%">sys_setresgid16</td>
    <td width="80%">long sys_setresgid16(old_gid_t rgid, old_gid_t egid, old_gid_t sgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">171</td>
    <td width="15%">sys_getresgid16</td>
    <td width="80%">long sys_getresgid16(old_gid_t * rgid, old_gid_t * egid, old_gid_t * sgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">172</td>
    <td width="15%">sys_prctl</td>
    <td width="80%">long sys_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">173</td>
    <td width="15%">sys_rt_sigreturn</td>
    <td width="80%">int sys_rt_sigreturn(unsigned long __unused)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">174</td>
    <td width="15%">sys_rt_sigaction</td>
    <td width="80%">long sys_rt_sigaction(int sig, const struct sigaction *act, struct sigaction *oact, size_t sigsetsize)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">175</td>
    <td width="15%">sys_rt_sigprocmask</td>
    <td width="80%">long sys_rt_sigprocmask(int how, sigset_t * set, sigset_t * oset, size_t sigsetsize)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">176</td>
    <td width="15%">sys_rt_sigpending</td>
    <td width="80%">long sys_rt_sigpending(sigset_t * set, size_t sigsetsize)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">177</td>
    <td width="15%">sys_rt_sigtimedwait</td>
    <td width="80%">long sys_rt_sigtimedwait(const sigset_t * uthese, siginfo_t * uinfo, const struct timespec *uts, size_t sigsetsize)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">178</td>
    <td width="15%">sys_rt_sigqueueinfo</td>
    <td width="80%">long sys_rt_sigqueueinfo(int pid, int sig, siginfo_t * uinfo)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">179</td>
    <td width="15%">sys_rt_sigsuspend</td>
    <td width="80%">int sys_rt_sigsuspend(sigset_t * unewset, size_t sigsetsize)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">180</td>
    <td width="15%">sys_pread</td>
    <td width="80%">ssize_t sys_pread(unsigned int fd, char *buf, size_t count, loff_t pos)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">181</td>
    <td width="15%">sys_pwrite</td>
    <td width="80%">ssize_t sys_pwrite(unsigned int fd, const char *buf, size_t count, loff_t pos)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">182</td>
    <td width="15%">sys_chown16</td>
    <td width="80%">long sys_chown16(const char *filename, old_uid_t user, old_gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">183</td>
    <td width="15%">sys_getcwd</td>
    <td width="80%">long sys_getcwd(char *buf, unsigned long size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">184</td>
    <td width="15%">sys_capget</td>
    <td width="80%">long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">185</td>
    <td width="15%">sys_capset</td>
    <td width="80%">long sys_capset(cap_user_header_t header, const cap_user_data_t data)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">186</td>
    <td width="15%">sys_sigaltstack</td>
    <td width="80%">int sys_sigaltstack(const stack_t * uss, stack_t * uoss)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">187</td>
    <td width="15%">sys_sendfile</td>
    <td width="80%">ssize_t sys_sendfile(int out_fd, int in_fd, off_t * offset, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">188</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">189</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">190</td>
    <td width="15%">sys_vfork</td>
    <td width="80%">int sys_vfork(struct pt_regs regs)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">191</td>
    <td width="15%">sys_getrlimit</td>
    <td width="80%">long sys_getrlimit(unsigned int resource, struct rlimit *rlim)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">192</td>
    <td width="15%">sys_mmap2</td>
    <td width="80%">long sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">193</td>
    <td width="15%">sys_truncate64</td>
    <td width="80%">long sys_truncate64(const char *path, loff_t length)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">194</td>
    <td width="15%">sys_ftruncate64</td>
    <td width="80%">long sys_ftruncate64(unsigned int fd, loff_t length)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">195</td>
    <td width="15%">sys_stat64</td>
    <td width="80%">long sys_stat64(char *filename, struct stat64 *statbuf, long flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">196</td>
    <td width="15%">sys_lstat64</td>
    <td width="80%">long sys_lstat64(char *filename, struct stat64 *statbuf, long flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">197</td>
    <td width="15%">sys_fstat64</td>
    <td width="80%">long sys_fstat64(unsigned long fd, struct stat64 *statbuf, long flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">198</td>
    <td width="15%">sys_lchown</td>
    <td width="80%">long sys_lchown(const char *filename, uid_t user, gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">199</td>
    <td width="15%">sys_getuid</td>
    <td width="80%">long sys_getuid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">200</td>
    <td width="15%">sys_getgid</td>
    <td width="80%">long sys_getgid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">201</td>
    <td width="15%">sys_geteuid</td>
    <td width="80%">long sys_geteuid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">202</td>
    <td width="15%">sys_getegid</td>
    <td width="80%">long sys_getegid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">203</td>
    <td width="15%">sys_setreuid</td>
    <td width="80%">long sys_setreuid(uid_t ruid, uid_t euid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">204</td>
    <td width="15%">sys_setregid</td>
    <td width="80%">long sys_setregid(gid_t rgid, gid_t egid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">205</td>
    <td width="15%">sys_getgroups</td>
    <td width="80%">long sys_getgroups(int gidsetsize, gid_t * grouplist)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">206</td>
    <td width="15%">sys_setgroups</td>
    <td width="80%">long sys_setgroups(int gidsetsize, gid_t * grouplist)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">207</td>
    <td width="15%">sys_fchown</td>
    <td width="80%">long sys_fchown(unsigned int fd, uid_t user, gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">208</td>
    <td width="15%">sys_setresuid</td>
    <td width="80%">long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">209</td>
    <td width="15%">sys_getresuid</td>
    <td width="80%">long sys_getresuid(uid_t * ruid, uid_t * euid, uid_t * suid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">210</td>
    <td width="15%">sys_setresgid</td>
    <td width="80%">long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">211</td>
    <td width="15%">sys_getresgid</td>
    <td width="80%">long sys_getresgid(gid_t * rgid, gid_t * egid, gid_t * sgid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">212</td>
    <td width="15%">sys_chown</td>
    <td width="80%">long sys_chown(const char *filename, uid_t user, gid_t group)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">213</td>
    <td width="15%">sys_setuid</td>
    <td width="80%">long sys_setuid(uid_t uid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">214</td>
    <td width="15%">sys_setgid</td>
    <td width="80%">long sys_setgid(gid_t gid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">215</td>
    <td width="15%">sys_setfsuid</td>
    <td width="80%">long sys_setfsuid(uid_t uid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">216</td>
    <td width="15%">sys_setfsgid</td>
    <td width="80%">long sys_setfsgid(gid_t gid)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">217</td>
    <td width="15%">sys_pivot_root</td>
    <td width="80%">long sys_pivot_root(const char *new_root, const char *put_old)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">218</td>
    <td width="15%">sys_mincore</td>
    <td width="80%">long sys_mincore(unsigned long start, size_t len, unsigned char *vec)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">219</td>
    <td width="15%">sys_madvise</td>
    <td width="80%">long sys_madvise(unsigned long start, size_t len, int behavior)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">220</td>
    <td width="15%">sys_getdents64</td>
    <td width="80%">long sys_getdents64(unsigned int fd, void *dirent, unsigned int count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">221</td>
    <td width="15%">sys_fcntl64</td>
    <td width="80%">long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">222</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">223</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">224</td>
    <td width="15%">sys_gettid</td>
    <td width="80%">long sys_gettid(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">225</td>
    <td width="15%">sys_readahead</td>
    <td width="80%">ssize_t sys_readahead(int fd, loff_t offset, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">226</td>
    <td width="15%">sys_setxattr</td>
    <td width="80%">long sys_setxattr(char *path, char *name, void *value, size_t size, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">227</td>
    <td width="15%">sys_lsetxattr</td>
    <td width="80%">long sys_lsetxattr(char *path, char *name, void *value, size_t size, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">228</td>
    <td width="15%">sys_fsetxattr</td>
    <td width="80%">long sys_fsetxattr(int fd, char *name, void *value, size_t size, int flags)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">229</td>
    <td width="15%">sys_getxattr</td>
    <td width="80%">ssize_t sys_getxattr(char *path, char *name, void *value, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">230</td>
    <td width="15%">sys_lgetxattr</td>
    <td width="80%">ssize_t sys_lgetxattr(char *path, char *name, void *value, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">231</td>
    <td width="15%">sys_fgetxattr</td>
    <td width="80%">ssize_t sys_fgetxattr(int fd, char *name, void *value, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">232</td>
    <td width="15%">sys_listxattr</td>
    <td width="80%">ssize_t sys_listxattr(char *path, char *list, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">233</td>
    <td width="15%">sys_llistxattr</td>
    <td width="80%">ssize_t sys_llistxattr(char *path, char *list, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">234</td>
    <td width="15%">sys_flistxattr</td>
    <td width="80%">ssize_t sys_flistxattr(int fd, char *list, size_t size)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">235</td>
    <td width="15%">sys_removexattr</td>
    <td width="80%">long sys_removexattr(char *path, char *name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">236</td>
    <td width="15%">sys_lremovexattr</td>
    <td width="80%">long sys_lremovexattr(char *path, char *name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">237</td>
    <td width="15%">sys_fremovexattr</td>
    <td width="80%">long sys_fremovexattr(int fd, char *name)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">238</td>
    <td width="15%">sys_tkill</td>
    <td width="80%">long sys_tkill(int pid, int sig)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">239</td>
    <td width="15%">sys_sendfile64</td>
    <td width="80%">ssize_t sys_sendfile64(int out_fd, int in_fd, loff_t * offset, size_t count)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">240</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">241</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">242</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">243</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">244</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">245</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">246</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">247</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">248</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">249</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">250</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">251</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">252</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">253</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">254</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">255</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">256</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">257</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">258</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
  <tr>
    <td width="5%">259</td>
    <td width="15%">sys_ni_syscall</td>
    <td width="80%">long sys_ni_syscall(void)&nbsp;</td>
  </tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2006/10/linux_2429_system_calls_table_1.html</link>
         <guid>https://www.jollen.org/blog/2006/10/linux_2429_system_calls_table_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 07 Oct 2006 13:11:24 +0800</pubDate>
      </item>
            <item>
         <title>Name Service Switch 程式設計：原理探討</title>
         <description><![CDATA[我們以一個 'gethostbyname' 的例子來說明 NSS 的運作原理

假設我們想要查詢網路上某台 host 的 IP 位址，那麼 GNU C Library 手冊告訴我們說，有一個 gethostbyname() 的函數可以使用。

不過，在真正寫 code 時，其實並不建議呼叫此函數，在說明原因前，有一句必名言是大家一定要銘記在心的：

Multi-threaded safe code must be reentrant. 

由於 gethostbyname() 並非 reentrant 的版本；所以，呼叫 gethostbyname() 並不是正確寫法，我們必須改成呼叫 reentrant 的版本：gethostbyname_r()。

NSS module 裡 non-reentrant 的函數，是不能使用在 multi-threaded applications 的設計上的，但是根據 NSS manual 的解釋，其實 gethostbyename() 與 gethostbyname_r() 都會呼叫到 NSS module 裡相同的函數，原因是 NSS module 只提供 reentrant 版本的 gethostbyname() 服務。

接下來讓我們來討論，gethostbyname() 與 gethostbyname_r() 倒底會呼叫到 NSS module 裡的哪一個函數。

<strong>_nss_service_function</strong>

當我們呼叫 gethostbyname_r() 函數時 (或 gethostbyname)，假設我們在 /etc/nsswitch.conf 裡有一行這樣的設定：

hosts:      files

那麼根據之前 Jollen 在 Blog 裡的<a href="http://www.jollen.org/blog/2006/10/name_service_switch_1.html#more">說明</a>，此時會使用到的 NSS module 會是 libnss_files.so.2，並且以下的 routine 會被呼叫：

_nss_files_gethostbyname_r

查看一下：

# nm /lib/libnss_files.so.2 |egrep "gethostbyname"
00004320 T _nss_files_gethostbyname2_r
00003ba0 T _nss_files_gethostbyname_r

現在我們終於知道了，當我們呼叫查詢的函數 'function' 時，在 libnss_SERVICE.so.2 裡的相對應函數便會被呼叫，此函數的 naming 方式為：

_nss_service_function

另外，NSS module 僅包含 reentrant 的版本。

最後我們要講的是，gethostbyename() 與 gethostbyname_r()，或是之前提到的 getpwnam() 都是實作在 libc.so.6 裡的。For example：

# nm /lib/libc.so.6 |egrep "T.gethostbyname"
000f72b0 T gethostbyname
000f7480 T gethostbyname2
000f78c0 T gethostbyname2_r@GLIBC_2.0
000f7660 T gethostbyname2_r@@GLIBC_2.1.2
000f7b80 T gethostbyname_r@GLIBC_2.0
000f7930 T gethostbyname_r@@GLIBC_2.1.2

# nm /lib/libc.so.6 |egrep "T.getpwnam"
000acf50 T getpwnam
000ad5c0 T getpwnam_r@GLIBC_2.0
000ad440 T getpwnam_r@@GLIBC_2.1.2

這就是為什麼我們在 ldd (or objdump) 時，都看不到 libnss_SERVICE.so.6 的道理．當我們做查詢時，NSS 才去載入對應的 service module。]]></description>
         <link>https://www.jollen.org/blog/2006/10/name_service_switch.html</link>
         <guid>https://www.jollen.org/blog/2006/10/name_service_switch.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 04 Oct 2006 14:30:21 +0800</pubDate>
      </item>
            <item>
         <title>Name Service Switch 程式設計：基本觀念</title>
         <description><![CDATA[有些 C 函數的運作會依據 localhost 的設定而有不同行為，而這些設定的做法是透過「設定檔」(eg. /etc/services) 來實現。目前在 GNU/Linux 系統底下，可以看到許多這樣的設定檔，我們把這些設定檔稱為 system database，這些資料庫主要是以文字檔方式儲存重要的系統資訊，例如: /etc/passwd 存放使用者名稱、/etc/services 存放網路服務的通訊埠 (port) 設定等等。

GNU C library 實作的 "Name Service Switch" 函數群便是用來讀取所有 system database 內容的介面！

<strong>System Database</strong>

NSS 支援 11 種資料庫名稱：aliases、ethers、group、hosts、netgroup、networks、protocols、passwd、rpc、services 與 shadow。分別說明如下。

aliases: Mail 別名

ethers: Ethernet 號碼

group: 使用者群組資料庫，即 /etc/group 

hosts: Host names 與 IP 對應資料庫，即 /etc/hosts 或是利用 name server 轉換

netgroup: "hostname username domainname" 的組合稱為 netgroup

networks: 即 /etc/networks 

protocols: 通訊協定資料庫，即 /etc/protocols

passwd: 使用者名稱資料庫，即 /etc/passwd

rpc: Remote procedure call 資料庫

services: 即 /etc/services 資料庫

shadow: 即 /etc/shadow 檔

<strong>NSS Service 與 Lookup</strong>

NSS 支援 7 種服務類型，說明如下。

1. nisplus (or nis+): 使用 NIS+ (NIS version 3) 服務
2, nis (or yp): 使用 NIS (NIS version 2) 服務 (也叫 YP, YellowPage)
3. dns: 使用 DNS (Domain Name Service) 服務
4. files: 使用一般的檔案讀取服務
5. db: 使用 database (.db) 檔案讀取服務
6. compat: 使用 NIS compat mode 服務
7. hesiod: 使用 Hesiod 服務做 user lookups

GNU 的實作是把每一個服務 (service) 實作成不同的 module (shared library)，因此可以在 GNU/Linux 系統底下找到這些相對應的 shared libraries：

libnss_nisplus.so.2
libnss_nis.so.2
libnss_dns.so.2
libnss_files.so.2
libnss_compat.so.2
libnss_hesiod.so.2

不過其實還有下面 3 個：

/lib/libnss_ldap.so.2
/lib/libnss_winbind.so.2
/lib/libnss_wins.so.2

GNU 的 NSS module 是以 libnss_SERVICE.so.2 來命名。

在這些 module (library) 裡的函數，是無法讓程式直接使用的，因此了解這些 module 裡的函數如何被使用 (呼叫) 是一個重要且有趣的題目。

查詢前面所提 11 種資料庫資訊的動作稱為 lookup，lookup 的方法便是 NSS 的服務類型 (NSS service)，也就是我們可以利用以上共 7 種方式來查詢 system database；不過，以 files、db、nis 與 nisplus 這 4 種方式是比較常用的。

由此可知，不同的 system database 可以用不同的服務來做查詢。因此，系統管理員必須告訴 NSS，我們想要他利用哪一種服務類型，來查詢 (lookup) 指定的 system database。這個動作的管理方式便是透過 /etc/nsswitch.conf 設定檔來完成。

<strong>/etc/nsswitch.conf</strong>

看一下 glibc-2.3.3 預設定 nsswitch.conf 設定檔內容如下：

<pre>
# /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
#
passwd:         db files
group:          db files
shadow:         db files
hosts:          files dns
networks:       files dns
protocols:      db files
services:       db files
ethers:         db files
rpc:            db files
netgroup:       db files
</pre>

這樣其實就很清楚了，例如：passwd 資料庫 (/etc/passwd) 的讀取方式 (lookup service) 有 db/files 二種。以  embedded Linux 系統來說，我們會這樣設定：

passwd:         files

也就是說，直接以檔案讀取方式來查詢 /etc/passwd。那麼，之前提過的 embedded Linux 系統建置之 thttpd 個案分析，為何需要 libnss_files.so.2 檔案，觀念就很清楚了。

此外，大家也可以了解一下 red hat linux 或其它 Linux distribution 的 nsswitch.conf 設定佈署方式。

這個部份的文件主要是 glibc 的手冊，大家可參考 <a href="http://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html#Name-Service-Switch">http://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html#Name-Service-Switch</a>

-- jollen
作者／陳俊宏 <jollen dot jollen dot org>
http://www.jollen.org]]></description>
         <link>https://www.jollen.org/blog/2006/10/name_service_switch_1.html</link>
         <guid>https://www.jollen.org/blog/2006/10/name_service_switch_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Tue, 03 Oct 2006 22:21:14 +0800</pubDate>
      </item>
            <item>
         <title>Library Dependency 的議題要點</title>
         <description><![CDATA[在建立 embedded Linux 系統 (root filesystem) 時，程式庫相依 (library dependencies) 的議題是相當重要的一個題目。

當 root filesystem 缺少必要的 library 時，程式當然是無法執行的，甚致系統也會無法順利啟動。依據 Jollen 在「Embedded Linux / ARM9 開發實戰」的課程所提的幾個觀念，我們可以了解到在建構 embedded Linux 系統時，應具備的正確觀念與基本能力。

我們把「如何找出所需的 library」方法整理出 3 項的基本要點，依照這 3 種基本款來加入 library 將能解決幾乎所有的 library dependency 問題，這 3 種項基本要點為：

(1) 先利用 cross toolchain 的 objdump 觀察「NEEDED」的項目，加入 library。
(2) 再檢查這些 library 是否相依其它 library。
(3) 最後要檢視應用程式是否使用到需要特定 library 的「service」。

要點 1. 跟 2. 對大家來說沒有什麼問題，要點 3. 在我們的 training 課程裡，我們以建構 thttpd (embedded Web server) 的實際案例來做講解。

<strong>關於建構 thttpd 的案例</strong>

thttpd 使用到 NSS (Name Service Switch)，因此若沒有將 libnss_SERVICE.so 加到 root filesystem，thttpd 在執行時可能會遇到一些奇怪的問題。舉個例子，當 thttpd 透過 /etc/passwd 去尋找 (查詢) UNIX user 時，會用到 libnss_files.so (不讀 /etc/shadow)，因此會看到以下的錯誤訊息：

unknown user - root

出現這個錯誤的原因是 thttpd 讀不到 'root' 使用者，要深入探討這個問題的原理，必須從以下的程式碼片斷開始探討：

<pre>
    403     /* If we're root and we're going to become another user, get the uid        /gid
    404     ** now.
    405     */
    406     if ( getuid() == 0 )
    407         {
    408        <strong> pwd = getpwnam( user );</strong>
    409         if ( pwd == (struct passwd*) 0 )
    410             {
    411             syslog( LOG_CRIT, "unknown user - '%.80s'", user );
    412             (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0,         user );
    413             exit( 1 );
    414             }
    415         uid = pwd->pw_uid;
    416         gid = pwd->pw_gid;
    417         }
</pre>

這段程式碼是 thttpd 2.25b 的程式片斷，位於 thttpd.c 的 main() 函數裡。關於 libnss_SERVICE.so 的議題，Jollen 打算另外再做討論，因為還會與 libc 有關係。

在這裡我們由系統建構的角度來看這個問題。因為我們已經習慣用 objdump 來觀察程式的相依 library，所以當 objdump 的畫面跟我們預期的不同時，經常一時無法反應過來。例如，以下的訊息是我們所「預期」的：

<pre>
# /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-objdump -x thttpd|more
...
Dynamic Section:
<strong>  NEEDED      libcrypt.so.1
  NEEDED      libnss_files.so.2</strong>
  NEEDED      libc.so.6
...
</pre>

但是實際的訊息卻是像這樣的：

<pre>
# /opt/crosstool/gcc-3.4.1-glibc-2.3.3/arm-9tdmi-linux-gnu/bin/arm-9tdmi-linux-gnu-objdump -x thttpd|more
...
Dynamic Section:
<strong>  NEEDED      libcrypt.so.1
  NEEDED      libc.so.6</strong>
...
</pre>

我們可以用一知半解的思考邏輯來解決問題：thttpd 呼叫到 getpwnam() 函數，此函數由 libnss_compat 提供，因此解決方案是把 libnss_files.so 加到 root filesystem 裡即可。

且慢！前面才講到 libnss_compat，怎麼後面是把 libnss_files 加到 root filesystem？是這樣的，libnss_compat 用來讀 /etc/shadow，但是現在我們只需要由 /etc/passwd 讀 Unix user，所以使用 libnss_files.so 就行了。

執行 thttpd 的話，再加上指定 username 的參數來執行：

# thttpd -p 80 -d /var/www <strong>-u root</strong>

libnss_SERVICE.so 是包含在 glibc 裡的程式庫，因此可以直接由 cross toolchain 裡取得，不必再另行建置。

有關 NSS (Name Service Switch) 可參考以下網頁:

Linux / Unix Command: nsswitch.conf - <a href="http://linux.about.com/od/commands/l/blcmdl5_nsswitc.htm">http://linux.about.com/od/commands/l/blcmdl5_nsswitc.htm</a>

其它網路資源:

<a href="http://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html">http://www.gnu.org/software/libc/manual/html_node/Name-Service-Switch.html</a>
<a href="http://mirrors.usc.edu/pub/gnu/Manuals/glibc-2.2.3/html_chapter/libc_28.html">http://mirrors.usc.edu/pub/gnu/Manuals/glibc-2.2.3/html_chapter/libc_28.html</a>

此處我們以「service」的角度來探討這個問題：因為 thttpd 使用到 Name Service Switch，所以需要加入 libnss_SERVICE.so。另外一種探討的角度是：由 programming 的角度來思考，大家可以試著去研究這個問題，還挺好玩的！

附帶一提，如果要讀 shadow passwd 的話，是使用 libnss_compat.so。]]></description>
         <link>https://www.jollen.org/blog/2006/10/library_dependency_1.html</link>
         <guid>https://www.jollen.org/blog/2006/10/library_dependency_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 03 Oct 2006 17:57:18 +0800</pubDate>
      </item>
            <item>
         <title>嵌入式 Linux 應用: Mesh Networking 演講稿 (精簡版部份內容)</title>
         <description><![CDATA[#2: 今天 Jollen 要介紹的題目是 "Mesh Router"，實作平臺是 Kendin KS8695P 與 embedded Linux 系統。我們在 Kendin KS8695P (ARM9) 的平臺上嵌入 embedded Linux 系統，透過此平臺與 mesh wireless 的 embedded Linux 系統，將能實作出 mesh networking 的系統。

今天我們的重點 (Lecture 8) 是介紹 open source 解決方案與 mesh networking；同時我們也會做一個簡單的 demo。

<a href="http://www.jollen.org/blog/2006/10/03/jollen.org_Lecture_8_%20Mesh%20Router%20Project.pdf">簡報下載</a>

#3: mesh networking 可以取代 Wi-Fi "hot spots" 的架構，並且簡省無線網路的佈署成本，由於我們以現有的 802.11a+g 的無線網卡來實作，因此可以使用許多現成的 open source 軟體來配合。以藉此達成單點對多點 (point to multipoint)、多點對多點 (multipoint to multipoint) 或是單點對單點 (point-to-point) 的架構實作。

#6: 這是我們所期望做到的規格 (specification)，很有趣的是，開放源碼套件居然能滿足我們所開出的所有規格，這意謂我們的 project 需要自己開發軟體的機會幾乎是 0%；事實上，我們 90% 以上的工作都是在做系統整合 (Linux administration) 和 UI (web-based) 的開發。

#7: 這是一個實際 (real-world) 的案例，讓我們來說明我們倒底在 root filesystem 裡放了哪些東西，以及「如何加入」這些東西。

#8: 要 build 一個基本的 wireless 嵌入式 Linux 系統 (root filesystem)，我們會需要這些基本的開放源碼套件，另外由於 zeroconf 遭遇到專利問題，因此我們並沒有在這份簡單裡展示 zeroconf IP 的功能。

#9: 再來是 Linux 的無線網路解決方案，在驅動程式方面，我們使用的 Atheros 與 Prism 晶片都有支援。在 user application 方面，wireless routing protocol 的解決方案是 quagga，這是一個相當知名的 open source 專案。另外，wireless tools 是必備的，因此我們要透過它來設定無線網路。

#16: 這是我們利用 iwconfig 在 Kendin 平臺上啟動無線網路的畫面，ath0 介面是 Atheros 網卡 (802.11g)，eth2 是 Intersil 網路 (802.11b/g)。

#17: 我們把 ath0 設定成 "ad-hoc" 模式。

#18: 利用 'iwlist' 指令偵測 channel。

#19: 利用 'iwlist' 掃描 eth2 介面。

ps. 不知道大家還想要哪一頁的詳細講稿 @@"

]]></description>
         <link>https://www.jollen.org/blog/2006/10/post_1.html</link>
         <guid>https://www.jollen.org/blog/2006/10/post_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 02 Oct 2006 23:58:18 +0800</pubDate>
      </item>
            <item>
         <title>嵌入式OS的重要共通議題</title>
         <description><![CDATA[學習WinCE與Embedded Linux的重要課程是「系統程式」（system software）的觀念，不管是WinCE還是Embedded Linux，有幾個核心的系統程式或作業系統基礎，絕對是要用力研究的。

第一個是process的觀念，process是執行中的程式，如何控制process（生成與刪除）、非同步的signal處理等；第二個是process synchronization的觀念，process是concurrent（同步）在執行，所以會產生race condition（競賽問題）問題，解決race condition現象的方法、semaphore、monitor、mutual exclusive、locking等，是這個觀念主題的重點。

第二個是thread的程式設計方法與thread同步的方法，配合multi-threaded所實作的event-driven軟體架構，還有更進階的event manager的做法，是這個部份的重點。
因此，有志進入嵌入式系統領域的讀者，可以直接由WinCE或Embedded Linux技術切入，首先當然要以軟體層的學習為主，並且加強作業系統與系統程式這二門學科的能力，接著把最好的一道菜—驅動程式—徹底學通後，絕對能在嵌入式系統領域有所發揮。

-- 作者／陳俊宏 (jollen)
部份原文刊載於 Run! PC 雜誌 8 月號
歡迎任意轉貼引用．但請務必<a href="http://www.jollen.org/license.html">註明出處</a>

]]></description>
         <link>https://www.jollen.org/blog/2006/09/os.html</link>
         <guid>https://www.jollen.org/blog/2006/09/os.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Sat, 30 Sep 2006 13:39:23 +0800</pubDate>
      </item>
            <item>
         <title>Linux smartphone 之路</title>
         <description><![CDATA[相較於Microsoft的Windows Mobile的成功，過去的Linux smart phone 之路顯然有點坎坷。雖然大敵在前，但是Linux開發社群對Linux手機的熱情絲毫未減。終於，去年（２００５）年，由11家Linux與處理器大家共同成立的Linux手機標準論壇（Linux Phone Standards Forum – LiPS）提出具體的Linux smart phone架構圖。

LiPS發起廠商共有11家大廠，分別是：ARM Ltd., Cellon, Esmertec, France Telecom/Orange, FSMLabs, Huawei, Jaluna, MIZI Research, MontaVista, Open Plug, and PalmSource。最近又加入了Texas Instruments、ZTE、Telecom三家大廠。

<img alt="figure_02.gif" src="http://www.jollen.org/blog/2006/09/30/figure_02.gif" width="450" height="293" />
圖: LiPS　的工作範圍（kernel 部份是OSDL的任務）
(Source: LinuxDevices.com / LiPS)

LiPS的主要任務為「標準化（standardizing）Linux手機」，並提出可行的Linux手機OS架構。更明確來說，LiPS在標準化「Linux手機的middleware層」，middleware提供電話功能的API、GUI、裝置管理功能、相關的安全性管理、電話簿功能。今年下半年將可以看到LiPS釋出的第一版Linux手機規格！

除了LiPS外，OSDL（Open Source Development Labs）也未曾缺席Linux的手機發展，OSDL裡的MLI（Mobile Linux Initiative）也於２００５年成立，MLI的任務為「maximize the market opportunity for Linux-based devices」。目前的成員有：MontaVista Software, Motorola, PalmSource, Trolltech, Wind River。

LiPS與OSDL的合作相當密切，LiPS著重moddleware的規格定義，OSDL的MLI則是著墨於kernel層。MLI的任務是發展出適合Linux mobile phone使用的kernel，LiPS的任務是發展出明確的手機軟體架構（software stack）。

<strong>Mobilinux 4.0</strong>

Mobilinux是目前可以取得，最具體的Linux手機產品。

Mobilinux 4.0是MontaVista的新一代主力產品，Mobilinux 4.0其實是由MontaVista原先的Linux Consumer Electronics Edition (CEE) 3.1 與 Linux Consumer Professional Edition (Pro) 3.1 產品所延伸而來的新一代Linux mobile phone OS。

Linux Consumer Electronics Edition (CEE) 3.1 與 Linux Consumer Professional Edition (Pro) 3.1 已是 design win 的產品，市面上可看到十多款的手機產品。

Mobilinux 4.0使用2.6系列的kernel，real-time的支援與效能表現比以往更優秀，Mobilinux 4.0也承習Linux CEE的電源管理（power management）強化功能。Mobilinux 4.0的real-time patch是Open Source Real-time Linux Project的成果。

* Mobilinux - http://mobilinux.com/
* Linux 手機展示場 - http://www.linuxdevices.com/articles/AT9423084269.html 

kernel 2.6的一個重要特色是：支援SD/SDIO/MMC，Mobilinux 4.0也強化對SD/SDIO/MMC的支援，對於Linux手機的應用是相當重要的里程碑。

<img alt="figure_03a.jpg" src="http://www.jollen.org/blog/2006/09/30/figure_03a.jpg" width="150" height="150" /><br />
<img alt="figure_03b.jpg" src="http://www.jollen.org/blog/2006/09/30/figure_03b.jpg" width="150" height="150" /><br />
<img alt="figure_03c.jpg" src="http://www.jollen.org/blog/2006/09/30/figure_03c.jpg" width="150" height="150" /><br />
<img alt="figure_03d.jpg" src="http://www.jollen.org/blog/2006/09/30/figure_03d.jpg" width="150" height="150" /><br />
<img alt="figure_03e.jpg" src="http://www.jollen.org/blog/2006/09/30/figure_03e.jpg" width="150" height="150" /><br />

圖: 基於 Mobilinux 的 Linux 智慧型手機（由上至下依序是：Motorola A728、Motorola E680、NEC N700i、NEC N902i、Panasonic P902i）

目前Mobilinux的design win手機產品有15支，所有的Mobilinux Linux手機可以到Mobilinux網站查詢。

-- 作者／陳俊宏 (jollen)
原文刊載於 Run! PC 雜誌 9 月號
歡迎任意轉貼引用．但請務必<a href="http://www.jollen.org/license.html">註明出處</a>]]></description>
         <link>https://www.jollen.org/blog/2006/09/linux_smartphone.html</link>
         <guid>https://www.jollen.org/blog/2006/09/linux_smartphone.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Sat, 30 Sep 2006 12:48:38 +0800</pubDate>
      </item>
            <item>
         <title>最精彩的一道菜：驅動程式</title>
         <description><![CDATA[軀動程式本身是屬於「軟體硬介面」的程式設計技術，不管是學習WinCE或是Embedded Linux，最精彩的部份絕對是驅動程式莫屬。由於嵌入式系統整體來看，除了軟體開發外，也包含硬體的客制化，因此驅動程式在嵌入式系統技術領域中，佔了舉足輕重的地位。 

學習驅動程式需要確實瞭解硬體的規格與微處理器架構，並且工程師還要能分得清楚哪些東西是介面（interfacing）也就是與硬體無關的程式（machine-independent）；以及哪些是站在第一線做硬體控制的程式（machine-dependent）。各種軟體硬介面與滙流排也都要精通。

<strong>現在的嵌入式系統學習主軸</strong>

現今嵌入式系統的實作，幾乎都會加入嵌入式作業系統（embedded OS）的元素，有了作業系統，我們都可以為目標裝置「寫軟體」。總合來看，如果要學習所謂的嵌入式系統，從熱門的WinCE或Embedded Linux領域切入是相當不錯的選擇。

驅動程式是「寫軟體」與「做硬體」的 “connectivity”，因此現今資訊業界最熱門的嵌入式系統學習主軸為驅動程式的設計。

<strong>WinCE驅動程式</strong>

WinCE驅動程式的核心人物當然就是在WDM（Windows Driver Model）身上了。WDM是Windows	98/2000之後的驅動程式架構，WDM是一個嚴密的分層（layered）架構，架構層間以IRPs（I/O Request Packets）做通訊。

WDM驅動程式分為三種類型：bus driver、function driver與filter driver。Bus driver是device-independent的驅動程式，主要在驅動I/O bus，例如：PCI bus driver、USB bus driver；function driver是 “device” 的驅動程式，我們常講的「驅動程式設計」大部份都是講 function driver，function driver 主要在驅動各種裝置，因此大多是由裝置廠造商撰寫並提供給使用者安裝，function driver 的設計大多著墨在「讀/寫」外部裝置。Filter driver是非必要的驅動程式，主要在過瀘 I/O requests。

WDM驅動程式的設計是使用Windows DDK，學習資源豐富並且完整；相較於Linux驅動程式，WDM驅動程式的學習材料較系統化。

<strong>Linux驅動程式</strong>

Linux驅動程式採取嚴謹的分層式架構設計（layered architecture），利用分層的架構設計來徹底區分generic device driver（machine independent）與machine dependent driver。

Linux驅動程式採用分層架構的觀念設計，透過「註冊」與「回呼」的機制來清楚地區分每一層的關係。分層架構的實作必須在下層將自己註冊給上層，上層再回呼下層；上層的驅動程式必須提供註冊函數供下層呼叫，下層驅動程式所使用的註冊函數也將決定自己的上層架構。

與 user application 如何互動，是撰寫驅動程式時所要考慮的重要一環，因此在撰寫驅動程式時，要提供什麼「功能」給應用程式引用，就必須事先定義清楚。Linux的generic device driver層已經幫我們把這些功能定義清楚了。Linux驅動程式如何透過 I/O port 或 I/O memory來控制裝置，也就是與晶片組的溝通，方式是使用 Linux kernel 所提供的I/O函數來存取並控制實體硬體裝置。

Linux驅動程式的學習困難度較高，並且也沒有像是Windows DDK這樣的完整開發工具；但是若能掌握正確的學習步驟，要邁向高手之路並非遙不可及。

-- 作者／陳俊宏 (jollen)
原文刊載於 Run! PC 雜誌 8 月號
歡迎任意轉貼引用．但請務必<a href="http://www.jollen.org/license.html">註明出處</a>]]></description>
         <link>https://www.jollen.org/blog/2006/09/post.html</link>
         <guid>https://www.jollen.org/blog/2006/09/post.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Sat, 30 Sep 2006 12:46:06 +0800</pubDate>
      </item>
            <item>
         <title>嵌入式系統開發導論 (刊載於 Run! PC 8 月份)</title>
         <description><![CDATA[<strong>超熱門美食：嵌入式系統</strong>

什麼是「嵌入式系統」？這個熱到不行的題目倒底是什麼東西！嵌入式系統已經是全球科技業的共通話題了，就讓我們來簡單說明什麼叫「嵌入式系統」（Embedded Systems）吧。

美國工程師協會所定義的．．．嗯！看到這句話大家大概又要傻眼了，怎麼每個人解釋嵌入式系統都要搬出美國工程師協會的定義來交差呢！這真的是落伍啦。嵌入式系統不過是廣義還是狹義的定義，肯定都是包山包海的又臭又長。

我們這裡所要講的嵌入式系統目標是鎖定在熱門的「嵌入式作業系統平臺技術」（Embedded OS），所以如果我們再繼續縮小主題的話，沒錯！題目就只剩下 WinCE、Symbian 與 Linux 了！

如果以學習熱度來講的話，WinCE與 Embedded Linux 絕對是不「二」主題。所以我們要講的嵌入式系統就是Windows Mobile與Embedded Linux。本文將會明確鎖定幾個主題，並給予一個學習的方向，讓有志進入嵌入式系統領域的朋友能抓住主要的幾個主題。

<strong>功能導向的系統</strong>

言歸正傳，所謂的嵌入式系統簡而言之是一種「執行部份特定功能」的系統，實作上並不限定技術範疇，只要能將特定的功能（function）「嵌入」到目標裝置（target device）裡，包含這些功能的整體系統（或平臺）即是「嵌入式系統」。

一部目標裝置裡頭，可能會有數十個甚致數百、數千個嵌入式系統，每個「系統」均負責執行一項專責的功能。

由於目前的目標裝置都必須嵌入甚為複雜的功能，所以「嵌入式作業系統」成為嵌入式系統不可或缺的要素。例如，一支完全沒有生命力的「手機硬體」，在嵌入Windows Mobile與application後，就變成可以聽MP3、跟女朋友聊天、看MTV與使用各種功能的活力裝置了！有了完整的嵌入式系統技術，任何目標裝置都會因為嵌入式系統的注入，而產生生命，充滿活力！

<strong>目標裝置導向的系統</strong>

由於嵌入式系統是功能導向的系統，因此必須設計、選擇或購買正確（或適合）的目標裝置，才能開始實作並嵌入「嵌入式系統」。因此，嵌入式系統的技術是以功能與目標裝置為分類的一種技術。

例如，與PDA相關的目標裝置（即硬體）、與MP3撥放器相關的目標裝置、與3G手機相關的目標裝置等等；使用這些目標裝置所開發的特定功能系統，便是PDA的嵌入式系統、MP3音樂撥放的嵌入式系統、3G手機的嵌入式系統。

附帶值得一提的是，由於嵌入式系統的目標裝置，在硬體規格或架構上往往重疊性很大，所以很多SOC的廠商都會推出所謂的「公板」或「參考設計」（Reference Design），下游的開發者往往只要把公板做部份修改或小量的客製化，即可滿足自己的需求。

<strong>使用嵌入式作業系統</strong>

因為以功能為導向，因此當要嵌入的功能很複雜或非常多樣化時，使用嵌入式作業系統才能解決許多技術面的問題。有了嵌入式作業系統，「功能的實作」往往只是在「寫程式」，所以最終可以把嵌入式系統變成軟體開發的工作。

<strong>目標裝置 (Target Device) 的特性</strong>

嵌入式系統大多數都不使用標準的IBM PC硬體，而且有些嵌入式系統的目標裝置都是很特殊的。不過，我們可以將嵌入式系統目標裝置（target device）的主要硬體差異做一個說明。
我們透過與IBM PC的差異比較來做解釋。

嵌入式系統使用SOC（System-on-Chip）的處理器，這些SOC都是專為嵌入式系統或是手持行動裝置設計的，因此具備低功率、低耗電與面積小的特色。

在RAM方面，嵌入式系統的RAM大多在2MB到128MB之間，並且都是屬是SRAM/SDRAM的技術。很少使用IBM PC規格的DDR記憶體。

在儲存裝置方面，嵌入式系統採用NAND flash技術來存放系統程式。例如iPOD就有一部份產品使用NAND flash來存放MP3。使用NAND flash做為主要的儲存裝置是嵌入式系統的主要趨勢之一。
嵌入式系統主要透過序列埠輸出資訊；而個人電腦則是透過VGA顯示卡顯示圖形資料，嵌入式系統大多使用TFT-LCE做為圖形介面。

個人電腦當然是一般用途（general-purpose）的電腦，因此可以安裝許多有趣的應用軟體。嵌入式系統則是屬於特定用途（specific-purpose）的電腦，主要是針對特定功能所設計。

<strong>WinCE簡介</strong>

由於微軟的智慧型手機與台灣代工伙伴合作的成功，使得Windows Mobile成為大家耳熟能詳的嵌入式系統技術。那麼Windows Mobile與Windows CE到底是不是一樣的東西？

基本上，Windows Mobile與Windows CE是一樣的；Windows CE是微軟專為嵌入式系統所推出的embedded OS，Windows CE可應用於網路設備、Set-top-Box、工業控制器等嵌入式裝置。

Windows Mobile 5.0採用Windows CE 5.0的核心，是專為PocketPC與Smartphone所推出的產品。WinCE的開發環境可以相當低的花費取得，甚致可以取得免費版本；Windows Mobile的開發環境則是必須經由微軟授權後取得。

WinCE的產品開發門檻底、程式開發工具友善使用性佳、使用者介面豐富與完整的多媒體支援，使得WinCE在行動手持裝置的產品開發上具備相當的優勢。

以專為智慧型手機推出的Windows Mobile 5.0來說，除了使用性佳的操作介面與強大的多媒體撥放支援外，更可以執行Mobile Office套裝軟體（Word Mobile、PowerPoint Mobile Viewer等）、使用MSN與收發Email等。

Windows Mobile上能執行許多PC上常用的許多應用程式，例如：Mobile IE、Windows Media Player 10 Mobile 5，都是讓Windows Mobile智慧型手機更聰明好用的關鍵。在嵌入式軟體開發方面，Windows Mobile支援 .NET CF（Compact Framework）；開發工具目前也整合成一套Visual Studio 2005，開發Windows Mobile的應用程式更加輕鬆愉快；需要資料庫應用的應用程式，也能使用SQL Mobile解決方案。

<strong>Embedded Linux簡介</strong>

Embedded Linux其實並不是一個作業系統，而是代表「應用Linux系統於embedded system」的名詞。因此用即是Embedding Linux來說明Embedded Linux技術是再恰當不過了。Embedded Linux的技術核心主軸是在研究「如何將Linux系統嵌入至嵌入式目標裝置裡」。

根據William E. Peisel 於Whitepaper: Embedding Linux一文對Embedded Linux的看法，Embedded Linux的定位為：「A software platform for embedded systems and devices」。

嵌入式Linux系統的技術主軸有二：(1) Linux kernel 與 (2) root filesystem。Root filesystem即是「小型的Linux系統」，root filesystem裡頭存放Linux的系統架構（filesystem hierarchy）、指令、工具、應用程式、shared libraries、驅動程式等。

Embedded Linux的root filesystem必須純手工打造（from zero / <a href="http://www.jollen.org/blog/2006/09/embedded_linux.html">from scratch</a>），因此熟悉Linux系統的操作、設定與管理是必要的基礎技能。

相較於WinCE友善的程式開發環境，Embedded Linux的開發環境則是比較不容易上手的。目前專門針對Embedded Linux的開發工具尚不成熟，因此我們都是在Linux的”PC”上使用Linux一般性的程式開發工具來進行嵌入式Linux的發展。

Embedded Linux的開發工具，主要以GCC和glibc為核心。GCC（<a href="http://www.gnu.org/software/gcc/gcc.html">http://www.gnu.org/software/gcc/gcc.html</a>）是GNU Compiler Collection的縮寫，也就是許多編譯器的收集，目前支援的程式語言有：C、C++、Objective-C、Fortran 、Java、Ada。GLIBC是GNU的C標準程式庫，GLIBC提供system call的界面函數與標準的C函數。GLIBC也符合許多標準與規格，讓使用GLIBC的程式可以更容易移植到其它UNIX平臺。

目前應用Embedded Linux技術的產品，大多偏向「非手持型」的嵌入式系統，例如：網路設備、Set-top-box、工業控制器等。

<strong>Linux kernel</strong>

Linux 是一個作業系統核心的實作，<a href="http://kernel.org">Linux kernel</a> 加上其它必要的系統工具 (utilities) 與其它專案程式碼的 Linux 作業系統則稱為 Linux system，一般認為標準的稱呼為 GNU/Linux (system)，這是因為 Linux system 使用的系統工具大多是 GNU 的程式碼。

目前仍在使用本的kernel版本可分為4大分支：

1. Kernel 2.0.x：已經停止發展的早期版本。
2. Kernel 2.2.x：已經停止發展的早期版本。
3. Kernel 2.4.x：重要的版本分支，目前仍廣為使用中，因此仍然持續有驅動程式與處理器架構相關程式碼的更新。
4. Kernel 2.6.x：重要的Linux kernel里程碑，許多新技術、新觀念、驅動程式支援與作業系統架構都在此版本實作，是目前更新最頻繁的版本分支。

目前（2005年）的最新kernel版本為2.6.x系列，由於kernel的開發者很早就已經轉移重心至2.6系列，因此2.4系列的版本分支也將漸漸停止發展。許多2.4版本裡的重大bug也只在2.6系列版本做修正。

2.4 系列的 kernel 仍普遍使用於嵌入式系統中，但缺點是驅動程式的支援較不足；2.6 系列的 kernel 是目前最流行的分支，這個系列的 kernel 加入許多新技術的實作，當然也包含對嵌入式系統的支援，並且具備較完整的驅動程式。

<strong>Embedded Linux的重要規格標準</strong>

Embedded Linux是基於Linux系統的特殊應用，當然也要符合眾多標準才行。LSB與FHS標準是重要的二大標準，跟隨標準不但可以提供系統間的相容性，也可以提供我們一個Linux系統的建構依據。

由 FSG (Free Standards Group) 所主持的 LSB (Linux Standard Base) 專案即是在制定 GNU/Linux 的標準。根據LSB標準所發展的GNU/Linux系統，才能提供應用程式最小的可執行環境，並且可在依循LSB標準的Linux distributions上執行無誤。例如，我們可以在符合LSB標準的Red Hat Linux上發展應用程式，只要自行發展的Embedded Linux系統符合LSB標準所訂定的規範，應用程式就可以順利移植到Embedded Linux上執行。

LSB 標準也提供我們發展 Embedded Linux 的依據，雖然 Embedded Linux 系統是最小化的 Linux，但因為 Embedded Linux 是嵌入式系統的軟體平臺，所以我們不能任意精簡 Linux 系統，在精簡的過程中仍要保留最基本的作業系統環境，而 LSB 的標準正是在制定這些基本的需求。

LSB Specification共分成二大類型：

1. Generic Specification（LSB-generic）：定義不會經常改變與各處理器同通之一般標準。
2. Architecture Specifications（LSB-arch）：定義不同處理器架構之標準。

完整的 LSB 規格文件為 gLSB 配合特定的 archLSB 文件，若我們以 IA32 之電腦架構為主，應選擇參考 LSB Common 與 LSB IA32 二份文件。

LSB - <a href="http://www.linuxbase.org/spec">http://www.linuxbase.org/spec</a>
如何進行LSB 3.0的認證測試：<a href="http://www.opengroup.org/lsb/cert/docs/LSB_Certification_Guide.html">http://www.opengroup.org/lsb/cert/docs/LSB_Certification_Guide.html</a>。

FHS 全名為 Filesystem Hierarchy Standard，是一份定義檔案與目錄標準的文件，FHS 的標準定義了目錄與檔案的擺放位置，而 UNIX-like 的系統則是根據這個標準來管理整個檔案結構。因此，不管是系統廠商、Linux/UNIX distribution 發展者、應用程式作者、套件管理者、系統維護人員都應該要依照 FHS 的標準來管理 UNIX 系統的目錄與檔案。

<strong>豐富的Free Software資源</strong>

Embedded Linux的特色是大量使用Free Software的資源，「任何你想要的軟體，幾乎都能在網路上找到自由軟體」已經成為Embedded Linux技術的重要支柱。自由軟體資源包山包海，舉凡應用程式、系統工具、網路工具、程式庫、圖形介面、小型瀏覽器、程式發展工具等等都能找得到。

因此學習Embedded Linux有一個很重要的階段就是「玩Linux！」，了解Linux的世界有什麼軟體可以用，對於將來發展Embedded Linux有絕對的幫助。甚致更進一步去學習別人寫程式的技巧，都能累積Embedded Linux的功力。

-- 作者／陳俊宏 (jollen)
原文刊載於 Run! PC 雜誌 8 月號
歡迎任意轉貼引用．但請務必<a href="http://www.jollen.org/license.html">註明出處</a>]]></description>
         <link>https://www.jollen.org/blog/2006/09/_run_pc_8.html</link>
         <guid>https://www.jollen.org/blog/2006/09/_run_pc_8.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Sat, 30 Sep 2006 12:00:55 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 是程式？是一台裝置？還是什麼？</title>
         <description><![CDATA[Embedded Linux 倒底是什麼東西啊？最早 Jollen 的書曾經節錄國外專欄的解釋，企圖以最簡單的方式來定義「Embedded Linux」：Embedded Linux is a software platform for embedded system。因此，Embedded Linux 是一種「特殊的應用方法」，並不是一個程式。

今天在 LinuxDevices.com 的 forum 看到一則 post，<a href="http://linuxdevices.com/cgi-bin/board/UltraBoard.pl?Action=ShowPost&Board=100&Post=749&Idle=0&Sort=0&Order=Descend&Page=0&Session=">原始發問</a>者顯然誤解了 Embedded Linux 的意思，不過有位好心人很邁力地解釋 "regular Linux" 與 "embedded Linux" 的觀念，他的開場白寫的真的很不錯：
<strong>
'Embedded Linux' is not an application program, it is a kernel and set of libraries and utilities designed to run on an embedded system(for example, a router).</strong>

真然是一針見血，講的真好。Embedded Linux 是一個 kernel (for specific-platform) 與一些 libraries/utilities 的集合 (即 root filesystem)；因此所謂的 embedded Linux 通常是以一個 kernel image 與 root filesystem image 的形式 deliver 給 end-user 的。

其實初學 embedded Linux 的同學，也經常被 embedded Linux 的名詞所困惑，因為我們都假設大家對 embedded Linux 都有一定程度的「sense」了，所以很少去解釋 embedded Linux 與一般 Linux 的差異；在對初學者的教育訓練上，我們試著以「Top-down 最小化 Linux 系統」與 embedded Linux distribution (eg. PeeWeeLinux)，再配合 PC Linux 與 embedded Linux 的差異比較來解釋「Linux (GNU/Linux) 系統應用於嵌入式裝置」的觀念，目前看來成效還算不錯呢！

由於嵌入式 Linux 是「Linux (GNU/Linux) 系統的特殊應用」，因此所有的基本技能 (enabling technologies) 是建構在傳統的 Linux 系統操作與管理能力上的；所以「學 embedded Linux 是不是就不用去學 Linux 指令與 Linux administration」問題的答案肯定是「NO」。]]></description>
         <link>https://www.jollen.org/blog/2006/09/embedded_linux_2.html</link>
         <guid>https://www.jollen.org/blog/2006/09/embedded_linux_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Sat, 30 Sep 2006 11:29:09 +0800</pubDate>
      </item>
            <item>
         <title>QEMU 虛擬機器</title>
         <description><![CDATA[<a href="http://fabrice.bellard.free.fr/qemu/">QEMU</a> 是一個可以模擬 x86、x86_64、ARM、、SPARC、SPARC64、PowerPC、PowerPC64、MIPS、m68k 與 SH-4 處理器的 open source 軟體。簡單說，QEMU 就是一套虛擬機器 (virtual machine) 的軟體。

QEMU 提供二種模擬模式：(1) Full system emulation；(2) User mode emulation。Full system emulation 模擬完整的系統，即處理器與週邊；user mode emulation 則是能在不同的處理器平臺上執行其它處理器平臺的程式。

以下是引用自 QEMU 官方的介紹：

QEMU is a generic and open source processor emulator which achieves a good emulation speed by using dynamic translation. 

QEMU 的 <a href="http://fabrice.bellard.free.fr/qemu/status.html">Status</a> 網頁表列了目前支援的 CPU 與這二種模擬模式的支援狀況。在 QEMU 的 <a href="http://fabrice.bellard.free.fr/qemu/roadmap.html">roadmap</a> 裡，也驚見 "Full MAC OS X support as guest OS" 的　計畫，真是令人期待。

網路上<a href="http://www.linuxdevices.com/news/NS9983843412.html">有人</a>利用 QEMU 安裝了 Versatile，然後灌了一套 Debian ARM 在玩。看起來頗為有趣，此外，QEMU 應是目前最佳的模擬器首選，這真是學習微處理器系統的好教具。

]]></description>
         <link>https://www.jollen.org/blog/2006/09/qemu.html</link>
         <guid>https://www.jollen.org/blog/2006/09/qemu.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Thu, 28 Sep 2006 23:38:31 +0800</pubDate>
      </item>
            <item>
         <title>libiconv: 字元集碼編轉換</title>
         <description><![CDATA[實作 Linux 系統程式時，常需要做「Unicode 與 Big5」間的字元編碼轉換；嵌入式 Linux 的應用也經常會遇到這樣的需求，例如我們原則上會將文件存成 Unicode，當程式執行時，再決定要輸出成  Unicode 或 Big5 (or GBxxxx)，這時就要使用到 GNU 的 libiconv 專案。

GNU libiconv 用來做字元間的編碼轉換，已經廣泛被使用在 GNU/Linux 系統中，例如 PHP 的 iconv 系統即是使用 GNU libiconv。libiconv 要移植到 ARM9 平臺上也是非常容易的。GNU libiconv 的官方首頁是: <a href="http://www.gnu.org/software/libiconv/">http://www.gnu.org/software/libiconv/</a>

把 libiconv 套件解開並編譯後，可以在 src/ 目錄下找到 iconv 執行檔，這是 libiconv 為我們寫好的一個字元轉碼 (conversion) 工具，這個工具相當的實用，比如以 jollen 的網站來說，jollen.org 的網頁是以 unicode 儲存，但是我們發佈的頁面是以 big5 編碼為主，我們所使用的轉換工具便是 iconv。

iconv 的使用可以參考 <a href="http://www.gnu.org/software/libiconv/documentation/libiconv/iconv.1.html">http://www.gnu.org/software/libiconv/documentation/libiconv/iconv.1.html</a>，我們舉一個例子來說明，比如我想把 big5.txt 文件 (Big5 encode) 轉換成 Unicode (UTF-8)，那麼只要執行：

$ iconv -f BIG5 -t UTF-8 big5.txt

就可以了，參數 -f 指定來源編碼，參數 -t 指定目的編碼，編碼後的字串會輸出到 stdout。字元集 BIG5 也可以寫成 BIG-5，或是 BIG-FIVE，或是 BIGFIVE；要怎麼知道 iconv 可以處理 (接受) 哪些字元集 (character set )，只要執行 'iconv -l' 就可以查詢了，輸出結果會是一大票的字元集列表。

這是使用 iconv 工具的方式，假如要自己寫程式的話也是非常簡單的，因為 libiconv 裡頭只有 3 個函數：

iconv_t <a href="http://www.gnu.org/software/libiconv/documentation/libiconv/iconv_open.3.html">iconv_open</a> (const char* tocode, const char* fromcode)：開啟 libiconv。
size_t <a href="http://www.gnu.org/software/libiconv/documentation/libiconv/iconv.3.html">iconv</a> (iconv_t cd, const char* * inbuf, size_t * inbytesleft, char* * outbuf, size_t * outbytesleft)：執行轉碼。
int <a href="http://www.gnu.org/software/libiconv/documentation/libiconv/iconv_close.3.html">iconv_close</a> (iconv_t cd)：做完轉碼後關閉 libiconv。

libiconv-1.xx/src/iconv.c 本身就是一個很棒的範例了，大家可以參考。


]]></description>
         <link>https://www.jollen.org/blog/2006/09/libiconv_1.html</link>
         <guid>https://www.jollen.org/blog/2006/09/libiconv_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 27 Sep 2006 23:10:52 +0800</pubDate>
      </item>
            <item>
         <title>RISC 嵌入式平臺 (PowerPC) 的 VGA 解決方案</title>
         <description><![CDATA[從事系統程式工作的朋友大都聽過 Open Firmware 的標準，因為工作上的需要，允許 Jollen 可以花一點時間整理 Open Firmware 的資源。至於為什麼要來讀 Open Firmware，就要由 x86 的 VGA 說起了。

從以前我們做過的 PowerPC + x86 VGA 專案，以至於對 LinuxBIOS 玩票性質的研究，都能推導到一個未來 (也是現在) 值得投資的主題，那就是 <a href="http://www.openfirmware.org/">Open Firmware</a>。Jollen 最近發現在 <a href="http://www.openbios.info/">OpenBIOS</a> 的專案網站中，出現了一個 FCode Suite 的套件，這是 IBM 的 David Paktor 老兄在八月份 (2006) 丟到 OpenBIOS 專案的貢獻 (不過他在9月5日才發佈這項消息</a>)。

<strong>OpenBIOS</strong>

OpenBIOS 的專案目的是實作一個 IEEE 1275-1994 (即 Open Firmware 標準) 相容的 firmware。ＯpenBIOS 提供的 FCODE Suite 將 ANS <a href="http://www.faqs.org/faqs/computer-lang/forth-faq/part1/">Forth</a> 相容的程式碼翻譯成 FCODE 中介形式，可以提供我們開發 platform-independent 的 boot-time device drivers (可以看 <a href="http://www.mail-archive.com/openib-general@openib.org/msg24287.html">David 的 email</a>)。

這裡先暫時把相關的資源 (VGA) 再整理一下，等到公司的 PowerPC 平臺真的 ready 後，才可能有機會來 study FCode Suite。

<strong>Video Graphics Array</strong>

VGA BIOS 提供了一組 VGA 卡的服務函數，實作在主機板上的 BIOS 裡。非常早期的 IBM PC 就有提供 VGA BIOS (或稱為 Video BIOS)。VGA BIOS 主要是一大堆 screen I/O 的函數，這是 x86 架構下的產物，自然也就以 x86 assembly 寫成。

VGA BIOS 歷經歲月的擴充與積累，一直到 1987 年 VGA 出現為止，才大致成為現今我們所看到的樣貌。VGA 全名是 Video Graphics Array，VGA BIOS 也相容於之前的 CGA/EGA。

CGA - Color Graphics Adapter
EGA - Enhanced Graphics Adapter

VGA BIOS 透過 INT10H (10 號中斷) 來提供 DOS 程式在瑩幕上畫圖，以前學過 x86 assembly 的朋友一定都知道這號有名的中斷。標準的 VGA BIOS 提供以下的功能：

* CGA/EGA/VGA routines, INT 10H - 功能編號 00h~0Fh
* EGA/VGA routines, INT 10H - 功能編號 10h~13h
* VGA routines, INT 10H - 功能編號 1Ah~1Ch
* 標準的 VGA 參數表與字型表

另外，常常聽到的 "VESA" 全名是 Video Electronics Standards Association，這是一個制定 VGA BIOS Extensions 的組織，目前有主要的二份規格: VBE 2.0 (1994) 與 VBE 3.0 (1998)。VBE 制定現在耳熟能詳的 virtual screen areas 與 framebuffer 功能，像是 Linux 的 VESA Framebuffer driver (linux/drivers/video/vesafb.c) 便是能支援 VBE 2.0 compliant 的顯示卡 (因此 vesafb.c 是 x86 平台才能用的)。

以下是在網路上找到的 10 號中斷說明:

<a href="http://home.educities.edu.tw/wanker742126/asm/ap07.html">http://home.educities.edu.tw/wanker742126/asm/ap07.html</a>
<a href="http://www.bookcase.com/library/dos/ints/int10.html">http://www.bookcase.com/library/dos/ints/int10.html</a>

跟 x86 assembly 有關的:

<a href="http://www.csn.ul.ie/~darkstar/assembler/">http://www.csn.ul.ie/~darkstar/assembler/</a>
<a href="http://burks.bton.ac.uk/burks/language/asm/asmtut/asm1.htm#toc">http://burks.bton.ac.uk/burks/language/asm/asmtut/asm1.htm#toc</a>

<strong>x86 中斷表</strong>

Ralf Brown's Interrupt List 這個實在是太有名了，就不多做介紹了，這位老兄整理的中斷服務表是以前寫 MS-DOS assembly 的寶典，以前還記得這份列表的最新更新都只透過 Fido Net 在散佈，所以以往 Dailup-BBS 的時代要找到這份列表都要去找台灣 Fido Net 的站台才下載得到。

現在這份列表已經可以由 WWW 很方便的下載了。下載的網址是<a href="http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html">http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html</a>

<strong>LinuxBIOS v.s. x86 VGA</strong>

這算是比較古老的議題了，而且也是屬於實作面的問題。

把 VGA 卡插到 PowerPC 平臺上後，當然是，不會動的！VGA BIOS code 要能在 RISC 平臺上執行，必須要有 x86 emulator，由於 VGA BIOS 是 INT 10H 中斷服務，所以除了能能模擬 x86 外 (instruction set)，對於 INT 10H 與其它中斷的模擬也是必要的；另外就是 x86 上的 I/O port 要對應到 I/O memory。

<a href="http://www.linuxbios.org/index.php/Welcome_to_LinuxBIOS">LinuxBIOS</a> 提供的 <a href="http://www.linuxbios.org/data/vgabios/">FreeVGA</a> (Architecture Independent Video Graphics Initialization for LinuxBIOS) 應該是 x86 VGA 解決方案的<u>第一選擇</u>。不過我們以前的專案在摸擬 VGA 時，用的是 U-Boot + x86emu，在這裡我們使用的 x86emu 是 SciTech 的版本，目前只能由 SciTech SNAP SDK 取得。使用 U-Boot + SciTech x86emu 是<u>第二選擇</u>，也是我們用的方法。]]></description>
         <link>https://www.jollen.org/blog/2006/09/vga_video_graphics_array.html</link>
         <guid>https://www.jollen.org/blog/2006/09/vga_video_graphics_array.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">U-Boot , ARM9 and Pre-OS 相關</category>
        
        
         <pubDate>Mon, 25 Sep 2006 23:21:15 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 系統性的教學看法</title>
         <description><![CDATA[依照建立 "root filesystem" 的方法，我們可以區分以下 4 大類型 (Jollen 個人看法，僅供參考)：

1. Embedded Linux Distribution.
2. Hand-made.
3. Build system.
4. Metadata build system.

<strong>Embedded Linux Distribution</strong>

比如像是 <a href="http://www.debian.org/ports/arm/">Debian for ARM</a> 這類型的 pre-built Linux distribution for embedded Linux 其實是不太實用的，除了 <a href="http://www.jollen.org/blog/2006/09/_powerpc_powerpc.html">large-scale</a> 的應用或是教學用途 (eg. PeeWeeLinux) 外，實務上並不是很經常使用這個方法。

<strong>Hand-made</strong>

就是純手工打造法啦！

在網路上 (特別是大陸網站下載的文件) 有很高的比率都是介紹如何採用 "hand-made" 的方式來建立 (bottom-up) root filesystem，不過這種方式雖然未來非常有可能不會再使用，但是絕對100%有必要去學習，幾個理由不外乎是：

(1) 透過 hand-make (aka <a href="http://www.linuxfromscratch.org/">Linux from scratch</a>) 才能了解最根本的觀念。
(2) 透過 hand-make 學習到整體性的 embedded Linux 設計流程與方法。
(3) 能更深入學到建立 embedded Linux 系統的技巧。
(4) 練功。
(5) 練就建立 embedded Linux 系統的基本功，並學習所要具備的 Linux 系統管理能力。
(*6) very critical 的問題都要 hand-make 才得以解決。

這個方法是必學的，因為可以學到很深入的 cross-compilation 技巧，比如像是在 Jollen 課程裡提到的在某些類型的套件 "porting" 方式中，就要去修改 configure.in。

<strong>Build System</strong>

像是 Jollen 設計的「教具」Jollen-Kit! Builder 就是屬於這種類似，我們的 embedded Linux 課程是希望同學建立基本功，因此是由 hand-made 教起，課程的尾巴我們給同學 Jollen-Kit! Builder，並講解如何「有效率且系統化的做 cross compilation 與建立 root filesystem」。

Build system 大多建立在 Makefile 與 script 的體系之上，幾個知名的 build system，像是 uclibc-distribution、SnapGear、<a href="http://buildroot.uclibc.org/">Buildroot </a>(也經常被用來建立 arm-uclibc 的 toolchain) 等，都算是 build system。

<strong>Metadata Build System</strong>

這是一種更先進高階的 "build system"，以往我們都用 Makefiel / script 來建立 build system，但這種方法 (傳統 build system) 的缺點是：

(1) 無法有效解決套件間的相依 (dependency) 問題。
(2) 支援多平臺 (architecture) 時，很麻煩的！
(3) 有時改 configure.in 改到頭痛！
(4) 甚致要把套件 (package) 的 source code 也一起打包，不能設計成網路下載 (像 cflinux 這樣)。
(5) ...還有很多很多...

目前最具代表性 (也找不到別人了) 的專案是 <a href="http://www.openembedded.org">OpenEmbedded</a>，我們在今年初開始試著用 OpenEmbedded 來建立 embedded Linux，雖然還有不足的地方 (有時沒有比 build system 方便)，但是基本上 OpenEmbedded 已經完整展示了先進的 "Linux distribution for embedded system" 做法。

OpenEmbedded 採用 <a href="http://bitbake.berlios.de/manual/">BitBake</a> 來做套件的 cross compilation 與管理，BitBake 使用 .bb files 與 bbclass 來建立 Linux distribution。BItBake 的精神與觀念真的非常值得讚許，如果您曾經玩過 <a href="http://www.cflinux.hu/">cflinux</a> 或類似的 build system，一定會對 BitBake 與 OpenEmbedded 感到驚奇的。

雖然我們目前用 BitBake 來建立 metadata build system 還算小有收獲 (Jollen-Kit! Builder Enterprise)，不過要讓大家都能很上手的深入 OpenEmbedded 系統去做客製化、修改或是加入套件，還是有點障礙的，畢竟這不像我們上課介紹的做法那麼直覺。

而且還要挑戰 GNU autoconf，這是一大惡夢呀。

<strong>學習旅途</strong>

Embedded Linux Distribution -> Hand-made -> Build system -> Metadata build system -> 玩家!

不過不一定每個階段都要去涉獵，有些對 Linux 系統很熟的玩家級同學，甚致直接切入 build system 的修改。

最後要來 push 一下 = ="

假設對 Jollen 的課程有興趣的話，請務必了解一下我們的課程規劃，或是使用 Email 由我們提供1-by-1的咨詢，才不會花冤枉錢。
]]></description>
         <link>https://www.jollen.org/blog/2006/09/embedded_linux.html</link>
         <guid>https://www.jollen.org/blog/2006/09/embedded_linux.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Fri, 22 Sep 2006 22:59:17 +0800</pubDate>
      </item>
            <item>
         <title>利用 Monotone 與朋友客戶協同發展與測試</title>
         <description><![CDATA[Monotone 是一個版本控管系統，但是他跟 SVN/CVS 並無衝突，我們由 <a href="http://www.venge.net/monotone/">monotone</a> 首頁節錄官方的摘要說明如下：

monotone is a free distributed version control system. it provides a simple, single-file transactional version store, with fully disconnected operation and an efficient peer-to-peer synchronization protocol.

SVN 是廣為使用的版本控管系統，並將取代 CVS。然而 SVN/CVS 的操作與管理的學習成本是挺高的，我們無法「勸導」客戶積極地配合我們的 CVS 模式來跟我們的程式碼同步，還好 monotone 可以代替原本 CVS 的角色 (版本控制)，並提供更簡便的版本控管機制給「朋友」或「客戶」。

Monotone 將所有不同版本的程式碼建成「一個資料庫」，附檔名是 *.mtn，比如我把 Jollen-Kit! Builder 利用 monotone 成立一個專案 (project) 並建立 jkbuilder.mtn 資料庫，那麼我只要把 jkbuilder.mtn 遞交給朋友，我朋友就可以利用 monotone 把 jkbuilder.mtn 裡的程式碼 "check out" 出來。

Monotone 用在教育訓練也是很適合的，我們現在把一大堆課程的東西都「打包」成 *.mtn，同學只要下載 *.mtn 資料回去，就可以把課程的資料 (範例、文件與套件等) 都取出來。相當的方便，monotone 可以在某些場合取代 CVS 的使用，提供大家做參考。

比如我朋友 Andrew 拿到 jkbuilder-0928.mtn 後，要先用 mtn 做 "checkout" 的動作，這時 Andrew 下達的指令會是：

linux$ mtn --db=./jkbuilder-0928.mtn co --branch=org.jollen.dev

執行後，Andrew 就可以在 "org.jollen.dev/" 目錄下看到 checkout 出來的程式碼。Andrew 很熱血的幫我們改掉一下錯誤 (bug) 了！那我只要給 Andrew 一個簽證 (key)，他就可以做 "commit" 的動作。]]></description>
         <link>https://www.jollen.org/blog/2006/09/_monotone.html</link>
         <guid>https://www.jollen.org/blog/2006/09/_monotone.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Thu, 21 Sep 2006 22:57:50 +0800</pubDate>
      </item>
            <item>
         <title>一篇有關 Reentrant Code Program (可重覆進入程式碼) 的文章</title>
         <description><![CDATA["Reentrant Code" 是 embedded system 相關教科書很重要的一個主題，學習 Linux 驅動程式的學員也一定了解到這個主題的重要性。

由於 Linux 驅動程式裡的每一個 "operation" (eg. fops->read) 是 "共用" 的，例如 major number 相同的 device file (minor number 不同) 是共用同一塊 code，所以必須考量程式碼的「可重覆進入」寫法 (thinking about filp->private data)。

這也是為什麼在 Jollen 的 Linux 驅動程式課程裡，我們如此這般在強調 Reentrant Code (Reentrant   Function) 這個主題，也花了許多時間介紹 Linux 驅動程式的 "reentrant" 觀念。

<strong>Linux 驅動程式有許多主題是一點點搞不懂，就會全盤皆不懂的，reentrant code 的觀念是其中之一。</strong>由於許多學員對於 reentrant code 的理論不甚了解，因此建議未來要來上課的同學可以事先研讀這篇寫的相當好的文章：

<a href="http://www.unet.univie.ac.at/aix/aixprggd/genprogc/writing_reentrant_thread_safe_code.htm">http://www.unet.univie.ac.at/aix/aixprggd/genprogc/writing_reentrant_thread_safe_code.htm</a>

在 Jollen 多年的 Linux 驅動程式授課經驗中，reentrant code 是同學比較不容易搞清楚的觀念，可是卻也是一開始我們課程就會講到的主題；我們提到當上層 callback 下層的驅動程式時，什麼條件下下層必考慮可重覆進入的問題。<strong>要學好 Linux 驅動程式，建議無論如何也要搞懂這個主題！</strong>]]></description>
         <link>https://www.jollen.org/blog/2006/09/_reentrant_code_program.html</link>
         <guid>https://www.jollen.org/blog/2006/09/_reentrant_code_program.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 20 Sep 2006 22:12:11 +0800</pubDate>
      </item>
            <item>
         <title>嵌入式Linux的Web Application Framework</title>
         <description><![CDATA[今天在survey與初步build所謂的"<a href="http://www.linuxdevices.com/articles/AT5550934609.html">a web application framework in an embedded Linux system</a>"，這是8月份在LinuxDevices.com的一篇文章介紹的題目。Web-application或者是"Application Web化"是近幾年來非常明顯的一個趨勢，思考 Embedded System 的 Web Application (Embedded Web Applications ?) 是一個頗為有趣的主題，也有許多的 paper 在討論這個題目。

目前嵌入式Linux上的web application與desktop (PC) 的web application並無太大差異，許多文件也是只以"embedded web server"做為核心的出發點，這篇在linuxdevices.com上看到的文章也是。不過這篇文章倒值得對這個題目有興趣的朋友一讀。

作者開場白就點出了這個題目的主軸:

As devices are increasingly more networked, an embedded web server is becoming a standard way for users to interact with and configure an embedded device using a standard web browser.

對於web application in an embedded linux system的研究範疇，就 Jollen 目前的認知來講，可以分成幾個層級來探討：

(1) 低階做法：設計一個 "monolithic" 應用程式來處理 HTTP 並輸出 HTML，例如在 Jollen 的 "Embedded Linux 嵌入式系統原理與實務 ,3e" 書上給的 web.c (a dirty embedded web server) 範例。
(2) 中階做法：建構 embedded web server 的 root filesystem，例如加入 thttpd 的使用，如此一來便能透過 CGI 規範來設計 web application；thttpd 的 CGI 大多以 C 語言撰寫。
(3) 高階做法：建構 "framework" 來設計與實作 web application，linuxdevices.com 上的這篇文章就是在討論這種做法。

實作上，要建構 "web appication framework" 的基礎平臺 (aka root filesystem)，能選擇的 open source 解決方案其實很多 (各種排列組合)；不過 Jollen 把此文作者建議的 "components" for web appication framework 實際 build 後，發現還真的不賴說，大家可以試試！以下是該作者建議的 solution：

1. Web Server: <a href="http://www.0x50.org/">Cherokee</a> (1MiB)
2. Model: <a href="http://www.sqlite.org/">SQLite</a> (290KB)
3. View: <a href="http://www.clearsilver.net/">Clearsilver</a> (170KB)
4. Controller: Python (2-3MB)

看到 Model/View/Controller 了！這就是 "framework" 的重點－以MVC模式設計與實作web applications！

<img src="http://www.linuxdevices.com/files/misc/cliff_brake_web_app_framework_fig1.gif">
<strong>Figure-source (引用自):</strong> linuxdevices.com, http://www.linuxdevices.com/articles/AT5550934609.html.

這個Cherokee也是一個open source的embedded web server，試用後覺得還不賴，可以列入 <a href="http://www.acme.com/software/thttpd/">thttpd</a>/<a href="http://www.boa.org/">boa</a>外的其它選擇。Cherokee還真的挺強大的，讚讚讚，列入10月份Jollen-Kit! Builder Training Edition的更新roadmap，與所有學員分享！

節錄 Cherokee 的介紹：

Cherokee is a very fast, flexible and easy to configure Web Server. It supports the widespread technologies nowadays: FastCGI, SCGI, PHP, CGI, TLS and SSL encrypted connections, Virtual hosts, Authentication, on the fly encoding, Apache compatible log files, and much more.

目前 Jollen 把基本的環境 build 好了，10月份的Jollen-Kit! Builder Training Edition也會加入，目前則是還在拼湊demo架構 (應用程式) 中。因為 Python 初學中，程式寫的哩哩辣辣的 (why not PHP >_<")。

* "Must enable us to implement a clean MVC type architecture," I think this is good.
* "Must support a high level language like python for rapid development," Good but I'd like to think more.]]></description>
         <link>https://www.jollen.org/blog/2006/09/linuxweb_application_framework_1.html</link>
         <guid>https://www.jollen.org/blog/2006/09/linuxweb_application_framework_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Tue, 19 Sep 2006 21:37:37 +0800</pubDate>
      </item>
            <item>
         <title>U-Boot 的網路卡驅動程式架構</title>
         <description><![CDATA[PowerPC 再衝吧！整理一下這次 porting PowerPC 網路卡 driver 的重點。今天先就架構面的重點做整理。

要了解以下內容，您至少要對以下主題有基本的認知：

(1) U-Boot 如何使用 CONFIG_* 定義 driver，要把 networking 相關的 driver 打開。
(2) U-Boot 的開機與 Board-Level 初始化流程
(3) U-Boot 的 BSP 架構實作方法 (extern)

(Jollen 的 U-Boot 學員可參考講義 Lecture 3 & Lecture 4)

Jollen 的任務是將 vendor 提供的 DST 整合至 U-Boot，以支援我們 PowerPC 板子上的 PHY (Broadcom 的 Gigalan)。

<strong>net/eth.c:eth_initialize()</strong>

這是重要的 Networking 初始化的核心層，在這裡加入

int eth_initialize(bd_t *bis)
{
&nbsp;&nbsp;&nbsp;&nbsp;char enetvar[32], env_enetaddr[6];
&nbsp;&nbsp;&nbsp;&nbsp;int i, eth_number = 0;
&nbsp;&nbsp;&nbsp;&nbsp;char *tmp, *end;
&nbsp;&nbsp;&nbsp;&nbsp;eth_devices = NULL;
&nbsp;&nbsp;&nbsp;&nbsp;eth_current = NULL;

#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII)
&nbsp;&nbsp;&nbsp;&nbsp;miiphy_init();
#endif
&nbsp;&nbsp;&nbsp;&nbsp;...
#ifdef CONFIG_DB64360
&nbsp;&nbsp;&nbsp;&nbsp;mv6436x_eth_initialize(bis);
#endif
&nbsp;&nbsp;&nbsp;&nbsp;...
<font color="#0000FF">#if defined(CONFIG_PPC750GIGE)
&nbsp;&nbsp;&nbsp;&nbsp;ppc750_eth_initialize(bis);
#endif</font>
&nbsp;&nbsp;&nbsp;&nbsp;...
}

在這裡可以看到很多 driver 的定義 (CONFIG_*)，我們在最後加上自己板子的 driver 定義。ppc750_eth_initialize() 是 BSP code，所以必須實作到 board 的目錄裡面。然後在 board/xxx/實作 PHY 的 driver，因為是 BSP code，依架 U-Boot 的習慣，我們必須把 PHY 的 driver 放在 board 目錄下。

<strong>實作 ppc750_eth_initialize()</strong>

接下來的重要工作，當然就是實作 ppc750_eth_initialize()，U-Boot 在開機時，會呼叫我們 PHY driver 的進入點，而這個進入點函數就叫 ppc750_eth_initialize()，U-Boot 底下的網路卡 driver 基本架構大致雷同。以下的程式碼就是我們板子的 PHY driver 進入點：

int ppc750_eth_initialize(bd_t *bis) // called from eth.c:eth_initialize()
{
&nbsp;&nbsp;&nbsp;&nbsp;struct eth_device *dev[MAX_NUM_ETH_PORTS]; // ethernet device 的結構
&nbsp;&nbsp;&nbsp;&nbsp;int i;

&nbsp;&nbsp;&nbsp;&nbsp;printf("Initializing ethernet ports!\n");
	
&nbsp;&nbsp;&nbsp;&nbsp;for (i = 0; i < MAX_NUM_ETH_PORTS; i++)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (dev[i] == NULL) {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dev[i] = (struct eth_device *)malloc(sizeof(struct eth_device));
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 這裡是最重要的啦，要填寫 init, halt, send, recv 四個動作 (operation) 的實作函數，大家可以看一下 struct eth_device 的原型
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/		
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dev[i]->init = ppc750_ether_init; // 下達網路命令時，會先呼叫 init。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dev[i]->halt = ppc750_stop_ethernet; // 把網路關閉時回呼的。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dev[i]->send = ppc750_send_packet; //網路命令要送出封包時回呼的。
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dev[i]->recv = ppc750_poll; // 要接收封包時回呼的。

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;eth_register(dev[i]); // 註冊 ethernet device 到 U-Boot 核心層
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;return i;
}

到這裡是 U-Boot 提供給網路卡 driver 的架構，接著當然就是要實作 init, halt, send, recv 這四個 operation 了！底層 Driver 的 operation 是跟硬體相關的，也就是看 datasheet 寫 driver 的工作！
]]></description>
         <link>https://www.jollen.org/blog/2006/09/uboot_driver.html</link>
         <guid>https://www.jollen.org/blog/2006/09/uboot_driver.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">U-Boot , ARM9 and Pre-OS 相關</category>
        
        
         <pubDate>Mon, 18 Sep 2006 17:35:32 +0800</pubDate>
      </item>
            <item>
         <title>PowerPC 衝吧！</title>
         <description><![CDATA[這星期四五都在把玩公司的 PowerPC 板子，目標是 build BIOS (U-Boot)、kernel (2.6) 與基本的 root filesystem。因此就來把這二天建立 root filesystem 的心得簡單整理一下與大家分享。

PowerPC 是定義在 large-scale 的嵌入式系統，因此與一般的 small-scale embedded system 有幾個技術面的差異，若以 embedded Linux 的角度來看，幾點主要差異是：

(1) large-scale ES 通常都有 mass storage (eg. HD) 或利用 NFS/BOOTP/PXE 來開機後由 NFS server mount 完整系統，所以開機時只要先提供 NFS root filesystem 即可。
(2) 由 (1)，因此可以「灌」完整的 Linux distribution 在「NFS server」上，以 PowerPC 來講的話，可以選擇 <a href="http://www.terrasoftsolutions.com/">Yellow Dog Linux</a>、Red Hat Linux for PowerPC 與 <a href="http://www.debian.org/ports/powerpc/">Debian PPC</a> (或 Ubuntu PPC)；Jollen 的話是選擇用 Debian PPC。
(3) NFS root filesystem 可以用 busybox，不過建議是 porting 完整的 init 與 mount，因此需要下載 sysvinit 與 util-linux 二個套件。
(4) 要 mount NFS 的話，target 端最好要有 portmap daemon，以加速 NFS mount，也才不會出現「portmap: NFS server local not responding」之類的訊息。（建議是一定要裝啦）

其它該注意的小地方也給大家分享一下：

(1) 網路上可以教到 NFS mount 的做法有二種，一種是傳遞 "nfsroot" 參數給 kernel，由 kernel 去 mount，另一種是 build 一個 "<a href="http://www.tldp.org/HOWTO/Diskless-root-NFS-HOWTO.html">NFS root filesystem</a>"，然後用 fstab (也就是 mount) 去做，不過 Jollen 的建議是後者。有一些網路的文件也是建議後者。
(2) portmap 需要 tcp_wrapper 套件，記得一併下載。
(3) IP 的設定建議在開機時由 kernel 去做，不要開完機後再用 ifconfig 去設定。

提供這二天建的 NFS root filesystem for PowerPC 給大家參考，檔案要由 forum 下載。基本上，使用下列套件來 build root filesystem 就能做出一個簡單的 NFS rootfs 了 (這是 Jollen  使用的套件版本)：

* <a href="http://www.busybox.net/">busybox 1.1.3</a> (關掉內建的 mount / unmount)
* <a href="http://ftp.kernel.org/pub/linux/utils/util-linux/">util-linux 2.12r</a>
* <a href="http://ftp.porcupine.org/pub/security/index.html">tcp_wrapper 7.6</a>  (for NFS mount)
* <a href="http://ftp.porcupine.org/pub/security/index.html">portmap_4</a> (for NFS mount)

我們的板子採用 U-Boot 來做開機程式。記得要在 U-Boot 下設定 kernel command line，把 NFS 的設定加上去才行。以下 Jollen 設定 kernel 的開機參數（'ip='用來做Kernel Mode IP Configuration）：

u-boot> set bootargs console=ttyS0,115200 root=/dev/ram0 ramdisk=8192 netconsole=6665@10.100.10.12/eth0,6666@10.100.10.20/
ip=10.100.10.12:10.100.10.20:10.100.10.254:255.0.0.0:ppc:eth0

'netconsole=' 的話可有可無，加上去的話可以配合 <a href="http://netcat.sourceforge.net/">netcat</a> 來看到 "console" 畫面。這是我們的 PowerPC 板子用的開機指令：

u-boot> tftp 1000000 uImage; tftp 1200000 urootfs_ppc.img; bootm 1000000 1200000

開機後進入 Linux 命令模式後，再用 mount 把 NFS server 的目錄 mount 即可，由於我們用的 kernel 是 2.6，因此是用 'switch_root' 指令來變更 root 目錄，而不是用 "chroot"。
]]></description>
         <link>https://www.jollen.org/blog/2006/09/_powerpc_powerpc.html</link>
         <guid>https://www.jollen.org/blog/2006/09/_powerpc_powerpc.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">U-Boot , ARM9 and Pre-OS 相關</category>
        
        
         <pubDate>Sat, 16 Sep 2006 01:02:02 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式觀念解析, #7: 觀念大追擊</title>
         <description><![CDATA[您是否能看圖說明範例的觀念。

<p>作者／陳俊宏<br /><a href="http://www.jollen.org/">www.jollen.org</a></p> <p><img width="640" height="376" border="0" src="http://www2.jollen.org/EmbeddedLinux/publish/LDD/07a.jpg" /></p> <div align="center"> <table width="90%" border="1" id="table1">  <tr> <td bgcolor="#cccccc"><strong>TIP</strong> <p>fops所指的driver function其實是被Linux kernel所「回呼」（callback）。</p> <p>Linux驅動程式將fops「註冊」至kernel裡後，並不是被user application直接呼叫，而是透過system call  interface，因此fops所指的函數應是被kernel回呼。</p> <p>Callback的機制有一個好處是，當函數被呼叫時，表示此時系統符合該函數被回呼的條件。因此，driver  function可以預期自己是在符合一些條件的環境下執行。Callback機制另一個特點是，Linux kernel會傳遞「適當」的參數給driver  function，driver function可以直接使用所接收的參數資料。</p></td></tr></table></div> <p>這張圖是範例 (debug card 0.1.1) 的執行圖 (Execute  Flow/Path)，如果您能根據範例程式清楚地說明此圖，表示您已經掌握最主要的 Linux 驅動程式觀念了！</p>]]></description>
         <link>https://www.jollen.org/blog/2006/05/linux_7.html</link>
         <guid>https://www.jollen.org/blog/2006/05/linux_7.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 04 May 2006 10:27:24 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式觀念解析, #6: 依流程來實作 -- Physical Device Driver</title>
         <description><![CDATA[<p>接續前文的實作，繼續完成 physical device driver 部份；由於 physical device driver 與 I/O 存取密切相關，因此我們會先說明 Linux 的 I/O 存取函數。</p>
<p>作者／陳俊宏<br>
<a href="http://www.jollen.org">www.jollen.org</a> </p>
<p><b><font size="4" color="#FF0000">I/O 存取的觀念</font></b></p>
<p>I/O device必須透過I/O port來存取與控制，每個I/O port都會被指定一個memory address，稱為I/O port address（或port address），此即所謂的memory mapped I/O。</p>
<p>memory mapped I/O的意義為，我們可以透過I/O port被指定的memory address來存取I/O device，如此可將複雜的I/O device存取變成簡單的memory存取，也不需要使用 assembly 來存取 I/O device。</p>
<p>Memory-mapped I/O的觀念是將I/O port或I/O memory “mapping” 到 memory address上，此位址稱為I/O port address。採用memory-mapped I/O觀念的主要好處是可以將I/O device的存取變成記憶體存取。因此，對使用者而言，存取I/O裝置就會變成跟CPU的記憶體存取一樣。</p>
<p>RISC 架構的處理器，在 system design 方面，也都採取 memory-mapped I/O (I/O memory) 的觀念。</p>
<p><b><font size="4" color="#FF0000">Linux I/O Port 存取介面</font></b></p>
<p>在 x86 平臺上，I/O port與I/O memory可以看成是一樣的東西。但在學習Linux驅動程式實作時，則是要把二者清楚的分開來。若是要存取I/O port，Linux提供以下的I/O port存取介面：</p>
<blockquote>
<p>˙ unsigned inb(unsigned port);<br>
˙ unsigned inw(unsigned port);<br>
˙ unsigned inl(unsigned port);<br>
˙ void outb(unsigned char byte, unsigned port);<br>
˙ void outw(unsigned short word, unsigned port);<br>
˙ void outl(unsigned long word, unsigned port);</p>
</blockquote>
<p>若是要存取I/O “memory”，則改用以下函數：</p>
<blockquote>
<p>˙ unsigned readb(unsigned port);<br>
˙ unsigned readw(unsigned port);<br>
˙ unsigned readl(unsigned port);<br>
˙ void writeb(unsigned char byte, unsigned port);<br>
˙ void writew(unsigned short word, unsigned port);<br>
˙ void writel(unsigned long word, unsigned port);</p>
</blockquote>
<p>inb()表示要由I/O port address讀取1 byte的資料，outw()表示要輸出1 short word（2 bytes）的資料到指定的I/O port address；同理，readl()表示要由I/O memory address讀取1 long word（4 bytes）的資料，其它函數則依此類推。</p>
<p>範例透過I/O port 80H與debug card溝通，因此只要執行：</p>
<blockquote>
	<p>outb(num, 0x80);</p>
</blockquote>
<p>即可將數字”num”顯示在debug card上。有些debug card的規格也支援其它的I/O port位址，若要輸出到其它I/O port位址做測試，請自行修改範例。</p>
<div align="center">
	<table border="1" width="90%" id="table1">
		<tr>
			<td>在未學習ioremap()函數前，我們的範例都會以直接存取I/O port的方式來設計。但Linux device driver是「不能直接」存取I/O port或I/O memory的，必須將I/O port或I/O memory “remapping” 到kernel virtual address後才能存取裝置。<p>此觀念在學習 PCI 驅動程式設計時便能看到。</td>
		</tr>
	</table>
</div>
<p><b><font size="4" color="#FF0000">完成我們的範例</font></b></p>
<p>了解 Linux 驅動程式如存取 I/O device 後，我們就可以完成 ops-&gt;write 實作了！以下是我們的實作程式碼：</p>
<blockquote>
<pre>unsigned long IOPort = 0x80;
void write_card(unsigned int num)
{
	MSG("write 0x%02X (%d) to debug card", (unsigned char)num, num);
	<b>outb</b>((unsigned char)num, IOPort);
}
ssize_t card_write(struct file *filp, const char *buff, 
		size_t count, loff_t *offp)
{
	char *str;
	unsigned int num;
	int i;

	if (count == 0) return 0;

	filp-&gt;private_data = (char *)kmalloc(64, GFP_KERNEL);
	str = filp-&gt;private_data;

	if (copy_from_user(str, buff, count))
		return -EFAULT;

	/* atoi() */
	num = str[0]-'0'; for (i = 1; i < count; i++) {
		num = num*10 + (str[i]-'0');
	}

	write_card(num);

	return 1;
};
</pre>
</blockquote>
<p><b>
<font size="4" color="#FF0000">完整範例列表</font>
</b></p>
<blockquote>
<pre>/*
 *  Debug Card 0.1.1 - Port 80 Debug Card Driver
 *
 *  Copyright (C) 2004 www.jollen.org
 *
 *  This file may be redistributed under the terms of the GNU Public
 *  License.
 */

#include &lt;linux/module.h>
#include &lt;linux/kernel.h>
#include &lt;linux/init.h>

#include &lt;linux/config.h>
#include &lt;linux/ioport.h>
#include &lt;linux/errno.h>
#include &lt;linux/sched.h>
#include &lt;linux/mm.h>
#include &lt;asm/io.h>
#include &lt;asm/uaccess.h>
#include "card.h"

unsigned long IOPort = 0x80;

int card_release(struct inode *, struct file *);
int card_open(struct inode *, struct file *);
int card_ioctl(struct inode *, struct file *,
	  unsigned int, unsigned long);
ssize_t card_write(struct file *, const char *, 
		size_t, loff_t *);

void write_card(unsigned int);

void write_card(unsigned int num)
{
	MSG("write 0x%02X (%d) to debug card", (unsigned char)num, num);
	outb((unsigned char)num, IOPort);
}

int card_ioctl(struct inode *inode, struct file *filp,
	  unsigned int cmd, unsigned long arg)
{
	switch (cmd) {
		case IOCTL_RESET:
			write_card(0x00);
			break;
		default:
			return -1;
	}
    	return 0;
}

ssize_t card_write(struct file *filp, const char *buff, 
		size_t count, loff_t *offp)
{
	char *str;
	unsigned int num;
	int i;

	if (count == 0) return 0;

	filp-&gt;private_data = (char *)kmalloc(64, GFP_KERNEL);
	str = filp-&gt;private_data;

	if (copy_from_user(str, buff, count))
		return -EFAULT;

	/* atoi() */
	num = str[0]-'0'; for (i = 1; i < count; i++) {
		num = num*10 + (str[i]-'0');
	}

	write_card(num);

	return 1;
};

/**************************************************/

struct file_operations card_fops = {
	open:		card_open,
	write:		card_write,
	release:	card_release,
	ioctl:		card_ioctl,
}; 

int card_release(struct inode *inode, struct file *filp)
{
	MOD_DEC_USE_COUNT;
	kfree(filp-&gt;private_data);

	return 0;
};

int card_open(struct inode *inode, struct file *filp)
{
	MOD_INC_USE_COUNT;
	return 0;
};

int init_module(void)
{
	MSG("DEBUG CARD v0.1.1");
	MSG("  Copyright (C) 2004 www.jollen.org");

	if (register_chrdev(DEV_MAJOR, DEV_NAME, &card_fops) < 0) {
		MSG("Couldn't register a device.");
		return -1;
	}
    
	return 0;
}

void cleanup_module(void)
{
    if (unregister_chrdev(DEV_MAJOR, DEV_NAME))
	MSG("failed to unregister driver");
    else
	MSG("driver un-installed\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR(&quot;<a href="http://www.jollen.org">www.jollen.org</a>");</pre>
<pre><b>// card.h</b>
#ifndef _CARD_H_

#define MSG(format, arg...) printk(KERN_INFO "DEBUG CARD: " format "\n", ## arg)

#include &lt;linux/ioctl.h>

#define	DEV_MAJOR	121
#define	DEV_NAME	"debug"
#define DEV_IOCTLID	0xD0

#define	IOCTL_WRITE	_IOW(DEV_IOCTLID, 10, int)
#define	IOCTL_RESET	_IOW(DEV_IOCTLID, 0, int)

#endif</pre>
</blockquote>
<p><b><font size="4" color="#FF0000">寫 User Program 來測試</font></b></p>
<blockquote>
<pre>/*
 *  Debug Card 0.1.1 - Port 80 Debug Card 'User-Space' Driver
 *
 *  Copyright (C) 2004 www.jollen.org
 *
 *  This file may be redistributed under the terms of the GNU Public
 *  License.
 */

#include &lt;stdio.h>
#include &lt;unistd.h>

#include &lt;fcntl.h>
#include &lt;string.h>
#include &lt;sys/ioctl.h>
#include &lt;sys/types.h>
#include &lt;sys/stat.h>
#include "card.h"

int main(int argc, char *argv[])
{
    int devfd;
    unsigned int num = 0;

    if (argc == 1) argv[1] = "0";

    devfd = open("/dev/debug", O_RDWR);
    if (devfd == -1) {
	printf("Can't open /dev/debug\n");
	return -1;
    }

    printf("Resetting debug card...\n");
    ioctl(devfd, IOCTL_RESET, NULL);
    printf("Done. Wait 1 second...\n");
    sleep(1);

    printf("Writing %s...\n", argv[1]);
    write(devfd, argv[1], strlen(argv[1]));
    printf("Done.\n");

    close(devfd);

    return 0;
}

</pre>
</blockquote>
<p><b><font size="4" color="#FF0000">觀念大考驗</font></b></p>
<p>到這裡為止，我們已經完成階段性任務了--了解 Linux 驅動程式的架構觀念。</p>
<p>下一篇文章，我們會具體描繪出此範例的執行流程路徑；透過這張圖，大家便能考驗自己是否已經了解主要的驅動程式架構觀念了！</p>]]></description>
         <link>https://www.jollen.org/blog/2006/05/linux_6_physical_device_driver_1.html</link>
         <guid>https://www.jollen.org/blog/2006/05/linux_6_physical_device_driver_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 04 May 2006 10:18:26 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式觀念解析, #5: 依流程來實作 -- Virtual Device Driver</title>
         <description><![CDATA[<p>根據Linux驅動程式的一般化設計流程，我們來設計一個真正可以動的驅動程式。</p>
<p>作者／陳俊宏<br>
<a href="http://www.jollen.org">www.jollen.org</a> </p>
<p><b><font size="4" color="#FF0000">根據流程寫程式</font></b></p>
<p><b><font color="#000080">定義 file_operations</font></b></p>
<blockquote>
	<pre>struct file_operations card_fops = {
	open:		card_open,
	write:		card_write,
	release:	card_release,
	ioctl:		card_ioctl,
};</pre>
</blockquote>
<p>由此定義可以，我們所計的驅動程式將提供 open/write/close(release)/ioctl 4 個 system call 介面給 user application。</p>
<p><b><font color="#000080">實作 System Call</font></b></p>
<p>接著要實作我們所提供的4個 system call。open/close(即 release)/read/write/ ioctl 是初學 Linux 驅動程式最重要的 5 個 system call，了解如何實作不同的 system call，是學好 Linux 驅動程式的重要工作。</p>
<p>本文先介紹 open/close(release)/write 的實作。此部份說明如後。</p>
<p><font color="#000080"><b>註冊 Driver</b></font></p>
<p>將driver自己「註冊」到kernel的VFS層，註冊時所要呼叫的函數根據裝置類型的不同而不同。</p>
<p>將驅動程式「註冊」（registration）至kernel的動作必須在init_module()函數裡實作。根據裝置類型的不同，所呼叫的函數也不同，以下是幾個基本的裝置註冊函數：</p>
<blockquote>
	<p>˙ int register_chrdev(unsigned int major, const char * name, struct 
file_operations *fops)：註冊字元型驅動程式。<br>˙ int register_blkdev(unsigned int major, const char *name, struct 
file_operations *fops)：註冊區塊型驅動程式。<br>˙ int usb_register(struct usb_driver *new_driver)：註冊USB驅動程式。<br>˙ int pci_register_driver(struct pci_driver *)：註冊PCI驅動程式。</p>
</blockquote>
<p>本文範例註冊驅動程式的程式片斷如下：</p>
<blockquote>
	<pre>
#define	DEV_MAJOR	121
#define	DEV_NAME	"debug"	
#define MSG(format, arg...) printk(KERN_INFO "DEBUG CARD: " format "\n", ## arg)

int init_module(void)
{
	MSG("DEBUG CARD v0.1.1");
	MSG("  Copyright (C) 2004 www.jollen.org");

	if (register_chrdev(DEV_MAJOR, DEV_NAME, &card_fops) < 0) {
		MSG("Couldn't register a device.");
		return -1;
	}
    
	return 0;
}</pre>
</blockquote>
<p>register_chrdev()參數說明如下：</p>
<blockquote>
	<p>˙ 第1個參數：為device file的major number。該device file應在Linux系統底下以root身份手動建立。<br>˙ 第2個參數：<br>˙ 第3個參數：為驅動程式的fops。</p>
</blockquote>
<p>註冊的動作是寫在init_module()裡，因此當使用者執行insmod載入驅動程式時，register_chrdev()便會執行。由此可知，註冊驅動程式的時機為insmod時。相對的，在rmmod時，必須執行解除註冊的動作，此動作必須實作在cleanup_module()函數裡。</p>
<p>前面所介紹的4個註冊函數，其相對應的解除註冊函數如下：</p>
<blockquote>
	<p>˙ int unregister_chrdev(unsigned int major, const char * name) ：解除註冊字元型驅動程式。<br>˙ int unregister_blkdev(unsigned int major, const char *name) ：解除註冊區塊型驅動程式。<br>˙ void usb_deregister(struct usb_driver *driver)：解除註冊USB驅動程式。<br>˙ pci_unregister_driver(struct pci_driver *drv) ：解除註冊PCI驅動程式。</p>
</blockquote>
<p>範例debug card 0.1.0解除註冊的程式片斷如下：</p>
<blockquote>
	<pre>void cleanup_module(void)
{
    if (unregister_chrdev(DEV_MAJOR, DEV_NAME))
	MSG("failed to unregister driver");
    else
	MSG("driver un-installed\n");
}</pre>
</blockquote>
<p><u>Linux驅動程式的「註冊」是一個非常重要的動作，這個動作代表 Linux 驅動程式是一個嚴謹的分層式架構；換句話說，
Linux驅動程式的分層（layered）關係可透過「註冊」的程序來分析。</u></p>
<p><font color="#000080"><b>定義chipset標頭檔</b></font></p>
<p>我們所要設計的 Port 80H 除錯卡驅動程式，不需要定義標頭檔；此部份可參考 kernel 裡的 BTTV 驅動程式。</p>
<p><font color="#000080"><b>定義I/O wrapper function</b></font></p>
<p>我們所要設計的 Port 80H 除錯卡驅動程式，不需要定義 I/O wrapper function；此部份可參考 kernel 裡的 BTTV 
驅動程式。</p>
<p><font color="#000080"><b>實作chipset控制函數</b></font></p>
<p>在我們所要設計的 Port 80H 除錯卡驅動程式中，我們是直接使用 kernel 的 I/O 介面來控制除錯卡，physiacl device 
driver 的部份將在下一篇文章再做說明。</p>
<p><b><font size="4" color="#FF0000">open/release實作</font></b></p>
<p>open與release是Linux驅動程式最基本的2個system call。驅動程式應先實作此2個system call。</p>
<div align="center">
	<table border="1" width="90%" id="table1">
		<tr>
			<td bgcolor="#CCCCCC">
			<p align="center">為了方便說明起見，本文後文將以「fops-&gt;open」表示實作open system call的driver function。其它system call亦同。</td>
		</tr>
	</table>
</div>
<p>open與release system call的執行時機如下：</p>
<blockquote>
	<p>1. 當user application執行open()函數時，便呼叫Linux kernel的open system call，即執行fops-&gt;open。</p>
	<p>2. 當user application執行close()函數時，便呼叫Linux kernel的close system 
call，即執行fops-&gt;release。</p>
</blockquote>
<p>Linux驅動程式註冊至kernel時會指定device file的major number，user application便可以透過此符合此major 
number的device file與硬體溝通，即Linux驅動程式是透過VFS架構層與user 
application溝通。</p>
<p>file_operation是Linux驅動程式支援VFS的重要結構。學習file_operation的重要目的如下：</p>
<blockquote>
	<p>1. 了解每一個system call的用途。</p>
	<p>2. 了解每一個system call的實作「原則」。</p>
</blockquote>
<p>System call的實作原則即driver function所要負責處理的基本工作。學習Linux device driver的重要工作之一，便是一一了解fops裡每一個system call的實作原則，並依照實際需求來實作不同的 system call。</p>
<p><font color="#000080"><b>open System Call</b></font></p>
<p>以open system call為例，fops-&gt;open是在user呼叫open()函數時執行，即當user開啟driver所指定的device file時呼叫fops-&gt;open。</p>
<p>fops-&gt;open實作原則如下：</p>
<ol>
	<li>將usage count加一（increment）</li>
	<li>檢查inode-&gt;i_rdev</li>
	<li>檢查裝置是否錯誤</li>
	<li>初始化裝置</li>
	<li>將驅動程式自己的資料結構放到filp-&gt;private_data</li>
</ol>
<p>以下是本範例的fops-&gt;open實作：</p>
<blockquote>
	<pre>int card_open(struct inode *inode, struct file *filp)
{
	MOD_INC_USE_COUNT;
	return 0;
};
</pre>
</blockquote>
<p><font color="#000080"><b>release System Call</b></font></p>
<p>當user application 呼叫close() 函數後，便執行fops-&gt;release。</p>
<p>有些驅動程式會將release method函數名稱命名為 XXX_close()，但建議以XXX_release()名稱為主，以避免混淆。</p>
<p>fops-&gt;release實作原則如下：</p>
<ul>
	<li>將 usage count 減一。</li>
</ul>
<p>
以下是本範例的fops-&gt;release實作：</p>
<blockquote>
	<pre>int card_release(struct inode *inode, struct file *filp)
{
	MOD_DEC_USE_COUNT;
	kfree(filp->private_data);  <span style="font-size: 9pt; font-weight: 700">//reentrant code 觀念 (本文尚未說明)</span>

	return 0;
};</pre>
</blockquote>]]></description>
         <link>https://www.jollen.org/blog/2006/05/linux_5_virtual_device_driver.html</link>
         <guid>https://www.jollen.org/blog/2006/05/linux_5_virtual_device_driver.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 04 May 2006 09:53:13 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式觀念解析, #4: Linux 驅動程式一般化設計流程</title>
         <description><![CDATA[了解重要的架構觀念面後，接著就是 Linux 驅動程式本身了！Linux 驅動程式的設計雖然沒有一定的標準流程，但是由「觀念」層面可以歸納出一個一般化的流程。

<p>作者／陳俊宏<br /><a href="http://www.jollen.org/">www.jollen.org</a> </p> <p><strong><font size="4" color="#ff0000">一般化設計流程</font></strong></p> <p>我們提過，依照驅動程式本身的實作，可以將 Linux 驅動程式分為 2 大部份：virtual device driver與physical device  driver。</p> <p><img width="640" height="475" border="0" src="http://www2.jollen.org/EmbeddedLinux/publish/LDD/04a.jpg" /></p> <p>此流程為一個觀念流程，實際撰寫驅動程式時，並不會完全以這個流程來設計。但初學Linux驅動程式時，則應以此流程為主循序學習，才能理解基本的Linux驅動程式實作。</p> <p>對Linux驅動程式而言，virtual device driver的重要性遠在physical device  driver之上，乍聽之下這或許不太能理解，因為沒有physical device driver是無法真正驅動硬體的。但實作上，physical device  driver是一成不變的程式寫法，能不能寫出好的驅動程式，關鍵是在virtual device driver的部份。</p> <p><strong><font size="4" color="#ff0000">struct file_operations</font></strong></p> <p>struct file_operations 是 kernel 提供的一個重要資料結構，這是學習 Linux  驅動程式第一個會認識的對象，也是最重要的一個主題。</p> <p>Linux 驅動程式建構在 file_operations 之上。file_operations定義驅動程式的system call與實作system  call的函數，我們把file_operations任何一個部份拿出來討論的話，都能切成virtual device driver與physical  device driver二個部份。</p> <p><strong><font size="4" color="#ff0000">流程解說</font></strong></p> <p>Virtual device driver往上是為了連結Linux kernel的VFS層，physical device  drvier往下是為了存取實體硬體。</p> <p><font color="#000080"><strong>Virtual Device Driver</strong></font></p> <p>Virtual device driver 的目的在於設計一個「機制」良好的kernel mode驅動程式，virtual device  driver也必須考慮與user application的互動。實作上，則是需要善用kernel所提供的介面（interface），即kernel  APIs。</p> <p>Virtual device driver再分為3階段的觀念實作：</p> <ol><li>定義 file_operations  </li><li>實作 system calls  </li><li>註冊 driver (VFS) </li></ol> <p>fops 是指向 file_operations 結構的指標，驅動程式呼叫 register_chrdev() 將fops註冊到 kernel  裡後，fops 便成為該 device driver 所實作的system call進入點。實作system  call的函數便是透過file_operations結構來定義，我們稱實作system call的函數為driver method。</p> <p>kernel 會在需要時回呼 (callback) 我們所註冊的driver method。因此，當 driver 裡的 method  被呼叫時，kernel便將傳遞參數（parameters）給 driver method，driver method可由 kernel  所傳遞進來的參數取得驅動程式資訊。</p> <p>註冊driver的動作呼叫register_chrdev()函數完成，此函數接受3個參數如下：</p> <ol><li>major：要註冊的裝置 major number  </li><li>name：device 名稱  </li><li>fops：driver 的 file operation </li></ol> <p>「註冊」這個動作觀念上是將fops加到kernel的VFS層，因此user application必須透過「device file」才能呼叫到driver  method。註冊這個動作的另一層涵意則是將driver method與不同的system call做「正確的對應」，當user  application呼叫system call時，才能執行正確的driver method。</p> <p><font color="#000080"><strong>Physical Device Driver</strong></font></p> <p>Physical device driver的目的在於實作控制硬體的程式碼。<u>Physical device driver  的設計必須隨時查閱晶片（chipsets）的 data sheet，並透過晶片的 control register 來控制裝置。</u></p> <p>理論上，我們可以將晶片的暫存器分成3大類：</p> <ol><li>data registers  </li><li>control registers  </li><li>status registers </li></ol> <p>Data register是晶片裡用來存放資料的暫存器，control register則是用來控制晶片行為的暫存器，status  register則保存目前晶片的狀態。設計控制硬體周邊的驅動程式時，需要了解硬體使用的晶片組，晶片組則需要參考IC設計廠商所提供的「datasheet」才能了解晶片組的暫存器名稱與用途，通常不同的暫存器會對應到一個「相對」的偏移位址（offset）。</p> <p>驅動程式則是要透過control  register才能控制晶片，因此需要隨時查閱晶片的datasheet，並了解每一個暫存器的用途。通常暫存器的每個位元（bit）也都是有特定用途的，因此設計驅動程式時，必須要很熟悉C語言的位元運算用法。</p> <p><u>實作上，首先會將晶片的 datasheet 寫成C語言的標頭檔，通常這個檔案都可以從 vendor 取得。</u></p> <p><u>接著再定義一組操作暫存器的I/O函數，我們稱這組函數為I/O wrapper function。</u>I/O wrapper  functions通常是重新定義Linux kernel所提供的readb()、writeb()或inb()、outb()系列函數所寫成的。</p> <p>最後，<u>利用I/O wrapper function實作一系列的控制函數，以控制實際硬體，我們稱此函數為chipset control  functions。</u>Chipset control functions是由實作system calls的函數（driver  method）所呼叫，因此在設計chipset control functions時也會回頭改寫driver method以符合此階段的實作。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/05/linux_4_linux.html</link>
         <guid>https://www.jollen.org/blog/2006/05/linux_4_linux.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 04 May 2006 09:06:23 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式觀念解析, #3: Device File</title>
         <description><![CDATA[Linux 驅動程式大架構的第二層為 VFS 層，由 VFS 所帶出來的重要觀念便是 device files。

<p>作者／陳俊宏<br /><a href="http://www.jollen.org/">www.jollen.org</a></p> 

<p><strong><font size="4" color="#ff0000">什麼是 Device File</font></strong></p> 

<p>Device files 是 UNIX 系統的獨特觀念，在 UNIX  系統底下我們把外部的周邊裝置均視為一個檔案，並透過此檔案與實體硬體溝通，這樣的檔案就叫做 device files，或 special files。</p> 

<p><img src="http://www2.jollen.org/EmbeddedLinux/publish/LDD/03a.jpg" /></p> 

<p>Linux device driver 與 user 的重要溝通橋梁為 device files，在 Linux 系統底下，我們看到的 device  files 如圖所示。檔案屬性的第一個位元如果顯示為 “c” 表示這是一個字元型裝置的 device file、若為 “b”  表示這是一個區塊型裝置的Device file。</p>

<p>Device file 的 major number 代表一個特定的裝置，例如 major number 為 1 為 null 虛擬裝置，major  number 定義於 kernel 文件目錄 Documentation/devices.txt 。Minor number  代表裝置上的子裝置，例如同一個硬碟上的分割區就用不同的 minor number 來代表，但其 major number 相同。</p>

<p><strong><font size="4" color="#ff0000">Device File 與驅動程式的關係</font></strong></p>

<p>我們在設計 device driver 時，會先透過一個 “註冊” (register) 的動作將自己註冊到 kernel 裡，註冊時，我們會指定一個  major number 參數，以指定此驅動程式所要實作的週邊裝置。當 user 開啟 device file 時，kernel 便會根據 device  file 的major number 找到對應的驅動程式來回應使用者。Minor number 則是 device driver 內部所使用，kernel  並不會處理不同的 minor number。</p>

<p>設計 device driver 的第一個步驟就是要定義 driver 所要提供的功能 (capabilities)，當 user application  呼叫open() system call 時，kernel 就會連繫相對應的 driver 來回應使用者。</p>

<p>file_operations 是學習 device driver 最重要的一個資料結構，file_operations內的成員為函數指標，指向  “system call 的實作函數”。file_operations 即是圖中的 VFS  層。換句話說，Linux驅動程式是透過file_operations來建構VFS層的支援。而file_operation裡的函數指標，即是指向每一個system  call的實作函數。</p>

<strong><p>變更紀錄</p></strong>
<ul>
<li>2006-12-21: 勝良兄反應了一個錯誤，已修正。</li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2006/05/linux_3_device_file.html</link>
         <guid>https://www.jollen.org/blog/2006/05/linux_3_device_file.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 04 May 2006 08:38:54 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式觀念解析, #2: System Calls</title>
         <description><![CDATA[Linux 驅動程式的大架構中，system call 是屬於第一層的架構；本文以一個 Linux 範例程式來展示 user application 與 Linux 驅動程式的關係。

<p>作者／陳俊宏<br /><a target="_blank" href="http://www.jollen.org/">www.jollen.org</a></p> <p><strong><font size="4" color="#ff0000">System Call 與驅動程式的關係</font></strong></p> <p>System call 是 user application 與 Linux device driver 的溝通介面。</p> <p>User application 透過呼叫 system call 來「叫起」driver 的 task，user application 要呼叫  system call 必須呼叫 GNU C 所提供的「wrapper function」，每個 system call 都會對應到 driver 內的一個  task，此 task 即是 file_operation 函數指標所指的函數。</p> <p> <img src="http://www2.jollen.org/EmbeddedLinux/publish/LDD/02a.jpg" /></p> <p>Linux 驅動程式與 user application 間的溝通方式是透過 system call，實際上 user application 是以  device file 與裝置驅動程式溝通。要達成此目的，驅動程式必須建構在此「file」之上，因此 Linux 驅動程式必須透過 VFS（virtual  file system）層來實作 system call。</p> <p><strong><font size="4" color="#ff0000">一個簡單的範例</font></strong></p> <p>/dev目錄下的檔案稱為device file，是 user application 用來與硬體裝置溝通的介面。以下是一個簡單的範例：</p> <blockquote><pre>int main(int argc, char *argv[])<br />{<br />    int devfd;<br /><br />    devfd = open("/dev/debug", O_RDONLY);<br /><br />    if (devfd == -1) {<br /> printf("Can't open /dev/debug\n");<br /> return -1;<br />    }<br /><br />    ioctl(devfd, IOCTL_WRITE, num);<br /><br />    close(devfd);<br /><br />    return 0;<br />}</pre></blockquote> <p>當我們打開/dev/debug檔案時，範例所呼叫open()函數會叫起支援/dev/debug驅動程式的對應函數；同理，我們對/dev/debug執行ioctl()函數時，也會叫起驅動程式的相對應函數。</p> <p>範例中的open()與ioctl()函數皆是GLIBC裡的函數，「叫起」驅動程式函數的動作涉及user space與kernel  space的切換，此動作藉由system  call介面來完成。設計一個支援”/dev/debug”裝置的驅動程式則是Linux驅動程式設計師所要負責的工作。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/05/linux_2_system_calls.html</link>
         <guid>https://www.jollen.org/blog/2006/05/linux_2_system_calls.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 04 May 2006 08:30:52 +0800</pubDate>
      </item>
            <item>
         <title>Linux 驅動程式觀念解析, #1: 驅動程式的大架構</title>
         <description><![CDATA[本文說明 Linux 驅動程式的整體大架構，並說明基本的三種驅動程式類型。了解 Linux 驅動程式的大架構，絕對是學好驅動程式的第一步。

作者／陳俊宏<br /><a href="http://www.jollen.org/">www.jollen.org</a><br /><br /><p><strong><font size="4" color="#ff0000">Linux 驅動程式的大架構</font></strong></p> <p>Linux 驅動程式的整體架構如下：</p> <ol><li>application 透過 system call 介面與 kernel 溝通。  </li><li>透過 kernel 的 VFS 層與 Linux 驅動程式物件溝通。  </li><li>Linux 驅動程式可分為 3 大類型，如下圖綠色部份。 </li></ol> <p><img width="640" height="386" border="0" src="http://www2.jollen.org/EmbeddedLinux/publish/LDD/01a.jpg" /></p> <p><strong><font size="4" color="#ff0000">Linux 驅動程式三類型</font></strong></p> <p>Linux device driver 可分成 3 種類型：</p> <ul><li>character device driver  </li><li>block device driver  </li><li>network device driver </li></ul> <p>驅動程式本身可分成 2 個層面來討論：</p> <ul><li>virtual device driver  </li><li>physical device driver </li></ul> <p><font color="#000080"><strong>Virtual Device Driver</strong></font></p> <p>往上層支援 Linux kernel 所提供的 Virtual File System 層，並藉此實作 system calls。使用者可透過  system call interface 與 device driver 溝通。</p> <p>Virtual device driver 的主題重要性大於 physical device driver，如何善用 Linux 所提供的介面  (interface) 來設計驅動程式，並配合 user application 來設計應用程式是這個主題的重點。與 user application  如何互動，是撰寫驅動程式時所要考慮的重要一環，只考量驅動程式本身的設計，而忽略或輕忽 user application 的設計，是錯誤的觀念。</p> <p>Virtual device driver 的目的在於善用 Linux 的 APIs 來設計機制 (mechanism) 與行為 (behavior)  良好的驅動程式，因此「觀念」的重要性遠大於「語法」的討論。本書秉持這樣的信念撰寫而成，仔細閱讀絕對是多多益善的。</p> <p><strong><font color="#000080">Physical Device Driver</font></strong></p> <p>往下層使用 Linux kernel 所提供的 device interface 來存取並控制實體硬體裝置。</p> <p>Physical device driver 則是討論「如何透過 I/O port 或 I/O  memory」來控制裝置，也就是與晶片組的溝通。這個部份需要實作晶片組的 data sheet，本書會以市面上最容易取得的 BT878  視訊擷取晶片為例做說明。</p> <p><strong><font size="4" color="#ff0000">小結</font></strong></p> <p>學習 Linux device driver 應由 character device driver 起步，因為許多重要的入門觀念均可藉由  character device driver 學得。</p> <p>OS 是設計良好的軟硬體介面，Linux 驅動程式設計即是在學習如何使用 Linux 提供的介面來設計驅動程式。</p> <p>在 OS 上應使用設計良好的 API 來撰寫驅動程式，使用 OS API 的最大優點是使得驅動程式的設計抽象化  (abstraction)，我們可以不需要太深入硬體層次。</p> <p>Linux kernel 所提供的 API 均經過良好設計，因此使用 kernel 所提供的 API 可以確保系統運行的安全性與穩定性。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/05/linux_1.html</link>
         <guid>https://www.jollen.org/blog/2006/05/linux_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Thu, 04 May 2006 08:15:29 +0800</pubDate>
      </item>
            <item>
         <title>Kernel 2.6 核心與模組編譯</title>
         <description><![CDATA[Jollen 的 Linux 驅動程式課程是以 kernel 2.4 做為實習環境，但 kernel 2.6 與 kernel 2.4 的核心與模組編譯步驟不同；本文說明如何在 kernel 2.6 環境下編譯核心與模組，供同學參考。

作者／陳俊宏<br />www.jollen.org<br /><br /><p><strong><font color="#ff0000" size="4">Kernel 2.4 &amp; 2.6 編譯</font></strong></p><p>底下用一個表格來比較編譯 kernel 2.4 與 kernel 2.6 的步驟差異。</p><span class="mcon"><table id="table4" width="478"><tr><td class="c" align="center" bgcolor="#c0c0c0"><strong>Kernel 2.4</strong></td><td class="c" align="center" bgcolor="#c0c0c0"><strong>Kernel 2.6</strong></td></tr><tr><td class="c" valign="top" bgcolor="#cccccc"><pre>1.編譯設定：</pre><pre># make menuconfig</pre><pre>2.編譯核心：</pre><pre># make dep </pre><pre># make bzImage</pre><pre>3.編譯模組並安裝：</pre><pre># make modules </pre><pre># make modules_install </pre></td><td class="c" valign="top" bgcolor="#cccccc"><pre><span class="mcon">1.編譯設定：</span></pre><pre># make menuconfig</pre><pre><span class="mcon">2.編譯核心與模組：</span></pre><pre># make</pre><pre><span class="mcon">3.安裝：</span></pre><pre># make modules_install install</pre></td></tr></table></span><p><strong><font color="#ff0000" size="4">Kernel 2.6 模組編譯</font></strong></p><p>kernel 2.6 的模組「模組化」程度相當高，因此在 kernel 2.6 環境下編譯模組時，是以 sub-module 的觀念來實施，與 kernel 2.4 的獨立編譯觀念不同。首先，舉我們的 debug card 範例來說明編譯 kernel 2.6 模組的方式；接著再說明安裝 kernel 2.6 模組的方法。</p><table id="table5" cellspacing="0" cellpadding="0" width="100%" border="0"><tr><td valign="top" bgcolor="#ffffff">1. 先撰寫編譯模組的 Makefile 如下：</td></tr></table><blockquote>obj-m := card.o<br /><br />KDIR := /lib/modules/$(shell uname -r)/build<br />PWD := $(shell pwd)<br /><br />default:<br />$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules</blockquote><p>2. 編譯：</p><blockquote><p># make</p></blockquote><p>編譯後可以得到 card.ko，這才是真正的 kernel 2.6 模組；kernel 2.6 模組的副檔名為 "*.ko"。</p><p>3. 將 card.ko 複制到模組目錄下：</p><blockquote><p># cp card.ko /lib/modules/`uname -r`/kernel/drivers/</p></blockquote><p>4. 重新產生模組的 "dependency descriptions"：</p><blockquote><p># depmod -a</p></blockquote><p>5. 安裝模組：</p><blockquote><p># insmod ./card.ko</p></blockquote><p>或是：</p><blockquote><p># modprobe card</p></blockquote><p>同樣是利用 insmod 或 modprobe 指令將 card.ko 安裝至系統即可。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/05/kernel_26.html</link>
         <guid>https://www.jollen.org/blog/2006/05/kernel_26.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Tue, 02 May 2006 11:08:20 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 的系統管理,#4: 網路設定</title>
         <description><![CDATA[課程專案中，我們會在載入網路卡驅動程式後，再手動設定網路組態。在 Embedded Linux 系統中，我們會以命令列方式來設定網路的組態。

作者／陳俊宏<br />www.jollen.org<br /><br /><p><strong><font size="4" color="#ff0000">ifconfig 指令</font></strong></p> <p>ifconfig 指令是Linux網路組態設定的主要系統管理命令。以下我們說明如何手動設定這些網路組態：</p> <ul><li>IP  </li><li>gateway  </li><li>netmask  </li><li>DNS  </li><li>broadcast </li></ul> <p><strong><font color="#0000ff">設定 IP</font></strong></p> <p>例如，要將系統的 IP 設定為 59.120.22.215，則執行以下命令：</p> <blockquote> <p>linux# ifconfig eth0 59.120.22.215</p></blockquote> <p><u>同時請將 loopback device 也加上去：</u></p> <blockquote> <p>linux# ifconfig lo 127.0.0.1</p></blockquote> <p>直接執行 'ifconfig' 不加任何參數的話，可以看到目前的網路組態設定：</p> <blockquote><pre><strong>eth0</strong> Link encap:Ethernet HWaddr 00:13:D4:C4:0D:ED<br />inet addr:59.120.22.215 Bcast:59.120.22.255 Mask:255.255.255.0<br />UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1<br />RX packets:1768542 errors:0 dropped:0 overruns:0 frame:0<br />TX packets:2411565 errors:0 dropped:0 overruns:0 carrier:0<br />collisions:0 txqueuelen:100<br />RX bytes:216276166 (206.2 Mb) TX bytes:2221236364 (2118.3 Mb)<br /><br /><br /><strong>lo</strong> Link encap:Local Loopback<br />inet addr:127.0.0.1 Mask:255.0.0.0<br />UP LOOPBACK RUNNING MTU:16436 Metric:1<br />RX packets:453762 errors:0 dropped:0 overruns:0 frame:0<br />TX packets:453762 errors:0 dropped:0 overruns:0 carrier:0<br />collisions:0 txqueuelen:0<br />RX bytes:959840798 (915.3 Mb) TX bytes:959840798 (915.3 Mb)</pre></blockquote> <p>lo 是所謂的 loopback 界面，eth0 就是我們剛剛設定好的乙太網路界面，有了這層界面後，才能連上  Internet。<u>這個動作必須在成功載入網路卡的驅動程式後才能執行。</u></p> <p>別忘了，我們還沒替 eth0 界面設定好 gateway 與broadcast。gateway  是閘訊通，正確設定閘訊通才能連接外面的網路，否則只能連接區域網路內的主機，broadcast  是廣播位址，有了這個位址才能廣播封包到區域網路內的所有主機，例如要設定 gateway 與 broadcast 分別為 59.120.22.254 與  59.120.22.255。</p> <p><strong><font color="#0000ff">設定 broadcast</font></strong></p> <p>執行：</p> <blockquote> <p>linux# ifconfig eth0 broadcast <strong>59.120.22.255</strong></p></blockquote> <p>再執行 ifconfig 就可以發現 eth0 的 broadcast 位址已經被改變了。</p> <p><font color="#0000ff"><strong>設定 Netmask</strong></font></p> <p>例如，要將 eth0 的 netmask 設定成 255.255.255.0，則執行：</p> <blockquote> <p>linxu# ifconfig eth0 <strong>netmask</strong> 255.255.255.0</p></blockquote> <p><strong><font size="4" color="#ff0000">其它組態的設定方法</font></strong></p> <p><strong><font color="#0000ff">設定 Gateway</font></strong></p> <p>修改 Gateway 設定：</p> <blockquote> <p>linux# route del default <br />linux# route add default gw  59.120.22.254</p></blockquote> <p>我們先刪除 default 再加入新的 gateway 位址，不過這個刪除的動作也可以省略不做。</p> <p>此時我們已經加入了一個預設的 gateway，直接執行 'route' 不加任何參數，便能看到目前的 gateway 設定：</p> <blockquote><pre>linux# route<br />Kernel IP routing table<br />Destination Gateway Genmask Flags Metric Ref Use Iface<br />59.120.22.0 * 255.255.255.0 U 0 0 0 eth0<br />169.254.0.0 * 255.255.0.0 U 0 0 0 eth0<br />127.0.0.0 * 255.0.0.0 U 0 0 0 lo<br />default 59-120-22-254.H 0.0.0.0 UG 0 0 0 eth0</pre></blockquote> <p><strong><font color="#0000ff">設定 DNS</font></strong></p> <p>最後要設定的是 DNS，DNS 負責 Domain Name與 IP 位址之間的轉換，例如我們打 embedded.jollen.org，經由查詢  DNS，得知其 IP 位址為 59.120.22.215，有了 IP 位址才得以和遠端的主機連線，因此正確設定一台可用的 DNS是很重要的。</p> <p>設定 DNS 的方法很簡單，只要編輯 /etc/resolv.conf 即可，以下是一個範例：</p> <blockquote><pre>search jollen.org <br />nameserver 163.14.192.1   #第一個 nameserver 意同 primary DNS<br />nameserver 168.14.1.1     #第二個 nameserver 意同 secondary DNS</pre></blockquote> <p>第一行是設定網域名稱搜尋，第二、三行則是設定 DNS server。search 通常用在區域網路，例如我們的主機位於 jollen.org  的網域，則設定 search jollen.org 的話，只要打主機名稱：</p> <blockquote> <p>linux# telnet www</p></blockquote> <p>效果跟</p> <blockquote> <p>linux# telnet www.jollen.org</p></blockquote> <p>是相同的。如果設定多個 domain 的話，則依序搜尋到一個合法的位址為止，例如：</p> <blockquote> <p>search jollen.org xxx.com</p></blockquote> <p>若 telnet www 的話，會先找 www.jollen.org，如果失敗會接著找 www.xxx.com。</p> <p>nameserver 是設定 DNS，這裡的 DNS 必須是 IP 位址，系統會依序向這些名稱伺服器做查詢，向第一個 DNS 做查詢若失敗，則往下向第二個  DNS 做查詢，依此類推。</p> <p><strong><font color="#0000ff">/etc/hosts</font></strong></p> <p>我們可以把常用的 domain name 編輯到 /etc/hosts 裡，然後設定好 host.conf，如此一來當我們使用這些常用的 domain  name 時就可以不必再經由 DNS 的查詢程序，進而提升速度，例如以 www.jollen.org 為例，只要在 /etc/hosts 加上：</p> <blockquote> <p>59.120.22.215 www.jollen.org jollen</p></blockquote> <p>這樣一來執行 "telnet jollen" 就等於是 "telnet 59.120.22.215"。</p> <p><strong><font color="#0000ff">/etc/hosts.conf</font></strong></p> <p>設定好 /etc/hosts 後還要檢查一下 /etc/hosts.conf 的設定，確定名稱查詢時會先查詢 /etc/hosts，請檢查  /etc/hosts.conf 是否有一行：</p> <blockquote> <p>order hosts,bind</p></blockquote> <p>表示先查詢 /etc/hosts，失敗的話再向 DNS 主機查詢。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/04/embedded_linux_4.html</link>
         <guid>https://www.jollen.org/blog/2006/04/embedded_linux_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 24 Apr 2006 16:20:30 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 的系統管理,#3: initial scripts</title>
         <description><![CDATA[initial scripts 是在開機時自動執行的 script，本文說明 Red Hat Linux 的 init script 概念，在我們的 Embedded Linux / x86 以及 Embedded Linux / ARM9 的課程中，同學會看到 /etc/inittab 配合 init script 的實作方法。

作者／陳俊宏<br />www.jollen.org<br /><br /><p><strong><font size="4" color="#ff0000">/etc/rc.d/ 概念</font></strong></p> <p>請同學參照 Red Hat Linux 的系統檔案來閱讀本文，我們的課程實作與 Red Hat Linux 的 init script  寫法頗有差異，但研讀 Red Hat Linux 的 init script 架構可以學習到完整的 init script 概念。同時，Red Hat  Linux 的 init script 也是符合 LSB 標準的做法。</p> <p>/etc/rc.d/ 裡的檔案在開機時，會由 init 來執行，因此，我們可以把一些設定編輯在裡面，在開機時自動做好設定，這些文字的執行檔，我們稱它為  script，又因為是開機時會執行的 script，所以也稱為 bootup scripts。</p> <p>/etc/rc.d/rc?.d/ 裡的檔案都鍊結到 /etc/rc.d/init.d/ 裡，因此，我們只要針對/etc/rc.d/init.d/  裡的檔案來做討論即可。</p> <p>init 會根據 /etc/inittab 裡的預設 run level 來執行 /etc/rc.d/rc?.d/ 目錄裡的鍊結，例如我們在  /etc/inittab 預設 run level為 3：</p> <blockquote> <p>id:3:initdefault:</p></blockquote> <p>則 init 會去執行 /etc/rc.d/rc3.d/ 目錄裡的檔案，指令 telinit 可以改變系統的 run-level，例如：</p> <blockquote> <p>linux# telinit 5</p></blockquote> <p>表示將系統的 run level 改變成 5，則 init 在看到 run level 的改變時，就會去執行/etc/rc.d/rc5.d/  裡的檔案。</p> <p><strong><font color="#0000ff">/etc/rc.d/rc?.d 檔案格式</font></strong></p> <p>/etc/rc.d/rc?.d 裡的檔案格式都具有特殊義意，例如：</p> <blockquote> <p>S17httpd<br />K18httpd</p></blockquote> <p>"S" 表示 start，"K" 表示 stop (kill)，17 是優先序，數字低的先執行，httpd 則是表示這個檔案鍊結到  /etc/rc.d/init.d/httpd，因此 S17httpd 是「第 17 順位執行httpd」的意思，同理，K18httpd 則代表「第 18  順位關閉  httpd」，優先序是為了控制程式執行的順序，因為有一些程式會要求必須先載入一些特定程式才能執行，所以就利用優先序來控制，另外，優先序並不一定要是連續的數字，但執行時一定是由小而大執行，例如  S15httpd 執行完後，可能不會有 S16 的順序，所以就繼續往下執行。</p> <p>/etc/rc.d/init.d 裡的 scripts 還提供一些參數，例如 Apache web server 的 script 為  /etc/rc.d/init.d/httpd，除了在開機時會自動被執行外，在命令模式下執行：</p> <blockquote> <p>linux# /etc/rc.d/init.d/httpd stop</p></blockquote> <p>表示要終止 httpd (Apache web server) 的執行，Red Hat Linux 的 init script  實作可以讓使用者下達命令參數，此部份亦制定於 LSB 標準中。</p> <p>例如，/etc/rc.d/init.d 裡的 script 提供了 6 種參數，但要注意的是，並不是所有的 script 都提供所有的參數，這 6  個參數為：</p> <ul><li>start  </li><li>stop  </li><li>restart  </li><li>force-reload  </li><li>reload  </li><li>status </li></ul> <p>範例：</p> <blockquote> <p>linux# /etc/rc.d/init.d/httpd restart<br />linux# /etc/rc.d/init.d/httpd  start</p></blockquote> <p>這二個指令分別是重新啟動及啟動 httpd 的意思。當我們修改 Apache 的設定後，就可以下第一個命令來重新啟動 httpd，使新的設定生效。</p> <p>再以一個例子說明，當我們想要關閉防火牆時，應該把 iptables 服務關閉，此時可以下達此命令：</p> <blockquote> <p>linux# /etc/rc.d/init.d/iptables <strong>stop</strong></p></blockquote>]]></description>
         <link>https://www.jollen.org/blog/2006/04/embedded_linux_3_initial_scrip.html</link>
         <guid>https://www.jollen.org/blog/2006/04/embedded_linux_3_initial_scrip.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 24 Apr 2006 15:53:58 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 的系統管理,#1: 重要設定檔</title>
         <description><![CDATA[學習 Embedded Linux 需要一定的 Linux 系統管理能力，Jollen 所設計的「Embedded Linux / x86」便是在教授這樣的主題。本系列文章是要給來上Embedded Linux / x86 課程的學員在家先修的。

作者／陳俊宏<br />www.jollen.org<br /><br /><p><strong><font size="4" color="#ff0000">重要設定檔</font></strong></p> <p>為了能更了解 Linux 整個系統，學習 Linux 的第一步，必須先把幾個常用的設定檔研究一下。底下整理出幾個常用檔案。</p> <p><font color="#0000ff"><strong>系統管理相關設定檔</strong></font></p> <p>□ /etc/passwd</p> <p>使用者帳號檔案。</p> <p>□ /etc/shadow</p> <p>儲存只有 root 才能讀取的帳號資訊，例如編碼過的密碼字串。</p> <p>□ /etc/group</p> <p>使用者群組檔案。</p> <p>系統的定時裝置，可以讓系統在特定的時間或時間間隔執行指定的工作。</p> <p>□ /etc/inittab <sup><strong><font color="#ff0000">(must have!)</font></strong></sup></p> <p>設定開機時要啟動的 service 與 process。</p> <p>□ /etc/fstab  <sup><strong><font color="#ff0000">(must have!)</font></strong></sup></p> <p>設定分割區與 mount 路徑的檔案。</p> <p>□ /etc/termcap</p> <p>各種不同終端機、印表機的字元定義。</p> <p><strong><font color="#0000ff">網路相關設定檔</font></strong></p> <p>□ /etc/hosts</p> <p>存放 IP 與host name 對應表。</p> <p>□ /etc/hosts.allow </p> <p>設定允許連線的 host。</p> <p>□ /etc/hosts.deny </p> <p>設定拒絕連線的 host。</p> <p>□ /etc/resolv.conf</p> <p>設定 DNS 主機。</p> <p>□ /etc/inetd.conf </p> <p>設定 inetd daemon 服務，也就是電腦所提供 TCP/IP 服務。<u>Busybox 仍然提供舊式的 inetd 服務，Embedded  Linux 較適合使用舊式的 inetd.conf 來實作。</u></p> <p>Linux系統的檔案結構原則上應依照 Linux Standard Base 所制定的標準實作。</p>]]></description>
         <link>https://www.jollen.org/blog/2006/04/embedded_linux_1.html</link>
         <guid>https://www.jollen.org/blog/2006/04/embedded_linux_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Mon, 24 Apr 2006 15:26:08 +0800</pubDate>
      </item>
            <item>
         <title>Embedded Linux 的系統管理,#2: /etc/fstab 與 /etc/inittab</title>
         <description><![CDATA[介紹 2 個重要的系統設定檔，這 2 個設定檔是最小化 Linux 能順利啟動的關鍵。

作者／陳俊宏<br />www.jollen.org<br /><br /><p><strong><font size="4" color="#ff0000">/etc/fstab 的用途</font></strong></p> <p>/etc/fstab 是設定分割區與 mount 目錄關係的檔案，開機時會依這個檔案的內容 mount 檔案系統，mount 檔案系統的命令為：</p> <blockquote> <p>linux# mount -av</p></blockquote> <p>會依 /etc/fstab 的內容 mount 進除了 root 以外的所有檔案系統。</p> <p>root 是在開機時由核心所 mount 進來的。<u>root 先由核心 mount 成 read-only，然後由使用者手動 remount 成  read-write。</u></p> <p><strong><font color="#0000ff">/etc/fstab 設定格式</font></strong></p> <p>一個簡單的 /etc/fstab 設定範例如下：</p> <blockquote><pre>#Device mount point filesystem type option<br />LABEL=/ / ext2 defaults 1 1<br />/dev/sda5 /home ext2 defaults 1 2<br />/dev/cdrom /mnt/cdrom iso9660 noauto,owner,ro 0 0<br />/dev/fd0 /mnt/floppy auto noauto,owner 0 0<br />LABEL=/usr /usr ext2 defaults 1 2<br />none /proc proc defaults 0 0<br />none /dev/pts devpts gid=5,mode=620 0 0<br />/dev/sda7 swap swap defaults 0 0 </pre></blockquote> <p>每個欄位所代表的義意為：</p> <ul><li>device 指定分割區  </li><li>mount point 指定要 mount 目錄  </li><li>filesystem type 指定檔案系統，比較常用的檔案系統有： </li></ul> <blockquote> <p>‧ msdos<br />‧ vfat<br />‧ nfs<br />‧ swap<br />‧ ext2</p></blockquote> <p>使用這些檔案系統前，請先確認核心是否支援，要知道目前核心的狀態支援那些檔案系統，可以查看 /proc/filesystems：</p> <blockquote><pre>ext2<br />nodev proc<br /><br />iso9660<br />nodev devpts<br />nodev usbdevfs </pre></blockquote> <p>表示目前核心支援 ext2/proc/iso9660/devpts/usbdevfs 的檔案格式，其中proc、devpts 與 usbdevfs  前面有一個 nodev 關鍵字，表示這二個檔案系統其實是存在記憶體中的，並非真正存在於儲存裝置中。</p> <ul><li>option 設定參數，如果我們要 mount 的是硬碟的分割區，不管是什麼檔案系統，只要設定為 defaults 即可。  </li><li>第一個數字，0 或者空白表示不要 dump，1 表示要 dump，dump 是用來將系統備份到磁帶機的指令，備份時，如果想要略過這個分割區，在一欄填 0  即可，如困2這一欄是 1 的話， dump 時就會備份這個分割區。  </li><li>第二個數字，是設定當系統有不正常關機情形時，要如何檢查這些檔案系統。首先 fsck 會依序檢查這一欄為 1 的檔案系統，然後同時檢查這一欄為 2  的檔案系統，0 或者空白的話表示不做檢查。一般而言，只會將 root 設定為 1，其它都設定為 0 或 2。一些特定的檔案系統，像是  swap、iso9660、nfs、proc 都設定為0。 </li></ul> <p>proc 是比較特殊的檔案系統，/proc 其實是存在於記憶體中的，/proc  裡的檔案紀錄了目前系統的狀態，藉由觀察這裡面的檔案我們可以得知許多系統的資訊。</p> <p>例如，要檢查目前 swap partition 的狀態可以查看 /proc/swaps：</p> <blockquote><pre>Filename Type Size Used Priority<br />/dev/sda7 partition 136512 4648 -1 </pre></blockquote> <p>最後舉個例子，例如為電腦新增了一顆硬碟，然後切出了一個 /dev/sdb8 的 partition，並且想將這個 partition 做來做 ftp 的  home，這時就要在 /etc/fstab 裡加上一行：</p> <blockquote><pre>/dev/sdb8 /home/ftp ext2 defaults 1 2</pre></blockquote> <p><strong><font size="4" color="#ff0000">/etc/inittab 設定</font></strong></p> <p><strong><font color="#0000ff">init process</font></strong></p> <p>init 是在核心 mount 進 root 後，第一個執行的程式 (第一個 process)，init 所負責的工作包括：</p> <ul><li>執行 /etc/rc.d/rc?.d/ 裡的 scripts  </li><li>將系統由開機狀態 (boot-up state) 切換至多使用者狀態 (multiuser state)  </li><li>負責載入 tty (本地端) 的 login shell  </li><li>負責開機與關機的程序 (procedure)。 </li></ul> <p>開機完成後，init 仍會保持在背景執行，並同時監視與修改系統執行狀態。init 監視許多系統的動作，適時改變系統的狀態，這些動作定義在  /etc/inittab 檔案裡。系統狀態劃分成七個等級，稱之為 run level，/etc/inittab 即告訴 init，在不同的 run level  下所要執行的程式與命令，Linux 下的 run level 有共七個，分別為：</p> <ul><li>run level 0 關機  </li><li>run level 1 單一使用者模式  </li><li>run level 2 多使用者模式(沒有NFS)  </li><li>run level 3 多使用者模式  </li><li>run level 4 保留  </li><li>run level 5 X11  </li><li>run level 6 重開機 </li></ul> <p><strong><font color="#0000ff">以 Red Hat Linux 的 /etc/inittab 為例</font></strong></p> <p>不正確的 /etc/inittab 設會使得系統不正常，而且無法登入，所以修改 /etc/inittab 前請先做好備分，/etc/inittab  的設定如下：</p> <blockquote><pre>#<br /># inittab This file describes how the INIT process should set up<br /># the system in a certain run-level.<br /><br />#<br /># Author: Miquel van Smoorenburg, &lt;miquels@drinkel.nl.mugnet.org><br /># Modified for RHS Linux by Marc Ewing and Donnie Barnes<br /><br />#<br /># Default runlevel. The runlevels used by RHS are:<br /># 0 - halt (Do NOT set initdefault to this)<br /># 1 - Single user mode<br /># 2 - Multiuser, without NFS (The same as 3, if you do not have networking)<br /># 3 - Full multiuser mode<br /># 4 - unused<br /># 5 - X11<br /># 6 - reboot (Do NOT set initdefault to this)<br /><br /># <br /><br />id:3:initdefault:<br /><br /># System initialization.<br /><br />si::sysinit:/etc/rc.d/rc.sysinit<br /><br /><br />l0:0:wait:/etc/rc.d/rc 0<br />l1:1:wait:/etc/rc.d/rc 1<br />l2:2:wait:/etc/rc.d/rc 2<br />l3:3:wait:/etc/rc.d/rc 3<br />l4:4:wait:/etc/rc.d/rc 4<br />l5:5:wait:/etc/rc.d/rc 5<br />l6:6:wait:/etc/rc.d/rc 6<br /><br /><br /><br /># Things to run in every runlevel.<br /><br />ud::once:/sbin/update<br /><br /><br /><br /># 處理 [ctrl]-[alt]-[del] 的按鍵<br />ca::ctrlaltdel:/sbin/shutdown -t3 -r now<br /><br /><br /><br /># 如果我們有 UPS，這一行的設定會在斷電時通知我們，並在 2 分鐘後自動關機。 <br />pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"<br /><br /><br /><br /># 如果在關機前恢復電力，則放棄關機。<br />pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"<br /><br /><br /><br /><br /><br /># 用來登入系統 (本地端)。<br />1:2345:respawn:/sbin/mingetty tty1<br />2:2345:respawn:/sbin/mingetty tty2<br />3:2345:respawn:/sbin/mingetty tty3<br />4:2345:respawn:/sbin/mingetty tty4<br />5:2345:respawn:/sbin/mingetty tty5<br />6:2345:respawn:/sbin/mingetty tty6<br /><br /><br /><br /># gdm 程式。<br />x:5:respawn:/etc/X11/prefdm -nodaemon</pre></blockquote> <p>每個欄位所代表的義意如下：</p> <ul><li>第一個欄位，是 init 內部使用的名稱，用來辨認每一個項目，執行命令 "ps ax" 所看到的名稱。  </li><li>第二個欄位，表示這個項目的 run level。  </li><li>第三個欄位，關鍵字欄位，其中 respawn 是一個重要的關鍵字。以 "5:2345:respawn:/sbin/mingetty tty5"  為例說明，init 會依據設定，在本地端開啟一個終端機，當這個終端機超過時間沒有人 login 或太久沒有按鍵時，這個終端機就會結束執行，respawn  即在告訴 init 再重新執行這個終端機，否則，在一段時間後，我們會發現這個終端機消失了，無法利用 [alt]-[f5] 切換終端機。  </li><li>第四個欄位：要執行的命令與參數 </li></ul> <p>第一行的 "id:3:initdefault:" 是很重要的一行，用來告訴 init，預設的 run level，在這裡我們設定開機後的預設 run  level 為 3，那麼下面設定的命令都會被執行：</p> <blockquote><pre>l3:3:wait:/etc/rc.d/rc 3<br /><br />ud::once:/sbin/update<br />ca::ctrlaltdel:/sbin/shutdown -t3 -r now<br />pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"<br />pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"<br />1:2345:respawn:/sbin/mingetty tty1<br />2:2345:respawn:/sbin/mingetty tty2<br />3:2345:respawn:/sbin/mingetty tty3<br />4:2345:respawn:/sbin/mingetty tty4<br />5:2345:respawn:/sbin/mingetty tty5<br />6:2345:respawn:/sbin/mingetty tty6</pre></blockquote> <p>第二個如果是空白，則表示在所有的 run level 都會執行，例如：</p> <blockquote> <p>ca::ctrlaltdel:/sbin/shutdown -t3 -r now</p></blockquote> <p>表示在按下 [ctrl]-[alt]-[del] 的事件發生時，在所有 run level 狀態下，都會去執行/sbin/shutdown -t3 -r  now 的命令。這一欄也可以指定多個 run level，例如：</p> <blockquote> <p>6:2345:respawn:/sbin/mingetty tty6</p></blockquote> <p>表示在進入 run level 2/3/4/5 時，都會執行 "/sbin/mingetty tty6"。</p> <p><font color="#0000ff"><strong>改變 run level</strong></font></p> <p>如果我們將預設 run level 改為 5：</p> <blockquote> <p>id:3:initdefault:</p></blockquote> <p>那麼：</p> <blockquote> <p>x:5:respawn:/etc/X11/prefdm -nodaemon</p></blockquote> <p>這一行便會生效，"/etc/X11/prefdm -nodaemon" 的命令就會被執行，也就是我們熟悉的gdm。</p> <p>我們可藉由 telinit 指令來改變目前的 run level，例如在 run level 3 狀態下執行：</p> <blockquote> <p>linux# telinit 5</p></blockquote> <p>就會馬上進入 run level 5，執行 gdm。在 gdm 下如果想回到 console  模式，可以按下[ctrl]-[alt]-[f1]，後然再執行：</p> <blockquote> <p>linux# telinit 3</p></blockquote> <p>即可。</p>]]></description>
         <link>https://www.jollen.org/blog/2005/09/embedded_linux_2_etcfstab_etci.html</link>
         <guid>https://www.jollen.org/blog/2005/09/embedded_linux_2_etcfstab_etci.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Sun, 04 Sep 2005 11:06:51 +0800</pubDate>
      </item>
            <item>
         <title>Linux Kernel Porting #2, 基本流程與方法</title>
         <description>Copyright (c) 2005 www.jollen.org
Last date: 2005/01/17

在將 Linux kernel porting 到其它的 ARM 平臺之前, 必須先取得標準 Linux kernel,然後再做 ARM Linux patch。

下載以下檔案:

ftp://ftp.arm.linux.org.uk/pub/armlinux/source/kernel-patches/v2.4/patch-2.4.26-vrs1.gz
ftp://ftp.nsysu.edu.tw/Linux/Kernel/linux/kernel/v2.4/linux-2.4.26.tar.bz2

安裝 Linux kernel 與 patch:

# bzip2 -dc linux-2.4.26.tar.bz2 | tar xf -
# mv linux-2.4.26 linux-2.4.26-vrs1
# cd linux-2.4.26-vrs1
# gzip -dc ../patch-2.4.26-vrs1.gz | patch -p1 -

本文以 KS8695 板子為實例,說明如何將 kernel 2.4.26 (vrs1) porting 到其它的 ARM 平臺。對於所有的 ARM 平臺而言,我們可以歸納出 3 個基本的 porting 流程如下:

1. 登錄一個 Machine ID
2. 編輯 config.in 設定檔與 Makefile.

linux/arch/arm/config.in
arch/arm/Makefile
arch/arm/boot/Makefile
arch/arm/mach-XXX/Makefile

3. 修改原始檔如下.

arch/arm/kernel/entry-armv.S
arch/arm/kernel/debug-armv.S
arch/arm/mach-XXX/arch.c
arch/arm/mach-XXX/irq.c
arch/arm/mach-XXX/mm.c
include/asm/arch/dma.h
include/asm/arch/hardware.h
include/asm/arch/io.h
include/asm/arch/irq.h
include/asm/arch/irqs.h
include/asm/arch/keyboard.h
include/asm/arch/memory.h
include/asm/arch/param.h
include/asm/arch/system.h
include/asm/arch/timex.h
include/asm/arch/uncompress.h
include/asm/arch/vmalloc.h

以上是 porting Linux 到其它 ARM 平臺的大原則與方法。</description>
         <link>https://www.jollen.org/blog/2005/01/linux_kernel_porting_2.html</link>
         <guid>https://www.jollen.org/blog/2005/01/linux_kernel_porting_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Mon, 17 Jan 2005 16:13:20 +0800</pubDate>
      </item>
            <item>
         <title>Linux Kernel Porting #1, 基本概念</title>
         <description>(原文發表於 2005/01/17)
作者／陳俊宏
Copyright (c) 2005 www.jollen.org
Last date: 2005/01/17

理論上，一個完整的 Embedded Linux 系統開機過程可分成 3 個階段:

bootstrap loader
boot loader
linux kernel

bootstrap loader 比較像是 PC 上的 BIOS,大部份的target device,像是evaluation board、reference board等,都是在設計板子時由廠商設計完成並提供(ship)給終端使用者(end-user)。

Boot loader 則是像 desktop Linux 上的 LILO、GRUB 等,支援 ARM 的 boot loader 像是比較知名的 RedBoot 等都可以用在 ARM 板子上,一般 boot loader 都會提供 serial port 傳輸與 MTD 支援,讓我們可以燒錄資料到 ROM (flash) 裡。本文會以KS8695 (ARM9) 板子來當例子,這張板子上的 boot loader 還支援 ethernet 傳輸,可以讓我們透過網路將資料直接燒錄到 ROM 裡面。

* 可以參考筆者所著”Embedded Linux 嵌入式系統—原理與實務 (2e),學貫”第6 章有關 Linux 在 PC (x86) 上的開機流程講解。

我們主要所要講解的部份為 linux kernel 本身的 porting。再次,就理論上,對於linux kernel本身的porting可以分為2個層面來討論:

Microprocessor architecture level
Board support package (BSP) level

Microprocessor architecture level 主要的工作是讓 Linux kernel 能支援不同的處理器架構,例如:PowerPC、ARM、MIPS...等等。Microprocessor 又可分為 MMU 與 MMU-less 的架構,前者可使用標準的 Linux kernel 來 porting,後者可使用 uClinux 做為解決方案。Linux kernel 本身 (包含 uClinux) 對於微處理器架構方面的 porting 工作都已經完成了! 所以我們沒有可以發揮的空間,因此 BSP level 的 porting 才是目前做 kernel porting 最主要的工作。

我們以 KS8695 板子為例來做說明。Board support package 故名思義就是要修改 kernel 讓 kernel 可以支援我們所設計的板子,我們所使用的板子採用 KENDIN KS8695 SoC,這顆 SoC 是 ARM9TDMI 的架構,因此我們要先對 Linux kernel 做 ARM Linux 的 patch,讓 Linux kernel 能完整支援 ARM 微處理器。

接著,就要修改 patch 好的 Linux kernel (ARM9) 讓 kernel 可以支援 KS8695 這顆 SoC。也就是基於支援 ARM9 (armv) 的 Linux kernel 來加入 (porting) 對 KS8695 SoC 的支援,即進行 KS8695 的 board support package level porting。

針對 ARM (with MMU) 的 BSP porting 工作,是存在一個標準的操作流程的! 在本文#2中將會介紹此 porting 流程。</description>
         <link>https://www.jollen.org/blog/2005/01/linux_kernel_porting_1_1.html</link>
         <guid>https://www.jollen.org/blog/2005/01/linux_kernel_porting_1_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Linux Device Drivers &amp; Kernel</category>
        
        
         <pubDate>Mon, 17 Jan 2005 16:11:38 +0800</pubDate>
      </item>
            <item>
         <title>Build uClibc for ARM</title>
         <description>作者／陳俊宏
Copyright (c) 2004 www.jollen.org
Last date: 2004/08/11

本文示範如何建立支援ARM9的uClibc。筆者所使用的target device為KS8695的板子,microprocessor是KENDIN的ARM9TDMI。

   1. 下載uClibc-0.9.20. 因為某些原因,我們 使用0.9.20的 版本,請 不要下載其它的板本,以 免無法順利編譯。下載後的 uClibc-0.9.20 必須做小部份修改,因此 請讀者直接下載筆者修改好的uClibc。

      http://www.jollen.org/EmbeddedLinux/ARM/uClibc-0.9.20-xwire.tar.gz

   2. 解開uClibc後並進入設定選單:

      # tar zxvf uClibc-0.9.20-xwire.tar.gz
      # cd uClibc-0.9.20-xwire
      # make menuconfig

      接著直接離開並儲存設定即可,筆者 修改過的版本預設值支援KS8695板子。

   3. 修改 Linux kernel 路徑設定。將 .config 檔裡的 KERNEL_SOURCE 設定改到正確的 kernel 路徑:

      KERNEL_SOURCE=&quot;/home/xwire/linux-2.4.26-vrs1&quot;

      編譯 uClibc 時會參考到 kernel 的標頭檔,因此必須對標準的 kernel 做 ARM patch 以及 KS8695 board-specific 的 PATCH。讀者可先下載筆者提供的標頭檔套件，以順利編譯 uClibc。

   4. 編譯uClibc:

      # make

   5. 安裝uClibc的開發環境:

      # make install PREFIX=/usr/local/uClibc-0.9.20

   6. 安裝uClibc的執行環境:

      # make install_target TARGET_PREFIX=/home/rootfs/lib

      請將路徑指到正確的filesystem (ARM9) 路徑,由於我們開啟shared library的 功能,因 此必須安裝runtime環 境至target的filesystem裡。</description>
         <link>https://www.jollen.org/blog/2004/08/build_uclibc_for_arm.html</link>
         <guid>https://www.jollen.org/blog/2004/08/build_uclibc_for_arm.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 11 Aug 2004 16:10:42 +0800</pubDate>
      </item>
            <item>
         <title>建立 Cross GNU Toolchains for ARM</title>
         <description>作者／陳俊宏
Copyright (c) 2004 www.jollen.org
Last date: 2004/08/11

   1. 下載以下套件:

      gcc-3.4.1.tar.bz2
      newlib-1.12.0.tar.gz
      binutils-2.15.tar.bz2

   2. 解開套件:

      # bzip2 -dc gcc-3.4.1.tar.bz2 | tar xf -
      # tar zxvf newlib-1.12.0.tar.gz
      # bzip3 -dc binutils-2.15.tar.bz2 | tar xf -

   3. 建立編譯用目錄:

      # mkdir build-gcc build-binutils

   4. 首先編譯GNU binutils套件:

      # cd build-binutils
      # ../binutils-2.15/configure --srcdir=../binutils-2.15 --target=arm-elf --prefix=/usr/local/gnu-3.4.1 --enable-softfloat –-enable-shared
      # make all
      # make install

      - ARM7TDMI 與 ARM9TDMI 不支援硬體浮點運算。
      - 筆者使用的 target device (KS8695P) 是 ARM9TDMI 的裝置,ARM9TDMI並沒有DSP,因此必須建立啟用軟體浮點運算功能。
      - 編譯完成後可以在 /usr/local/gnu-3.4.1 目錄下找到執行檔。

   5. 編譯GCC與Newlib:

      一開始必須先將 newlib 放到 gcc 原始碼目錄下,才能和gcc一併編譯.

      # cd gcc-3.4.1
      # ln -s ../newlib-1.12.0 newlib

      接著設定GCC.

      # cd ..
      # cd build-gcc
      # ../gcc-3.4.1/configure --srcdir=../gcc-3.4.1 --target=arm-elf --prefix=/usr/local/gnu-3.4.1 --with-cpu=arm9 --with-newlib --enable-threads --with-float=soft --without-headers –-enable-languages=c

      GCC 3.4 必須在編譯設定時加上 “--with-float=soft|hard” 來指定要使用軟體或硬體浮點運算,在這裡我們必須指定使用軟體浮點運算。GCC 3.4 針對 ”arm-elf” 的 cross toolchains 改用由 Nicolas Pitre 所設計的ARM軟體浮點運算功能,這個新的ARM軟體浮點運算實作不但體積小而且比原來的C版本速度更快,因此請務必將target設定成arm-elf,GCC預設才會去使用新的軟體浮點運算。

      執行編譯並安裝:

      # make all
      # make install

   6. 最後可以在 /usr/local/gnu-3.4.1 目錄下取得完整的 Cross GNU toolchains for ARM。得到 cross toolchain 後,下一步則是要cross-compiling uClibc 或 glibc,取得C程式庫後才能開始cross-compiling應用程式。</description>
         <link>https://www.jollen.org/blog/2004/08/_cross_gnu_toolchains_for_arm.html</link>
         <guid>https://www.jollen.org/blog/2004/08/_cross_gnu_toolchains_for_arm.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 11 Aug 2004 16:09:40 +0800</pubDate>
      </item>
            <item>
         <title>Nano-X 程式設計, #1：介紹與安裝</title>
         <description><![CDATA[本篇首先要簡單說明一下 Microwindows 的基本概念與架構，然後再說明讓 Microwindows 支援 X11 的編譯方式，最後則是一個 Hello World 的程式。一開始我們會先在 X 上發展 Microwindows 的程式，在之後的專欄裡我們會適時將發展環境移到 Embedded Linux 系統。

作者: 陳俊宏<br /> <a href="http://www.jollen.org/">www.jollen.org</a><br /><br /><p><font size="4" color="#ff0000"><strong>Microwindows 的網路資源</strong></font></p>    <p>官方網站: <a href="http://www.microwindow.org/">http://www.microwindow.org</a> </p>    <p>SDK 手冊: <a href="http://embedded.centurysoftware.com/docs/nx/">http://embedded.centurysoftware.com/docs/nx/</a> </p>    <p>入門文件 (Getting Started): <a href="http://home.twcny.rr.com/embedded/microwin/install/index.html">http://home.twcny.rr.com/embedded/microwin/install/index.html</a> </p>    <p>下載 (原始碼): <a href="ftp://microwindows.org/pub/microwindows/microwindows-0.89.tar.gz">ftp://microwindows.org/pub/microwindows/microwindows-0.89.tar.gz</a> </p>    <p>下載 (字型, 含中文字型): <a href="ftp://microwindows.org/pub/microwindows/microwindows-fonts-0.89.tar.gz">ftp://microwindows.org/pub/microwindows/microwindows-fonts-0.89.tar.gz</a> </p>    <p><font size="4" color="#ff0000"><strong>Microwindows 簡介</strong></font></p>    <p><strong><font color="#0000ff">分層式架構 (Layered Design)</font></strong></p>    <p>Microwindows 是採用 3 層式架構設計，共分為 3 層：</p>    <ol><li><em>lowest level</em> - 螢幕, mouse/touchpad, keyboard 與其它硬體的         driver.</li><li><em>mid level</em> - 可移植 (portable) 與 device-independent         的繪圖引擎 (graphics engine).</li><li><em>upper level</em> - 提供給 programmer 的 API,         我們的程式只能呼叫 Microwindows 的 API 來繪圖.</li></ol>    <p>我們所要學的就是最上層的 API,     本專欄的主要目的就是在介紹 Microwindows API 的使用方法。</p>    <p><strong><font color="#0000ff">Microwindows 的 Linux Driver 支援</font></strong></p>    <p>在螢幕方面，Microwindows 支援 Linux 2.2.x 的 framebuffer，在     16-bit Embedded Linux kernel (ELKS) 也可運作。</p>    <p>在滑鼠方面，Microwindows 支援 GPM 與 serial port     滑鼠；另外也有 touch panel 的軀動程式。</p>    <p>在鍵盤方面，Microwindows 支援 tty 鍵盤讀取，在 Linux 與 ELKS     都可運作。</p>    <table width="100%" border="1">      <tr>        <td width="50%" bgcolor="#c0c0c0"><strong>Hardware Device</strong></td>        <td width="50%" bgcolor="#c0c0c0"><strong>Driver</strong></td>      </tr>      <tr>        <td width="50%">Linux 2.2.x framebuffer</td>        <td width="50%">scr_fb.c fb.c fblin<em>[1248]</em>.c</td>      </tr>      <tr>        <td width="50%">GPM Mouse</td>        <td width="50%">mou_gpm.c</td>      </tr>      <tr>        <td width="50%">Serial Mouse</td>        <td width="50%">mou_ser.c</td>      </tr>      <tr>        <td width="50%">Touch Panel</td>        <td width="50%">mou_tp.c</td>      </tr>      <tr>        <td width="50%">tty keyboard</td>        <td width="50%">kbd_tty.c</td>      </tr>    </table>    <p><strong><font color="#0000ff">Microwindows 的 API</font></strong></p>    <p>Microwindows 有 2 種不同的 API:</p>    <ul><li>Microwindows API</li><li>Nano-X API</li></ul>    <p>Microwindows API 與 Microsoft Win32 和 WinCE GDI 相容，因此使用     Microwindows API 的視窗系統為 message-based 的架構 (也就是     message-passing system 的視窗系統)。</p>    <p>Nano-X API 可以概略的比喻為小型的 X server 系統，Nano-X API     是依據 Xlib API 來設計。因此可以很直覺的了解到，利用 Nano-X     API 所設計的視窗系統為 client/server 架構。</p>    <p><strong><font color="#0000ff">Microwindows 的優點</font></strong></p>    <p>1. 在 PC 上支援 X，易於測試發展。</p>    <p>2.  針對 Embedded System 支援 framebuffer。</p>    <p>3. 系統小。</p>    <p>4. API 簡單易學。</p>    <p>5. 支援 TrueType fonts 字型處理。</p>    <p>6. 支援 ARM/MIPS/ELKS。</p>    <p>* <font size="2"><em>ELKS 是 16-bit 的 Linux kernel</em></font></p>   <p><font size="4" color="#ff0000"><strong>安裝 Microwindows</strong></font></p>    <p>Microwindows 不但可以在 framebuffer 上跑，也可以在 X     上跑。一開始學 Microwindows 時，我們會先在 X  上測試，所以沒有特別說明的話之後的 Microwindows 程式都是在 X Window     System 上跑 。</p>   <p>要讓 Microwindows 在 X 上執行，編譯前要先做一些設定：</p>    <p>1. 先下載 microwindows 並解開。</p>    <p>2. 執行 X Window System，進入圖形環境；到 microwin-0.89/src/ 執行 xconfigure。(圖 1)</p>   <p align="center"><img width="310" height="152" border="0" src="http://www.jollen.org/linux/microwindows/xconfigure.jpg" /><br /> 圖 1：Microwindows 設定介面</p>   <p>3. 然後<strong>選取</strong>下面幾個項目：</p>    <blockquote>      <p>Compiling options -> Optimize</p>      <p>Platform -> Linux (native)</p>    <p>Platform -> Linux (native) -> Options -> Screen driver -> X11 (再點       options 可設定解析度)</p>      <p>Platform -> Linux (native) -> Options -> Mouse driver -> GPM       mouse (視您的滑鼠而訂)</p>      <p>Platform -> Linux (native) -> Options -> Keyboard driver -> TTY</p>      <p>I/O -> Have File I/O</p>      <p>I/O -> Have bmp support</p>      <p>I/O -> Have jpeg support</p>      <p>I/O -> Have freetype support (truetype fonts) (再點 options       設定路徑, <font color="#ff0000">請使用 Free Type 1, Red Hat Linux 7.x       以後的使用者應設定 include dir 為 /usr/include/freetype1, </font><font color="#000000">因為筆者會講到中文字型,       所以請勾選 freetype 支援</font>)</p>      <p>Libraries to compile -> NanoX (本文只講 NanoX, 所以 Microwindows       不用選)</p>      <p>Libraries to compile -> Libraries to Shared libs (可選可不 選,       因為現階段還不會 porting 到 Embedded System, 沒什麼關係!)</p>      <p>Libraries to compile -> NanoX -> Options -> Enable shared mem       support</p>      <p>Libraries to compile -> NanoX -> Options -> Compile demos (不想編譯範例的話可以不選)</p>    </blockquote>    <p>4. 設定好後在 microwin-0.89/src/ 下執行 <em>make </em>開始編譯<em>。</em></p>    <p>5. 編譯好後打 <em>make install</em> 安裝。因為我們有勾選 shared     libraries，所以可以把下面這幾個檔案殺掉 (不殺也行!):</p>    <blockquote>      <p>/usr/lib/libmwdrivers.a<br />      /usr/lib/libmwengine.a<br />      /usr/lib/libmwfonts.a<br />      /usr/lib/libmwobjects.a<br />      /usr/lib/libnano-X.a<br />      /usr/lib/libvncauth.a</p>    </blockquote>    <p>6. 然後到 microwin-0.89/src/bin/ 下執行範例：</p>    <blockquote>      <p>$ ./nano-X &amp; sleep 1        (執行       Microwindows 的 nano X server)</p>      <p>$ ./nanowm &amp; sleep 1      (執行 anao X       server 的 Window Manager)</p>      <p>$ ./demo       &amp;                             (執行展示程式)</p>    </blockquote>    <p>7. 如果可以看到這個畫面，表示 Microwindows     安裝成功。接下來就可以開始寫第一個程式了!</p>    <p><font size="4" color="#ff0000"><strong>Nano-X API 入門 - Hello World</strong></font></p>    <p><font size="2"><em>* 本專欄以 Nano-X API 為主，對 Microwindows API     有興趣的網友可參考官方網站的文件。</em></font></p>    <blockquote>  <pre>#include &lt;stdio.h><br />#include &lt;microwin/nano-X.h><br /><br />GR_WINDOW_ID wid;<br />GR_GC_ID gc;<br /><br />void event_handler (GR_EVENT *event);<br /><br />int main (void)<br />{<br />   if (GrOpen() &lt; 0) {<br />        fprintf (stderr, "GrOpen failed");<br />        exit (1);<br />   }<br /><br />   gc = GrNewGC();<br />   GrSetGCForeground (gc, 0xFF0000);<br /><br />   wid = GrNewWindowEx(GR_WM_PROPS_APPFRAME |<br />                       GR_WM_PROPS_CAPTION  |<br />                       GR_WM_PROPS_CLOSEBOX,<br />                       "jollen.org",<br />                       GR_ROOT_WINDOW_ID, <br />                       0, 0, 200, 200, 0xFFFFFF);<br /><br />   GrSelectEvents(wid, GR_EVENT_MASK_CLOSE_REQ | GR_EVENT_MASK_EXPOSURE);<br /><br />   GrMapWindow(wid);<br />   GrMainLoop(event_handler);<br />}<br /><br />void event_handler (GR_EVENT *event)<br />{<br />   switch (event->type)<br />   {<br />      case GR_EVENT_TYPE_EXPOSURE:<br />           GrText(wid, gc, 50, 50, "Hello World", -1, GR_TFASCII);<br />    break;<br />      case GR_EVENT_TYPE_CLOSE_REQ: <br />    GrClose();<br />           exit (0);<br />      default: break;<br />   }<br />}</pre>   </blockquote>  <p>這是一個 Microwindows 的 Hello World 程式，編譯時要連結 nano-X  程式庫：</p>    <blockquote> <p>gcc -o hello hello.c <em>-lnano-X</em></p>    </blockquote> <p>利用這個程式我們就可以測試是否能順利編譯並執行  Microwindows 系統，在執行程式前，別忘了跑 nano-X 跟nanowm 喔！</p>    <p align="center"><img width="309" height="319" border="0" src="http://www.jollen.org/linux/microwindows/hello.jpg" /><br /> 圖 2：Hello World</p>    <p>下一期我們將開始解說這個程式的內容，與更多的  Microwindows 程式設計。</p>
<strong>注記</strong>
<p>2007.01.05: 改標題，以搭配整體專欄。</p>]]></description>
         <link>https://www.jollen.org/blog/2004/08/linux_microwindows_1.html</link>
         <guid>https://www.jollen.org/blog/2004/08/linux_microwindows_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Wed, 11 Aug 2004 10:56:15 +0800</pubDate>
      </item>
            <item>
         <title>Nano-X 程式設計, #2：「Hello World」</title>
         <description><![CDATA[<p><b>說在前頭</b></p>
<p><i><font size="2">Jollen: 對於「學習」這擋事，其實我有很多自己的想法。以「學程式庫」來講，我的看法是...</font></i></p>
<p>學習<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->任何一套程式庫或 application 
framework，絕對不是「函數導向」的學習法，也就不能以學習函數做為主軸；正確的方式應該是「主題式（subjective）」的做法，也就是由「Hello 
World」開始（我想這個已經成為萬年標準了！），然後由不同主題的範例來深入，由於每個主題的範例都有其目的，因此可以透過範例，將達成目的的做法學起來。</p>
<p>其次，每個範例一定都包含「隱含」的觀念，除了學會做法外，也要把裡頭的觀念挖出來研究，以加強能力。Jollen 在撰寫這些程式庫、程式語言與 
application framework 的教<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->學專欄時，都是用這套思考邏輯來規劃整個大綱，並且也是我自己的寫作習慣。</p>
<p>在這樣的學習<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->方式與前提下，「老師不會教、學生必須自己來」的工作是：</p>
<blockquote>
	<p>1. 安裝（當然囉！）</p>
	<p>2. 準備 API 手冊</p>
	<p>3. 瀏覽 API 手冊</p>
	<p>4. 查 API 的能力與<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->技巧</p>
	<p>5. 一邊看 code 一邊查 API 手冊</p>
	<p>6. 編譯範例</p>
	<p>7. 改範例亂玩</p>
</blockquote>
<p>歡迎提供您的<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->意見，或是不同看法讓我參考。</p>
<p><b>Nano-X 的 &quot;Hello World&quot;</b></p>
<p>hello.c 是我們的第一個 Nano-X 程式。在這<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->個範例中，我們要學習基本的 Nano-X 程式設計觀念如下：</p>
<p>˙ GC（Graphics Context）<br>
˙ 視窗（windows）<br>
˙ Nano-X程式設計流程<br>
˙ 事件處理</p>
<p><b>先看範例：hello.c</b></p>
<p>以下是 <i>hello.c</i> 的完整程<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->式碼：</p>
<blockquote>
	<pre>/*
 * Copyright(c) 2003,2004 www.jollen.org
 *
 * - Microwindows nano-X API example.
 * - hello.c
 */

#include <stdio.h>
#include <microwin/nano-X.h>

GR_WINDOW_ID wid;
GR_GC_ID gc;

void event_handler (GR_EVENT *event);

int main (void)
{
   if (GrOpen() < 0) {
        fprintf (stderr, "GrOpen failed");
        return -1;
   }

   gc = GrNewGC();
   GrSetGCForeground (gc, 0xFF0000);

   wid = GrNewWindowEx(GR_WM_PROPS_APPFRAME |
                       GR_WM_PROPS_CAPTION  |
                       GR_WM_PROPS_CLOSEBOX,
                       "jollen.org",
                       GR_ROOT_WINDOW_ID, 
                       0, 0, 200, 200, 0xFFFFFF);<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->

   GrSelectEvents(wid, GR_EVENT_MASK_CLOSE_REQ | GR_EVENT_MASK_EXPOSURE);

   GrMapWindow(wid);
   GrMainLoop(event_handler);

   return 0;<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->
}

void event_handler (GR_EVENT *event)
{
   switch (event->type)
   {
      case GR_EVENT_TYPE_EXPOSURE:
           GrText(wid, gc, 50, 50, "Hello World", -1, GR_TFASCII);
	   break;
      case GR_EVENT_TYPE_CLOSE_REQ: 
	   GrClose();
      default: break;
   }
}</pre>
</blockquote>
<p><b>再進行觀念的強化</b></p>
<p>了解怎<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->麼使用 Nano-X 畫出「Hello World」後，我們挑出幾個重要的觀念來做加強：</p>
<p>1. GC（Graphics Context）</p>
<p>在 Nano-X 系統裡，我們可以把 GC 視為一張畫布，透過 Nano-X 的函數我們就可以在 GC 上繪圖。呼叫 <i>GrNewGC()</i> 函數可以建立一個 GC，若 GC 建立成功，則 
Nano-X server 會傳回一個 GC 的 ID。我們會在程式一開始的時間就建立一個 GC，程式除了可以在 GC 上繪圖外，GC 的資料也<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->能複製或透過選取函數（selection）做區域處理。</p>
<p>2. 視窗（windows）</p>
<p>Nano-X 的視窗是在程式一開始時呼叫 <i>GrNewWindow() </i>函數所建立，若建立成功則可以取得 Window 
ID。視窗當然具備邊框（border）與按鈕等各種視窗<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->屬性，視窗屬性可以在視窗建立時指定，或是透過 window manager 做修改。</p>
<p><b>心得小結：Nano-X 程式設計流程</b></p>
<p>以下歸納出 Nano-X 程式設<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->計的步驟：</p>
<p>1、初始化與 server 的連<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->線。呼叫<i> GrOpen()</i> 函數，若傳回小於 0 的值，表示無法與 Nano-X server 連線。程式寫法如下：</p>
<blockquote>
	<p><i>if (GrOpen() &lt; 0) {<br>
&nbsp;&nbsp; fprintf (stderr, &quot;GrOpen failed&quot;);<br>
&nbsp;&nbsp; return -1;<br>
	}</i></p>
</blockquote>
<p>2、建立新的 GC：</p>
<blockquote>
	<p><i>GR_GC_ID gc;<br>
	gc = GrNewGC();</i></p>
</blockquote>
<p>若執行成功則傳回 GC 的 ID，其資料<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->型別為 <i>GR_GC_ID</i>。</p>
<p>3、設定 GC 的前景色：</p>
<blockquote>
	<p><i>GrSetGCForeground (gc, 0xFF0000);</i></p>
</blockquote>
<p>指定 GC 的 ID 並設定其前景色。</p>
<p>4、呼叫 <i>GrNewWindowEx()</i> 函數建立<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->新視窗：</p>
<blockquote>
	<p><i>GR_WINDOW_ID wid;</i></p>
	<p><i>wid = GrNewWindowEx(GR_WM_PROPS_APPFRAME |<br>
&nbsp;&nbsp; GR_WM_PROPS_CAPTION |<br>
&nbsp;&nbsp; GR_WM_PROPS_CLOSEBOX,<br>
&nbsp;&nbsp; &quot;jollen.org&quot;,<br>
&nbsp;&nbsp; GR_ROOT_WINDOW_ID, <br>
&nbsp;&nbsp; 0, 0, 200, 200, 0xFFFFFF);</i></p>
</blockquote>
<p>5、選取事件。呼叫 <i>GrSelectEvents()</i> 函數選擇<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->我們想要處理的事件：</p>
<blockquote>
	<p><i>GrSelectEvents(wid, GR_EVENT_MASK_CLOSE_REQ | GR_EVENT_MASK_EXPOSURE);</i></p>
</blockquote>
<p>以 <i>hello.c</i> 為例，表示我們想要處理視窗關閉請求（<i>GR_EVENT_MASK_CLOSE_REQ</i>）與視窗顯示（<i>GR_EVENT_MASK_EXPOSURE</i>）事件。</p>
<p>6、呼叫 <i>GrMapWindow() </i>函數顯示<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->視窗，並產生 <i>GR_EVENT_MASK_EXPOSURE</i> 事件：</p>
<blockquote>
	<p>GrMapWindow(wid);</p>
</blockquote>
<p>7、呼叫 <i>GrMainLoop()</i> 函數進入 Nano-X 的分派迴圈：</p>
<blockquote>
	<p><i>GrMainLoop(event_handler);</i></p>
</blockquote>
<p>傳遞給 <i>GrMainLoop()</i> 函數的參數為函數指標，指向處理事件的函數（event 
handler）。在分派迴圈裡，若產生我們所指定的事件時就會呼叫事件處理函數，因此我們<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->必須在事件處理函數做事件處理。</p>
<p><b>事件（Event）處理</b></p>
<p>在分派迴圈裡，產生我們所指定的事<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->件時，就會呼叫事件處理函數。以下是 <i>hello.c</i> 的事件處理函數：</p>
<blockquote>
	<p><i>void event_handler(GR_EVENT *event)<br>{<br>&nbsp;&nbsp;
switch(event-&gt;type)<br>&nbsp;&nbsp;
{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
case GR_EVENT_TYPE_EXPOSURE:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
GrText(wid, gc, 50, 50, &quot;<u>Hello World</u>&quot;, -1, GR_TFASCII);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
case GR_EVENT_TYPE_CLOSE_REQ: <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
GrClose();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
default: break;<br>&nbsp;&nbsp;
}<br>}</i></p>
</blockquote>
<p>分派迴圈會傳遞一個事件訊息的指標給事件<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->處理函數，即程式中的event變數。我們可透過此變數來判斷所產生的事件，並做出相對應的處理：</p>
<ul>
	<li><i>GR_EVENT_TYPE_EXPOSURE</i>：視窗出現後產生<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->此事件，我們在此事件產生後呼叫 <i>GrText()</i> 函數在畫面上顯示文字。</li>
	<li><i>GR_EVENT_TYPE_CLOSE_REQ</i>：當視<!-- "Copyright (c) 2003,2004,2007 www.jollen.org. All rights reserved." -->窗關閉的請求發生時，則產生此事件。我們在此事件產生後呼叫 <i>GrClose()</i> 函數關閉與nano-X 
server的連線。</li>
</ul>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
	<tr><td bgcolor="#FF5959"><img alt="" height="1" width="1"></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td bgcolor="#FFD8CA"><font size="-1"><b>Also See</b></font></td></tr>
</table>
<table border="0" cellpadding="2" cellspacing="0" width="100%">
	<tr><td width="100%">
		<ul>
			<li>2004.08.11:
			<a href="http://www.jollen.org/blog/2004/08/linux_microwindows_1.html">
			Nano-X 程式設計, #1：介紹與安裝</a></li>
		</ul>
	</td></tr>
</table>]]></description>
         <link>https://www.jollen.org/blog/2004/04/nano-x_programming_2.html</link>
         <guid>https://www.jollen.org/blog/2004/04/nano-x_programming_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 26 Apr 2004 20:10:32 +0800</pubDate>
      </item>
            <item>
         <title>最小化 Linux 觀念與實施方法</title>
         <description><![CDATA[本文說明最小化Linux的觀念以及實施的方法，這是 Embedded Linux / x86 課程的先備知識，透過這篇文章的說明，同學可以了解到最小化一個 Linux 所應實施的步驟，以有效學習 Embedded Linux 課程。

作者／陳俊宏<br />  www.jollen.org<br /><br /><p><strong><font size="4" color="#ff0000">以觀念寫成的步驟</font></strong></p> <h4><font color="#000080">1. 準備 host computer</font></h4> <p>一開始我們必須建立一個工作平臺 (即實驗電腦 - host computer)，也就是在 IBM PC 上安裝 Linux  distribution。建議使用的 Linux distribution 為 Red Hat Linux 6.2 以上的版本，因為我們並不需要使用到  kernel 2.4 的版本，因此選擇安裝 Red Hat Linux 6.2 做為實驗環境。</p> <p>您應具備的基本知識為：</p> <ul><li>編譯 Linux kernel</li><li>Linux 管理技術</li></ul> <h4><font color="#000080">2. 選擇 target device</font></h4> <p>target device (目的裝置) 的選擇較常見的有 floppy disk、flash memory。現在有很多 floppy disk +  Linux (floppy disk) 的 project，要製作能安裝在 floppy disk 上的最小化 Linux 並不難，一般的 Linux  開機磁片都可以稱為最小化的 Linux (利用 mkbootdisk 指令製作開機片)。</p> <p>不過我們的目的是要建立給 Embedded Systems 用的 Embedded Linux，因此至少要能符合 3 項基本需求：</p> <ul><li>完整 system libraries：為了能執行各種應用程式。</li><li>標準 filesystem hierarchy：為了能提高程式在不同 Linux 系統的相容性。</li><li>符合 Red Hat Linux 架構：為了能順利將發展的程式由 host computer 移植到 target device。</li></ul> <p>在這裡我們選擇以 32M disk-on-Module 的 flash memory 做為 target device。</p> <h4><font color="#000080">3. 在 target device 上建立 filesystem hierarchy</font></h4> <p>根據 Red Hat Linux 的階層檔案架構與 FHS 標準來建立 target device 的 filesystem  hierarchy。我們可以在 host computer 上的空目錄建立後再拷貝到 target device，或是直接將 target device  mount 到 host computer，直接寫入 target device。另外在 host computer 規劃一個新的分割區也是很好的做法。</p> <p>若想要使用 RAM disk image，則必須在 host computer 上建立整個系統。</p> <h4><font color="#000080">4. 建立 device file</font></h4> <p>使用 mknod 來建立 device file，device file 應根據 Linux Allocated Devices 文件  (Documentation/devices.txt 或 http://www.lanana.org/docs/device-list/)  來建立。通常我們只要建立會用到的 device file 即可。</p> <p>在這裡為了方便起見，我們直接拿 PeeWeeLinux 的 device files 套件來使用即可，因此 /dev 裡的 device file 如下：</p> <table width="100%" border="1" id="table1">  <tr>   <td width="100%" bgcolor="#dbdbdb">   <pre>crw-------    1 root     root       5,   1 Jan  7  2000 console<br />lrwxrwxrwx    1 root     root            3 May  5 13:07 fb -> fb0<br />crw-r--r--    1 root     root      29,   0 Aug 15  2000 fb0<br />crw-r--r--    1 root     root      29,  32 Aug 15  2000 fb1<br />crw-r--r--    1 root     root      29,  64 Aug 15  2000 fb2<br />crw-r--r--    1 root     root      29,  96 Aug 15  2000 fb3<br />crw-r--r--    1 root     root      29, 128 Aug 15  2000 fb4<br />crw-r--r--    1 root     root      29, 160 Aug 15  2000 fb5<br />crw-r--r--    1 root     root      29, 192 Aug 15  2000 fb6<br />crw-r--r--    1 root     root      29, 224 Aug 15  2000 fb7<br />brw-------    1 root     root       2,   0 Jan  7  2000 fd0<br />brw-------    1 root     root       2,   1 Jan  7  2000 fd1<br />brw-r--r--    1 root     root     100,   0 Mar  5  2001 fla<br />brw-r--r--    1 root     root     100,   1 Mar  5  2001 fla1<br />brw-r--r--    1 root     root     100,   2 Mar  5  2001 fla2<br />brw-r--r--    1 root     root     100,   3 Mar  5  2001 fla3<br />brw-r--r--    1 root     root     100,   4 Mar  5  2001 fla4<br />crw-r--r--    1 root     root       1,   7 Jan  7  2000 full<br />brw-rw----    1 root     root       3,   0 Jan  7  2000 hda<br />brw-rw----    1 root     root       3,   1 Jan  7  2000 hda1<br />brw-rw----    1 root     root       3,  10 Jan  7  2000 hda10<br />brw-rw----    1 root     root       3,  11 Jan  7  2000 hda11<br />brw-rw----    1 root     root       3,  12 Jan  7  2000 hda12<br />brw-rw----    1 root     root       3,  13 Jan  7  2000 hda13<br />brw-rw----    1 root     root       3,  14 Jan  7  2000 hda14<br />brw-rw----    1 root     root       3,  15 Jan  7  2000 hda15<br />brw-rw----    1 root     root       3,  16 Jan  7  2000 hda16<br />brw-rw----    1 root     root       3,   2 Jan  7  2000 hda2<br />brw-rw----    1 root     root       3,   3 Jan  7  2000 hda3<br />brw-rw----    1 root     root       3,   4 Jan  7  2000 hda4<br />brw-rw----    1 root     root       3,   5 Jan  7  2000 hda5<br />brw-rw----    1 root     root       3,   6 Jan  7  2000 hda6<br />brw-rw----    1 root     root       3,   7 Jan  7  2000 hda7<br />brw-rw----    1 root     root       3,   8 Jan  7  2000 hda8<br />brw-rw----    1 root     root       3,   9 Jan  7  2000 hda9<br />brw-rw----    1 root     root       3,  64 Jan  7  2000 hdb<br />brw-rw----    1 root     root       3,  65 Jan  7  2000 hdb1<br />brw-rw----    1 root     root       3,  74 Jan  7  2000 hdb10<br />brw-rw----    1 root     root       3,  75 Jan  7  2000 hdb11<br />brw-rw----    1 root     root       3,  76 Jan  7  2000 hdb12<br />brw-rw----    1 root     root       3,  77 Jan  7  2000 hdb13<br />brw-rw----    1 root     root       3,  78 Jan  7  2000 hdb14<br />brw-rw----    1 root     root       3,  79 Jan  7  2000 hdb15<br />brw-rw----    1 root     root       3,  80 Jan  7  2000 hdb16<br />brw-rw----    1 root     root       3,  66 Jan  7  2000 hdb2<br />brw-rw----    1 root     root       3,  67 Jan  7  2000 hdb3<br />brw-rw----    1 root     root       3,  68 Jan  7  2000 hdb4<br />brw-rw----    1 root     root       3,  69 Jan  7  2000 hdb5<br />brw-rw----    1 root     root       3,  70 Jan  7  2000 hdb6<br />brw-rw----    1 root     root       3,  71 Jan  7  2000 hdb7<br />brw-rw----    1 root     root       3,  72 Jan  7  2000 hdb8<br />brw-rw----    1 root     root       3,  73 Jan  7  2000 hdb9<br />brw-------    1 root     root      22,   0 Jan  7  2000 hdc<br />brw-rw----    1 root     root      22,   1 Jan  7  2000 hdc1<br />brw-rw----    1 root     root      22,  10 Jan  7  2000 hdc10<br />brw-rw----    1 root     root      22,  11 Jan  7  2000 hdc11<br />brw-rw----    1 root     root      22,  12 Jan  7  2000 hdc12<br />brw-rw----    1 root     root      22,  13 Jan  7  2000 hdc13<br />brw-rw----    1 root     root      22,  14 Jan  7  2000 hdc14<br />brw-rw----    1 root     root      22,  15 Jan  7  2000 hdc15<br />brw-rw----    1 root     root      22,  16 Jan  7  2000 hdc16<br />brw-rw----    1 root     root      22,   2 Jan  7  2000 hdc2<br />brw-rw----    1 root     root      22,   3 Jan  7  2000 hdc3<br />brw-rw----    1 root     root      22,   4 Jan  7  2000 hdc4<br />brw-rw----    1 root     root      22,   5 Jan  7  2000 hdc5<br />brw-rw----    1 root     root      22,   6 Jan  7  2000 hdc6<br />brw-rw----    1 root     root      22,   7 Jan  7  2000 hdc7<br />brw-rw----    1 root     root      22,   8 Jan  7  2000 hdc8<br />brw-rw----    1 root     root      22,   9 Jan  7  2000 hdc9<br />brw-rw----    1 root     root      22,  64 Jan  7  2000 hdd<br />brw-rw----    1 root     root      22,  65 Jan  7  2000 hdd1<br />brw-rw----    1 root     root      22,  74 Jan  7  2000 hdd10<br />brw-rw----    1 root     root      22,  75 Jan  7  2000 hdd11<br />brw-rw----    1 root     root      22,  76 Jan  7  2000 hdd12<br />brw-rw----    1 root     root      22,  77 Jan  7  2000 hdd13<br />brw-rw----    1 root     root      22,  78 Jan  7  2000 hdd14<br />brw-rw----    1 root     root      22,  79 Jan  7  2000 hdd15<br />brw-rw----    1 root     root      22,  80 Jan  7  2000 hdd16<br />brw-rw----    1 root     root      22,  66 Jan  7  2000 hdd2<br />brw-rw----    1 root     root      22,  67 Jan  7  2000 hdd3<br />brw-rw----    1 root     root      22,  68 Jan  7  2000 hdd4<br />brw-rw----    1 root     root      22,  69 Jan  7  2000 hdd5<br />brw-rw----    1 root     root      22,  70 Jan  7  2000 hdd6<br />brw-rw----    1 root     root      22,  71 Jan  7  2000 hdd7<br />brw-rw----    1 root     root      22,  72 Jan  7  2000 hdd8<br />brw-rw----    1 root     root      22,  73 Jan  7  2000 hdd9<br />prw-------    1 root     root            0 Jan  7  2000 initctl<br />crw-r-----    1 root     root       1,   2 Jan  7  2000 kmem<br />brw-rw----    1 root     root       7,   0 Jan  7  2000 loop0<br />brw-rw----    1 root     root       7,   1 Jan  7  2000 loop1<br />brw-rw----    1 root     root       7,   2 Jan  7  2000 loop2<br />brw-rw----    1 root     root       7,   3 Jan  7  2000 loop3<br />brw-rw----    1 root     root       7,   4 Jan  7  2000 loop4<br />brw-rw----    1 root     root       7,   5 Jan  7  2000 loop5<br />brw-rw----    1 root     root       7,   6 Jan  7  2000 loop6<br />brw-rw----    1 root     root       7,   7 Jan  7  2000 loop7<br />crw-rw-rw-    1 root     root       1,   1 Jan  7  2000 mem<br />lrwxrwxrwx    1 root     root            5 May  5 13:07 mouse -> psaux<br />crw-rw-rw-    1 root     root       1,   3 Jan  7  2000 null<br />crw-r-----    1 root     root       1,   4 Jan  7  2000 port<br />crw-rw----    1 root     root      10,   1 May  6  1998 psaux<br />brw-r-----    1 root     root       1,   1 Jan  7  2000 ram<br />brw-rw----    1 root     root       1,   0 Jan  7  2000 ram0<br />brw-rw----    1 root     root       1,   1 Jan  7  2000 ram1<br />brw-rw----    1 root     root       1,   2 Jan  7  2000 ram2<br />brw-rw----    1 root     root       1,   3 Jan  7  2000 ram3<br />brw-rw----    1 root     root       1,   4 Jan  7  2000 ram4<br />brw-rw----    1 root     root       1,   5 Jan  7  2000 ram5<br />brw-rw----    1 root     root       1,   6 Jan  7  2000 ram6<br />brw-rw----    1 root     root       1,   7 Jan  7  2000 ram7<br />brw-rw----    1 root     root       1,   8 Jan  7  2000 ram8<br />brw-rw----    1 root     root       1,   9 Jan  7  2000 ram9<br />lrwxrwxrwx    1 root     root            4 May  5 13:07 ramdisk -> ram0<br />crw-r--r--    1 root     root       1,   8 Jan  7  2000 random<br />crw-rw-r--    1 root     root      10, 135 Jan  7  2000 rtc<br />brw-rw----    1 root     root       8,   0 May  6  1998 sda<br />brw-rw----    1 root     root       8,   1 May  6  1998 sda1<br />brw-rw----    1 root     root       8,   2 May  6  1998 sda2<br />brw-rw----    1 root     root       8,   3 May  6  1998 sda3<br />brw-rw----    1 root     root       8,   4 May  6  1998 sda4<br />brw-rw----    1 root     root       8,  16 May  6  1998 sdb<br />brw-rw----    1 root     root       8,  17 May  6  1998 sdb1<br />brw-rw----    1 root     root       8,  18 May  6  1998 sdb2<br />brw-rw----    1 root     root       8,  19 May  6  1998 sdb3<br />brw-rw----    1 root     root       8,  20 May  6  1998 sdb4<br />brw-rw----    1 root     root       8,  32 May  6  1998 sdc<br />brw-rw----    1 root     root       8,  33 May  6  1998 sdc1<br />brw-rw----    1 root     root       8,  34 May  6  1998 sdc2<br />brw-rw----    1 root     root       8,  35 May  6  1998 sdc3<br />brw-rw----    1 root     root       8,  36 May  6  1998 sdc4<br />brw-rw----    1 root     root       8,  48 May  6  1998 sdd<br />brw-rw----    1 root     root       8,  49 May  6  1998 sdd1<br />brw-rw----    1 root     root       8,  50 May  6  1998 sdd2<br />brw-rw----    1 root     root       8,  51 May  6  1998 sdd3<br />brw-rw----    1 root     root       8,  52 May  6  1998 sdd4<br />crw-------    1 root     root       4,   0 Jan  7  2000 systty<br />crw-rw-rw-    1 root     root       5,   0 Jan  7  2000 tty<br />crw-------    1 root     root       4,   0 Jan  7  2000 tty0<br />crw--w----    1 root     root       4,   1 Jan  7  2000 tty1<br />crw-------    1 root     root       4,   2 Jan  7  2000 tty2<br />crw-------    1 root     root       4,   3 Jan  7  2000 tty3<br />crw-------    1 root     root       4,   4 Jan  7  2000 tty4<br />crw-------    1 root     root       4,   5 Jan  7  2000 tty5<br />crw-------    1 root     root       4,   6 Jan  7  2000 tty6<br />crw-------    1 root     root       4,   7 Jan  7  2000 tty7<br />crw-------    1 root     root       4,   8 Jan  7  2000 tty8<br />crw-------    1 root     root       4,   9 Jan  7  2000 tty9<br />crw-------    1 root     root       4,  64 Jan  7  2000 ttyS0<br />crw-------    1 root     root       4,  65 Jan  7  2000 ttyS1<br />crw-------    1 root     root       4,  66 Jan  7  2000 ttyS2<br />crw-------    1 root     root       4,  67 Jan  7  2000 ttyS3<br />crw-r--r--    1 root     root       1,   9 Jan  7  2000 urandom<br />crw-rw-rw-    1 root     root       1,   5 Jan  7  2000 zero</pre>   </td>  </tr> </table> <span style="font-weight: 400; text-decoration: underline;"><br />真正的 Embedded  Linux 應用會以 mknod 或是 mkext2fs 指令來建立 device files，並且 /dev 目錄下也只會有真正用到的 device  files，不會有這麼多的 device file。</span> <h4><font color="#000080">5. 安裝 glibc 至 target device</font></h4> <p>glibc 是標準 C 程式庫，我們可以直接由 host computer 將 glibc 的檔案拷貝到 target device。如果您想使用與  host computer 不同版本的 glibc，則要注意版本的向下相容性問題；不過建議 target device 與 host computer  的環境最好要一樣，這樣比較容易測試與除錯。</p> <p>因為 Linux 是支援 shared libraries 的 OS，所以 target device 與 host computer 的 glibc  版本不同也不需要重新編譯所有的程式，除非 target device 不是在 x86 上執行，這時才需要做重新編譯 (corss-compiling) 的動作。</p> <p>glibc 也可以考慮改用專用給 Embedded Systems 用的 uClibc。</p> <h4><font color="#000080">6. 建立 base Linux system</font></h4> <p>base Linux system 的建立包括安裝其它 system libraries、utilities、compiler、shell ...  等等，至於到底應該要有什麼基本系統，應參考 LSB 標準。另外 LFS 文件也只做為建立 base Linux system 的參考，但建議應以 LSB  標準為主。</p> <p>另外 Lineo 公司也發展許多專門給 Embedded System 用的高品質軟體，因此請使用底下 2 個專案套件：</p> <p>(<a href="http://opensource.lineo.com/projects.html">http://opensource.lineo.com/projects.html</a>)</p> <ul><li>BusyBox (大量使用中的熱門專案)</li><li>TinyLogin (大量使用中的熱門專案)</li></ul> <p>uClibc 與有名的 uClinux 也都是 Lineo 公司的貢獻。</p> <h4><font color="#000080">7. 設定 init</font></h4> <p>init 的設定就比較容易了，我們只要根據 runlevel 來設定 /etc/inittab 即可。另外，如果要跟 Red Hat Linux 的  /etc/rc.d 架構相容，可以自行修改 BusyBox 裡的 init.c 程式 (#define INIT_SCRIPT)。</p> <p>請參考「<a href="http://www.jollen.org/blog/2006/04/embedded_linux_3_initial_scrip.html">Embedded  Linux 的系統管理,#3: initial scripts</a>」一文。</p> <h4><font color="#000080">8. 編譯 Linux kernel</font></h4> <p>根據 target device 所在的硬體環境編譯 Linux kernel。編譯好後會得到 Linux kernel image (zImage/bzImage)。</p> <p>我們的課程會為同學事先準備好已編譯完成的 Linux kernel。</p> <h4><font color="#000080">9. 設定 boot loader</font></h4> <p>Linux 下的 boot loader 從老當益壯的 LILO、syslinux，到 grub 都可以使用。使用 DOM 的話，假如 boot  loader 發生「not found BIOS drive」的話，只要手動做 BIOS 位址設定即可。以 LILO 為例，則是：</p> <table width="100%" border="1" id="table2">  <tr>   <td width="100%" bgcolor="#dbdbdb">disk = /dev/hdc<br />   bios = 0x80<br />   boot = /dev/hdc</td>  </tr> </table> <h4><font color="#000080">10. (參考) 使用 RAM disk image</font></h4> <p>前面 (~9.) 我們將 Linux 系統放在 physical 儲存裝置，如果您想讓檔案系統放在 RAM 裡 (例如在 RAM 很多的 PC  上)，可以使用 RAM disk 技術，將檔案系統與 Linux kernel image 做成 compressed RAM disk image。</p> <h4><font color="#000080">Embedded Linux 解決方案</font></h4> <p>前面我們介紹純手工打造的最小化 Linux，不過就非特殊用途的場合來講，現在有很多 Embedded Linux distribution  的現成解決方案可以給怕麻煩的人使用，我們可以不必親自從零開始做起。例如 PeeWeeLinux、Embedded Debian 或其它商業性質的產品。</p> <h4><font size="4" color="#ff0000">結語</font></h4> <p>在這裡我們針對建立最小化 Linux 的觀念做了介紹，Embedded Linux 系統的基礎為最小化的 Linux，本文章說明製作最小化 Linux  的方法、步驟與觀念。</p>]]></description>
         <link>https://www.jollen.org/blog/2004/03/_linux.html</link>
         <guid>https://www.jollen.org/blog/2004/03/_linux.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Fri, 05 Mar 2004 11:04:50 +0800</pubDate>
      </item>
            <item>
         <title>uClinux-dist 使用教學：uClinux 與 uClinux-dist 介紹</title>
         <description><![CDATA[<p><b>uClinux 介紹</b></p>
<p>uClinux 唸做「you-see-linux」，是由字置詞「u」（micro之意）與「c」（control之意）所組成的複合字。由 uClinux 
的名稱可以知道，這是將Linux 移植到控制用微控制處理器上的 Linux kernel。</p>
<p>目前 uClinux 已經將 Linux kernel 的移植（porting）到許多不同的處理器架構上，uClinux 目前移植的 kernel 
最新版本為： kernel 2.0.39、kernel 2.4.32 與 kernel 2.6.17。</p>
<p>uClinux 所支援的處理器有：M68k、ColdFire、ARM、Sparc、NEC v850、H8/300、OPENcores 
OR1000、Altera NIOS and Xilinx Microblaze。</p>
<p>uClinux 本身是 Linux kernel 的移植，許多情況下我們都可以直接使用廠商提供事先編譯完成的 uClinux。若想要自行編譯給 ARM 
使用的 Linux kernel，建議使用uClinux-dist。</p>
<p><b>uClinux-dist 介紹</b></p>
<p>uClinux 有許多不同處理器平臺的移植，uClinux-dist 則是將不同處理器的 uClinux 整合至一個套件裡。此外，uClinux-dist 
也整合了許多的軟體與系統工具，並且提供設定 root filesystem 的介面。</p>
<p>因此，使用 uClinux-dist 可以很方便的設定 Linux kernel 與 root filesystem，並且透過 uClinux-dist 
的 Makefile 環境，我們就可以很方便的做跨平臺編譯。針對不同平臺所需要的其它處理工作（如 FLAT 格式轉換、.text/.data 節區的切割等等），uClinux-dist 
也會自動幫我們完成。</p>
<p>可以小結使用 uClinux-dist 的主要好處有：</p>
<p>˙ 簡化編譯過程<br>
˙ 不需下達雜複的 <i>arm-elf-gcc</i> 參數<br>
˙ 不需下達雜複的 <i>arm-elf-ld</i> 參數<br>
˙ 自動進行 kernel 的 .text/.section 節區處理<br>
˙ 將 ELF 格式轉 FLAT 格式<br>
˙ 自動進行 VMA 與 LMA 的位址重新對應<br>
˙ 處理不同平臺的新增節區設定</p>
<p>除此之外，uClinux-dist 也提供 glibc/uClibc/uC-libc 的選擇，因此建議大家下載 uClinux-dist 使用。uClinux-dist 
的官方下載網址：<a href="http://www.uclinux.org/pub/uClinux/dist/">http://www.uclinux.org/pub/uClinux/dist/</a></p>
<p>由於編譯完成的 uClinux kernel 還需要針對硬體平臺做許多處理才能下載到實驗板使用，例如：<u>針對不同的程式起啟位址重新調整所有節區的VMA/LMA 
位址，因此若透過 uClinux-dist 的整合環境，便可以簡化我們的工作</u>。</p>
<p><strong>注記</strong></p>
<ul>
<li>2006.12.27: 更新（Editor: Jollen）</li>
</ul>]]></description>
         <link>https://www.jollen.org/blog/2004/02/uclinux_uclinux-dist.html</link>
         <guid>https://www.jollen.org/blog/2004/02/uclinux_uclinux-dist.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Thu, 26 Feb 2004 13:40:42 +0800</pubDate>
      </item>
            <item>
         <title>uClinux-dist 使用教學：uClinux-dist 的使用</title>
         <description><![CDATA[<p><b>uClinux-dist 基本操作</b></p>
<p>將uClinux-dist-20030909.tar.gz解壓後，執行：</p>
<blockquote>
	<p># make menuconfig</p>
</blockquote>

<p>進入文字選單設定介面（圖1）。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-01.jpg" alt="" />
<p>圖1 uClinux-dist的設定主選單</p>

<p>進入「Target Platform Selection」選單，開始設定target端平臺的參數（圖2）。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-02.jpg" alt="" />
<p>圖2 選擇 Vendor/Product</p>

<p>在 uClinux-dist 裡已經預先建立許多平臺的參數資料，因此我們可以在「Vendor/Product」選單裡選取我們的硬體平臺，並使用 
uClinux-dist 的預設參數。</p>
<p>在這裡我們選擇「GDB/ARMulator」來配合後面的章節進行實作（圖3）。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-03.jpg" alt="" />
<p>圖3 在Vendor/Product選項中找到 GDB/ARMulator平臺</p>

<p>接著在「kernel Version」選單中選則 Linux kernel 版本，在這裡我們選擇「linux-2.0.x」（圖4）。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-04.jpg" alt="" />
<p>圖4 使用linux-2.0.x</p>

<p>再來是有關filesystem的設定。在「Libc Version」裡，選擇「uC-libc」做為標準C程式庫（圖5）。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-05.jpg" alt="" />
<p>圖5 使用uC-libc</p>

<p>回到「Target Platform Selection」主選單，選擇「Customize kernel Settings (NEW)」後儲存設定離開（圖6）。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-06.jpg" alt="" />
<p>圖6 選取「Customize Kernel Settings (NEW)」進入kernel設定介面</p>

<p>在圖6的畫面中連續選擇「Exit」後會出現圖7的畫面，在此畫面選擇「Yes」儲存設定後離開。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-07.jpg" alt="" />
<p>圖7 儲存設定後離開</p>
<p><b>uClinux kernel設定選單</b></p>
<p>離開uClinux-dist主選單後，會進入Linux kernel的設定選單（圖8）。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-08.jpg" alt="" />
<p>圖8 Linux kernel設定選單</p>

<p>在 Linux kernel的設定選單裡進行相關設定，其中關於記憶體使用配置可查詢各平臺的使用手冊。設定完成後儲存離開。由於我們選擇的平臺為 GDB/ARMulator，因此直接使用預設值即可。選擇「Exit」儲存設定後離開（圖9）。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-09.jpg" alt="" />
<p>圖9 儲存 kernel 設定後離開</p>

<p><b>Filesystem 設定選單</b></p>
<p>若在 uClinux-dist 主選單選擇「Customize Vendor/User settings (NEW)」（圖6）則可以自行設定 filesystem。圖7是 uClinux-dist 的 filesystem 設定介面，在這裡我們可以自行設定 filesystem 的套件。</p>

<img src="http://www.jollen.org/blog/2004/02/uclinux-dist-10.jpg" alt="" />
<p>圖10 Filesystem 設定選單</p>
<p>設定完成後選擇「Exit」，然後再選擇「Yes」儲存設定後離開。</p>

<p><b>注記</b></p>
<ul>
<li>2006.12.27: 本文是以 20030909 的 uClinux-dist 為例，目前最新版本是 20060803。（Edit by Jollen）
</ul>]]></description>
         <link>https://www.jollen.org/blog/2004/02/uclinux_uclinux-dist_2.html</link>
         <guid>https://www.jollen.org/blog/2004/02/uclinux_uclinux-dist_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Thu, 26 Feb 2004 13:40:32 +0800</pubDate>
      </item>
            <item>
         <title>uClinux-dist 使用教學：編譯 uClinux-dist 與常見問題</title>
         <description><![CDATA[<p><b>編譯 uClinux-dist</b></p>
<p>完成 kernel 與 filesystem 設定後，在命<!--copyright 2006 by www.jollen.org-->令模式執行編譯命令：</p>
<blockquote>
	<p><i># make dep<br>
	# make</i></p>
</blockquote>
<p>進行 kernel 與應用程式的跨平臺編譯。在編譯之前，請檢查跨平臺編譯工具組（如GNU armtools）是否已安裝，同時在環境變數 <i>PATH</i> 
的搜尋路徑裡也要能找到相關的程式。</p>
<p>uClinux-dist 會<!--copyright 2006 by www.jollen.org-->幫我們製作 root filesystem 的映像檔（image file），最後編譯完成的 kernel 與 root 
filesystem 映像檔會存放於 <i>image/</i> 目錄下。編譯成功後會得到 4 個主要的檔案如下：</p>
<p>˙<i> images/linux.text</i>：kernel<!--copyright 2006 by www.jollen.org-->的.text節區。<br>
˙ <i>images/linux.data</i>：kernel的.data節區。<br>
˙<i> images/romfs.img</i>：filesystem的映像檔。<br>
˙<i> images/image.bin</i>：以上 3 個檔<!--copyright 2006 by www.jollen.org-->案合併後的映像檔。</p>
<p>在這裡我將 uClinux-dist 設定成「GDB/ARMulator」，這樣我們之後就可以用 GDB/ARMulator 來玩這個 root 
filesystem 了。</p>
<p><b>常見問題</b></p>
<p>在許多無法成功編譯 uClinux-dist 的案例中，最常見到的<!--copyright 2006 by www.jollen.org-->問題有2個。</p>
<p>第一種情況為 GNU armtools 安裝不完整，導致在編譯的過程中出現錯誤，例如在 uClinux-dist 中選用 uClibc，但並未安裝 
uClibc 套件，或是系統設定的問題，導致編<!--copyright 2006 by www.jollen.org-->譯器無法找到正確的程式庫位置。因此若在編譯 uClinux-dist 的過程出現錯誤，請先檢查 GNU 
armtools 的安裝是否完整，同時也記得安裝所選用的 C 程式庫。</p>
<p>第二種情況為套件的設定與 C 函式庫有衝突，例如有些套件不能使用 uClibc，必須改用 uC-libc 才能成功<!--copyright 2006 by www.jollen.org-->編譯。</p>]]></description>
         <link>https://www.jollen.org/blog/2004/02/uclinux_uclinux-dist_3.html</link>
         <guid>https://www.jollen.org/blog/2004/02/uclinux_uclinux-dist_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Thu, 26 Feb 2004 13:40:31 +0800</pubDate>
      </item>
            <item>
         <title>uClinux-dist 使用教學：安裝 uClinux 的 GNU ARM toolchain</title>
         <description><![CDATA[<p>由 uClinux 網站<!--copyright 2006 by www.jollen.org-->下載 GNU armtools：</p>
<p><a href="http://www.uclinux.org/pub/uClinux/m68k-elf-tools/">
http://www.uclinux.org/pub/uClinux/m68k-elf-tools/</a></p>
<p>請下載此檔案：</p>
<p>
<a href="http://www.uclinux.org/pub/uClinux/m68k-elf-tools/arm-elf-tools-20030314.sh">
http://www.uclinux.org/pub/uClinux/m68k-elf-tools/arm-elf-tools-20030314.sh</a></p>
<p>下載完成後，直接<!--copyright 2006 by www.jollen.org-->執行 <i>
arm-elf-tools-20030314.sh </i>即可安裝：</p>
<blockquote>
	<p># sh arm-elf-tools-20030314.sh</p>
</blockquote>
<p>uClinux 所提供的 GNU armtools 會安裝於 <i>/usr/local</i> 目錄下。</p>
<p><b>FLAT 格式的</b><!--copyright 2006 by www.jollen.org--><b>轉換工具</b></p>
<p>uClinux 所提供的 GNU armtools 也有一個用來處<!--copyright 2006 by www.jollen.org-->理 
FLAT 格式轉換的工具，因此我們會有以下 2 個執行檔：</p>
<p>˙<i> /usr/local/bin/arm-elf-ld</i>：呼叫arm-elf-ld.real並協助我們<!--copyright 2006 by www.jollen.org-->將檔案轉換成FLAT格式。<br>
˙ <i>/usr/local/bin/arm-elf-ld.real</i>：真正的linker。</p>
<p>使用 uClinux 的 toolchains 時，只要在<!--copyright 2006 by www.jollen.org-->執行 <i>
arm-elf-ld</i> 時加上 '-elf2flt' 參數即可將檔案轉換成 FLAT 格式；另外，設定<i> FLTFLAGS</i> 
環境變數，即可傳遞參數給 <i>elf2flt</i> 指令。例如：'export FLTFLAGS=-z' 表示要製作成 ZFLAT 的格式。</p>]]></description>
         <link>https://www.jollen.org/blog/2004/02/uclinux_uclinux-dist_4.html</link>
         <guid>https://www.jollen.org/blog/2004/02/uclinux_uclinux-dist_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Thu, 26 Feb 2004 13:40:17 +0800</pubDate>
      </item>
            <item>
         <title>uClinux-dist 使用教學：使用 GDB/ARMulator 來做測試</title>
         <description><![CDATA[	<p>一開始<!--copyright 2006 by www.jollen.org-->執行 GDB/ARMulator 時，指定 kernel 
	做為執行檔：</p>
	<blockquote>
		<p># /usr/local/bin/arm-elf-gdb<b> linux.2.0.x</b><br>
		GNU gdb 5.0<br>
		Copyright 2000 Free Software Foundation, Inc.<br>
		GDB is free software, covered by the GNU General Public License, and you 
		are<br>
		welcome to change it and/or distribute copies of it under certain 
		conditions.<br>
		Type &quot;show copying&quot; to see the conditions.<br>
		There is absolutely no warranty for GDB. Type &quot;show warranty&quot; for 
		details.<br>
		This GDB was configured as &quot;--host=i686-pc-linux-gnu 
		--target=arm-elf&quot;...<br>
		(no debugging symbols found)...</p>
	</blockquote>
	<p><b><font size="2" color="#000080">注意！GDB/ARMulator 是 GDB 加上 ARMulator 
	patch 後的特殊版本，必須自行由 GDB 原始碼製作。</font></b></p>
	<p>進入 gdb 命令模式後，在 gdb 命令模<!--copyright 2006 by www.jollen.org-->式下輸入 <i>
	target sim</i> 命令：</p>
	<blockquote>
		<p>(gdb) <b>target sim</b><br>
		Loaded ROM boot.rom<br>
		Loaded ROM boot.rom<br>
		Loaded ROM boot.rom<br>
		Loaded ROM boot.rom<br>
		Connected to the simulator.</p>
	</blockquote>
	<p>指定 taget 端為 ARM simulator。此時 GDB 會載入<i> boot.rom</i> 做為 root filesystem，<font color="#FF0000"><u>因此請將 
	root filesystem的檔案名稱修改為 <i>boot.rom</i>（或建立link）</u></font>。接著再輸入 <i>load</i> 
	命令載入kernel：</p>
	<blockquote>
		<p>(gdb) load<br>Loading section .text, size 0x77b84 vma 0x2000000<br>Loading section .rodata, size 0x8ddb vma 0x2077b84<br>Loading section .data, size 0x8fc8 vma 0x2080a60<br>Start address 0x<!--copyright 2006 by www.jollen.org-->2000000<br>Transfer rate: 4507960 bits/sec.</p>
	</blockquote>
<p>由 GDB 的訊息可以看出，uClinux for GDB/ARMulator 的 .text 節區起始位址為 
0x20000000，此位址會因為硬體平臺的不同而不同。uCliinux-dist 已經幫我們設定好許多平臺的起始位址參數，因此使用<!--copyright 2006 by www.jollen.org--> 
uClinux-dist 時就不必自己手動重設節區位址。</p>
<p>輸入run命令開機：</p>
<blockquote>
	<p>(gdb) run</p>
</blockquote>
<p>執行結果：</p>
<blockquote>
	<p>Starting program: /home/Embedded/GDB/linux.2.0.x<br>
	Found an Atmel AT91F40xxx arm7 processor<br>
	Atmel AT91 series microcontroller support (c) 2000,2001 Lineo Inc.<br>
	kernel binary is in RAM -- reserving 643k for the kernel<br>
	Calibrating dela<!--copyright 2006 by www.jollen.org-->y loop.. ok - 13.00 
	BogoMIPS<br>
	Memory: 1372k/2048k available (224k kernel code, 0k reserved, 196k data)<br>
	Swansea University Computer Society NET3.035 for Linux 2.0<br>
	NET3: Unix domain sockets 0.13 for Linux NET3.035.<br>
	Swansea University Computer Society TCP/IP for NET3.034<br>
	IP Protocols: ICMP, UDP, TCP<br>
	Linux version 2.0.39.uc2 (davidm@moby) (gcc version 2.95.3 20010315 (release)(ColdFire 
	patches - 20010318 from http://fiddes.net/coldfire/)(-msep-data patches)) 41 
	Fri Jun 14 10:54:27 EST 2002<br>
	Atmel USART driver version 0.99<br>
	ttyS0 at 0xfffd0000 (irq = 2) is a builtin Atmel APB USART<br>
	ttyS1 at 0xfffcc000 (irq = 3) is a builtin Atmel APB USART<br>
	Ramdisk driver initialized : 16 ramdisks of 4096K size<br>
	Blkmem copyright 1998,1999 D. Jeff Dionne<br>
	Blkmem copyright 1998 Kenneth Albanowski<br>
	Blkmem 1 disk images:<br>
	0: 4000000-B4BB07FF (RO)<br>
	PPP: version 2.3.8 (demand dialling)<br>
	TCP compression code copyright 1989 Regents of the University of California<br>
	PPP line discipline registered.<br>
	PPP MPPE compression registered<br>
	SLIP: version 0.8.4-NET3.019-NEWTTY (dynamic channels, max=256).<br>
	CSLIP: code copyright 1989 Regents of the University of California.<br>
	VFS: Mounted root (romfs filesystem).<br>
	Shell invoked to run file: /etc/rc<br>
	Command: hostname GDB-ARMulator<br>
	Command: /bin/expand /etc/ramfs.img /dev/ram0<br>
	Command: mount -t proc proc /proc<br>
	Command: mount -t ext2 /d<!--copyright 2006 by www.jollen.org-->ev/ram0 /var<br>
	Command: mkdir /var/tmp<br>
	Command: mkdir /var/log<br>
	Command: mkdir /var/run<br>
	Command: mkdir /var/lock<br>
	Command: cat /etc/motd<br>
	Welcome to<br>
	____ _ _<br>
	/ __| ||_|<br>
	_ _| | | | _ ____ _ _ _ _<br>
	| | | | | | || | _ \| | | |\ \/ /<br>
	| |_| | |__| || | | | | |_| |/ \<br>
	| ___\____|_||_|_| |_|\____|\_/\_/<br>
	| |<br>
	|_|<br>
	<br>
	GDB/ARMulator support by &lt;davidm@snapgear.com&gt;<br>
	For further information check:<br>
	http://www.uclinux.org/<br>
	<br>
	Execution Finished, Exiting<br>
	<br>
	Sash command shell (version 1.1.1)<br>
	/&gt;</p>
</blockquote>]]></description>
         <link>https://www.jollen.org/blog/2004/02/uclinux-dist_gdbarmulator.html</link>
         <guid>https://www.jollen.org/blog/2004/02/uclinux-dist_gdbarmulator.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">Embedded Linux: Platforms and Applications</category>
        
        
         <pubDate>Thu, 26 Feb 2004 13:40:03 +0800</pubDate>
      </item>
            <item>
         <title>Linux 的應用（刊載於 PC2000 雜誌十二月號）-- Video Streaming 探討 (6)</title>
         <description><![CDATA[本期將是我們一系列 Video Streaming 專欄的結束，本期最重要的工作當然免不了是對之前的內容做系統性的整理，然後再對 Video Streaming 的應用就目前最被討論的理論做簡單介紹。

作者: 陳俊宏<br />  <a href="http://www.jollen.org/">www.jollen.org</a><br /><br /><p><font size="4" color="#ff0000"><strong>Video Streaming 系統概觀</strong></font></p>   <p>一個完整的 Video Streaming 系統應該包含四個部份：</p>   <ul><li>       <p>Content (例如影像、聲音、coded 資料)     </p></li><li>       <p>Server    </p></li><li>      <p>Client    </p></li><li>      <p>Data network (例如 Internet，或任何連結 server-client 的媒介)   </p></li></ul>   <p>那麼我們在這一系列 Video Streaming 主題介紹的有那些呢？從一開始的名詞介紹、影像擷取、RTP 通訊協定，我們將重心放在 contente 與 data network 上。當然我們系列主題名為「Linux 的應用」，最關鍵的地方則是在如何在 Linux 環境下設計影像擷取程式。</p>   <p>對於 data network 的部份因為與 Linux 較無關，但也是 Video Streaming 的重點主題之一，所以也介紹了 jrtplib 這套程式庫給大家，在底下的部份也會補充 Nsync 這套 toolkit。</p>   <p>而 server 端似乎是我們著墨不深的地方，但也沒關係，在這最後一篇的文章裡，我們針對理論方面做補充的介紹，我們將以 Video Conferencing 實作時，實務上會面臨的主題來做解，包括：多媒體資料的儲存、OS 的磁碟排程 (SCAN-EDF)，以做為您搜尋參考資料的起頭。</p>   <p>在 client 方面，我們在最後的完整版範例程式時，示範了如何利用 SDL 將擷取下來的影像資料秀在螢幕上。這裡也會配合了上一篇所介紹的 mmap 擷取方式，以將影像資料填入 SDL 的 display 結構方式秀圖。</p>   <p><font size="4" color="#ff0000"><strong>Video Streaming 的開始</strong></font></p>   <p>我們一開始就介紹了 Video Streaming 是一種經由網路來撥放影音檔案的技術，Video "Streaming" 的基本概念&#29234;「一邊下載一邊撥放」，我們稱之&#29234;「Play as received」。</p>   <p>經由 Internet 如果要收看遠端伺服器的電影檔案，最原始的做&#29234;是「下載後再撥放」，也就是經由 FTP 或 HTTP 將整個檔案下載至本地端後再利用撥放程式來撥放，我們稱之&#29234;「Play after download」。</p>  <p>舉個最簡單的情況，你可能在下載 MP3 之前想要試聽一下音樂的內容，而下載音樂片段的方式又顯得不夠友善，這時如果利用 Video Streaming 的技術來讓網友視聽，不但方便，而且不必浪費時間來下載不喜歡的音樂的。</p>   <p>除此之外，大家最熟悉的莫過於在線上即時播放影片的 real player了。</p>   <p><strong><font color="#0000ff">Real Video 產品介紹</font></strong></p>   <p>Real Video 是 Real Networks 公司的產品，Real Video 主要支援了 video-on-demand*1 的功能。Real Video 可以讓我們經由網站來播放串流影像 (streaming video)。</p>   <p>由於我們的最終目的是實作出一個可以做 video streaming 的軟體，所以在這裡我們將以 Real Video 做為標竿，並以 Linux 為基礎來設計 video streaming 的軟體。</p>   <p><font size="4" color="#ff0000"><strong>其它經典的 streaming 範例程式</strong></font></p>   <p>網路上有幾套很值得玩味研究的相關開放原始碼軟體，我們也曾經提過。VIC 和 VideoLAN 則是其中絕對優秀的教學範例。</p>   <p><strong><font color="#0000ff">VIC</font></strong></p>  <p>VIC 也是屬於 Open Source 的軟體。VIC 全名為video conferencing，故名其義，VIC 是一種視訊會議的軟體。VIC 是由加州柏克來大學的 Network Research Group 所發展。</p>   <p>VIC 是相當棒非常適合用來研究 Video Streaming 的 Open Source 軟體，主要是因為 VIC 幾乎包含了 Video Streaming 相關的技術。</p>   <p>VIC 值得我們研究的原因是因為 VIC 支援了底下所列的功能：</p>   <ul><li>       <p>IPv6    </p></li><li>      <p>使用 video4linux 的影像擷取功能    </p></li><li>      <p>H261、H263 與 H263+ codec     </p></li><li>       <p>Software JPEG 與 BVC 編碼     </p></li><li>       <p>Raw YUV packetiser/codec    </p></li><li>      <p>RTIP/RTP 通訊協定     </p></li><li>       <p>the IP Multicast Backbone (MBone)    </p></li><li>      <p>支援 video4linux 的 mmap   </p></li></ul>   <p>這些特色幾乎已經包括 Video Streaming 所應具備的技術了，基於這些特點，VIC 的原始程式碼相當吸引人，因此有意研究 Video Streaming 的 programmer 應該好好閱讀一下 VIC 的原始程式碼。</p>   <p><strong><font color="#0000ff">VideoLAN</font></strong></p>  <p>VideoLAN 是一個可以做 MPEG 與 DVD 擴播 (broadcast) 播放的軟體，VideoLAN 分成二個部份，一個是 VLAN server，另一個則是 vlc 用戶端播放程式。</p>   <p>VLAN server 將 DVD 與 MPEG 影像利用 broadcast 方式擴播到區域網路上，使用者端再利用 vlc 接收封包並播放。這樣做的好處是可以減少重覆的 I/O 動作，VLAN server 將影像擴播出去後，區域網路上的用戶端再利用 vlc 接收封包並播放。</p>   <p>VideoLAN 支援 X11、SDL、Linux framebuffer、GGI、BeOS API、MacOS X API 播放方式，並且支援 DVD 與 AC3 (杜比音效)。</p>   <p><strong><font size="4" color="#ff0000">影像編碼技術介紹 (coded)</font></strong></p>   <p>目前學術界已經發展出許多處理影像訊號壓縮及編碼的技術   (codecs)，談到這些技術，應用最廣泛的編碼標準底下四種：</p>  <ul><li>      <p>H.261    </p></li><li>      <p>H.263    </p></li><li>      <p>JPEG, MJPEG     </p></li><li>       <p>MPEG  </p></li></ul>  <p>本文第一篇文章就是在對這四種編碼技術做簡單的介紹。</p>  <p><font size="4" color="#ff0000"><strong>data network 的技術主題</strong></font></p>   <p>Video 在做 Streaming 時，有三種方式可以應用：broadcasting、unicasting、multicasting。</p>   <p>broadcasting 的方式比較單純，他是在 LAN 上直接將一個個的影像封包丟到網路上 (server 端)，再由 client 的應用程式自網路上取回封包播放。但網路硬體層上，仍有許多需要考慮的問題，例如在 Shared Non-Switched Enthernet 上時，就會發生一些小問題。</p>   <p>unicasting 與 multicasting 都是屬於 IP 的傳輸方式。unicasting 採取 1 對 1 的方向傳影像給遠端，稱為 Video-on-Demand (VoD)，multicasting 則是 1 對多的傳輸方式，稱為 Near-Video-on-Demand (NVoD)。未來 IPv6 將支援 IP Multicasting，因此 Video Streaming 的應用將更為廣泛。</p>   <p>在通訊協定方面，我們也介紹了 RTP。</p>   <p>RTP 全名為 Real-Time Protocol，RTP 是在 UDP 封包之前多加 10 bytes 的檔頭，裡面記載有時間、序號、壓縮型態等資訊。RTP 是目前大多數 Video Streaming 軟體所使用的通訊協定。</p>   <p>RTP 可用來針對各種不同的多媒體格式做 Streaming 的工作，因為我們將影像分解成數個 RTP 封包再傳送出去，因此會遇到許多網路技術常會遇到的問題。例如，因為封包送達的時間不一，造成播放時會畫面不流暢的現像，因此，在播發時就必須使用一個緩 衝區 (playout buffer) 來暫時存放並處理網路上接受到的封包。</p>   <p>由網路上接收的影像封包因為彼此之間到達的時間間隔不同 (Synchronous Data Packets)，所以必須利用緩衝區將這些封包做緩衝，讓彼此之間的時間間隔一樣 (Isochronous Data Packets)。</p>   <p>其它重要的通訊協定像是 SIP、或是 FEC (forward error correction) 除錯技術，都是一定要去研究的主題。</p>   <p><font size="4" color="#ff0000"><strong>影像擷取卡</strong></font></p>  <p>在 Linux 下設計影像擷取程式，當然一定要配備有適合的影像擷取卡。</p>   <p>我們曾經介紹給大家的是 Osprey 100 這張影像擷取卡。Osprey 100 是 Real Networks 公司所推薦配合他們產品的一張影像擷取卡，配合 Osprey 100 與 RealNetworks 的產品我們可以利用 broadcast 或 on-demand 做到實況轉播 (live) 的功能。</p>   <p>Osprey 100 在硬體功能上可以支援到每秒 30 個畫面 (fps -- frame per second)，並且支援 NTSC 與 PAL 輸入。</p>   <p>不過在實作上，筆者並不使用 Osprey 100。筆者使用的影像擷取卡是 ，這張卡算是比較「俗」一點的卡，但是也有好處，因為在 Linux 上很容易安裝。</p>   <p>以筆者這張卡為例，使用的是 Brooktree Corporation 的卡，所以只要安裝 bttv 模組即可，同時，bttv 模組在 Linux kernel 2.2.17 下也會用到 i2c-old 與 videodev 兩個模組，所以也要一併安裝。在命令列下，安裝這三個模組的命令為：</p>   <blockquote>     <p>linux# insmod i2c-old<br />     linux# insmod videodev<br />     linux# insmod bttv</p>   </blockquote>   <p>當然要確定 Linux kernel 有編譯這三個模組的支援，然後再把這三個模組加到 /etc/modules.conf (Red Hat 7.0) 裡。</p>   <p>不同版本的 kernel 所要安裝的模組不一定相同！還請注意，例如 i2c 相關模組就是如此。</p>   <p><font size="4" color="#ff0000"><strong>Linux 上可用的影像擷取卡</strong></font></p>   <p>在http://www.linhardware.com/db/searchproduct.cgi?_catid=17 網頁上可以找到在 Linux 上支援程度比較好的幾張影像擷取卡。而一般 Linux 上較受歡迎的影像擷取卡則是 Hauppauge 的幾張卡, 筆者使用的也是 Hauppauge 的卡。</p>   <p>在 linhardware 網站上可以找到底下六張卡:</p>   <ul><li>       <p>Hauppauge 401 WinTV-radio dbx-TV stereo     </p></li><li>       <p>Hauppauge WinTV PCI TV Card     </p></li><li>      <p>Hauppauge WinTV-GO PCI TV Card     </p></li><li>      <p>Hauppauge WinTV-PCI  Hauppauge     </p></li><li>      <p>Hauppauge WinTV-Radio+NICAM     </p></li><li>      <p>Hauppauge WinTV/PCI TV Card   </p></li></ul>   <p>關於 Linux 對於影像擷取卡支援的中文文件 (HOWTO) 可以在 CLDP 網站上取得:</p>   <blockquote>     <p><a target="_blank" href="http://www.linux.org.tw/CLDP/Hardware-HOWTO-22.html">http://www.linux.org.tw/CLDP/Hardware-HOWTO-22.html</a></p>  </blockquote>  <p><strong><font color="#0000ff">影像擷取卡支援的視訊系統</font></strong></p>  <p>大部份影像擷取卡都會具備一組視訊輸入端子, 即 S-Video (Y/C) 端子或 Composite 端子。</p>   <p>在台灣的標準當然是 NTSC 系統, 一般而言, 我們是希望一張影像擷取卡可以支援越多視訊系統越好, 包括:   NTSC/PAL/PALN/PLAM/SECAM。</p>  <p>可使用的視訊裝置有較常見的 CCD, 或是家用 V8、Hi8 皆可, 一般而言我們也是希望一張影像擷取卡可以接越多視訊裝置越好。</p>   <p><font size="4" color="#ff0000"><strong>BT 878 晶片</strong></font></p>   <p>跟隨在影像擷取卡之後的主題當然就是 BT878 晶片的介紹，因為支援 BT 8x8 晶片的 BTTV 軀動程式是我們設計影像擷取軟體的核力主力！</p>   <p>目前大部份的數位影像擷取卡大部份都是以 BT878 單顆晶片為影像擷取卡之中心。BT878 運作方式是以軟體來進行影像解壓縮工作, BT878 晶片負責將擷取之影像丟給 Linux 做影像處理, 而 BTTV 則是 Linux kernel 的 BT878 晶片軀動程式。</p>   <p>由於影像是利用 BT878 擷取後交由軟體來做影像處理, 因此在處理效能上自然就會比較差。如果是經由網路來傳送影像的話, 我們就會再利用影像壓縮技術 (H.261/H.263...等等) 來做影像處理。</p>   <p><font size="4" color="#ff0000"><strong>什麼是 BTTV</strong></font></p>   <p>BTTV 是 Linux 上的 Bt848/849/878/879 晶片的軀動程式, 主要功能是做頁框的截取 (frame grabber)。</p>   <p>BTTV 是 video4linux 裡重要的軀動程式, 目前分為二個版本:</p>   <ul><li>       <p>0.8.x 的發展中版本     </p></li><li>       <p>0.7.x 的穩定版本   </p></li></ul>   <p><font size="4" color="#ff0000"><strong>BTTV 相關應用軟體 - xawtv</strong></font></p>   <p>官方網站: <a href="http://bytesex.org/xawtv/index.html">http://bytesex.org/xawtv/index.html</a></p>  <p>安裝方式:</p>  <blockquote>    <p>linux# ./configure <br />    linux# make depend<br />     linux# make<br />     linux# make install</p>   </blockquote>   <p>如果您有 Red Hat Linux 7.1 PowerTools 光碟片的話, 也可以直接由 PowerTools 光碟片安裝 xawtv 套件:</p>   <blockquote>     <p>linux# rpm -ivh xawtv-3.34-1.i386.rpm</p>   </blockquote>   <p>安裝 xawtv 需要 libjpeg 與 libjpeg-devel 套件, 如果您是使用 Red Hat Linux 7.1 的話, 應該安裝底下二個套件:</p>   <ul><li>       <p>libjpeg-6b-15.i386.rpm (Disc 1)     </p></li><li>       <p>libjpeg-devel-6b-15.i386.rpm (Disc 2)   </p></li></ul>   <p>xawtv 整個架構可以分成 7 個部份如下:</p>   <ul><li>       <p><strong>xawtv</strong>: 主程式部份。     </p></li><li>       <p><strong>fbtv</strong>: linux console 模式的 TV 應用程式, 使用 linux kernel 2.2.x 的 framebuffer。     </p></li><li>       <p><strong>set-tv</strong>: 命令列模式的工具, 用來設定 video4linux 的參數。     </p></li><li>      <p><strong>streamer</strong>: 命令列模式的工具, 用來捉取動態影像與 avi 影像。     </p></li><li>      <p><strong>radio</strong>: radio 應用程式。     </p></li><li>       <p><strong>webcam</strong>: 將捉取的影像以 FTP 方式上傳到 Web Server 端, 用來設計 Web 即時影像的工具。     </p></li><li>       <p><strong>alevtd</strong>: videotext pages 的 Web Server。   </p></li></ul>   <p><font color="#0000ff"><strong>xawtv 的 video4linux</strong></font>   </p>      <p>xawtv 是相當好的 video4linux 方面的教材，我們極力推薦讀者研究 xawtv 的 video4linux 部份的原始碼。   </p>      <p>將取回 xawtv 的原始程式碼解開後, 在 libng/ 目錄下可以看到 grab-v4l.c 的檔案, 另外還有一個 grab-v4l2.c 的檔案, 這是 video4linux2 (version 2) 的版本。   </p>      <p>在 xawtv 的 video4linux 主題現身之前，我們很詳盡介紹了 video4linux 的基本設計方法，接下來在 xawtv 之後更是再進一步說明了 video4linux 的經典 – mmap 擷取技巧。   </p>      <p>那麼跨越三期內容的程式碼那一個才是完整的呢？事實上都沒有，不過請讀者們放心，本期我們將列出所有我們曾經介紹過的主題所實作的程式，當然是完整的實作程式碼！   </p>      <p><strong><font color="#0000ff">video4linux 使用的設備檔</font></strong>   </p>      <p>Linux 下與 video4linux 相關的設備檔與其用途：   </p>      <table width="411" border="1">   <tr>     <td width="86">  <p>/dev/video  </p>        </td>     <td width="72">  <p>Video  </p>        </td>     <td width="225">  <p>Capture Interface   </p>        </td>   </tr>   <tr>     <td width="86">  <p>/dev/radio  </p>        </td>     <td width="72">  <p>AM/FM  </p>        </td>     <td width="225">  <p>Radio Devices   </p>        </td>   </tr>   <tr>     <td width="86">  <p>/dev/vtx  </p>        </td>     <td width="72">  <p>Teletext  </p>        </td>     <td width="225">  <p>Interface Chips   </p>        </td>   </tr>   <tr>     <td width="86">  <p>/dev/vbi  </p>        </td>     <td width="72">  <p>Raw  </p>        </td>     <td width="225">  <p>VBI Data (Intercast/teletext)   </p>        </td>   </tr> </table> <p>video4linux 除了提供 programmer 與影像擷取有關的 API 外，也支援其它像是收音機裝置。   </p>      <p>接下來介紹 video4linux 設計方式，所使用的 Linux kernel 版本為 2.2.16。這篇文章將簡單介紹實作 video4linux 的方法，所以請準備好 Linux kernel 原始碼下的 Documentation/v4l/API.html 文件並了解 What's video4linux。 </p>      <p><strong><font size="4" color="#ff0000">Video Streaming 的其它關鍵議題</font></strong>   </p>      <p>接下來的主題將介紹 Video Streaming 其它值得研究的主題，我們會在最後才提出來的原因是因為這些主題將不會影響我們之前的程式實作，但在設計完整的 Video Streaming 系統時，則是有必要加以考慮的。   </p>      <p><strong><font color="#0000ff">Video Conferencing 應用的重要性</font></strong>   </p>      <p>Video Conferencing 在多媒體設計上之所以重要，最重要的原因是因為 Video Conferencing &#28169;及的技術議題包括：   </p>      <ul><li>      <p>即時性問題 (real-time systems problem)    </p></li><li>      <p>互動式應用程式的 latency 與 throughput 問題  </p></li></ul>  <p>這樣的問題當然首先是發生在網路頻寬的問題上，由於網路視訊會議系統耗費大量的頻寬，而且網路視訊會議的品質也容易受網路品質與頻寬影響，因此這是值得我們研究的問題之一。   </p>      <p>Video Conferencing 另外一個迷人的地方是在於 Video Conferencing 提供良好的 person-to-person 環境。Video Conferencing 應用軟體在多媒體程式設計上，常常也被視為「殺手級」的應用之一，可見 Video Conferencing 應用的重要性。 </p>      <p><strong><font color="#0000ff">Video Conferencing 的應用領域</font></strong>   </p>      <p>Video Conferencing 目前的應用領域則是有：遠距教學 (distance learning systems)、遠端診視系統 (remote consultation systems)、遊戲…等等。   </p>      <p>就如同我們先前所講的，解決網路、傳輸問題變成是下一代通訊應用的的關鍵。在軟體的支援上也是如此，當然這其中有許多的解決方案是設計新的多媒體作業系統 (Multimedia Operating Systems) 來解決。   </p>      <p>   因為我們要實現 VOD 的技術，因此將會涉及網路的主題，所以要考慮的層面也會比較多。除了網路相關問題外，也會在底下一併討論其它幾個主要的大問題。   </p>      <p><strong><font color="#0000ff">Video Streaming 的傳送問題</font></strong>   </p>      <p>Video Conferencing 所遭遇到的第一個問題是如何遞送 (deliver) 影像串流 (video streams)，這其中又要考慮到串流的管理、與網際網路即時性 (real-time over the Internet) 問題。   </p>      <p>Video Conferencing 重要的關鍵之一是在於如何有效縮短 latency。所以我們也必須尋找一個有效的方便，來適應各種不同網路頻寬的環境。   </p>      <p><strong><font color="#0000ff">Video Streaming 的資料儲存問題</font></strong>   </p>      <p>Video Streaming 的應用還要考慮的問題則是儲存設備 (storage) 的選擇。Video Streaming 的應用必須要有良好的儲存環境，來儲存各種型態的多媒體資料，包含：文字、影像、聲音、圖片等等，每種資料的特性都不相同。   </p>      <p>檔案系統 (filesystem) 對於多媒體物件的管理也是很重要的因素之一，必須要有一個可以快速存取並且有效管理多媒體物件的檔案系統，才能滿足效能的需求。   </p>      <p><strong><font color="#0000ff">作業系統的磁碟儲存</font></strong>  </p>    <p>在現階段 Video Streaming 以至於多媒體應用程式的設計上，對於 OS 支援的磁碟排程 (Disk Scheduler) 也被列入我們考慮研究的項目之一。傳統上，一般我們設計 OS 時都會選擇 SCAN 或是 SSTF 演算法，不過這些傳統的磁碟排程演算法並無法滿足我們的需求。 </p>      <p>較先進的磁碟排程演算法應考慮到 Video 與 Audio 的應用，而目前較普遍被選擇用來設計 multimedia I/O 系統的磁碟排程演算法則是  SCAN-EDF 演算法。   </p>      <p>SCAN-EDF 演算是結合 SCAN 與 EDF 優點的解決方案，SCAN 是眾所皆知的 seek optimizing 磁碟排程演算法；而 EDF (Earliest Deadline First) 則是屬於 real-time scheduling 的演算法。 </p>      <p><strong><font color="#0000ff">磁碟排程對於 Video Streaming 應用的影響</font></strong>   </p>      <p>引進 SCAN-EDF 磁碟排程演算法的重要之處在於我們必須要能支援 real-time request，其影響的範圍包括：   </p>      <ul><li>      <p>Maximum allowable streams    </p></li><li>      <p>Reponse time  </p></li></ul>  <p>SCAN-EDF 已被分析並證實可以改善以上的效能，那麼，對於目前廣受歡迎的 Linux 而言，由於 SCAN-EDF 已經早就在實作應用的範圍內了，我們可以將 SCAN-EDF 磁碟排程演算法加到 Linux kernel 裡。如此一來，Linux 在 Video Streaming 的應用上也算是重要的效能改良。 </p>      <p><strong><font color="#0000ff">Video Conferencing 的 Synchronization 問題</font></strong>   </p>      <p>當我們進行多方 (n-way) 視訊會議時，程式總不能讓每個人所看到的影像畫面都不相同吧！就算無法真正做到每個人的畫面同一時間都相同，但至少也要控制在合理可接受的範圍之內。   </p>      <p>Synchronization (同步) 問題的研究主要是在建立互動式 (interactive) 的多媒體應用程式上，同步問題的解決是需要相當多的時間與精神的，好在目前有重量級的 toolkit 供我們使用，那就是 Nsync (in-sync)。   </p>      <p>Nsync 共包含二大部份：</p>  <ul><li>      <p>Synchronization definition language    </p></li><li>      <p>Run-time presentation management system  </p></li></ul>  <p>當然我們的 Video Conferencing 應用程式當然也需要 Nsync 的幫忙！   </p>      <p><strong><font size="4" color="#ff0000">程式補充包</font></strong>  </p>    <p>底下我們將補充二個簡單的函數，供讀者使用，這二個函數與我們的範例程式並沒有直接關係，但在測試時可能會有機會使用到：  </p>    <ul><li>     <p>jpeg.c：將擷取的影像資料利用 libjpeg 存成 JPEG 圖檔。    </p></li><li>      <p>ppm.c：將擷取的影像直接寫成 PPM 圖檔。  </p></li></ul>  <p><strong><font color="#0000ff">存成 JPEG 圖檔部份</font></strong>   </p>      <p><em>檔案：jpeg.h</em><br />   /*<br />    * JoTV - Video Streaming Systems<br />    * (c) 2001 Jollen &lt;jollen@o3.net><br />    */<br />   #ifndef _JPEG_H_<br />   #define _JPEG_H_<br />   <br />   int write_jpeg(char *filename, IMG *img, int width, int height,<br />                  int quality, int gray);<br />   <br />   #endif<br />   <br />   檔案：jpeg.c<br />   /*<br />    * JoTV - Video Streaming Systems<br />    *<br />    * jpeg.c - output image to the jpeg files<br />    * (c) 2001 Jollen &lt;jollen@o3.net><br />    */<br />   #include &lt;stdio.h><br />   #include &lt;stdlib.h><br />   #include &lt;string.h><br />   #include &lt;errno.h><br />   #include &lt;time.h><br />   #include &lt;fcntl.h><br />   #include &lt;unistd.h><br />   #include &lt;ctype.h><br />   #include &lt;jpeglib.h><br />   #include &lt;jerror.h><br />   #include &lt;jconfig.h><br />   #include &lt;pthread.h><br />   #include &lt;sys/param.h><br />   #include &lt;sys/types.h><br />   #include "JoTV.h"<br />   <br />   int write_jpeg(char *filename, IMG *img, int width, int height,<br />           int quality, int gray)<br />   {<br />      struct jpeg_compress_struct jcfg;<br />      struct jpeg_error_mgr jerr;<br />     FILE *fp;<br />     unsigned char *line;<br />      int line_length;<br />      int i;<br />  <br />     if ((fp = fopen(filename,"wb")) == NULL) {<br />            fprintf(stderr,"write_jpeg: can't open %s: %s\n", filename, strerror(errno));<br />        return -1;<br />      }<br />  <br />     jcfg.err = jpeg_std_error(&amp;jerr);<br />      jpeg_create_compress(&amp;jcfg);<br />  <br />     jcfg.image_width  = width;<br />      jcfg.image_height = height;<br />      jcfg.input_components = gray ? 1: 3; // 3 sample per pixel (RGB)<br />      jcfg.in_color_space = gray ? JCS_GRAYSCALE: JCS_RGB;<br />      jpeg_set_defaults(&amp;jcfg);  // problem ...<br />   <br />      jpeg_stdio_dest(&amp;jcfg, fp);<br />   <br />      jpeg_set_quality(&amp;jcfg, quality, TRUE);<br />      jpeg_start_compress(&amp;jcfg, TRUE);<br />   <br />      line_length = gray ? width : width * 3;<br />      line = (unsigned char *)img;<br />  <br />     for (i = 0; i &lt; height; i++, line += line_length)<br />         jpeg_write_scanlines(&amp;jcfg, &amp;line, 1);<br />   <br />      jpeg_finish_compress(&amp;jcfg);<br />     jpeg_destroy_compress(&amp;jcfg);<br />     fclose(fp);<br />  <br />     return 0;<br />   }   </p>      <p>函數的呼叫方法如下，直接加到範例的主程式裡即可：   </p>      <p>write_jpeg("JoTV.jpg", img, DEFAULT_WIDTH,  DEFAULT_HEIGHT, 75, FALSE /*not grey*/);  </p>    <p>中標=存成 PPM 圖檔部份   </p>      <p><em>檔案：ppm.c</em><br />   /*<br />    * JoTV - Video Streaming Systems<br />    *<br />    * ppm.c - output image to the ppm file<br />    * (c) 2001 Jollen &lt;jollen@o3.net><br />    */<br />   int write_ppm(IMG *);<br />   <br />   int write_ppm(IMG *img)<br />   {<br />      FILE *fp;<br />  <br />     fp = fopen("test.ppm", "w");<br />      fprintf(fp, "P6\n%d %d\n255\n", NTSC_WIDTH, NTSC_HEIGHT);<br />      fwrite(img, NTSC_HEIGHT, 3*NTSC_WIDTH, fp);<br />      fclose(fp);<br />  }<br />  </p>    <p>數的呼叫方法如下，直接加到範例的主程式裡即可：  </p>    <p>write_ppm(img);  </p>    <p>圖檔會存成檔名：test.ppm，由於大部份的繪圖軟體對於 TIFF 的支援較好，所以可以再利用 ppm2tiff 工具將 PPM 圖檔轉成 TIFF 格式：   </p>      <p>$ ppm2tiff test.ppm test.tif   </p>      <p><font size="4" color="#ff0000"><strong>Video Streaming 的應用實例</strong></font>   </p>      <p>經過這一系列的 Linux Video Streaming 應用探討，我們已經可以有能力設計出許多簡單的 Video Streaming 應用程式。例如以 video4linux、bttv、rtp 和 jpeg 壓縮技術便能實作簡單的 Webcam 應用程式。 </p>      <p>底下是我們利用 Java 實作出來的 Linux Webcam 應用程式，在遠端直接以瀏覽器來執行即可。   </p>      <p><img width="375" height="397" border="0" src="http://www.jollen.org/linux/video_streaming/6-1.jpg" /><br /> 圖 1 利用 Java 與 Video Streaming 技術設計的 Linux Webcam   </p>      <p><img width="376" height="392" border="0" src="http://www.jollen.org/linux/video_streaming/6-2.jpg" /><br />  圖 2 利用 Java 與 Video Streaming 技術設計的 Linux Webcam   </p>      <p>Webcam 與 CCD 結合的應用包括：遠距教學、視訊會議、安全監控…等等。   </p>]]></description>
         <link>https://www.jollen.org/blog/2001/10/linux_video_streaming_6.html</link>
         <guid>https://www.jollen.org/blog/2001/10/linux_video_streaming_6.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Thu, 18 Oct 2001 12:06:00 +0800</pubDate>
      </item>
            <item>
         <title>Linux 的應用（刊載於 PC2000 雜誌十一月號）-- Video Streaming 探討 (5)</title>
         <description><![CDATA[本期將以完整的程式範例為主, 說明之前未深入說明的地方。並且更詳細地介紹video4linux 如何以 mmap (filp-flop) 方式擷取影像資料, 同時也會展示如何將擷取出來的影像存成圖檔, 並且利用繪圖軟體開啟。

作者: 陳俊宏<br /> <a href="http://www.jollen.org/">www.jollen.org</a><br /><br /><p><font size="4" color="#ff0000"><strong>mmap 的初始化從那裡開始</strong></font></p>  <p>繼前四期介紹有關 Video Streaming 的內容後, 最近收到幾位讀者的來信, 詢問有關 video4linux 利用 mmap 擷取影像的方法。video4linux 以 mmap 擷取影像的方法在本文第 4 篇曾經簡單介紹過, 但是有讀者希望可以做更詳細的介紹, 因此筆者特別將相關的程式碼完整列出供參考。</p>  <p>要提到 mmap 的初始化, 我們要配合第 2 篇文章的程式範例。底下是對影像擷取裝置做初始化的程式碼, 與第 2 篇文章的範例比較, 底下的函數設計的更完整:</p>  <blockquote>    <p>int device_init(char *dev, int channel, int norm)<br />    {<br />       int i;<br />   <br />      if (dev == NULL) {<br />          dev = "/dev/video0";              //set to default device<br />       }<br />   <br />      if (v4l_open(dev, &amp;vd)) {<br />          return -1;<br />       } else {<br />          v4l_grab_init(&amp;vd, screen_width,    screen_height);  //wake up drivers!<br />          v4l_close(&amp;vd);<br />      }<br />   <br />      if (v4l_open(dev, &amp;vd)) return -1;<br />       if (v4l_get_channels(&amp;vd)) return -1;<br />       if (v4l_set_norm(&amp;vd, norm)) return -1;<br />       if (v4l_mmap_init(&amp;vd)) return -1;<br />       if (v4l_switch_channel(&amp;vd, channel)) return -1;<br />    <br />       printf("%s: initialization OK... %s\n"<br />                "%d channels\n"<br />                "%d audios\n\n",    dev, vd.capability.name, vd.capability.channels, vd.capability.audios);<br />   <br />      for (i = 0; i &lt; vd.capability.channels; i++) {<br />          printf("Channel %d: %s (%s)\n", i,    vd.channel[i].name,<br />         v4l_norms[vd.channel[i].norm].name);<br />      }<br />   <br />      printf("v4l: mmap's address = %p\n", vd.map);<br />      printf("v4l: mmap's buffer size = 0x%x\n", vd.mbuf.size);<br />      printf("v4l: mmap's frames = %d (%d max)\n", vd.mbuf.frames,    VIDEO_MAX_FRAME);<br />      for (i = 0; i &lt; vd.mbuf.frames; i++) {<br />          printf("v4l: frames %d's offset = 0x%x\n",    i, vd.mbuf.offsets[i]);<br />      }<br />   <br />      printf("v4l: channel switch to %d (%s)\n", channel,    vd.channel[channel].name);<br />   <br />      // start initialize grab<br />       if (v4l_get_picture(&amp;vd)) return -1;<br />       if (v4l_set_palette(&amp;vd, DEFAULT_PALETTE)) return -1;<br />       if (v4l_grab_init(&amp;vd, screen_width, screen_height)) return -1;<br />       if (v4l_grab_sync(&amp;vd)) return -1;<br />    <br />       return 0;<br />    }</p>  </blockquote>  <p>我們又把 device_init() 寫的更完整了。粗體字的地方是我們初始化 mmap 的程式碼, 一開始的程式可能又讓人覺得一臉汒然:</p>  <blockquote>    <p>if (v4l_open(dev, &amp;vd)) {<br />       return -1;<br />     } else {<br />       v4l_grab_init(&amp;vd, screen_width, screen_height);  //wake up drivers!<br />       v4l_close(&amp;vd);<br />    }</p> </blockquote> <p>將 device 開啟成功後, 做了一次 v4l_grab_init 後再把 device 關掉, 用意何在呢? 其實, 是因為 bttv 的 driver 是以 module 的方式安裝到 Linux kernel, 所以 bttv driver 會因為沒有被使用, 而「睡覺了」。</p>  <p>我們加上一次 v4l_grab_init() 的目的就是為了要「叫醒」bttv 的 driver, 其實這個動作可有可無, 但一般認為加上會比較好。</p>  <p>v4l_mmap_init() 是對 mmap 做初始化的工作, 不過要特別注意, 這個動作要在 channel 與 norm 都設定好後才進行, 底下會再說明一次。</p>  <p>v4l_mmap_init() 相當重要, 因為我們要利用 mmap() 函數將 v4l_deivce 結構裡的 map「連接」起來。mmap() 是 POSIX.4 的標準函數, 用途是將 device 給 map 到記憶體, 也就是底下粗體字的地方:</p>  <blockquote>    <p>int v4l_mmap_init(v4l_device *vd)<br />    {<br />       if (v4l_get_mbuf(vd) &lt; 0)<br />       return -1;<br />    <br />       if ((vd->map = mmap(0, vd->mbuf.size, PROT_READ|PROT_WRITE,    MAP_SHARED, vd->fd, 0)) &lt; 0) {<br />          perror("v4l_mmap_init:mmap");<br />         return -1;<br />       }<br />      return 0;<br />    }</p>  </blockquote>  <p>PROT_READ 表示可讀取該 memory page , PROT_WRITE 則是可寫入, MAP_SHARED 則是讓這塊 mapping 的區域和其它 process 分享。第一個參數旦 0 是啟始位置, vd->mbuf.size 則是長度 (length)。vd->fd 則是 device 的 file description, 最後一個參數是 offset。</p>  <p>v4l_get_mbuf() 和之前介紹過的沒有什麼出入。在新的 device_init() 函數裡, 我們也把初始化好的 mmap 相關資訊印出。</p>  <p><font size="4" color="#ff0000"><strong>channel 與 norm</strong></font></p>  <p>我們提過, 在做 v4l_mmap_init() 前要先做 channel 與 norm 的設定, 分別是 v4l_get_channels() 與 v4l_set_norm() 函數。</p>  <p>在這裡要捕充說明一點, 以筆者的 CCD 頭來講, 和擷取卡是以 Composite1 連接, 所以在 channel 方面, 就要利用 v4l_switch_channel() 將 channel 切到 Composite1 端。</p>  <p>v4l_switch_channel() 程式碼如下:</p>  <blockquote>    <p>int v4l_switch_channel(v4l_device *vd, int c)<br />    {<br />       if (ioctl(vd->fd, VIDIOCSCHAN, &amp;(vd->channel[c])) &lt; 0) {<br />          perror("v4l_switch_channel:");<br />         return -1;<br />       }<br />      return 0;<br />    }</p>  </blockquote>  <p>傳入的 c 是 channel, 而 channel number 我們已經在 device_init() 裡列印出來:</p>  <blockquote>    <p>Channel 0: Television <br />   Channel 1: Composite1 <br />   Channel 2: S-Video</p>  </blockquote>  <p>我們可以看到 Composite1 位於 Channel 1 (由 0 算起), 所以 v4l_switch_channel() 的參數 c 要傳入 1。</p>  <p><font size="4" color="#ff0000"><strong>如何設定 norm</strong></font></p>  <p>norm 的話就比較單純一點, 參數如下:</p>  <blockquote>    <p>VIDEO_MODE_PAL <br />   VIDEO_MODE_NTSC <br />   VIDEO_MODE_SECAM <br />   VIDEO_MODE_AUTO</p> </blockquote> <p>這些參數都定義於 videodev.h 檔案裡。v4l_set_norm() 是我們用來設定 norm 的函數, 程式碼如下:</p>  <blockquote>    <p>int v4l_set_norm(v4l_device *vd, int norm)<br />    {<br />       int i;<br />   <br />      for (i = 0; i &lt; vd->capability.channels; i++) {<br />          vd->channel[i].norm = norm;<br />       }<br />   <br />      if (v4l_get_capability(vd)) {<br />          perror("v4l_set_norm");<br />         return -1;<br />       }<br />      if (v4l_get_picture(vd)) {<br />          perror("v4l_set_norm");<br />      }<br />      return 0;<br />    }</p>  </blockquote>  <p>要仔細注意, 我們是對所有的 channel 設定 norm, 設定完成後, 底下又做了一次 v4l_get_capability(), 主要目的是確保每個 channel 的設定都有被設定成功。然後呼叫 v4l_get_picture。</p>  <p>v4l_get_capability() 會利用 ioctl() 取得設備檔的相關資訊，並且將取得的資訊放到 struct video_capability 結構裡。同理，v4l_get_picture() 也會呼叫 ioctl() ，並將影像視窗資訊放到 struct video_picture 結構。</p>  <p><font size="4" color="#ff0000"><strong>如何 get picture</strong></font></p>  <p>取得設備資訊後，我們還要再取得影像資訊，所謂的影像資訊指的是輸入到影像捕捉卡的影像格式。</p>  <p>在 _v4l_struct 結構裡，我們宣告 channel 如下：</p>  <blockquote>    <p>struct video_picture picture;</p>  </blockquote>  <p>初始化 picture 的意思就是要取得輸入到影像捕捉卡的影像資訊，我們設計 v4l_get_ picture() 函數來完成這件工作。</p>  <p>v4l_get_ picture () 完整程式碼如下：</p>  <blockquote>    <p>int v4l_get_picture(v4l_device *vd)<br />    {<br />       if (ioctl(vd->fd, VIDIOCGPICT, &amp;(vd->picture)) &lt; 0) {<br />          perror("v4l_get_picture:");<br />         return -1;<br />       }<br />      return 0;<br />    }</p>  </blockquote>  <p>傳遞VIDIOCGPICT 給 ioctl() 則會傳回影像的屬性 (image properties)，這裡則是將影像屬性存放於 vd-> picture。</p>  <p>這部份我們也曾經介紹過, 在這裡要再捕充一點。如果是以 GREY 方式擷取影像, 那麼我們可以利用 VIDIOCSPIC 來設定像素的亮度與灰階度, 請參考 API.html 裡的 struct video_picture 說明。</p>  <p><font size="4" color="#ff0000"><strong>初始化 grab</strong></font></p>  <p>初始化 grab 的程式碼如下:</p>  <blockquote>    <p>if (v4l_get_picture(&amp;vd)) return -1;<br />     if (v4l_set_palette(&amp;vd, DEFAULT_PALETTE)) return -1;<br />     if (v4l_grab_init(&amp;vd, screen_width, screen_height)) return -1;<br />     if (v4l_grab_sync(&amp;vd)) return -1;</p>  </blockquote>  <p>v4l_get_picture() 與之前介紹的一樣, 而 v4l_set_palette() 則是用來設定調色盤, 由於我們希望得到的是 RGB32, 所以 DEFAULT_PALETTE 定義成:</p>  <blockquote>    <p>#define DEFAULT_PALETTE VIDEO_PALETTE_RGB32</p>  </blockquote>  <p>如果沒有硬體轉換, 前一篇文章 (4) 我們也提到將 YUV (PAL) 轉成 RGB 的方法了。再來將就是對 grab 做初始化, v4l_grab_init() </p> <blockquote>   <p>int v4l_grab_init(v4l_device *vd, int width, int height)<br />    {<br />       vd->mmap.width = width; <br />      vd->mmap.height = height; <br />      vd->mmap.format = vd->picture.palette; <br />        vd->frame_current = 0;<br />       vd->frame_using[0] = FALSE;<br />       vd->frame_using[1] = FALSE;<br />    <br />       return v4l_grab_frame(vd, 0);<br />    }</p>  </blockquote>  <p>初始化的目的是將 mmap 結構填入適當的值。針對 RGB32、NTSC 的 CCD 影像擷取, mmap 的大小不妨設定成 640*480 或 320*240 都可以, 給定 mmap 的大小後, 再來還要將 format 填入調色盤類型。</p>  <p>最後設定 frame_current 變數與 frame_using[] 陣列, 這裡等於上一篇 (4) 介紹的 frame 變數與  framestat[] 陣列。</p>  <p>如何所有的程式碼都沒有錯誤, 當裝置正常軀動時, 就可以看到底下的初始化訊息, 這裡的訊息比起之前的範例更清楚、完整:</p>  <blockquote>    <p>/dev/video0: initialization OK... BT878(Chronos Video Shuttle I)<br />    3 channels<br />    3 audios<br />    <br />    Channel 0: Television (NTSC)<br />    Channel 1: Composite1 (NTSC)<br />    Channel 2: S-Video (NTSC)<br />    v4l: mmap's address = 0x40173000<br />    v4l: mmap's buffer size = 0x410000<br />    v4l: mmap's frames = 2 (32 max)<br />    v4l: frames 0's offset = 0x0<br />    v4l: frames 1's offset = 0x208000<br />    v4l: channel switch to 1 (Composite1)<br />    <br />    Image pointer: 0x4037b000</p>  </blockquote>  <p><font size="4" color="#ff0000"><strong>v4l_grab_frame() 的用處</strong></font></p>  <p>讀者可能還不明白 v4l_grab_frame() 的用途, v4l_grab_frame() 是真正將影像放到 mmap 裡的函數。</p>  <p>我們重寫一次 v4l_grab_frame() 函數, 並且再說明一次:</p>  <blockquote>    <p>int v4l_grab_frame(v4l_device *vd, int frame)<br />    {<br />       if (vd->frame_using[frame]) {<br />               fprintf(stderr, "v4l_grab_frame: frame %d is already used.\n", frame);<br />          return -1;<br />       }<br />   <br />      vd->mmap.frame = frame;<br />       if (ioctl(vd->fd, VIDIOCMCAPTURE, &amp;(vd->mmap)) &lt; 0) {<br />          perror("v4l_grab_frame");<br />         return -1;<br />       }<br />      vd->frame_using[frame] = TRUE;<br />       vd->frame_current = frame;<br />       return 0;<br />    }</p>  </blockquote>  <p>因為我們用 frame_using[] 陣列來紀錄那個 frame 已經被使用, 所以一開始當然要先判斷目前的 frame 是否已經被使用:</p>  <blockquote>    <p>if (vd->frame_using[frame]) {<br />       fprintf(stderr, "v4l_grab_frame: frame %d is already used.\n", frame);<br />       return -1;<br />     }</p>  </blockquote>  <p>如果沒有被使用, 就把 mmap 的 frame 填入 frame 編號, 然後利用 VIDIOCMCAPTURE 擷取出影像。結束前要把目前 frame 的狀態標示成使用中 (frame_using[]), 然後把 frame_current 指定成現在的 frame, 完成工作後離開。</p>  <p><font size="4" color="#ff0000"><strong>mmap 如何做 filp-flop</strong></font></p> <p>這是一位讀者問的問題。這個問題問的相當聰明, 每個人可能都有不同的方法來做 flip-flop 的動作, 這裡筆者以 2 個 frame 為例, 我們可以再寫一個函數來做 flip-flop:</p>  <blockquote>    <p>int device_grab_frame()<br />    {<br />       vd.frame_current = 0;<br />    <br />       if (v4l_grab_frame(&amp;vd, 0) &lt; 0)<br />          return -1;<br />    <br />          return 0;<br />       }<br />   <br />   int device_next_frame()<br />    {<br />       vd.frame_current ^= 1;<br />       if (v4l_grab_frame(&amp;vd, vd.frame_current) &lt; 0)<br />          return -1;<br />    <br />       return 0;<br />    }</p>  </blockquote>  <p>device_next_frame() 是主要核心所在, 因為我們只有二個 frame, 所以 frame_current 不是 0 就是 1。</p>  <p><font size="4" color="#ff0000"><strong>擷取出來的影像放在那裡</strong></font></p> <p>因為我們特別寫了上面的函數來做 mmap 的 flip-flop, 所以在主程式裡就改用 device_next_frame 來持續擷取影像。</p>  <p>所以配合主程式, 我們的程式寫法如下:</p>  <blockquote>    <p>device_next_frame();                      //Ok, grab a frame.<br />    device_grab_sync();                       //Wait until captured.<br />    <br />    img = device_get_address();               //Get image pointer.<br />    printf("\nImage pointer: %p\n", img);</p> </blockquote> <p>這段程式就是我們的重點好戲, 當我們呼叫 device_next_frame() 擷取 frame 之後, 必須做一個等待的動作, 讓 frame 擷取完成再取出影像。</p>  <blockquote>    <p>v4l_grab_sync() 程式碼如下:<br />    <br />    int v4l_grab_sync(v4l_device *vd)<br />    {<br />       if (ioctl(vd->fd, VIDIOCSYNC, &amp;(vd->frame_current)) &lt; 0) {<br />          perror("v4l_grab_sync");<br />      }<br />      vd->frame_using[vd->frame_current] = FALSE;<br />       return 0;<br />    }</p>  </blockquote>  <p>利用 VIDIOCSSYNC 等待完成後, 別忘了將目前 frame 的狀態改回未被使用。接下來我們要問, 擷出出來的 frame 到底放到那裡去了呢?</p>  <p>答案就是之們利用 mmap() 將 device 所 map 的記憶體裡, 因為我們是利用 mmap (flip-flop) 方式, 所以會有 2 個 (或以上) 的 frame, 這時就要計算一下 offset, 才知道到底目前的影像資料被放到那裡了。</p>  <p>算式如下:</p>  <blockquote>    <p>vd.map + vd.mbuf.offsets[vd.frame_current]</p> </blockquote> <p>device_get_address() 函數就是這麼回事。</p>  <p><font size="4" color="#ff0000"><strong>如何輸出影像資料呢</strong></font></p> <p>輸出影像資料的方法很多, 可以直接輸出到 framebuffer 上, 或是利用 SDL 顯示。在這裡筆者要示範最原始的方法 – 輸出到檔案裡。</p>  <p>當我們利用 device_get_address() 取得 frame 的影像資料後, 再將 frame 的影像資料輸出成 PPM 格式的檔案。</p>  <p>程式碼如下:</p>  <blockquote>    <p>FILE *fp;<br />    <br />    fp = fopen("test.ppm", "w");<br />    fprintf(fp, "P6\n%d %d\n255\n", NTSC_WIDTH, NTSC_HEIGHT);<br />    fwrite(img, NTSC_WIDTH, 3*NTSC_HEIGHT, fp);<br />    fclose(fp);</p> </blockquote> <p>先利用 fprintf() 寫入 PPM 檔案的檔頭資訊, 然後以 fwrite() 將傳回的影像資料寫到檔案裡。</p>  <p>img 指向記憶體裡的 frame 影像資料, 寫入時, 請特別注意粗體字的地方, 因為我們是用 RGB32 的調色盤, 而 RGB 是以 3 個 sample 來表示一個 pixel, 所以要乘上 3。</p>  <p>如果是 GREY 調色盤, 就不用再乘 3 了。最後將輸出的 PPM 檔案轉換格式成 TIFF 就可以用一盤的繪圖軟體打開了:</p>  <blockquote>    <p>linux$ ppm2tiff test.ppm test.tiff</p>  </blockquote>  <p><font size="4" color="#ff0000"><strong>將影像存成 JPEG 的方法</strong></font></p>  <p>最後我們再完成一個功能, 就可以實作出一個完整的 Webcam 軟體。之前我們將影像存成 PPM 格式的圖檔, 不過因為檔案過太, 會造成傳輸的不便。因此, 我們勢必要將影像資料存成更小的檔案才具實用性。</p>  <p>JPEG 或MJPEG 都是在本文第 1 篇介紹過的格式。以 JPEG 來存放圖檔, 相當容易可以實作出 Webcam 的功能, 但缺點就是無法傳送聲音資料。</p>  <p>我們使用 mpeglib 來完成這項任務, mpeglib 可至 www.ijg.org 下載。</p>  <p>將影像資料存成 JPEG 的方法在「各大」與 video streaming 有關的軟體 (例如: xawtv) 都可以看得到範例。不過因此這部份已脫離 v4l 的主, 所以筆者只列出底下的 write_jpeg() 完整函數, 供讀者使用:</p>  <blockquote>    <p>int write_jpeg(char *filename, unsigned char * img, int width, int height, int quality, int gray)<br />    {<br />       struct jpeg_compress_struct jcfg;<br />      struct jpeg_error_mgr jerr;<br />      FILE *fp;<br />      unsigned char *line;<br />       int line_length;<br />      int i;<br />   <br />      if ((fp = fopen(filename,"w")) == NULL) {<br />          fprintf(stderr,"write_jpeg: can't open %s: %s\n", filename,    strerror(errno));<br />         return -1;<br />       }<br />   <br />      jcfg.image_width  = width;<br />       jcfg.image_height = height;<br />       jcfg.input_components = gray ? 1: 3;        // 3 sample per pixel (RGB)<br />       jcfg.in_color_space = gray ? JCS_GRAYSCALE: JCS_RGB;<br />       jcfg.err = jpeg_std_error(&amp;jerr);<br />    <br />       jpeg_create_compress(&amp;jcfg);<br />         jpeg_stdio_dest(&amp;jcfg, fp);<br />    <br />       jpeg_set_defaults(&amp;jcfg);<br />      jpeg_set_quality(&amp;jcfg, quality, TRUE);<br />          jpeg_start_compress(&amp;jcfg, TRUE);<br />    <br />       line_length = gray ? width : width * 3;<br />       for (i = 0, line = img; i &lt; height; i++, line += line_length)<br />               jpeg_write_scanlines(&amp;jcfg, &amp;line, 1);<br />    <br />       jpeg_finish_compress(&amp;jcfg);<br />         jpeg_destroy_compress(&amp;jcfg);<br />      fclose(fp);<br />   <br />      return 0;<br />    }</p>  </blockquote>  <p>利用 mpeglib 寫入 JPEG 影像資料時, 必須分別對每行 scanline 寫入。呼叫範例:</p>  <blockquote>    <p>write_jpeg("test01.jpg", img, NTSC_WIDTH, NTSC_HEIGHT, 50, FALSE );</p>  </blockquote>  <p>第一個參數是圖檔名稱, 第二個參數是影像資料, 然後第三、第四個參數接著影像的大小, 第五個參數 50 表示 JPEG 圖檔的壓縮品質 (quality), 最後一個參數 FALSE 表示影像資料不是 grey (灰階) 影像。</p>  <p>灰階影像與彩色影像的差別在於 input_components、in_color_space 與 scanline 的長度。</p>  <p><font size="4" color="#ff0000"><strong>結語</strong></font></p> <p>在一連串的 Video Streaming 主題裡, 我們學到 video4linux 擷取影像的方式, 以 mmap (flip-flop) 來連續擷取影像, 並做到 VOD 的功能是我們的最終目的。到這裡為止, 我們已經有能力實作出簡單的 Webcam 軟體, 類似這種取固定間隔傳送影像的方式應用也很廣, 例如路口交通狀況回報。</p>  <p>利用到這裡所學的方法, 將擷取的影像存成 JPEG, 然後放到 Web 上, 固定一段時間更新, 我們也可以設計一套簡單的路口交通狀況回報系統, 或是家裡的監視系統。</p>  <p>後面接著的主題, 將會以現有的程式為基礎, 實作真正具有 VOD 能力的軟體。</p>]]></description>
         <link>https://www.jollen.org/blog/2001/09/linux_video_streaming_5.html</link>
         <guid>https://www.jollen.org/blog/2001/09/linux_video_streaming_5.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Fri, 14 Sep 2001 08:31:00 +0800</pubDate>
      </item>
            <item>
         <title>Linux 的應用（刊載於 PC2000 雜誌九月號）-- Video Streaming 探討 (4)</title>
         <description><![CDATA[本期的重點在介紹擷取 frame 的方法, 並且將重心由 video4linux 轉移到網路方面。在網路影像即時傳送方面, 我們採用的 RTP 也是各大廠商使用的標準, 在這一期裡, 我們將可以學習到利用 JRTPLIB 來加入網路功能的方法。

作者: 陳俊宏<br />  <a href="http://www.jollen.org/"> www.jollen.org</a><br /><br /><p><font size="4" color="#ff0000"><strong>video4linux 擷取 frame 的方法</strong></font></p>   <p>在上一期的 xawtv 裡, 我們看到了 xawtv 的影像擷取功能, 其中對我們最重要的部份是利用 video4linux 做影像擷取的部份。</p>   <p>只要可以寫出 video4linux 的軀動部份, 要做影像擷取其實是很容易的, 我們利用的是 mmap 的方式來擷取影像。</p>   <p><font color="#0000ff"><strong>mmap 擷取方式</strong></font></p>   <p>為了說明如何以 mmap 方式來擷取影像, 我們不建議讀者直接去研究 xawtv 關於這部份的程式碼。研究過幾個有關支援 mmap 影像擷取的軟體原始碼後, 我們建議讀者去下載一支名為 EffecTV 的程式, 其官方網頁為:</p>   <blockquote>     <p><a href="http://effectv.sourceforge.net/index.html">http://effectv.sourceforge.net/index.html</a></p>  </blockquote>  <p>EffecTV 是日本人設計的程式, 也是經由 video4linux 做影像擷取, 在 mmap 的程式碼方面, EffecTV 會比較容易懂, 同時也可以藉由 EffecTV 來學習一些影像處理的技巧。</p>   <p>EffecTV 是一個可以支援特效功能的視訊軟體, 是頗有趣的程式。</p>   <p><strong><font color="#0000ff">主要函數介紹</font></strong></p>  <p>EffecTV 裡與影像擷取 (frame grab) 有關的函數為:</p>   <blockquote>     <p>int video_grab_check(int palette);<br />     int video_set_grabformat();<br />     int video_grabstart();<br />     int video_grabstop();<br />     int video_syncframe();<br />     int video_grabframe();</p>  </blockquote>  <p>這些函數定義在 video.h 裡。我們不再重覆介紹 video4linux 初始化的地方, 在 frame grab 方面, 呼叫 video_grabstart() 開始進行影像擷取的工作, 程式碼如下:</p>   <blockquote>     <p>/* Start the continuous grabbing */<br />     int video_grabstart()<br />     {<br />        vd.frame = 0;<br />        if(v4lgrabstart(&amp;vd, 0) &lt; 0)<br />           return -1;<br />        if(v4lgrabstart(&amp;vd, 1) &lt; 0)<br />           return -1;<br />        return 0;<br />     }</p>   </blockquote>   <p>其中主角是 v4lgrabstart() 函數, 這個函數被實作在 v4lutils/v4lutils.c 裡, 程式碼如下:</p>   <blockquote>     <p>/*<br />    * v4lgrabstart - activate mmap capturing<br />    *<br />    * vd: v4l device object<br />    * frame: frame number for storing captured image<br />    */<br />     int v4lgrabstart(v4ldevice *vd, int frame)<br />     {<br />        if(v4l_debug) fprintf(stderr, "v4lgrabstart: grab frame %d.\n",frame);<br />        if(vd->framestat[frame]) {<br />           fprintf(stderr, "v4lgrabstart: frame %d is already used to grab.\n", frame);<br />        }<br />       vd->mmap.frame = frame;<br />        if(ioctl(vd->fd, VIDIOCMCAPTURE, &amp;(vd->mmap)) &lt; 0) {<br />           v4lperror("v4lgrabstart:VIDIOCMCAPTURE");<br />          return -1;<br />        }<br />       vd->framestat[frame] = 1;<br />        return 0;<br />     }</p>   </blockquote>   <p>v4lgrabstart() 是利用 mmap 的方式來取得影像。v4lgrabstart() 也是利用 ioctl() 來完成這個低階的動作, 與第本文第二篇實作 video4linux 時一樣, 寫法為:</p>   <blockquote>     <p>ioctl(vd->fd, VIDIOCMCAPTURE, &amp;(vd->mmap));</p>   </blockquote>   <p>vd 裡的 framestat 欄位主要是紀錄目前的 frame 狀態:</p>   <blockquote>     <p>vd->framestat[frame]</p>   </blockquote>   <p>這個欄位定義在 v4lutils.h 裡, 而利用 mmap 的方式我們需要兩個 frame 來存放影像資料, 所以 framestat 宣告成二個元素的陣列, 我們將 EffecTV 的 v4l 結構定義完整列出如下:</p>   <blockquote>     <p>struct _v4ldevice<br />     {<br />        int fd;<br />        struct video_capability capability;<br />        struct video_channel channel[10];<br />        struct video_picture picture;<br />        struct video_clip clip;<br />        struct video_window window;<br />        struct video_capture capture;<br />        struct video_buffer buffer;<br />        struct video_mmap mmap;<br />        struct video_mbuf mbuf;<br />        struct video_unit unit;<br />        unsigned char *map;<br />        pthread_mutex_t mutex;<br />        int frame;<br />        int framestat[2];<br />        int overlay;<br />     };</p>   </blockquote>   <p>請讀者回頭對應一下本文第二篇文章所實作的內容, EffecTV 的實作更為完整。</p>   <p>當我們開始 grab 影像到其中一個 frame 時, 我們就把 frame 的狀態設成 1:</p>   <blockquote>     <p>vd->framestat[frame] = 1;</p>   </blockquote>   <p>然後利用 v4lsync() 等待 frame 擷取完成, 利用 ioctl() 傳入 VIDIOCSYNC 可以檢查 frame 是否已經擷取完成:</p>   <blockquote>     <p>if(ioctl(vd->fd, VIDIOCSYNC, &amp;frame) &lt; 0) {<br />        v4lperror("v4lsync:VIDIOCSYNC");<br />       return -1;<br />           }<br />           vd->framestat[frame] = 0;<br />           return 0;</p>   </blockquote>   <p>如果 frame 已擷取完成, 那麼我們就將 frame 的狀態設成 0, 表示目前這個 frame 並沒有在做擷取的動作, 也因此在 v4lsync() 一開始的地方我們會先做這部份的檢查:</p>   <blockquote>     <p>if(vd->framestat[frame] == 0) {<br />        fprintf(stderr, "v4lsync: grabbing to frame %d is not started.\n", frame);<br />           }</p>   </blockquote>   <p>v4lsync() 函數也是一個重要的函數, 程式碼如下:</p>   <blockquote>     <p>/*<br />    * v4lsync - wait until mmap capturing of the frame is finished<br />    *<br />    * vd: v4l device object<br />    * frame: frame number<br />    */<br />     int v4lsync(v4ldevice *vd, int frame)<br />     {<br />        if(v4l_debug) fprintf(stderr, "v4lsync: sync frame %d.\n",frame);<br />        if(vd->framestat[frame] == 0) {<br />           fprintf(stderr, "v4lsync: grabbing to frame %d is not started.\n", frame);<br />        }<br />       if(ioctl(vd->fd, VIDIOCSYNC, &amp;frame) &lt; 0) {<br />           v4lperror("v4lsync:VIDIOCSYNC");<br />          return -1;<br />        }<br />       vd->framestat[frame] = 0;<br />        return 0;<br />     }</p>   </blockquote>   <p>在 EffecTV 裡則是要呼叫 video_syncframe() 函數來做 frame 等待的動作, 而 video_syncframe() 則會去呼叫 v4lsync() 函數。video_syncframe() 函數的原始碼如下:</p>   <blockquote>     <p>int video_syncframe()<br />     {<br />        return v4lsyncf(&amp;vd);<br />     }</p>   </blockquote>   <p><font color="#0000ff"><strong>frame 擷取實作</strong></font></p>   <p>看過這幾個核心函數後, 那麼在 EffecTV 裡會在那裡用到這些函數呢? EffecTV 是一個輸出特效畫面的視訊軟體, 在 effects/ 目錄下每個檔案都是獨立支援一種特效的, 因此我們介紹的這幾個函數都是由每個特效獨立來呼叫使用。</p>   <p>讓我們來看 Life 這個特效的主程式 – life.c, 首先應該先由 lifeStart() 函數看起, 其程式碼如下:</p>   <blockquote>     <p>int lifeStart()<br />     {<br />        screen_clear(0);<br />       image_stretching_buffer_clear(0);<br />       image_set_threshold_y(40);<br />       field1 = field;<br />        field2 = field + video_area;<br />        clear_field();<br />       if(video_grabstart())<br />          return -1;<br />     <br />        stat = 1;<br />        return 0;<br />     }</p>   </blockquote>   <p>lifeStart() 在完成一些初始化的設定工作後, 會呼叫 video_grabstart() 函數開始進行影像擷取。在 lifeDraw() 函數裡, 則是呼叫 video_syncframe() 等待 frame 擷取完成後再做輸出的動作。</p>   <p><font color="#0000ff"><strong>YUV 與 YIQ</strong></font></p>   <p>在 PAL 視訊標準方面, 主要的模式為 YUV, 這與我們在電腦上常用的 RGB 不同。相對的, 在 NTSC 視訊標準, 則是使用 YIQ 模式。</p>   <p>針對這二種視訊影像模式, 我們還必須設計 YUV 與 RGB、YIQ 與 RGB 的轉換程式。在 EffecTV 裡也有 yuv.c 的程式碼負責做轉換的工作。</p>   <p><strong><font color="#0000ff">RGB 介紹</font></strong></p>   <p>RGB 以三原色紅、綠、藍 (Red-Green-Blue) 來表現影像, 將紅色與藍色重疊後會成為品紅色 (magenta)、紅色與綠色重成為黃色 (yellow), 三色重疊則是白色 (white)。</p>   <p>RGB 的三原色指的是光線的顏色, 並非顏料的顏色, RGB 模式常使用於監視器上, 與 PAL 或 NTSC 視訊的標準不同。</p>   <p><strong><font color="#0000ff">YUV 與 YIQ 的轉換</font></strong></p>   <p>YUV 、 YIQ 與 RGB 之間的換係與轉換公式如下:</p>   <blockquote>     <p>Y = 0.299R + 0.587G + 0.114B<br />     U = B – Y<br />     V = R – Y<br />     I = 0.877(R-Y)cos33 – 0.492(B-Y)sin33<br />     Q = 0.877(R-Y)sin33 + 0.492(B-Y)cos33</p>   </blockquote>   <p>RGB 是由 R, G, B 三原色組成, 同理 YUV 是由 Y, U, V 三個元素組成。在 PAL 實作 U, V 我們使用的轉換公式為:</p>   <blockquote>     <p>U = 0.492(B-Y)<br />     V = 0.877(R-Y)</p>   </blockquote>   <p>YIQ 則可以簡化成轉換矩陣:</p>   <blockquote>     <p>(手稿)</p>   </blockquote>   <p><font size="4" color="#ff0000"><strong>JRTPLIB 的使用方法</strong></font></p>   <p>在 Video Streaming 方面, 有了影像擷取的程式實作能力後, 要完成完整的影像串流軟體, 例如視訊會議軟體, 當然就必須要加入網路傳送的功能。</p>   <p>為了能利用網路傳送影像, 並且做到 real-time (即時) 的功能, 我們必須使用 RTP 通訊協定來完成。在這裡我們已經完成第一大部份的工作了, 接下來就是加入網路即時傳送撥放的功能, 到這裡 video4linux 已告一段落, 我們將 Video Streaming 的主角換到 RTP 繼續討論。</p>   <p><strong><font color="#0000ff">加入 RTP Protocol </font></strong></p>  <p>利用 Video Streaming 來設計視訊會議軟體, 其中在技術層面不可或缺的一部份就是 RTP Protocol 的部份。RTP 也是 VoIP (Voice over IP) 相關技術所使用的通訊協定。</p>   <p>為了配合 Video Streaming 來設計完整的視訊會議軟體, 我們勢必要加入 RTP 的技術。在這方面, 我們選擇使用現成的 RTP 程式庫 – JRTPLIB。</p>   <p>與 video4linux 程式庫不同的是, video4linux 在決策上我們選擇自行發展, 但 JRTPLIB 則是一個很成熟的專案了, 而且仍在持續維護, 未來 JRTPLIB 還會加入 IPV6 與 multicasting 方面的完整支援, 因此使用 JRTPLIB 來發展我們的 Video Streaming 軟體才是解決之道。</p>   <p><strong><font color="#0000ff">JRTPLIB 簡介</font></strong></p>   <p>RTP 全名為 Real-time Transport Protocol, 定義於 RFC 1889 與 RFC 1890, 我們在第一篇文章裡已經簡單介紹過 RTP。</p>   <p>在 RFC 1889 裡, 對 RTP 的定義為:</p>   <blockquote>     <p>RTP: A Transport Protocol for Real-Time Applications</p>   </blockquote>   <p>而在 RFC 1890 裡, 對 RTP 的描述則是:</p>   <blockquote>     <p>RTP Profile for Audio and Video Conferences with Minimal Control</p>   </blockquote>   <p>即然我們要利用 Video Streaming 來設計視訊會議方面的軟體, 對於 RTP 的討論與研究則是必修功課之一。對視訊會議軟體而言, RTP 也提供 Audio 部份的支援, 事實上, 任何與 real-time (即時) 相關的話題都與 RTP 脫不了關係。</p>   <p>與 JRTPLIB 相關的計畫包括 JVOIPLIB 與 JTHREAD, 這兩個程式庫對我們的工作是相關有幫助的, 本文則先將重心放在 JRTPLIB上面。</p>   <p>JRTPLIB 實作了 RTP 協定, 並且提供了簡單易用的 API 供軟體開發使用。JRTPLIB 也支援了 session, 並且可在底下平臺執行:</p>   <ul><li>MS-Windows 95,98 and NT </li><li>Linux </li><li>FreeBSD </li><li>HP-UX </li><li>Solaris </li><li>VxWorks </li></ul>  <p>JRTPLIB 的官方首頁為:</p>   <blockquote>     <p><a href="http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.html">http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.html</a></p>  </blockquote>  <p>使用前請務必先閱讀一下 JRTPLIB 的版權宣告。</p>   <p><strong><font color="#0000ff">JRTPLIB 的第一個程式</font></strong></p>   <p>底下我們介紹過 JRTPLIB 的設計方法後, 大家就會發覺到 JRTPLIB 實在很好上手。要利用 RTP 通訊協定傳送資料, 第一步要先建立一個 session, 方法如下:</p>   <blockquote>     <p>#include "rtpsession.h"<br />     <br />     int main(void)<br />     {<br />        RTPSession sess;<br />     <br />        sess.Create(5000);<br />       return 0;<br />     }</p>   </blockquote>   <p>第一步我們要先把 rtpsession.h 給 include 進來:</p>   <blockquote>     <p>#include "rtpsession.h"</p>   </blockquote>   <p>接下來再產生 RTPSession 類別的物件:</p>   <blockquote>     <p>RTPSession sess;</p>   </blockquote>   <p>最後再建立 session 就完成最簡單的初始動作了:</p>   <blockquote>     <p>sess.Create(5000);</p>   </blockquote>   <p>Create() 成員函數接收一個 portbase 的參數, 指定 session 的 port, 接著開始初始化 timestamp 與 packet sequence number。RTPSession::Create() 程式碼如下:</p>   <blockquote>     <p>int RTPSession::Create(int localportbase,unsigned long localip)<br />     { <br />       int status;<br />     <br />        if (initialized)<br />           return ERR_RTP_SESSIONALREADYINITIALIZED;<br />        if ((status = rtpconn.Create(localportbase,localip)) &lt; 0)<br />           return status;<br />        if ((status = contribsources.CreateLocalCNAME()) &lt; 0)<br />        {<br />          rtpconn.Destroy();<br />          return status;<br />        }<br />       CreateNewSSRC();<br />       localinf.CreateSessionParameters();<br />       rtcpmodule.Initialize();<br />       initialized = true;<br />        return 0;<br />     }</p>   </blockquote>   <p>Create() 接著會再建立一個 SSRC:</p>   <blockquote>     <p>CreateNewSSRC();</p>   </blockquote>   <p>SSRC 為 local synchronization source identifier。</p>   <p><strong><font color="#0000ff">指定目的端</font></strong></p>  <p>接下來再指定目的端的 IP 位址:</p>   <blockquote>     <p>unsigned long addr = ntohl(inet_addr("127.0.0.1"));<br />      sess.AddDestination(addr,5000);</p>   </blockquote>   <p>這裡表示我們要將封包傳送到 127.0.0.1 (本地端) 的 port 5000, 只要照著套用即可。</p>   <p><font color="#0000ff"><strong>傳送 RTP 封包</strong></font></p>   <p>sess.SendPacket("1234567890",10,0,false,10);</p>   <p>1234567890 是要傳送的字串, 第二個參數 (10) 表示傳送字串的長度, 第三個參數為 payload type, 第四個參數為 mark flag, 最後第五個參數則是 timestamp 的遞增單位。</p>   <p>在下一期裡, 我們將會配合 SDL 來做影像的輸出, 因此我們會在下一期再介紹如何接收 RTP 封包。我們使用 SDL 主要目的是為了將影像輸出到螢幕上。</p>   <p><strong><font color="#0000ff">RTP 的封包格式</font></strong></p>   <p>RTP 的標準受許多大廠採用, 例如: Microsoft、Intel, 也因此我們需要了解一下 RTP 的低層技術部份。</p>   <p>RTP 與其它 Internet 通訊協定一樣, 在封包裡也會有封包檔頭, 接著才是封包的資料。</p>   <p align="center"><img width="615" height="251" border="0" src="http://www.jollen.org/linux/video_streaming/4-1.jpg" /><br />  圖 1 是 RTP 的封包檔頭格式, 整個檔頭分為 10 個欄位 (field)。</p>   <p>在 RTPsession::SendPacket() 的第三個參數與第四個參數分別是 payload type 與 mark flag, 在 RTP 封包檔頭裡, 分別是 Payload 欄位與 M 欄位。</p>   <p>Payload 欄位的長度為 7 bits, M 欄位的長度為 1 bits。</p>   <p><strong><font color="#0000ff">RTP 的 Payload type</font></strong></p>   <p>RTP 檔頭的 Payload type 指定封包資料的編碼方式, 我們列出五個常用的 Audio 標準, 與三個常用的 Video 標準, 其中 JPEG/H.261/H.263 我們本文第一篇裡都有做過簡單的介紹。常用的 Payload type 如下表:</p>   <div align="center">    <center>    <table width="699" border="1">      <tr>        <td width="168">Payload type</td>        <td width="106">編碼標準</td>        <td width="171">支援Audio或Video</td>        <td width="226">Clock Rate (Hz)</td>      </tr>      <tr>        <td width="168">2</td>        <td width="106">G.721</td>        <td width="171">A</td>        <td width="226">8000</td>      </tr>      <tr>        <td width="168">4</td>        <td width="106">G.723</td>        <td width="171">A</td>        <td width="226">8000</td>      </tr>      <tr>        <td width="168">7</td>        <td width="106">LPC</td>        <td width="171">A</td>        <td width="226">8000</td>      </tr>      <tr>        <td width="168">9</td>        <td width="106">G.722</td>        <td width="171">A</td>        <td width="226">8000</td>      </tr>      <tr>        <td width="168">15</td>        <td width="106">G.728</td>        <td width="171">A</td>        <td width="226">8000</td>      </tr>      <tr>        <td width="168">26</td>        <td width="106">JPEG</td>        <td width="171">V</td>        <td width="226">90000</td>      </tr>      <tr>        <td width="168">31</td>        <td width="106">H.261</td>        <td width="171">V</td>        <td width="226">90000</td>      </tr>      <tr>        <td width="168">34</td>        <td width="106">H.263</td>        <td width="171">V</td>        <td width="226">90000</td>      </tr>    </table>    </center>  </div>  <p><font size="4" color="#ff0000"><strong>Linux 的 IP Stacks</strong></font></p>   <p>提要網路的應用, 當然也要對 Linux IP Stacks 有簡單的認識, 我們建議大家直接去研究 Linux kernel 的程式碼, 當然現在已經有專門的書在做討論:</p>   <blockquote>     <p>Linux IP Stacks Commentary, Stephen T. Satchell &amp; H.B.J. Clifford, CoriolisOpen Press, ISBN 1-57610-470-2</p>   </blockquote>   <p>Linux 是網路作業系統, 而且 Linux 對於網路的支援也相當的完整, 包括2.4 系列 kernel 已經加入對 IPv6。 Linux kernel 與 module 提供的通訊層功能包括：</p>   <ul><li>各種 Ethernet、token ring 與 FDDI (Fiber Distributed Data Interface) 介面卡的軀動程式 </li><li>PPP、SLIP 與 SLIP 通訊協定的軀動程式 </li><li>提供 IPX (Internet Package Exchange) 通訊協定 </li><li>提供業餘無線電用的軀動程式 (AX25) </li><li>提供 AppleTalk 軀動程式 </li><li>其它連結層使用的軀動程式 </li><li>支援 router 的功能，包括 RIP (Router Information Protocol) 通訊協定 </li><li>支援 ICMP (Internet Control Message Protocol) 通訊協定 </li><li>支援 IGMP (Internet Group Message Protocol) 通訊協定 </li><li>支援 IP (Internet Protocol) 通訊協定 </li><li>支援 TCP (Transmission Control Protocol) 通訊協定 </li><li>支援 UDP (User Datagram Protocol) 通訊協定 </li></ul>  <p>inux IP Stacks 一書的書點放在基本與重要的 TCP/IP 服務上, 包含：遶送、封包管理、datagram 與 datastream。</p>   <p><strong><font color="#0000ff">Linux IP Stacks 導讀</font></strong></p>   <p>裡的導讀參考自 Linux IP Stacks 一書的第一章, 在研究 Linux IP Stacks 這本書前, 請讀者先好好研究一下這本書的結構, 到時才比較容易上手。</p>   <p>第二章的部份介紹 TCP/IP 的背景知識與歷史，包括 TCP/IP 的發展過程, 這一章原則上只要了解一下即可。</p>   <p>第三章則以學術的觀點來比較 TCP/IP 與 ISO 模型。這一章比較偏向 ISO 的理論，而書上解釋到, 要學習 ISO 模型理論的目的, 是為了能夠了解為什麼通訊層要分割成這幾層。</p>]]></description>
         <link>https://www.jollen.org/blog/2001/08/linux_video_streaming_4.html</link>
         <guid>https://www.jollen.org/blog/2001/08/linux_video_streaming_4.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 13 Aug 2001 03:32:00 +0800</pubDate>
      </item>
            <item>
         <title>Linux 的應用（刊載於 PC2000 雜誌八月號）-- Video Streaming 探討 (3)</title>
         <description><![CDATA[本期的重點在介紹影像擷取卡與影像擷取卡常見的 BT848/BT878 晶片。除了介紹影像擷取卡外, 也會來看 xawtv 這個有名的 video4linux/BTTV 應用程式。

作者: 陳俊宏 (<a href="http://www.jollen.org/">www.jollen.org</a>)<a href="http://www.jollen.org/"></a><p></p><p><strong><font size="4" color="#ff0000">什麼是影像擷取卡</font></strong></p>   <p>影像擷取卡的主要功能當然就是做影像的擷取了, 一張影像擷取卡應該擁有底下的基本功能。</p>    <p><strong><font color="#0000ff">影像擷取</font></strong></p>   <p>影像擷取卡可擷取的影像頁框 (frame) 的大小是很重要的, 本文第一篇裡提到的幾個基本影像頁框大小都應該要支援, 包含 NTSC (640x480) 與 PAL (768x576)。</p>    <p>影像擷取卡所捕捉的畫面都是屬於動態的畫面, 影像的來源則要看所支援的視訊系統與所有的視訊裝置。</p>    <p><font color="#0000ff"><strong>支援的視訊系統</strong></font></p>   <p>大部份影像擷取卡都會具備一組視訊輸入端子, 即 S-Video (Y/C) 端子或 Composite 端子。</p>    <p>在台灣的標準當然是 NTSC 系統, 一般而言, 我們是希望一張影像擷取卡可以支援越多視訊系統越好, 包括:    NTSC/PAL/PALN/PLAM/SECAM。</p>   <p>可使用的視訊裝置有較常見的 CCD, 或是家用 V8、Hi8 皆可, 一般而言我們也是希望一張影像擷取卡可以接越多視訊裝置越好。</p>    <p><font size="4" color="#ff0000"><strong>安裝影像擷取卡</strong></font></p>   <p>以筆者的影像擷取卡為例，使用的是 Chronos Video Shuttle I 的卡，所以只要安裝 bttv 模組即可，同時，bttv 模組在 Linux kernel 2.2.17 下也會用到 i2c-old 與 videodev 兩個模組，所以也要一併安裝。在命令列下，安裝這三個模組的命令為：</p>    <blockquote>   <p>linux# insmod i2c-old<br />    linux# insmod videodev<br />    linux# insmod bttv</p>    </blockquote>   <p>當然要確定 Linux kernel 有編譯這三個模組的支援，然後再把這三個模組加到 /etc/modules.conf (Red Hat 7.0) 裡。</p>    <p>如果是使用 Linux kernel 2.4.x (如 Red Hat Linux 7.1), 也可以直接在 /etc/modules.conf 裡加上一行:</p>    <blockquote>   <p>alias char-major-81 bttv</p>    </blockquote>   <p>就可以了。然後再利用上一篇文章 (2) 的程式來做初始化, 可以看到底下的訊息:</p>    <blockquote>   <p>/dev/video0: initialization OK... BT878(Chronos Video Shuttle I)<br />    3 channels<br />    3 audios</p>    <p>OK!</p>    </blockquote>   <p>BT878 晶片初始化成功, 影象擷取卡名稱為 Chronos Video Shuttle I, 成功找到並初始化 3 個 channel。</p>    <p>bttv 模組就是底下會提到的 BTTV 軀動程式, 主要用途是軀動影像擷取卡上的 BT848/BT878 晶片。</p>    <p><font size="4" color="#ff0000"><strong>影像擷取卡的應用</strong></font></p>   <p>在 PC 上加裝影像擷取卡, 就可以利用 PC 做影片剪輯的工作, 例如可以將類比的 V8 影片轉換成數位影像, 並且儲存到電腦裡, 或製作成光碟保存。</p>    <p>配合這類的軟體, 我們還可以 DIY 做影像的特效、轉場特效、字幕、旁白等等。利用 PC 做影像剪輯的工是很耗電腦資源的, 所以不能用太陽春的 PC, 不然可以會很累人的！</p>    <p><strong><font color="#0000ff">壓縮比</font></strong></p>   <p>壓縮比是判斷一張影像擷取卡優劣的主要關鍵所在, 由於動態的影像所佔用的空間相當大, 所以如果一張影像擷取卡無法有效將擷取出來的影像做壓縮, 所需的硬碟空間都會相當驚人。</p>    <p>壓縮比越好的影像擷取卡, 其工作效能越佳, 一般壓縮比大約是 4:1, 使用壓縮比越好的晶片, 當然也就越貴, 所以影像擷取卡是一分錢一分貨的。</p>    <p><font size="4" color="#ff0000"><strong>BT 878 晶片</strong></font></p>    <p>目前大部份的數位影像擷取卡大部份都是以 BT878 單顆晶片為影像擷取卡之中心。BT878 運作方式是以軟體來進行影像解壓縮工作, BT878 晶片負責將擷取之影像丟給 Linux 做影像處理, 而 BTTV 則是 Linux kernel 的 BT878 晶片軀動程式。</p>    <p>由於影像是利用 BT878 擷取後交由軟體來做影像處理, 因此在處理效能上自然就會比較差。如果是經由網路來傳送影像的話, 我們就會再利用影像壓縮技術 (H.261/H.263...等等) 來做影像處理。</p>    <p><strong><font size="4" color="#ff0000">什麼是 BTTV</font></strong></p>    <p>BTTV 是 Linux 上的 Bt848/849/878/879 晶片的軀動程式, 主要功能是做頁框<br />    的截取 (frame grabber)。</p>    <p>BTTV 是 video4linux 裡重要的軀動程式, 目前分為二個版本:</p>    <ul><li>0.8.x 的發展中版本</li><li>0.7.x 的穩定版本</li></ul>   <p><strong><font size="4" color="#ff0000">Linux 上可用的影像擷取卡</font></strong></p>    <p>在http://www.linhardware.com/db/searchproduct.cgi?_catid=17 網頁上可以找到在 Linux 上支援程度比較好的幾張影像擷取卡。而一般 Linux 上較受歡迎的影像擷取卡則是 Hauppauge 的幾張卡, 筆者使用的也是 Hauppauge 的卡。</p>    <p>在 linhardware 網站上可以找到底下六張卡:</p>    <ul><li>Hauppauge 401 WinTV-radio dbx-TV stereo</li><li>Hauppauge WinTV PCI TV Card </li><li>Hauppauge WinTV-GO PCI TV Card </li><li>Hauppauge WinTV-PCI  Hauppauge </li><li>Hauppauge WinTV-Radio+NICAM </li><li>Hauppauge WinTV/PCI TV Card</li></ul>   <p>關於 Linux 對於影像捕捉卡支援的中文文件 (HOWTO) 可以在 CLDP 網站上取得:</p>    <blockquote>   <p><a href="http://www.linux.org.tw/CLDP/Hardware-HOWTO-22.html">http://www.linux.org.tw/CLDP/Hardware-HOWTO-22.html</a></p>   </blockquote>   <p><font size="4" color="#ff0000"><strong>BTTV 相關軟體 - xawtv</strong></font></p>    <p>官方網站: <a href="http://bytesex.org/xawtv/index.html">http://bytesex.org/xawtv/index.html</a></p>   <p>安裝方式:</p>   <blockquote>   <p>1. linux# ./configure <br />   2. linux# make depend<br />    3. linux# make<br />    4. linux# make install</p>    </blockquote>   <p>如果您有 Red Hat Linux 7.1 PowerTools 光碟片的話, 也可以直接由 PowerTools 光碟片安裝 xawtv 套件:</p>    <blockquote>   <p>linux# rpm -ivh xawtv-3.34-1.i386.rpm</p>    </blockquote>   <p>安裝 xawtv 需要 libjpeg 與 libjpeg-devel 套件, 如果您是使用 Red Hat Linux 7.1 的話, 應該安裝底下二個套件:</p>    <blockquote>   <p>libjpeg-6b-15.i386.rpm (Disc 1)<br />    libjpeg-devel-6b-15.i386.rpm (Disc 2)</p>    </blockquote>   <p>xawtv 整個架構可以分成 7 個部份如下:</p>    <ul><li>xawtv: 主程式部份。</li><li>fbtv: linux console 模式的 TV 應用程式, 使用 linux kernel 2.2.x 的 framebuffer。</li><li>set-tv: 命令列模式的工具, 用來設定 video4linux 的參數。 </li><li>streamer: 命令列模式的工具, 用來捉取動態影像與 avi 影像。 </li><li>radio: radio 應用程式。</li><li>webcam: 將捉取的影像以 FTP 方式上傳到 Web Server 端, 用來設計 Web 即時影像的工具。</li><li>alevtd: videotext pages 的 Web Server。</li></ul>   <p><strong><font color="#0000ff">xawtv 的 video4linux</font></strong></p>    <p>xawtv 是相當好的 video4linux 方面的教材, 在上一期我們看過一遍 video4linux 的設計方法後, 接下來我們要先捨棄我們上一期所實作的 video4linux 程式庫!</p>    <p>因為我們將要帶導大家研究 xawtv 裡以 video4linux API 設計的影像捕捉 (grab) 部份的程式碼, 這個程式碼的實作更完整, 因此在開始進入這個主題前, 必須先理解上一期所介紹的內容, 並且懂得我們實作的程式庫內容, 才能順利進入狀況。</p>    <p>取回 xawtv 的原始程式碼解開後, 在 libng/ 目錄下可以看到 grab-v4l.c 的檔案, 另外還有一個 grab-v4l2.c 的檔案, 這是 video4linux2 (version 2) 的版本。這裡我們先介紹 video4linux 的版本 -grab-v4l.c。</p>    <p><font color="#0000ff"><strong>grab-v4l.c 的程式碼架構</strong></font></p>    <p>大略掃瞄過 grab-v4l.c 的程式碼後, 發現 grab-v4l.c 與上一期我們實作的 video4linux 程式庫的架構很像。</p>    <p>其實大多數利用 video4linux 來設計應用程式的軟體, 其 video4linux 的 API 部份大多是自行重寫的, 反而很少利用現成的程式庫, 因此仔細研究別人的程式碼, 再來改進我們自己的 video4linux 程式庫是我們的最終目的。</p>    <p>程式一開始也是宣告了一個 ng_driver 結構的變數 (v4l_driver):</p>    <blockquote>   <p>#ifndef __linux__<br />   const struct ng_driver v4l_driver;<br />    #else /* __linux__ */</p>    </blockquote>   <p>然後可以看到 open 與 close 二個函數:</p>    <blockquote>   <p>static void*   v4l_open(char *device);<br />    static int     v4l_close(void *handle);</p>    </blockquote>   <p>接下來還有許多函數的宣告, 同時還可以看到一些結構的宣告, 例如:</p>    <blockquote>   <p>static struct STRTAB norms_v4l[] = {<br />        {  VIDEO_MODE_PAL,     "PAL"   },<br />        {  VIDEO_MODE_NTSC,    "NTSC"  },<br />        {  VIDEO_MODE_SECAM,   "SECAM" },<br />        {  VIDEO_MODE_AUTO,    "AUTO"  },<br />        { -1, NULL }<br />    };</p>    </blockquote>   <p>我們可以用來顯示 channel 是 PAL/NTSC/SECAM 或是 AUTO。</p>    <p>xawtv 實作出來的 v4l_open() 函數可精彩了, 除了有設計函數應該要有的嚴謹的錯誤檢查外, v4l_open 也對硬體做了一些檢查。</p>    <p>xawtv 的 v4l 程式庫也有 v4l_close() 函數, 用來關閉已開啟的 video4linux 裝置。</p>    <p>在影像頁框擷取方面, 共有底下 5 個函數:</p>    <blockquote>   <p>static int v4l_setformat(void *handle, struct ng_video_fmt *fmt);<br />    static int v4l_startvideo(void *handle, int fps, int buffers);<br />    static void v4l_stopvideo(void *handle);<br />    static struct ng_video_buf* v4l_nextframe(void *handle);<br />    static struct ng_video_buf* v4l_getimage(void *handle);</p>    </blockquote>   <p>函數的名稱已經將其功能表達的很清楚了, 因此不再重覆解釋。另外比較特別的是, xawtv 的 video4linux 也支援了 framebuffer, 主要函數為:</p>    <blockquote>   <p>int v4l_setupfb(void *handle, struct ng_video_fmt *fmt, void *base)</p>    </blockquote>   <p>xawtv 的 video4linux 程式庫整體架構相當明白簡單, 程式碼也很好懂, 剩下的重點在於如何利用 video4linux 的 mmap 模式來擷取二頁的影像, xawtv 的 video4linux 程式庫的使用方式我們只要了解這一點即可。<br />    xawtv 的 video4linux 其中對影像擷取卡做初始化的方法我們已經會了, 再來就是做影象擷取的部份。</p>    <p>mmap 方式我們將在下一期再做介紹。接下來的部份將瀏覽一下 xawtv 的功能架構與使用界面, 以利我們去研究 xawtv 的程式碼, 並且還會介紹一個 Linux 下的 MP3 撥放程式 --, 會介紹 xxx 的原因是因為 xxx 也用到了 streaming 的技術來撥放 MP3 檔案。</p>    <p><strong><font color="#0000ff">xawtv 的功能</font></strong></p>    <p>底下我們將展示以 CCD (Composite 端子) 視訊裝置輸入影像, 並且利用 xawtv 顯示動態影象的設定方式。</p>    <p><strong>xawtv 的輸出結果</strong></p>    <p><br />    圖 1</p>    <p>這是 xawtv 顯示動態影象的結果, 我們利用的是 CCD 視訊裝置, 視訊來源為 Composite, TV Norm 為 NTSC。</p>    <p>要有正確的顯示結果, 必須正確地設定 TV Norm 與 Video Source 兩個項目。</p>    <p><strong>xawtv 的設定選單</strong></p>    <p>在 xawtv 的主畫面按二下滑鼠右鍵後, 就會出現圖 2 的視窗。幾個常用的功能說明如下:</p>    <ul><li>Full Screen on/off: 切換是否要全螢幕顯示, 不過影像的大小則是跟視訊裝置有關。</li><li>Grab Image: 將目前的畫面存成圖檔 (ppm 或 jpeg)。</li><li>Record Movie (avi): 錄製 avi 的電影檔。</li><li>Bright/Hue/Contrast/Color: 調整視訊的對比、亮度、顏色。</li></ul>   <p><br />    圖2</p>    <p><strong>設定 TV Norm</strong></p>    <p>點取 TV Norm 項目後, 將 TV Norm 設定在正確的參數, 筆者使用的是 NTSC 的 CCD, 因此必須將這個項目設成 NTSC。</p>    <p>#圖: xawtv-03.tif<br />    圖 3</p>    <p><strong>設定 Video Source</strong></p>    <p>點取 Video Source 後, 再選取正確的視訊來源, 大部份的 CCD 都是 Compoiste 端子, 因此選擇 Composite1。</p>    <p>#圖: xawtv-04.tif<br />    圖 4</p>    <p>xawtv 會列出目前影像擷取卡可用的視訊來源, 我們只要將視訊裝置接到正確的視訊來源, 並完成 TV Norm 與 Video Source 的設定後就可以正確顯示動態影像了。</p>    <p>利用 video4linux 來初始化影像擷取卡的方法在上一篇文章 (2) 中已列出完整範例程式碼, 並且做了詳細的說明。下一期我們將會更進一步來撰寫設定 TV Norm 與 Video Source 的程式碼。</p>    <p>配合介紹如何設計 TV Norm 與 Video Source 的設定程式碼, 我們還會根據 xawtv 的幾個功能並配合 video4linux 來實作 xawtv 上的功能 (frame grab), 並且會列出完整的程式碼範例做說明。</p>    <p>xawtv 也提供針對 FreeBSD 與 OpenBSD 的 BT848/BT878 軀動程式 (bktr) 所設計的影像擷取功能, 原始程式碼放在 xawtv 裡的 libng/ 目錄, 檔名為    grab-bsd.c 檔案。</p>    <p>(MP3 - streaming 部份...)</p>]]></description>
         <link>https://www.jollen.org/blog/2001/07/linux_video_streaming_3.html</link>
         <guid>https://www.jollen.org/blog/2001/07/linux_video_streaming_3.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Tue, 17 Jul 2001 06:49:00 +0800</pubDate>
      </item>
            <item>
         <title>Linux 的應用（刊載於 PC2000 雜誌六月號）-- Video Streaming 探討 (2)</title>
         <description><![CDATA[繼上一篇介紹過 Video Streaming 的影像標準與網路通訊協定後，本期將要實際介紹目前常見的 Video Streaming 產品，並且由基本構成開始講解。本期首先介紹 video4linux 的設計方式。

<p>繼上一篇介紹過 Video Streaming      的影像標準與網路通訊協定後，本期將要實際介紹目前常見的 Video      Streaming 產品，並且由基本構成開始講解。本期首先介紹 video4linux      的設計方式。</p>     <p><font color="#ff0000"><strong><big>Video Streaming 產品介紹</big></strong></font></p>     <p>目前在網路上流行的 Video Streaming 產品相當多，這些利用 Video      Streaming      技術設計的軟體在網路多媒體的應用已經有相當長的一段時間了。</p>     <p>底下先來介紹幾套常用的 Video Streaming 軟體。</p>     <p><font color="#0000ff"><strong>Read Video</strong></font></p>     <p>Real Video 是 Real Networks 公司的產品，Real Video 主要支援了      video-on-demand*1 的功能。Real Video 可以讓我們經由網站來播放串流影像      (streaming video)。</p>     <p>由於我們的最終目的是實作出一個可以做 video streaming      的軟體，所以在這裡我們將以 Real Video 做為標竿，並以 Linux      為基礎來設計 video streaming 的軟體。</p>     <p><strong><font color="#0000ff">mod_mp3</font></strong></p>     <p>mod_mp3 是 Open Source 的 streaming 軟體。mod_mp3 並不是 Video Streaming      的軟體，但同樣是利用 streaming 的技術所設計的 apache module。</p>     <p>mod_mp3 可以利用 apache 來架設 streaming server，主要的功能是將 MP3      放進 cache 裡，再利用撥放程式就可以經由網路享受 MP3 streaming      的服務。</p>     <p>mod_mp3 的架設相當簡單，將 mod_mp3 以 DSO 方式安裝後，只要在      httpd.conf 裡加上 VirtualHost 的設定即可：</p>     <blockquote>       <p>Listen 7000<br />       &lt;VirtualHost www.jollen.org:7000><br />       ServerName www.jollen.org<br />       MP3Engine On<br />       MP3CastName "jollen box"<br />       MP3Genre "Much, nutty"<br />       MP3 /home/nfs/private/mp3<br />       MP3Random On<br />       Timeout 600<br />       ErrorLog /var/log/mp3_stream.log<br />       &lt;/VirtualHost></p>     </blockquote>     <p>其中的設定項目說明如下：</p><ul><li>MP3 - MP3 路徑或檔名 </li><li>MP3Engine - 啟動或關閉 MP3 streaming server </li><li>MP3CastName - server name </li><li>MP3Genre - Genre that will be sent to the client </li><li>MP3Playlist - 如果 MP3 Player 支援 Playlist，可以設定這個項目 </li><li>MP3Cache - cache 目錄</li></ul>     <p><font color="#0000ff"><strong>VIC</strong></font></p>     <p>VIC 也是屬於 Open Source 的軟體。VIC 全名為video conferencing，故名其義，VIC      是一種視訊會議的軟體。VIC 是由加州柏克來大學的 Network Research Group      所發展。</p>     <p>VIC 是相當棒非常適合用來研究 Video Streaming 的 Open Source      軟體，主要是因為 VIC 幾乎包含了 Video Streaming 相關的技術。</p>     <p>VIC 值得我們研究的原因是因為 VIC 支援了底下所列的功能：</p><ul><li>IPv6</li><li>使用 video4linux 的捕像捕捉功能</li><li>H261、H263 與 H263+ codec</li><li>Software JPEG 與 BVC 編碼</li><li>Raw YUV packetiser/codec</li><li>RTIP/RTP 通訊協定</li><li>the IP Multicast Backbone (MBone)</li><li>支援 video4linux 的 mmap</li></ul>     <p>這些特色幾乎已經包括 Video Streaming      所應具備的技術了，基於這些特點，VIC      的原始程式碼相當吸引人，因此有意研究 Video Streaming 的 programmer      應該好好閱讀一下 VIC 的原始程式碼。</p>     <p><font color="#0000ff"><strong>VideoLAN</strong></font></p>     <p>VideoLAN 是一個可以做 MPEG 與 DVD 擴播 (broadcast) 播放的軟體，VideoLAN      分成二個部份，一個是 VLAN server，另一個則是 vlc 用戶端播放程式。</p>     <p>VLAN server 將 DVD 與 MPEG 影像利用 broadcast      方式擴播到區域網路上，使用者端再利用 vlc      接收封包並播放。這樣做的好處是可以減少重覆的 I/O 動作，VLAN      server 將影像擴播出去後，區域網路上的用戶端再利用 vlc      接收封包並播放。</p>     <p>VideoLAN 支援 X11、SDL、Linux framebuffer、GGI、BeOS API、MacOS X API      播放方式，並且支援 DVD 與 AC3 (杜比音效)。</p>     <p><font color="#ff0000"><strong><big>video4linux 實作</big></strong></font></p>     <p>看過幾套現成的 Video Streaming 後，還是要回到本文的主題 -- Linux      如何設計 Video Streaming 的應用程式。上一期所介紹的 Video Streaming      基本觀念是進入 Video Streaming 領域相當重要而且基本的知識，像是      PASL/NTSC、RTP...等等。</p>     <p>RealNetworks 公司的產品裡，要建置網站的即時 (live)      影像是相當容易的。只要利用 RealNetworks 公司的產品配合影像捕捉卡      (Video Capture Card) 與 CCD 就可以達到。</p>     <p>從這裡可以看出，如果我們想要實作一套這樣的小系統，第一個所要面臨的問題就是如何在      Linux 下軀動影像捕捉卡，再來就是如何設計影像捕捉的程式。</p>     <p>在影像捕捉卡方面，Linux kernel 2.2      版本的支援已經相當完備了，很多影像捕捉卡在 Linux kernel 2.2      上都可以順利軀動並且正常工作。</p>     <p>而在程式設計方面，我們則是先利用 Linux kernel 所提供的 video4linux      APIs 來設計程式。這一期的目的在於利用 video4linux      來實作一個供應用程式使用的程式庫 (library)。</p>     <p><font color="#0000ff"><strong>影像捕捉卡</strong></font></p>     <p>先來檢視一下 Osprey 100 這張影像捕捉卡。Osprey 100 是 Real Networks      公司所推薦配合他們產品的一張影像捕捉卡，配合 Osprey 100 與      RealNetworks 的產品我們可以利用 broadcast 或 on-demand 做到實況轉播 (live)      的功能。</p>     <p>Osprey 100 在硬體功能上可以支援到每秒 30 個畫面 (fps -- frame per      second)，並且支援 NTSC 與 PAL 輸入。</p>     <p>不過在實作上，筆者並不使用 Osprey 100。筆者使用的影像捕捉卡是      ，這張卡算是比較「俗」一點的卡，但是也有好處，因為在 Linux      上很容易安裝。</p>     <p>在繼續往下發展我們的系統前，必須先安裝好影像捕捉卡與軀動程式，這部份不在這篇文章的範圍，所以請您參考相關的文章來安裝軀動程式。</p>     <p>以筆者這張卡為例，使用的是 Brooktree Corporation      的卡，所以只要安裝 bttv 模組即可，同時，bttv 模組在 Linux kernel      2.2.17 下也會用到 i2c-old 與 videodev      兩個模組，所以也要一併安裝。在命令列下，安裝這三個模組的命令為：</p>     <blockquote>       <p>linux# insmod i2c-old<br />       linux# insmod videodev<br />       linux# insmod bttv</p>     </blockquote>     <p>當然要確定 Linux kernel      有編譯這三個模組的支援，然後再把這三個模組加到 /etc/modules.conf      (Red Hat 7.0) 裡。</p>     <p>不同版本的 kernel 所要安裝的模組不一定相同！還請注意，例如 i2c      相關模組就是如此。</p>     <p><font color="#0000ff"><strong>video4linux 使用的設備檔</strong></font><br />     <br />     Linux 下與 video4linux 相關的設備檔與其用途：</p>     <table border="1">       <tr>         <td width="249">/dev/video    </td>         <td width="710">Video Capture Interface </td>       </tr>       <tr>         <td width="249">/dev/radio    </td>         <td width="710">AM/FM Radio Devices </td>       </tr>       <tr>         <td width="249">/dev/vtx    </td>         <td width="710">Teletext Interface Chips </td>       </tr>       <tr>         <td width="249">/dev/vbi    </td>         <td width="710">Raw  VBI Data (Intercast/teletext)</td>       </tr>     </table>     <p>video4linux 除了提供 programmer 與影像捕捉有關的 API      外，也支援其它像是收音機裝置。</p>     <p>接下來介紹 video4linux 設計方式，所使用的 Linux kernel 版本為 2.2.16。這篇文章將簡單介紹實作      video4linux 的方法，所以請準備好 Linux kernel 原始碼下的      Documentation/v4l/API.html 文件並了解 What's video4linux。</p>     <p> <font color="#0000ff"><strong>_v4l_struct -- 定義資料結構</strong></font></p>     <p>首先，先定義會用到的資料結構如下：</p>     <blockquote>       <p>#ifndef _V4L_H_<br />       #define _V4L_H_</p>     </blockquote>     <p>實作 video4linux 時，必須 include 底下二個檔案：</p>     <blockquote>       <p>#include &lt;sys/types.h><br />       #include &lt;linux/videodev.h></p>     </blockquote>     <p>接下來是 PAL、CIF、NTSC 規格的畫面大小定義：</p>     <blockquote>       <p>#define PAL_WIDTH    768<br />       #define PAL_HEIGHT    576<br />       #define CIF_WIDTH    352<br />       #define CIF_HEIGHT    288 <br />       #define NTSC_WIDTH    640<br />       #define NTSC_HEIGHT    480 </p>     </blockquote>     <p>接下來我們的重點是 _v4l_struct structure，這個 structure 包含了在      API.html 提到，將會使用到的 data structure，底下將完整地定義 _v4l_struct，但在實作時並不會全部用到。</p>     <p>_v4l_struct 定義如下：</p>     <blockquote>       <p>struct _v4l_struct <br />       {<br />          int fd;<br />          struct video_capability capability;<br />          struct video_buffer buffer;<br />          struct video_window window;<br />          struct video_channel channel[8];<br />          struct video_picture picture;<br />          struct video_tuner tuner;<br />          struct video_audio audio[8];<br />          struct video_mmap mmap;<br />          struct video_mbuf mbuf;<br />          unsigned char *map;<br />       };</p>     </blockquote>     <p>為了設計方便，我們再做底下的定義：</p>     <blockquote>       <p>typedef struct _v4l_struct v4l_device;</p>     </blockquote>     <p>以後宣告 struct _v4l_struct 時，將一律使用 v4l_device。</p>     <p><font color="#0000ff"><strong>實作函數宣告</strong></font></p>     <p>底下宣告將要實作的 functions，我們採取 top-down      的實作方式，也就是先將所有會用到的函數事先規劃，並宣告在原始碼裡。當然，本文並不會介紹底下所有的函數，但重要的函數則會做說明。</p>     <p>實際做設計時，有些函數可能會在後期才會被設計出來。我們所要實作的函數與函數宣告如下：</p>     <blockquote>       <p>extern int v4l_open(char *, v4l_device *);<br />       extern int v4l_close(v4l_device *);<br />       extern int v4l_get_capability(v4l_device *);<br />       extern int v4l_set_norm(v4l_device *, int);<br />       extern int v4l_get_channels(v4l_device *);<br />       extern int v4l_get_audios(v4l_device *);<br />       extern int v4l_get_picture(v4l_device *);<br />       extern int v4l_grab_init(v4l_device *, int, int);<br />       extern int v4l_grab_frame(v4l_device *, int);<br />       extern int v4l_grab_sync(v4l_device *);<br />       extern int v4l_mmap_init(v4l_device *);<br />       extern int v4l_get_mbuf(v4l_device *);<br />       extern int v4l_get_picture(v4l_device *);<br />       extern int v4l_grab_picture(v4l_device *, unsigned int);<br />       extern int v4l_set_buffer(v4l_device *);<br />       extern int v4l_get_buffer(v4l_device *);<br />       extern int v4l_switch_channel(v4l_device *, int);</p>     </blockquote>     <p><font color="#0000ff"><strong>v4l_open() -- 開啟 device file</strong></font></p>     <p>首先，v4l_open() 是我們第一個應該要撰寫的函數。v4l_open()      用來開啟影像來源的設備檔。</p>     <p>依據 v4l_open() 的宣告，在應用程式裡，我們會這樣呼叫 v4l_open()： </p>     <blockquote>       <p>v4l_device vd;<br />       <br />       if (v4l_open("/dev/video0", &amp;vd)) {<br />          return -1;<br />       }</p>     </blockquote>     <p>在應用程式裡，我們宣告了一個 vd 變數 (v4l_device 型態)，再呼叫      v4l_open() 將設備檔開啟。</p>     <p>如果可以開啟 "/dev/video0" 則將取回的資訊放到 vd 裡，vd 是      v4l_device 也就是之前宣告的 _v4l_struct。</p>     <p>接下來，讓我們來看看 v4l_open() 要如何實作：</p>     <blockquote>       <p>#define DEFAULT_DEVICE "/dev/video0"<br />       <br />       int v4l_open(char *dev, v4l_device *vd)<br />       {<br />          if (!dev)<br />             dev = DEFAULT_DEVICE;<br />       <br />          if ((vd->fd = open(dev, O_RDWR)) &lt; 0) {<br />             perror("v4l_open:");<br />             return -1;<br />          }<br />       <br />          if (v4l_get_capability(vd))<br />             return -1;<br />       <br />          if (v4l_get_picture(vd))<br />             return -1;<br />       <br />          return 0;<br />       }</p>     </blockquote>     <p>為了設計出完整的 video4linux 程式庫，一開始我們就定義了      DEFAULT_DEVICE，當應用程式輸入的 dev      設備檔參數不存在時，就使用預設的設備檔名稱。程式片段如下：</p>     <blockquote>       <p>if (!dev)<br />          dev = DEFAULT_DEVICE;</p>     </blockquote>     <p>與一般 Linux Programming 一樣，我們使用 open() 將 device file 打開：</p>     <blockquote>       <p>if ((vd->fd = open(dev, O_RDWR)) &lt; 0) {<br />          perror("v4l_open:");<br />          return -1;<br />       }</p>     </blockquote>     <p>如果您不熟悉 Linux 下 open() 的使用方法，請參考 Linux programming      相關資料。熟悉 UNIX programming 的讀者一定知道，open() 也與 STREAMS      的觀念相關，這部份在後面會再另外做介紹。</p>     <p>將設備檔開啟後，把傳回來的 file description 放到 vd->fd 裡。</p>     <p>成功開啟設備檔後，根據 API.html      的說法，我們要先取得設備的資訊與影像視窗的資訊，所以這裡再實作      v4l_get_capability() 與 v4l_get_picture() 來完成這二件工作。</p>     <p>v4l_get_capability() 會利用 ioctl()      取得設備檔的相關資訊，並且將取得的資訊放到 struct video_capability      結構裡。同理，v4l_get_picture() 也會呼叫 ioctl()      ，並將影像視窗資訊放到 struct video_picture 結構。</p>     <p>v4l_get_capability() 函數程式碼如下：</p>     <blockquote>       <p>int v4l_get_capability(v4l_device *vd)<br />       {<br />          if (ioctl(vd->fd, VIDIOCGCAP, &amp;(vd->capability)) &lt; 0) {<br />             perror("v4l_get_capability:");<br />             return -1;<br />          }<br />          return 0;<br />       }</p>     </blockquote>     <p>在這裡，其實只有底下這一行才是 v4l_get_capability 的主力：</p>     <blockquote>       <p>ioctl(vd->fd, VIDIOCGCAP, &amp;(vd->capability)); </p>     </blockquote>     <p>其它部份都是屬於錯誤處理的程式碼，在本文，筆者都將函數寫的完整一點，即包含了錯誤檢查，因為我們想要實作一個      v4l 的 library。</p>     <p>vd->fd 是由 v4l_open 傳回來的 file descriptor，而傳遞 VIDIOCGCAP 給      ioctl() 則會傳回設備相關資訊，在這裡則是存放於 vd->capability。</p>     <p><font color="#0000ff"><strong>v4l_get_ picture() -- picture 的初始化</strong></font><br />     <br />     取得設備資訊後，我們還要再取得影像資訊，所謂的影像資訊指的是輸入到影像捕捉卡的影像格式。</p>     <p>在 _v4l_struct 結構裡，我們宣告 channel 如下：</p>     <blockquote>       <p>struct video_picture picture;</p>     </blockquote>     <p>初始化 picture      的意思就是要取得輸入到影像捕捉卡的影像資訊，我們設計 v4l_get_      picture() 函數來完成這件工作。</p>     <p>v4l_get_ picture () 完整程式碼如下：</p>     <blockquote>       <p>int v4l_get_picture(v4l_device *vd)<br />       {<br />          if (ioctl(vd->fd, VIDIOCGPICT, &amp;(vd->picture)) &lt; 0) {<br />             perror("v4l_get_picture:");<br />             return -1;<br />          }<br />          return 0;<br />       }</p>     </blockquote>     <p>傳遞VIDIOCGPICT 給 ioctl() 則會傳回影像的屬性 (image properties)，這裡則是將影像屬性存放於      vd-> picture。</p>     <p><font color="#0000ff"><strong>v4l_get_channels() -- channel 的初始化</strong></font><br />     <br />     接下來，我們還要再做 channel 的初始化工作。還記得在 _v4l_struct      結構裡，我們宣告 channel 如下：</p>     <blockquote>       <p>struct video_channel channel[8];</p>     </blockquote>     <p>channel 是一個 8 個元素的陣列，一般絕大部份都會宣告 4      個元素，因為大部份的影像捕捉卡都只有 4 個 channel。幾乎沒有影像捕捉卡有      8 個 channel的。</p>     <p>初始化 channel 的意思就是要取得「每個」 channel 的資訊，我們設計      v4l_get_channels() 函數來完成這件工作。</p>     <p>v4l_get_channels() 完整程式碼如下：</p>     <blockquote>       <p>int v4l_get_channels(v4l_device *vd)<br />       {<br />          int i;<br />       <br />          for (i = 0; i &lt; vd->capability.channels; i++) {<br />             vd->channel[i].channel = i;<br />       <br />             if (ioctl(vd->fd, VIDIOCGCHAN, &amp;(vd->channel[i]))        &lt; 0) {<br />                perror("v4l_get_channel:");<br />                return -1;<br />             }<br />          }<br />          return 0;<br />       }</p>     </blockquote>     <p>要記得，我們是對每個 channel      做初始化，所以必須用一個迴圈來處理每個 channel。那我們怎麼知道影像捕捉卡上有幾個      channel 呢？記得我們設計 v4l_open() 時也「順路」呼叫了      v4l_get_capability() 嗎！v4l_get_capability()      所取得的設備資訊，就包含了影像捕捉卡的 channel      數。這個資訊儲存於 vd->capability.channels 裡。</p>     <p>由於 v4l_get_capability() 是必備的程序，所以我們就順便寫在 v4l_open()      裡。當然，如果您沒有在 v4l_open() 裡呼叫 v4l_get_capability()，這樣的設計方式當然沒有錯，只是在設計應用程式時，要記得在      v4l_open() 後還要再呼叫 v4l_capability() 才行。</p>     <p>在迴圈裡，首先先替每個 channel 做編號：</p>     <blockquote>       <p>vd->channel[i].channel = i</p>     </blockquote>     <p>然後再取得 channel 的資訊：</p>     <blockquote>       <p>ioctl(vd->fd, VIDIOCGCHAN, &amp;(vd->channel[i]);</p>     </blockquote>     <p>傳遞 VIDIOCGCHAN 給 ioctl() 則會傳回 channel 的資 訊，這裡則是將      channel 的資訊存放於 vd-> channel[i]。</p>     <p>要注意一下，在 kernel 2.4 的 API.html 文件裡，粗心的 programmer 將      VIDIOCGCHAN 打成 VDIOCGCHAN，少了一個 "I"。</p>     <p><font color="#0000ff"><strong>v4l_get_audios() -- audio 的初始化</strong></font><br />     <br />     接下來，我們再做 audio 的初始化工作，audio 的初始化方式與初始化      channel 的方法很像。在 _v4l_struct 結構裡，我們宣告 auduio      的結構如下：</p>     <blockquote>       <p>struct video_audio audio[8];</p>     </blockquote>     <p>audio 是一個 8 個元素的陣列，與 channel      一樣。一般絕大部份都會宣告 4      個元素，因為大部份的影像捕捉卡都只有 4 個 audio。幾乎沒有影像捕捉卡有      8 個audio的。</p>     <p>初始化 audio 的意思就是要取得「每個」 audio 的資訊，我們設計      v4l_get_audios() 函數來完成這件工作。</p>     <p>v4l_get_audios() 完整程式碼如下：</p>     <blockquote>       <p>int v4l_get_audios(v4l_device *vd)<br />       {<br />          int i;<br />       <br />          for (i = 0; i &lt; vd->capability.audios; i++) {<br />             vd->audio[i].audio = i;<br />       <br />             if (ioctl(vd->fd, VIDIOCGAUDIO, &amp;(vd->audio[i]))        &lt; 0) {<br />                perror("v4l_get_audio:");<br />                return -1;<br />             }<br />          }<br />          return 0;<br />       }</p>     </blockquote>     <p>別忘了，我們仍然要對每個 audio      做初始化，所以必須用一個迴圈來處理每個 audio。那我們怎麼知道影像捕捉卡上有幾個      audio 呢？與取得 channel 的方式一樣，audio 數量的資訊儲存於      vd->capability. audios 裡。</p>     <p>在v4l_get_audios() 的迴圈裡，首先先替每個 audio 做編號：</p>     <blockquote>       <p>vd->audio[i].audio = i;</p>     </blockquote>     <p>然後再取得 audio 的資訊：</p>     <blockquote>       <p>ioctl(vd->fd, VIDIOCGAUDIO, &amp;(vd->audio[i]);</p>     </blockquote>     <p>傳遞 VIDIOCGAUDIO 給 ioctl() 則會傳回 audio 的資訊，這裡則是將 audio      的資訊存放於 vd-> audio[i]。</p>     <p>中標=<font color="#0000ff"><strong>v4l_close() -- 關閉裝置檔</strong></font></p>     <p>v4l_close()      程式相當簡單，所以不用再多做介紹啦！直接列出程式碼如下：</p>     <blockquote>       <p>int v4l_close(v4l_device *vd)<br />       {<br />          close(vd->fd);<br />          return 0;<br />       }</p>     </blockquote>     <p><font color="#ff0000"><strong><big>配合應用程式來設計</big></strong></font></p>     <p>設計了幾個函式後，接下來我們要實地設計一個應用程式來說明如何使用      v4l_xxx() 系列的函式。<br />     <br />     底下是一個在應用程式裡初始化影像捕捉卡，並且列出取得的資訊的程式範例      (完整程式碼)：</p>     <blockquote>       <p>#include &lt;stdio.h><br />       #include &lt;stdlib.h><br />       #include &lt;unistd.h><br />       #include "v4l/v4l.h"<br />       <br />       v4l_device vd;<br />       <br />       int device_init(char *dev)<br />       {<br />          if (dev == NULL) {<br />             dev = "/dev/video0"; //set to default device<br />          }<br />       <br />          if (v4l_open(dev, &amp;vd)) return -1;<br />          if (v4l_get_channels(&amp;vd)) return -1;<br />       <br />          printf("%s: initialization OK... %s\n"<br />                   "%d        channels\n"<br />                   "%d        audios\n\n",<br />                   dev,        vd.capability.name, vd.capability.channels,<br />                   vd.capability.audios);<br />       <br />          v4l_close(&amp;vd);<br />          return 0;<br />       }<br />       <br />       int main()<br />       {<br />          if (device_init("/dev/video0") == -1) {<br />             perror("device_init: failed...");<br />             exit(1);<br />          } else {<br />             printf("OK!\n");<br />          }<br />          exit(0);<br />       }</p>     </blockquote>     <p>我們將這個程式存成 main.c，整個程式不用再多做介紹了吧！程式裡用到的地方都有介紹過，其中      vd.capability.name 代表界面的 canonical name。</p>     <p>device_init() 最後呼叫 v4l_close()      將裝置檔關閉，別忘了這個重要的工作！</p>     <p>v4l/v4l.h 的內容如下 (完整程式碼)：</p>     <blockquote>       <p>#ifndef _V4L_H_<br />       #define _V4L_H_<br />       <br />       #include &lt;sys/types.h><br />       #include &lt;linux/videodev.h><br />       <br />       #define PAL_WIDTH 768<br />       #define PAL_HEIGHT 576<br />       #define CIF_WIDTH 352<br />       #define CIF_HEIGHT 288<br />       #define NTSC_WIDTH 640<br />       #define NTSC_HEIGHT 480<br />       <br />       struct _v4l_struct<br />       {<br />          int fd;<br />          struct video_capability capability;<br />          struct video_buffer buffer;<br />          struct video_window window;<br />          struct video_channel channel[8];<br />          struct video_picture picture;<br />          struct video_tuner tuner;<br />          struct video_audio audio[8];<br />          struct video_mmap mmap;<br />          struct video_mbuf mbuf;<br />          unsigned char *map;<br />       };<br />       <br />       typedef struct _v4l_struct v4l_device;<br />       <br />       extern int v4l_open(char *, v4l_device *);<br />       extern int v4l_close(v4l_device *);<br />       extern int v4l_get_capability(v4l_device *);<br />       extern int v4l_set_norm(v4l_device *, int);<br />       extern int v4l_get_channels(v4l_device *);<br />       extern int v4l_get_audios(v4l_device *);<br />       extern int v4l_get_picture(v4l_device *);<br />       extern int v4l_grab_init(v4l_device *, int, int);<br />       extern int v4l_grab_frame(v4l_device *, int);<br />       extern int v4l_grab_sync(v4l_device *);<br />       extern int v4l_mmap_init(v4l_device *);<br />       extern int v4l_get_mbuf(v4l_device *);<br />       extern int v4l_get_picture(v4l_device *);<br />       extern int v4l_grab_picture(v4l_device *, unsigned int);<br />       extern int v4l_set_buffer(v4l_device *);<br />       extern int v4l_get_buffer(v4l_device *);<br />       extern int v4l_switch_channel(v4l_device *, int);<br />       <br />       #endif</p>     </blockquote>     <p>為了維護方便，這裡我們建立一個 v4l/ 的目錄來放 v4l.h 與底下的      v4l.c 檔案。</p>     <p>編譯 main.c 時，也別了也要編譯 v4l.c，並且要指定 v4l.o 的位置給      main.o 才能順利 link；或者我們可以把 v4l.o 再做成 libv4l.a      形式，這是屬於 Linux programming      相關的主題，請自行參考這方面的資料。</p>     <p>我們的 v4l_xxx() 函數則是放在 v4l/v4l.c 檔案裡。v4l/v4l.c 的內容如下      (完整程式碼，只列出目前會用到的函數)：</p>     <blockquote>       <p>#include &lt;stdio.h><br />       #include &lt;unistd.h><br />       #include &lt;error.h><br />       #include &lt;assert.h><br />       #include &lt;fcntl.h><br />       #include &lt;sys/ioctl.h><br />       #include &lt;sys/types.h><br />       #include &lt;sys/mman.h><br />       #include &lt;linux/videodev.h><br />       #include "v4l.h"<br />       <br />       #define DEFAULT_DEVICE "/dev/video0"<br />       <br />       int v4l_open(char *dev, v4l_device *vd)<br />       {<br />          if (!dev)<br />             dev = DEFAULT_DEVICE;<br />       <br />          if ((vd->fd = open(dev, O_RDWR)) &lt; 0) {<br />             perror("v4l_open:");<br />             return -1;<br />          }<br />       <br />          if (v4l_get_capability(vd))<br />             return -1;<br />       <br />          if (v4l_get_picture(vd))<br />             return -1;<br />       <br />          return 0;<br />       }<br />       <br />       int v4l_get_capability(v4l_device *vd)<br />       {<br />          if (ioctl(vd->fd, VIDIOCGCAP, &amp;(vd->capability)) &lt; 0) {<br />             perror("v4l_get_capability:");<br />             return -1;<br />          }<br />          return 0;<br />       }<br />       <br />       int v4l_get_channels(v4l_device *vd)<br />       {<br />          int i;<br />       <br />          for (i = 0; i &lt; vd->capability.channels; i++) {<br />             vd->channel[i].channel = i;<br />       <br />             if (ioctl(vd->fd, VIDIOCGCHAN, &amp;(vd->channel[i]))        &lt; 0) {<br />                perror("v4l_get_channel:");<br />                return -1;<br />             }<br />          }<br />          return 0;<br />       }<br />       <br />       int v4l_get_audios(v4l_device *vd)<br />       {<br />          int i;<br />       <br />          for (i = 0; i &lt; vd->capability.audios; i++) {<br />             vd->audio[i].audio = i;<br />       <br />             if (ioctl(vd->fd, VIDIOCGAUDIO, &amp;(vd->audio[i]))        &lt; 0) {<br />                perror("v4l_get_audio:");<br />                return -1;<br />             }<br />          }<br />          return 0;<br />       }<br />       <br />       int v4l_get_picture(v4l_device *vd)<br />       {<br />          if (ioctl(vd->fd, VIDIOCGPICT, &amp;(vd->picture)) &lt; 0) {<br />             perror("v4l_get_picture:");<br />             return -1;<br />          }<br />          return 0;<br />       }<br />       <br />       int v4l_close(v4l_device *vd)<br />       {<br />          close(vd->fd);<br />          return 0;<br />       }</p>     </blockquote>     <p>當程程式無法初始化裝置時，會出現的錯誤訊息：</p>     <blockquote>       <p>v4l_open:: No such device<br />       device_init: failed...: No such device</p>     </blockquote>     <p>如果出現這樣的錯誤：</p>     <blockquote>       <p>v4l_open:: Device or resource busy<br />       device_init: failed...: Device or resource busy</p>     </blockquote>     <p>最大可能的原因可能是：(1 )軀動程式沒有安裝好或軀動程式不適用，(2)「前人」的程式忘了將裝置檔關閉。</p>     <p>如果程式可以順利初始化裝置，就會看到這樣的訊息：</p>     <blockquote>       <p>/dev/video0: initialization OK... BT878(Hauppauge new)<br />       3 channels<br />       1 audios<br />       <br />       OK!</p>     </blockquote>     <p>我們將取得的裝置資訊 print      到螢幕上，以了解取得的相關資訊。在下一期的文章裡，我們將會介紹更多      video4linux 的設計方法，來做到更進階的工作。</p>     <p>在這裡我們看到程式已經成功初始代我們的裝置，並且知道裝置有      3 個 channel、1 個 audio。</p>     <p><font color="#ff0000"><strong><big>STREAMS Programming</big></strong></font></p>     <p>接下來要介紹的是屬於觀念性的話題，比較不重要。我們將以理論為主，來講解      "STREAMS" 程式設計的基礎觀念。</p>     <p><font color="#0000ff"><strong>什麼是 STREAMS?</strong></font></p>     <p>在 Solaris 2 的 kernel 裡，STREAMS      定義了一個標準界面，這個界面主要的功能是提供裝置與 kernel      之間的 I/O 溝通管道。這個界面其實是由系統呼叫 (system calls)      與核心常式 (kernel routines) 所組成，我們可以簡單表示成下圖：</p>     <p><img width="499" height="440" alt="1.png (46483 bytes)" src="http://www.jollen.org/linux/video_streaming/1.png" /></p>     <p><em>圖 1</em></p>     <p>圖中的 Module 標示為 Optional，也就是在 Stream Head 與 Driver      之間，並不一定存在這個 Module，這個 Module      屬於中間者的角色，也就是，當 stream (解釋成資料串流或許比較好理解)      在 Stream Head 與 Driver 之間「流」動時，Module 會從中做額外的處理。</p>     <p>有時這個 Module      是相當重要的，因為資料串流必須經過特殊的處理，才能流向彼方。</p>     <p>這種 kernel 設計的方式相當好，因為 Module 一定是動態 (dynamic)      被裝到串流裡的。而且，這個 Module 是由 user process 所載入，因此，user      可以根據不同的心情「抽換」不同的 Module。</p>     <p>在最底下 Driver 的地方一般指的是 UNIX      底下的設備檔，到這裡，讀者有沒有感覺到，是不是有些觀念跟我們實作出來      video4linux 程式庫可以相連呢！</p>     <p>由圖可以看出，根據 stream 的流向，可以將 stream 分成 downstream 與      upstream。由於 stream 是雙向的，所以我們可以把 STREAMS      稱為全雙工模式 (full-duplex) 的資料處理與傳送。</p>     <p>我們可以把圖 1 再簡單表示成下圖：</p>     <p><img width="422" height="242" alt="2.png (28851 bytes)" src="http://www.jollen.org/linux/video_streaming/2.png" /></p>     <p><em>圖 2</em></p>     <p>由這裡可以發現一個事實，整個 STREAMS 的起點是 Driver，而終點是      User Process。在 user space 與 kernel space 之間則是由 Stream head 來連接。</p>     <p>當然，user process 可能是 local user process 或者 remote user process。目前為止，我們尚未進入      user process 的部份，所以暫時不會提到 RTP 等通訊協定的設計。</p>     <p>接下來，再介紹一下 downstream 與 upstream。通常，downstream 也稱為      write side，也就是寫入資料那一方；而 upstream 則稱為 read side，也就是讀取資料那一方。那麼，在      UNIX programming 裡，什麼時候會牽涉到 STREAMS 呢？</p>     <p>最簡單的例子莫過於由終端機讀取字元的範例了。一個簡單的程式片段如下：</p>     <blockquote>       <p>main()<br />       {<br />          char buf[1024];<br />          int fd;<br />          int count;<br />       <br />          if ((fd = open("/dev/tty1", )_RDWR)) &lt; 0) {<br />             perror("open: /dev/tty1");<br />             exit(1);<br />          }<br />       <br />          while ((count = read(fd, buf, sizeof(buf))) > 0) {<br />             if (write(fd, buf, count) != count) {<br />                perror("write: /dev/tty1");<br />                break;<br />             }<br />          }<br />       <br />          exit(0);<br />       }</p>     </blockquote>     <p>對 Network programming 而言，如果我們要經由 Socket      讀取字元，可以寫一個簡單的程式如下：</p>     <blockquote>       <p>int main(int argc, char *argv[])<br />       {<br />          char *buff = "Hello, socket!";<br />          int sockfd;<br />          struct sockaddr_in serv_addr;<br />       <br />          sockfd = socket(AF_INET, SOCK_STREAM, 0); <br />       <br />          serv_addr.sin_family = AF_INET;<br />          serv_addr.sin_addr.s_addr = inet_addr("192.168.1.10");            // ip<br />          serv_addr.sin_port = htons(3999);                        //        port<br />       <br />          connect(sockfd, &amp;serv_addr, sizeof(serv_addr));<br />          write(fd, buff, strlen(buff));<br />       <br />          close(sockfd);<br />          exit(0);<br />       }</p>     </blockquote>     <p>這是一個 client 端向 server      送出字元的程式範例，這段程式主要是要讓讀者看出，經由終端機設備寫入字元時，是利用      write() 函數，而在 socket 上寫入字元，卻也是利用 write() 函數。</p>     <p>這種 UNIX kernel 整合週邊設備與網路 I/O 的機制事實上就是 STREAMS      programming 所要解決的問題。整合 UNIX kernel 與網路 I/O 的工作首先由Dennis      Ritchie 這位大師所進行，所以現在我們才會擁有現今這麼強大的 UNIX      系統。</p>     <p>到目前為止，我們仍然只對 video capture card      做初始化的動作，並討論一些觀念，接下來的文章將以循序漸進的方式實作整個      Video Streaming 系統。</p>     <p><font color="#ff0000"><strong><big>下期預告</big></strong></font></p>     <p>下一期我們將實作更進階的 video4linux，並且討論一下 i2c 與 bttv      這兩個 driver，同時也介紹一些較實用且有趣的相關主題。</p>     <blockquote>       <p>*1: 見 PC2000/4月份：Linux 的應用--Video Streaming(一)</p>     </blockquote>     <p>網路資源&amp;參考資料：</p><ul><li>VideoLAN -- <a href="http://www.videolan.org/">http://www.videolan.org/</a></li><li>VIC -- <a href="http://www-nrg.ee.lbl.gov/vic/">http://www-nrg.ee.lbl.gov/vic/</a></li><li>-- <a href="http://www-mice.cs.ucl.ac.uk/multimedia/software/vic/">http://www-mice.cs.ucl.ac.uk/multimedia/software/vic/</a></li><li>mod_mp3 -- <a href="http://media.tangent.org/">http://media.tangent.org/</a></li><li>Linux kernel 文件 -- video4linux</li><li>Real Networks -- <a href="http://www.realnetworks.com/">http://www.realnetworks.com/</a></li><li>Solaris 2.5 -- STREAMS programming</li><li>BTTV -- <a href="http://www.metzlerbros.de/bttv.html">http://www.metzlerbros.de/bttv.html</a></li></ul>]]></description>
         <link>https://www.jollen.org/blog/2001/05/linux_video_streaming_2.html</link>
         <guid>https://www.jollen.org/blog/2001/05/linux_video_streaming_2.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Mon, 14 May 2001 14:36:00 +0800</pubDate>
      </item>
            <item>
         <title>Linux 的應用（刊載於 PC2000 雜誌四月號） -- Video Streaming 探討 (1)</title>
         <description><![CDATA[Video Streaming 的技術已經出現多年，在 Internet 上的應用也已經相當廣泛，而利用 Linux 建構 Video Streaming Server 也是相當熱門的話題。如果您想瞭解什麼是 Video Streaming，本文對 Video Streaming 的觀念與技術將做了清楚而基本的介紹。

<p><font color="#ff0000"><strong><big>Video Streaming 來的正是時候</big></strong></font></p>     <p>看到 "Video" Streaming 就知道這個技術與影音有關。Video      Streaming 是一種經由網路來撥放影音檔案的技術，Video      "Streaming"      的基本概念為「一邊下載一邊撥放」，我們稱之為「Play as received」。</p>     <p>經由 Internet      如果要收看遠端伺服器的電視檔案，最原始的做為是「下載後再撥放」，也就是經由      FTP 或 HTTP      將整個檔案下載至本地端後再利用撥放程式來撥放，我們稱之為「Play      after download」。</p>     <p>以最常見的例子來講，我們可以經由網路將 MP3      完整下載後再撥放，也就是 Play after download，或者我們也可以一邊下載      MP3 一邊聽音樂，這種方式則稱為 Play as received。其他的應用範例還很多，例如利用      RealPlayer「即時觀看影片或是聽音樂」，這種即時撥放的方式即是 Play      as received，我們稱這樣的技術為 Video Streaming Technology。</p>     <p>為什麼說 Video Streaming      的技術來的正是時候呢？舉個最簡單的情況，你可能在下載 MP3      之前想要試聽一下音樂的內容，而下載音樂片段的方式又顯得不夠友善，這時如果利用      Video Streaming      的技術來讓網友視聽，不但方便，而且不必浪費時間來下載不喜歡的音樂的。</p>     <p>隨著網路頻寬的改進與 IPv6 的出現，Video Streaming      的應用將越來越廣泛，利用 Video Streaming      來設計的資訊家電也會越來越多，例如較常被提及的網路電話      (InterPhone)。</p>     <p>除了寬頻網路的普及外，儲存設備的大進步也是加速 Video Streaming      進用普及的最大動力，例如利用 Linux 設計的 NAS      可以被用來當做大型的影音伺服器或是動態電影資料庫 (DMD)。</p>     <p><font color="#ff0000"><strong><big>VCD &amp; MPEG-1</big></strong></font></p>     <p>要提到撥放動態影音的先軀，絕對要先從 VCD      說起，因為這是在電腦出現後，最能深入消費者市場的產品。</p>     <p>想當年，要撥放 VCD 除了要有一台當時算是高擋貨的 1x CD-ROM      外，還要再另外加裝也是高擋貨的 MPEG 卡。撥放 VCD 其實就是 Video      Streaming 的技術，因為 VCD 的資料是一邊讀取一邊撥放，那為什麼要有      MPEG Card 呢？理所當然的，這是因為MPEG-1 的標準。</p>     <p>MPEG-1 被設計在 1x CD-ROM 上撥放 VCD，而 VCD 上的資料必須經由 MPEG      Card 解碼 (decode)      後才能撥放。但是隨著硬體的進步，現在可以經由軟體來做 MPEG-1      解碼的工作。我們將在後文介紹 MPEG-1 的設計概念。</p>     <p><font color="#ff0000"><strong><big>DVD &amp; MPEG-2</big></strong></font></p>     <p>MPEG-2 常被提到則是由於 DVD 的流行。不管是 MPEG-1 或是 MPEG-2，其實是一種編碼的技術，而      DVD-ROM 則是選擇了 MPEG-2 來當做它的壓縮標準。</p>     <p>而 DVD 之所以無法像 VCD      一樣能被「備份到硬碟」，則是由於其中又加進了三道的保護，第二道保護就是玩家們耳熟能詳的      CSS。在 Linux 下之所以無法有「合法」的 DVD Player，就是受制於 CSS      的關係。</p>     <p>利用 NAS 來設計 Video Server，要儲存並廣撥 VCD      已經沒有技術上的障礙，但是要儲存並擴撥 DVD，則有法律上的問題，這點是值得我們深思的一個問題。因為目前      CSS 的技術掌握在日本人手裏，要取得這項技術必須簽署 NDA，不過 CSS      並非不可破解想必大家都知道，但是就是有版權上的問題。</p>     <p><font color="#ff0000"><strong><big>影像編碼技術介紹</big></strong></font></p>     <p>目前學術界已經發展出許多處理影像訊號壓縮及編碼的技術 (codecs)，談到這些技術，應用最廣泛的編碼標準底下四種：</p><ol><li>) H.261</li><li>) H.263</li><li>) JPEG, MJPEG</li><li>) MPEG</li></ol>     <p>底下將分別簡單介紹這四種編碼技術。</p>     <p><strong><font color="#0000ff">H.261 標準</font></strong></p>     <p>H.261 的影像編碼標準出現在 1990 年的 ITU。一開始的用途主要是支援影像電話      (video phone) 與視訊會議 (video conferencing)。H.261      的格式有二種，分別有不同的解析度：</p><ol><li>) QCIF：176x144</li><li>) CIF：352x288</li></ol>     <p>H.261 的 fps (frames per second) 可以達到 7.5, 10, 15 與 30 fps。由於 H.261      一開始是架構在 ISDN B 上面，而 ISDN B 的傳輸速度為 64 Kbps，所以 H.261      也被稱為 Px64 (x = 1 to 30)。</p>     <p>CIF 全名為 (Common Intermediate Format)，主要是為了要支援各種不同解析度的電影而被定義出來，例如      NTSC, PAL, SECAM 電視系統。而 QCIF 則是 Quarter-CIF 也就是 CIF      解析度的一半。</p>     <p>除此之外，H.261 也可以說是 MPEG-1 標準的前輩。</p>     <p><font color="#0000ff"><strong>H.263 標準</strong></font></p>     <p>H.263 是 H.261 的加強版，誕生於 1994 年 (ITU)。H.263 開始支援 PSTN，不過要特別說明一點，H.263      比 MPEG-1 還要晚出現，而且 H.263 還是基於 MPEG-1 而發展。</p>     <p>H.263 的標準共支援五種不同的解析度，分別為：</p><ol><li>) Sub-QCIF：128x96</li><li>) QCIF：176x144</li><li>) CIF：352x288</li><li>) 4CIF：704x576</li><li>) 16CIF：1408x1152</li></ol>     <p>傳輸速度為 8 Kbps ~ 1.5 Mbps。除此之外，H.263 也是 MPEG-4      標準的基礎。</p>     <p><font color="#0000ff"><strong>JPEG</strong></font></p>     <p>提到 JPEG 的標準，大家可就不莫生了，由其是在 GIF 發出禁令後，JPEG      更是被廣泛應用。JPEG      的全名大家一定不莫生，他可是赫赫有名的一群技術團隊的縮寫 -      Joint Photographic Experts Group。</p>     <p>JPEG 是 24-bit 的 "true-color" 影像標準，JPEG 的工作是將 RGB      格式的影像轉換成 YCrCB      格式，目的是為了減少檔案大小，一般約可減少 1/3 ~ 1/2 左右。</p>     <p>不過 MJPEG 才是我們的主角，MJPEG 全名為 "Motion" JPEG，也就是會動的      JPEG 圖檔。許多 Video Streaming      的場合，像是簡單的視訊會議軟體都會使用 MJPEG 來取代 MPEG，原因無它，因為      MJPEG 格式簡單，但缺點是不支援聲音。</p>     <p><font color="#0000ff"><strong>MPEG-1 標準</strong></font></p>     <p>MPEG 的標準由 ISO (International Standards Organization) 所制定，全名為      Moving Pictures Experts Group (MPEG 為 ISO 工作)，這些團隊制定了包括 MPEG-1、MPEG-2、MPEG-4      等標準。</p>     <p>MPEG-1 的標準比 H.263 早出現，MPEG-1 制定於 1992      年，主要用途為：視訊會議、影像電話、電腦遊戲與支援第一代的      CD-ROM。MPEG-1 被設計來支援大部份的影像與 CD-ROM 的音效，傳輸速度為      1.5 Mbps (30 fps)。</p>     <p>除此之外，MPEG-1 也支援 playback，例如快轉、倒帶或是跳躍，這也是      MPEG-1 好用的地方。稍後我們會簡單介紹一下 MPEG-1 的原因，以明白      MPEG-1 如何做到這些功能。</p>     <p><font color="#0000ff"><strong>MPEG-2 標準</strong></font></p>     <p>MPEG-2 的標準出現於 1994 年，MPEG-2 相容於 MPEG-1。MPEG-2      的出現並不是為了要取代 MPEG-1 的舊標準，而是要加強 MPEG-1      不足的地方。因此 MPEG-2 更能昇任其它工作環境，例如 HDTV、視訊廣播。</p>     <p>同時，MPEG-2 的解析度也支援到 HDTV 1280x720。在音效方面，MPEG-2      也支援到 6 個頻道 (MPEG-1 只支援 2 個頻道)。MPEG-2      的傳輸速度也提昇至 2Mbps ~ 10 Mbps，因此需要 4x 的 CD-ROM，但也因此 4x      CD-ROM 只能儲存 18 分種的影像，所以我們利用 DVD-ROM 來儲存 MPEG-2      格式的影像，而且 DVD-ROM 也支援 Dolby 音效。</p>     <p><font color="#0000ff"><strong>MPEG-4 標準</strong></font></p>     <p>MPEG-4 的標準在 1993      年被提出，主要的應用用途比較廣，包括：視訊會議、影音郵件、無線裝置等等，支援的傳輸速度為      8Kbps ~ 35Mbps。</p>     <p>MPEG-4 可以傳送影像的物件，而不是只有影像的 "frame"，例如一連串的動作指令。因此，MPEG-1      與 MPEG-2 皆是 "frame-based" 的標準，而 MPEG-4 則是      "object-based"      的標準，未來在網路與多媒體的應會也會更普遍。</p>     <p>MPEG-4 目前在 Linux 上已經有 OpenMPEG      的專案計畫，發起本計畫的目地在希望可以在 Linux 上發展完整的      MPEG-4 支援環境。</p>     <p><font color="#ff0000"><strong><big>Video Streaming 的網路技術</big></strong></font></p>     <p>要建置一個完整的 Video Streaming      環境，在整體來看，要準備的硬體包括：伺服器、CCD、影像補捉卡等等，而在底層的技術面方面，除了前面提到的影像編碼技術外，也要配合通訊協定才能實作出      Video Streaming 的應用程式。</p>     <p><strong><font color="#0000ff">Video Streaming 的方式</font></strong></p>     <p>Video 在做 Streaming 時，有三種方式可以應用：broadcasting、unicasting、multicasting。</p>     <p>broadcasting 的方式比較單純，他是在 LAN      上直接將一個個的影像封包丟到網路上 (server 端)，再由 client      的應用程式自網路上取回封包播放。但網路硬體層上，仍有許多需要考慮的問題，例如在      Shared Non-Switched Enthernet 上時，就會發生一些小問題。</p>     <p>unicasting 與 multicasting 都是屬於 IP 的傳輸方式。unicasting 採取 1 對 1      的方向傳影像給遠端，稱為 Video-on-Demand (VoD)，multicasting 則是 1      對多的傳輸方式，稱為 Near-Video-on-Demand (NVoD)。未來 IPv6 將支援 IP      Multicasting，因此 Video Streaming 的應用將更為廣泛。</p>     <p><font color="#0000ff"><strong>IPv6</strong></font></p>     <p>IPv6 全名為 IP version 6，未來將取代目前所使用的 IPv4。IPv6      新增許多對多媒體傳輸的支援，例如 multicasting、authentication/encryption等。不過目前尚仍未全面升級，所以應用仍受限。</p>     <p><font color="#0000ff"><strong>UDP 通訊協定</strong></font></p>     <p>UDP 全名是 Universal Datagram Protocol，UDP 通訊協定是 Video Streaming      的基石。UDP 與 TCP 不同的是，UDP 並沒有錯誤檢查，不過這在 Video      Streaming 的應用是影嚮不太的。</p>     <p><font color="#0000ff"><strong>RTP 通訊協定</strong></font></p>     <p>RTP 全名為 Real-Time Protocol，RTP 是在 UDP 封包之前多加 10 bytes      的檔頭，裡面記載有時間、序號、壓縮型態等資訊。RTP      是目前大多數 Video Streaming 軟體所使用的通訊協定。</p>     <p>RTP 可用來針對各種不同的多媒體格式做 Streaming      的工作，因為我們將影像分解成數個 RTP      封包再傳送出去，因此會遇到許多網路技術常會遇到的問題。例如，因為封包送達的時間不一，造成播放時會畫面不流暢的現像，因此，在播發時就必須使用一個緩衝區      (playout buffer) 來暫時存放並處理網路上接受到的封包。</p>     <p>由網路上接收的影像封包因為彼此之間到達的時間間隔不同      (Synchronous Data Packets)，所以必須利用緩衝區將這些封包做緩衝，讓彼此之間的時間間隔一樣      (Isochronous Data Packets)。</p>     <p><font color="#0000ff"><strong>MPEG-1 的原理</strong></font></p>     <p>MPEG-1 的編碼原理大家都應該要認識一下。MPEG-1 將影像分成一個      Group，即 GOP (Group of Pictures)，每個 GOP 有一個 Entry Point，稱為 I-frame，每個      I-frame 的大小為 15KB，另外還有 P-frame 與 B-frame。</p>     <p>I-frame 稱為 Independent-frame，為每個 GOP 的起點，緊接著的是二個      B-frame，B-frame 稱為 Bi-directional frame，每個 B-frame 大小為 3KB，每二個      B-frame 之間再插入一個 P-frame，即 Predictive-frame，每個 B-frame 的大小為      8KB。</p>     <p>GOP 共有三種不同的 frame，每種 frame      存放的影像與性質皆不同，這在以後我們實做程式時會再做更清楚的介紹。例如，我們要將影片快轉時，可以忽略掉所有的      B-frame 與 P-frame，只撥放 I-frame，如此一來便可以節省許多貴寶的網路頻寬與時間，但前提是，I-frame      必須存放主要的影像資料，事實上也是如此。</p>     <p><big><font color="#ff0000"><strong>本期結語</strong></font></big></p>     <p>本篇文章主要的目的是要讓大家對影像的一些標準與 Video Streaming      所使用的通訊協定有一定的認識，更多與影像有關的內容也會陸續為大家做介紹。</p>     <p>下一期我們要介紹更多有趣的主題，包括 Linux 核心裡的 video4linux，與      Linux 環境下的影像專案與好用的 Video Streaming 應用程式。</p>]]></description>
         <link>https://www.jollen.org/blog/2001/02/linux_video_streaming_1.html</link>
         <guid>https://www.jollen.org/blog/2001/02/linux_video_streaming_1.html</guid>
                  <category domain="http://www.sixapart.com/ns/types#category">GNU Toolchains &amp; Linux Systems Programming</category>
        
        
         <pubDate>Tue, 13 Feb 2001 03:41:00 +0800</pubDate>
      </item>
      
   </channel>
</rss>