2013年9月27日 星期五

C++ 入門指南 - 封裝

http://pydoing.blogspot.tw/2012/10/cpp-encapsulation.html

Loda's blog

http://loda.hala01.com/

米歐的學習筆記

http://miox.cc/

AT Command

http://miox.cc/search/label/AT%20COMMAND

[轉] Android Toolchain與Bionic Libc


Android所用的Toolchain(即交叉編譯工具鏈)可從下面的網址下載:
http://android.kernel.org/pub/android-toolchain-20081019.tar.bz2。如果下載了完整的Android項目的源代碼,則可以在「/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin」目錄下找到交叉編譯工具,比如Android所用的arm-eabi-gcc-4.2.1。
Android並沒有採用glibc作為C庫,而是採用了Google自己開發的Bionic Libc,它的官方Toolchain也是基於Bionic Libc而並非glibc的。這使得使用或移植其他Toolchain來用於Android要比較麻煩:在Google公佈用於Android的官方Toolchain之前,多數的Android愛好者使用的Toolchain是在http://www.codesourcery.com/gnu_toolchains/arm/download.html 下載的一個通用的Toolchain,它用來編譯和移植Android 的Linux內核是可行的,因為內核並不需要C庫,但是開發Android的應用程序時,直接採用或者移植其他的Toolchain都比較麻煩,其他Toolchain編譯的應用程序只能採用靜態編譯的方式才能運行於Android模擬器中,這顯然是實際開發中所不能接受的方式。目前尚沒有看到說明成功移植其他交叉編譯器來編譯Android應用程序的資料。
與glibc相比,Bionic Libc有如下一些特點:
- 採用BSD License,而不是glibc的GPL License;
- 大小只有大約200k,比glibc差不多小一半,且比glibc更快;
- 實現了一個更小、更快的pthread;
- 提供了一些Android所需要的重要函數,如」getprop」, 「LOGI」等;
- 不完全支持POSIX標準,比如C++ exceptions,wide chars等;
- 不提供libthread_db 和 libm的實現
另外,Android中所用的其他一些二進制工具也比較特殊:
- 加載動態庫時使用的是/system/bin/linker而不是常用的/lib/ld.so;
- prelink工具不是常用的prelink而是apriori,其源代碼位於」 /build/tools/apriori」
- strip工具也沒有採用常用的strip,即「/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin」目錄下的arm-eabi-strip,而是位於/out/host/linux-x86/bin/的soslim工具。


Android Code Review Gerrit

https://android-review.googlesource.com/#/q/status:open,n,z

2013年9月25日 星期三

何謂udev?

udev 是Linux kernel 2.6系列的裝置管理器。它主要的功能是管理/dev目錄底下的裝置節點。它同時也是用來接替devfs及hotplug的功能,這意味著它要在添加/刪除硬體時處理/dev目錄以及所有使用者空間的行為,包括載入firmware時。

udev的最新版本依賴於升級後的Linux kernel 2.6.13的uevent介面的最新版本。使用新版本udev的系統不能在2.6.13以下版本啟動,除非使用noudev參數來禁用udev並使用傳統的/dev來進行裝置讀取



概要

在傳統的Linux系統中,/dev目錄下的裝置節點為一系列靜態存在的檔案,而udev則動態提供了在系統中實際存在的裝置節點。雖然devfs提供了類似功能,udev的支援者也給出了很多udev實作比devfs好的理由[1]
  • udev支援裝置的固定命名,而並不依賴於裝置插入系統的順序。預設的udev設定提供了儲存裝置的固定命名。任何硬碟都根據其唯一的檔案系統id、磁碟名稱及硬體連線的物理位置來進行識別。
  • udev完全在使用者空間執行,而不是像devfs在核心空間一樣執行。結果就是udev將命名策略從核心中移走,並可以在節點創建前用任意程式在裝置屬性中為裝置命名。

執行方式

udev是一個通用的核心裝置管理器。它以守護行程的方式執行於Linux系統,並監聽在新裝置初始化或裝置從系統中移除時核心(透過netlink socket)發出的uevent。
系統提供了一套規則用於匹配可發現的裝置事件和屬性的匯出值。匹配規則可能命名並創建裝置節點,並執行配置程式來對裝置進行設定。udev規則可以 匹配像核心子系統、核心裝置名稱、裝置的物理等屬性,或裝置序列號的屬性。規則也可以請求外部程式提供資訊來命名裝置,或指定一個永遠一樣的自訂名稱來命 名裝置,而不管裝置什麼時候被系統發現。

Linux 驅動程式觀念解析

http://www.jollen.org/blog/2006/05/

touch panel驅動程式實做

http://zh.scribd.com/doc/19234558/touch-panel

Android Booting

http://elinux.org/Android_Booting

The Android boot process from power on

http://www.androidenea.com/2009/06/android-boot-process-from-power-on.html

Android/Linux Source Code Reference

http://hala01.com/
http://androidxref.com/
https://android.googlesource.com/

2013年9月24日 星期二

stack vs heap:執行時期儲存兩大要角


現代電腦系統大多依照Von Neumann Architecture設計而成,其中一特色stored programming乃指『程式執行一定要將欲執行的指令跟資料放入記憶體方可執行』,由此可知執行過程中記憶體所佔的地位厥偉之處。但許多工程師卻搞不清楚記憶體中的stack跟heap space到底有何居別,下面簡單針對兩者加以論述,希望對讀者有所幫助~

三分天下。程式執行過程中其實主要分成三大區塊:global、stack、heap三塊。其中global區塊最最易理解,主要存放全域變數或宣告為static的靜態變數在此就不多做贅述;另外兩個區塊分別為stack跟heap這兩者往往混淆不清,尤其在java中有時候會出現stack overflow或heap overflow到底兩者差異在哪,若工程師連這都不清楚那以後怎麼去調整JVM中的heap memory space跟stack memory space的大小呢?


貼心的系統全自動化管理區塊:Stack Memory Space!在記憶體中不外乎就是要存放變數、函式相關資訊等資料,使運作過程可以順利取得所需的變數或函式所在地。要讓系統可以全自動化管理,代表需可被預期此變數或函數資訊的生命週期,一旦完全可預測代表可以安心的交由系統管理,這些資訊也將在執行過程中被存放在stack空間。Stack中常見的存放資訊如下:區域變數(local variable)、函式參數(function/method parameter)、函數的返回位址(function/method return address)等資訊。為何上述資訊會放於stack之中,簡單來說:

void method1() {

  int x = 100;

}

上述的int x = 100,系統會在stack中找一個區塊給x,另外裡面的內容為100。然而,x會被放入stack主要是因為在編譯時期系統已經可以預知x從何時開始配置跟何時結束回收(當然就是看所屬block結束就跟著回收),由於配置跟回收的規則明確,當然就往stack擺囉。

在舉一例子:

void method2() {

  method1();

}

上述當呼叫method1()時,系統會先把method2的返回位址存到stack當中,為何是存放在stack呢,因為函式的呼叫有後進先出的概念,當method1()被呼叫而開始執行,待結束時必定會查找該返回何處,故最後一定會讀取函式的返回位址,既然如此明確而有條理,當然也是往stack放!

可預測性外加後進先出的生存模式,令stack無疑是最佳的存放策略。由於程式語言中變數跟函式的生命週期皆為後進先出的概念,也就是越晚產生的會越先被回收或銷毀。正因如此只要是可預測性的相關資訊都是往stack存放。此外,由於stack中的資料之存活週期規律故由系統自行產生與回收其空間即可,就不勞工程師們費心啦!

天啊!程式中竟然有不可預測其存活時間的資料存在。在程式中,有部分的需求總是在執行中依據實際情況才會動態增減,這些資訊是難以被預測哪時候開始有?量有多少?何時該回收?…這些不可預測的因素造成上述的stack區塊不適合運用於此。當資訊為動態配置產生,系統會存放在另外一塊空間,稱之為『Heap』(注意這裡的Heap跟資料結構中的Heap不相關,可別會錯意!)。Heap的區塊專收執行期間動態產生的資料,由於為動態產生故結束點無法由系統來掌握,故需使用者自行回收空間。在C++或Java中利用new語法產生的就是動態配置的物件,需存放於heap中。

奇怪跑越久記憶體用越多的怪現象。許多時候執行的程式都沒有改變,但卻常出現隨時間執行越久程式所耗用的空間將越多,最後造成out of memory。工程師也不知為何如此,就是定期在out of memory之前restart程式即可。這中現象層出不窮,一般大多是因為工程師沒有正確將記憶體回收所導致。Heap中的資料如果沒有正常的回收,將會逐步成長到將記憶體消耗殆盡,下次發生上述問題的實後,切記自己檢查一下heap空間的資料有無正常回收。論述到此有些讀者可能會覺得納悶:為何在寫Java都不需要注意回收空間的問題?~答案是因為Java中會採用Garbage Collection(垃圾回收)的機制自動檢查Heap中哪些資料已經沒有被使用,當確認資料已經沒有使用會自動將空間回收,如此工程師就專注撰寫程式即可,不用擔心記憶體回收不當等問題。

The conclusion is…。當產生stack overflow一般是因為過多的函式呼叫(例如:遞迴太深)、或區域變數使用太多,此時請試著將stack size調大一點,另外檢查看看函式的呼叫跟變數的使用量。反之,當發生heap overflow請檢查是否都有正確將heap space的資料回收,另外採行的動態配置是否合理,不要過渡濫用而new出無謂的空間,若真的是程式過於複雜造成,請將heap size調大一些。

程式的撰寫,其實跟製造業的加工廠很相似,套句郭董常說的:『魔鬼藏在細節中』~~

2013年9月23日 星期一

TI DM320/270 DSP+ARM處理器的解決方案

  Ti通過與Ingenient合作,提供了最成熟的MP4設計方案。愛可視推出的世界上第一款MP4產品——愛可視 Jukebox便是採用的Ingenient方案。目前,包括愛可視、iRiver、微星等眾多著名廠商都採用了Ingenient的方案。
硬解碼才是王道即拖即放型閃存MP4導購(2)
圖:IIngenien技術公司基於TI DM2X及DM3X的多媒體硬件參考設計
  Ti的解決方案採用ARM處理器搭載DSP的方式,利用DM320/270 DSP芯片進行音視頻編解碼,ARM處理器負責系統以及支持外圍設備接口。TI DM320為純DSP芯片,因此必須配合ARM處理器才能組成完整的解決方案。該方案所支持的媒體類型非常豐富,包括MPEG4 SP/ASP、pX、MPEG1/2、WMV、WMA、QuickTime 6、H.264、AAC-LC、MP3等格式。其中除了H.264格式外的分辨率只有CIF(352×288)的水平外,其它均可實現D1分辨率(720×576)視頻(30fps)文件的實時解碼。另外,此方案的編碼能力也是相當強大,DM320方案錄製MPEG4 SP視頻可達到D1的質量(30fps),而DM270方案可錄製VGA(640×480)質量的視頻。該方案更可具備PVR的功能(即錄製和播放可雙工同時進行)。
  所有Ti方案均可支持USB2.0 OTG接口、支持HDD、SD、CF、MS等。
優點:
支持的媒體類型豐富,編解碼能力強
缺點:
  • 必須配合ARM處理器,成本不佔優勢,功耗較大;
  • 不支持網絡視頻格式RM、RMVB;
  • 屬於低性能應用處理器(ARM 80-160MHZ),軟件解決方案有限,需要客戶做大量的軟件編程工作。

[轉] 清楚了解 android.os 源碼中的Looper,Handler,Message,MessageQueue

Hi All:
Handler, Message, Looper, MessageQueue 是 android.os 中的class
也是深度開發 Application 時,必須具備的基本觀念,若清楚了解,
便可運用的當。
因為網路有太多模糊不清的文章,大家說法看起來也都不太一樣,
很容易讓人猜東猜西,想東想西的。至於,不想瞎猜的話,就不如直接把source code都讀懂吧。
因此本篇文章,目地在於,快速引導大家快速「正確」的了解,重點在於「正確性」
並且透過靜態 trace code  的方式,跟大家解釋它 source code 的運作原理。
因此,對於四個 class沒信心的時候,
就直接將文章看下去,文章會完整交代 trace source code的部份。
關於這四個 class 的結論:
========================================================
整個Handler, Message, MessageQueue, Looper 它們四個 class 只有一個共同的目標
就是讓程式碼,可以丟到其它 thread 去執行。這麼作有什麼好處呢 ??
例如 android 的 GUI 元件是 thread safe的 (意思是,元件的使用,無法multi-thread執行)
Activity 的畫面顯示是由 UI Thread所負責的,若是你寫了 mutlti-thread 程式時
又想更新畫面,就必須要將 Thread 內部的一段程式碼,交由 UI Thread 來執行才行。
OK, 上面四個 class 的共同目地已經說明完畢了,那麼這四個 class有其分工方式。
因此每個 class 的設計又有不同目地。說明如下 …
Handler 的目地,在於提供 callback function,預其給其它 Thread 作執行
但Handler又要如何 transfer 至其它 Thread 呢 ?  於是有了 Message
Message 的目地,將 Handler 包裝起來,傳送給其它 Thread
但是同時有多條 thread 不斷的在系統中傳遞 Message 那麼如何緩衝呢 ?
MessageQueue 的目地,是為了讓 Message 能夠作緩衝,好讓Message先暫存起來。
因此,當Message 已經被放在其它 Thread上的MessageQueue 之後,
它裡面包著 Handler,而 Handler上的 callback function 總得有人來執行吧 ??
Looper 的目地 :
就是為了將 Message 由 Thread 所對應的 MessageQueue 取出來,並且拿出 Handler
來執行它上面的 callback function.
當 Looper.java 中的 loop() 被呼叫起來之後,它就是在反覆作這件事
不斷將Handler由Message拆包出來,並且執行Handler上的callback function。
                                                                              
======================================================================
以上,已經將這四個class的關係完整說明了。看到這邊您還有疑慮嗎 ?
接下來小弟就直接講 trace source 的部份,
教你快速 trace 懂這些 code,迅速驗證出這四個 class 的用途。
以下開始 trace source code .. Follow Me ^____^
Looper 中的 mThread, mQueue 只有在 Ctor 建立,並且”不會再更改”
mThread = Thread.currentThread() //紀綠此Looper由那條Thread建立
mQueue = new MessageQueue() //每個Looper只有唯一的Queue
主要的執行函式為
Looper.java: loop()  {
  MessageQueue queue = myLooper().mQueue //取得CurrentThread下Looper的MsgQueue
  while(true) {
    Message msg = queue.next() //跳到msg一個message
    msg.target.dispatchMessage(msg)
    //target 被almost設定的方式,是透過Message.obtain(Handler h)設 h 為target
    msg.recycle(); //In Message class, 只有recycle()與obtain() 作sync同步
  }
}
                                                                              
上面程式中,所提到的東西,在以下深入探討。
(1) dispatchMessage(msg) 是如何重要呢 ?
它呼叫 Handler 上的 handleMessage().
———————————————
PS: 一般來說,我們會寫個 EHandler extends Handler,
並且重寫handleMessage()function 好讓 Handler 上的 handlerMessage()
被 UI Thread呼叫,來更新畫面。
——————————
(2) 至於 loop() 是如何被使用的呢 ?
typical example 大約是這樣子的
class LooperThread extends Thread {
  public Handler mHandler;
  public void run() {
    Looper.prepare();
    mHandler = new Handler() {
      public void handleMessage(Message msg) {
        // process incoming messages here
      }
    };
    Looper.loop();
}
類似的 typical example 在 Android 系統中的 ActivityThread.java :: main()
public static final void main(String[] args) {
  Looper.prepareMainLooper();
  ActivityThread thread = new ActivityThread();
  Looper.loop();
}
                                                                              
額外話 …
此範例trace下去將發現, Looper.mMainLooper 變數被設定為
(Looper)sThreadLocal.get()
許多重要的 android source code 皆會透過 getMainLooper() 函數取出
Looper.mMainLooper
(3) msg.target 是個 Handler 類別,  又是從何而來的呢 ?
直接copy高煥堂網路文章中的example code過來 …
講義摘錄之28:Anrdroid 的Message Queue(3/3) 的example code如下
class AnyClass implements Runnable {
 public void run() {
         Looper.prepare();
         h = new Handler(){
               public void handleMessage(Message msg) {
                  EventHandler ha = new EventHandler(Looper.getMainLooper());
                    String obj = (String)msg.obj + ”, myThread”;
                     Message m = ha.obtainMessage(1, 1, 1, obj);
                     ha.sendMessage(m); //sendMessage的原理,請見(4)的說明
         }
       };
  Looper.loop();
  }
}
                                                                              
我們直接由此來作解釋,
追蹤當中的 obtainMssage 可發現 target的由來。原理如下
Handler.java: Message obtainMessage(int what, int arg1, int arg2)
Message.java:
static Message obtain(Handler h, int what, int arg1, int arg2) {
        Message m = obtain();
        m.target = h;  // 這邊就是 msg.target 的由來
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
}
而 Message m = obtain() 是執行下面這段程式
    public static Message obtain() {
        synchronized (mPoolSync) { //與 recycle() 共用 mPoolSync
            if (mPool != null) {
                Message m = mPool;
                mPool = m.next;
                m.next = null;
                return m;
            }
        }
        return new Message();
    }
因此你可從 sample code 知道
Handler 呼叫 obtainMessage 的時候,其實是由 mPool 取出 Message來
將 msg.target 設為原 Handler. 並且設定好 what, arg1, arg2 等參數
好讓 Looper 來執行它 …
———————————————–
(4) 接續 (3) 中的 example code, 它的 sendMessage() 又作了什麼事呢 ?class AnyClass implements Runnable {
 public void run() {
         Looper.prepare();
         h = new Handler(){
               public void handleMessage(Message msg) {
                  EventHandler ha = new EventHandler(Looper.getMainLooper());
                    String obj = (String)msg.obj + ”, myThread”;
                     Message m = ha.obtainMessage(1, 1, 1, obj);
                     ha.sendMessage(m); //sendMessage 作什麼事呢?
         }
       };
  Looper.loop();
  }
}
                                                                              
以這個例子,簡單來說,Looper.getMainLooper() 會回傳一個ActivityThread的
Looper object, 即為 Looper.mMainLooper. 而mMainLooper有自己的mQueue
==================================================
在此穿插一小段 sendMessage() 的作用
Handler本身在暫存一個mQueue, 當Handler的成員函數sendMessage 被呼叫時,即是把帶著 Handler ha 的 Message m,enqueue 至 Handler自己存存的mQueue中。而mQueue的設置,通常是在建構子就被決定好的。因此你得特別注意 Handler 的建構子。
==================================================
像上面的例子中 sendMessage 即是把帶著 Handler ha 的 Message m,enqueue 至 mMainLooper.mQueue
sendMessage 即是把帶著 Handler ha 的 Message m,enqueue 至 mMainLooper.mQueue
好讓 mMainLooper.loop() 函數把 m 由這個 mMainLooper.mQueue取出(取出時名為msg)
來dispatchMessage,因此就會執行到 msg.target.handleMessage(0
也就是 exmaple code 中的 ha.handleMessage();
因為在 Handler ha = new Handler(Looper looper) 這 Ctor 時,
ha.mLooper = looper 便被紀錄下來,而且ha.mQueue=looper.mQueue也被紀錄下來
也就是 looper.mQueue    (PS:若是用 new Handler(), 則looper取Looper.myLooper())
當 ha.sendMessage 被執行時,便將 msg 塞入 looper.mQueue
—————————————————————–
                                                                                
(5) 所以整個 Looper, Message, MessageQueue, Handler 的運作原理是什麼?
因此你的 ha = JohnHandler(MaryLooper) 就像信紙一樣,上面寫著Dear MaryLooper:
上面寫著要執行的程式碼 handleMessage(msg)
透過信封(Message),以Handler.java 的 sendMessage 將信紙(Handler)傳出去
傳到 MaryLooper 的個人信箱 MessageQueue (也就是MaryLooper.mQueue)
在 MaryLooper 中,有個有個固定的 loop() 會不斷被執行
(假設當初宣告此looper的thread, 有 去running 此 function loop 的話 )
那麼 loop 會收到 Message msg. 而 msg.target (Handler) 即為 JohnHandler這封信紙
看著 JohnHandler 上有 handleMessage() 的信紙內容,
故對 Handler 執行了 dipsatchMessage(),因此執行了 JohnHandler
當初信紙內容的交辦事項。
======================================================================

[轉] 深入理解Android消息處理系統——Looper、Handler、Thread

熟悉Windows編程的朋友可能知道Windows程序是消息驅動的,並且有全局的消息循環系統。而Android應用程序也是消息驅動的,按道理來說也應該提供消息循環機制。實際上谷歌參考了Windows的消息循環機制,也在Android系統中實現了消息循環機制。Android通過Looper、Handler來實現消息循環機制,Android消息循環是針對線程的(每個線程都可以有自己的消息隊列和消息循環)。本文深入介紹一下Android消息處理系統原理。
Android系統中Looper負責管理線程的消息隊列和消息循環,具體實現請參考Looper的源碼。 可以通過Loop.myLooper()得到當前線程的Looper對象,通過Loop.getMainLooper()可以獲得當前進程的主線程的Looper對象。
前面提到Android系統的消息隊列和消息循環都是針對具體線程的,一個線程可以存在(當然也可以不存在)一個消息隊列和一個消息循環(Looper),特定線程的消息只能分發給本線程,不能進行跨線程,跨進程通訊。但是創建的工作線程默認是沒有消息循環和消息隊列的,如果想讓該線程具有消息隊列和消息循環,需要在線程中首先調用Looper.prepare()來創建消息隊列,然後調用Looper.loop()進入消息循環。如下例所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  class LooperThread extends Thread {
      public Handler mHandler;
 
      public void run() {
          Looper.prepare();
 
          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };
 
          Looper.loop();
      }
  }
這樣你的線程就具有了消息處理機制了,在Handler中進行消息處理。
Activity是一個UI線程,運行於主線程中,Android系統在啟動的時候會為Activity創建一個消息隊列和消息循環(Looper)。詳細實現請參考ActivityThread.java文件。
Handler的作用是把消息加入特定的(Looper)消息隊列中,並分發和處理該消息隊列中的消息。構造Handler的時候可以指定一個Looper對象,如果不指定則利用當前線程的Looper創建。詳細實現請參考Looper的源碼。
Activity、Looper、Handler的關係如下圖所示:


一個Activity中可以創建多個工作線程或者其他的組件,如果這些線程或者組件把他們的消息放入Activity的主線程消息隊列,那麼該消息就會在主線程中處理了。因為主線程一般負責界面的更新操作,並且Android系統中的weget不是線程安全的,所以這種方式可以很好的實現Android界面更新。在Android系統中這種方式有著廣泛的運用。
那麼另外一個線程怎樣把消息放入主線程的消息隊列呢?答案是通過Handle對象,只要Handler對象以主線程的Looper創建,那麼調用Handler的sendMessage等接口,將會把消息放入隊列都將是放入主線程的消息隊列。並且將會在Handler主線程中調用該handler的handleMessage接口來處理消息。
這裡面涉及到線程同步問題,請先參考如下例子來理解Handler對象的線程模型:
1、首先創建MyHandler工程。
2、在MyHandler.java中加入如下的代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.simon;
 
import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.os.Handler;
 
public class MyHandler extends Activity {
 static final String TAG = "Handler";
 Handler h = new Handler(){
     public void handleMessage (Message msg)
     {
      switch(msg.what)
      {
      case HANDLER_TEST:
       Log.d(TAG, "The handler thread id = " + Thread.currentThread().getId() + "\n");
       break;
      }
     }
    };
 
 static final int HANDLER_TEST = 1;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "The main thread id = " + Thread.currentThread().getId() + "\n");
 
        new myThread().start();
        setContentView(R.layout.main);
    }
 
    class myThread extends Thread
    {
     public void run()
     {
      Message msg = new Message();
      msg.what = HANDLER_TEST;
      h.sendMessage(msg);
      Log.d(TAG, "The worker thread id = " + Thread.currentThread().getId() + "\n");
     }
    }
}
在這個例子中我們主要是打印,這種處理機制各個模塊的所處的線程情況。如下是我的機器運行結果:
09-10 23:40:51.478: DEBUG/Handler(302): The main thread id = 1
09-10 23:40:51.569: DEBUG/Handler(302): The worker thread id = 8
09-10 23:40:52.128: DEBUG/Handler(302): The handler thread id = 1
我們可以看出消息處理是在主線程中處理的,在消息處理函數中可以安全的調用主線程中的任何資源,包括刷新界面。工作線程和主線程運行在不同的線程中,所以必須要注意這兩個線程間的競爭關係。
上例中,你可能注意到在工作線程中訪問了主線程handler對象,並在調用handler的對象向消息隊列加入了一個消息。這個過程中會不會出現消息隊列數據不一致問題呢?答案是handler對象不會出問題,因為handler對象管理的Looper對象是線程安全的,不管是加入消息到消息隊列和從隊列讀出消息都是有同步對象保護的,具體請參考Looper.java文件。上例中沒有修改handler對象,所以handler對象不可能會出現數據不一致的問題。
通過上面的分析,我們可以得出如下結論:
1、如果通過工作線程刷新界面,推薦使用handler對象來實現。
2、注意工作線程和主線程之間的競爭關係。推薦handler對象在主線程中構造完成(並且啟動工作線程之後不要再修改之,否則會出現數據不一致),然後在工作線程中可以放心的調用發送消息SendMessage等接口。
3、除了2所述的hanlder對象之外的任何主線程的成員變量如果在工作線程中調用,仔細考慮線程同步問題。如果有必要需要加入同步對象保護該變量。
4、handler對象的handleMessage接口將會在主線程中調用。在這個函數可以放心的調用主線程中任何變量和函數,進而完成更新UI的任務。
5、Android很多API也利用Handler這種線程特性,作為一種回調函數的變種,來通知調用者。這樣Android框架就可以在其線程中將消息發送到調用者的線程消息隊列之中,不用擔心線程同步的問題。
深入理解Android消息處理機制對於應用程序開發非常重要,也可以讓你對線程同步有更加深刻的認識。以上是最近Simon學習Android消息處理機制的一點兒總結,如有錯誤之處請不吝指教。
參考資料:
原文作者:Simon_fu

2013年9月22日 星期日

訊息傳遞(Message Passing)和函數呼叫(Function Call)的不同點


Procedure Language以函數作為寫作和執行的主體。函數由一群程式碼所組成,函數的開頭地址在Compile time或Link time就已經決定好了,而在Runtime呼叫函數時給予適當的參數。函數和記憶體之間沒有關聯性,而且函數A可以被任何其他函數呼叫,程式語言並沒有特別的規範。
Object-Oriented Language以Class作為寫作的主體,執行時主要由Object來紀錄程式狀態。由於物件導向程式語言將Object Variable和Object Method一起定義在Class內,再透過Encapsulation的機制規範存取Object Member的範圍,因此Object的Variable和Method就組成了一個完整的個體。
雖然Object Method寫起來就像Function,但執行Method內程式碼的機制和Function不同:
  1. Object Method定義物件接受到訊息時的反應, 也就是說執行Method時有一個隱形的參數, 意即這個物件(this)
  2. Function的實際地址在Compile或Link time就已經決定好了, 但對OO來說, Object必須在Runtime接受到訊息後, 才能決定實際要執行的Method。

Message Passing的語法

由於Java只能透過reference來存取物件,因此Message Passing的語法是reference.method
/**
 * 假設People有variable age,其值限定於0到130之間
 */
public class People {
    private int age;
    public People(int d) {
        this.age = d;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int d) {
        if (d >= 0 && d <= 130) { // 檢查年齡是否合法
            age = d;
        }
    }
    public void increaseAge() {
        if (age < 130) { // 檢查年齡是否合法
            this.age++;
        }
    }
    public static void main(String[] argv) {
        People e1, e2; // e1,e2 are references to Object People, not the Object themselves
        e1 = new People(3);
        e2 = new People(5);
        e1.setAge(30);
        e2.setAge(50);
        e1.increaseAge();
        e2.increaseAge();
    }
}
this這個keyword表示接收到此訊息的物件。由於設計時是定義Class, 而執行時則是Object接受訊息, Class只有一個, 但Object可以有很多個, 因此必須使用this表達現在接收到此訊息的物件。
由於Object Method具有隱形的物件參數, 因此Class Method不能去存取Object Member
public class ErrorCall {
    int data;  // object variable
    public void objectMethod() {
        data = 10;
    }
    public static void classMethod() {
        objectMethod(); // Compile Error
        data = 10; // Compile Error
    }
}

programming

http://programming.im.ncnu.edu.tw/

2013年9月20日 星期五

Context Switch

電腦最核心的就是所謂中央處理器, Central Procesor Unit (CPU), 基本上就是接受指令, 輸入資料, 計算, 輸出資料做這樣的動作, 而事實上早期的電腦, 大多只有一個 CPU, 但要處理很多 Job 工作, 而最簡單的想法就是一個工作做完做下一個工作, 但這樣感覺後面的工作就一直在等待, 因此有人想說把工作切成很多很多部份, 而 CPU 每次就處理這些工作的一部份, 這樣感覺就是同時在處理很多工作, 稱為多工 Multi-Processing.

但事實上一個 CPU 一瞬間只能處理一筆資料執行一個程式, 一個工作須要成千上萬次的執行, 所以也不至於每執行一個指另就換下一個工作, 因為資料通常放在記憶體或暫存器 (Register, 就當作較快的記憶體), 而要把資料放在 CPU 能夠執行的記憶體才能執行, 這資料叫做 Context (文本), 但如此往往上百個工作事實上也無法把所有文本 Content 放在記憶體, 通常也要放進硬碟之類的地方, 等到要執行時再把要執行的資料讀進來, 再把部份資料移出去, 這動作叫 Context Switch.

很不幸的, 這個 Context Switch 的動作本身須要由 CPU 去做, 因此若是這個 Context Switch 發生的次數太多, 所花的時間也變多, 每個工作能夠分派的 CPU 在一段時間內能處理的次數也會變少, 甚至原本大家乖乖排隊可以在下次新工作分派之前執行完的情形, 反而因為 Context Switch 變多資源耗費很多便得無法執行完的話, 就會發生超越臨界值 (Threshold) 現像, 最後甚麼事都不能做或做得相當慢.

2013年9月18日 星期三

USB PHY 及 DDR PHY的PHY是指什麼功能


PHY 是指實體層,係由網路7層中衍伸而來. PHY是當設備或IC 訊號與實體媒介聯絡之橋樑. PHY 不一定是類比訊號, 完全看產品之規格而異.

現代化的IC 設計,內部都是以State machine來控制.因為IC製程與省電之要求,IC內部都是小訊號傳輸. 當訊號必須與周邊零件聯繫時, 經過PHY 一方面是做訊號準位轉換, 增加驅動能力,讓訊號能跑遠;另一方面則是加強抗雜訊之能力與阻抗匹配.

USB 為 serial 4 pins 之結構,其中訊號標示為D+/D-, 它的PHY 採用差動訊號 (differential signal) NRTZ (non-return to zero)方法, 以數位方式傳輸. 大部分的USB IC 或是有 USB port 之SoC 都已將PHY 整合至IC 內部.

DDR 亦有PHY, 亦是數位方式傳輸,同樣已整合至IC內; 所以你看不到外掛的DDR PHY.

另一例是網路Ethernet 的PHY, 它是屬於類比訊號處理.


  • Q: PHY 是什麼電路 ?
    A: 基本是logic gate 組成,具智慧型判斷, 例如過電壓/過電流保護與回報至上層,接收資料準位判斷.如果你是IC 設計公司, 通常是買現成IP (矽晶智慧財)整合至CPU 內, 若要自己設計, 則要依規格去implement.

    你很難在網路上查到線路, 因為這些IP都是賣錢的.

2013年9月6日 星期五

元素的分類與標識(class和id)

http://zh.html.net/tutorials/css/lesson7.php


除了可以用class對元素進行分類以外,你還可以標識單個元素。這是通過HTML屬性id實現的。
HTML屬性id的特別之處在於,在同一HTML文檔中不能有兩個具有相同id值的元素。文檔中的每個id值都必須是唯一的。在其他情況下,你應該使用class屬性。