天天看點

移植之亂談

昨天有一個博友回複,說他已經完成了android系統在windows上的移植,其中比較難的是binder系統的移植。下面是兩個demo網址,我看了後倍有感觸。 http://v.youku.com/v_show/id_XMzIwMDkxOTQ4.html http://v.youku.com/v_show/id_XMzIwNzI2NTg4.html

這才是真正有技術含量的移植啊!

從做android開始,聽到的最多的就是移植+merge了。但總感覺都是:

1 簡單的把人家做好的,放到新的平台上,然後測試,修改。完完全全的改bug。甚至都不需要對系統,程式結構有什麼深入的了解。

2 做linux驅動更是這樣,到處去改參數,調闆子。(個人感覺都被硬體綁架了..)。

什麼後果?居然很多人一年都寫不了幾行代碼!!!我知道google有一個美女程式員,每天都要花4個小時來寫代碼,沒有什麼别的特殊目的,就是為了保持腦子的靈活。

另外,那些剛工作的人,改改bug就完成工作,而又不寫代碼,讓他們誤以為程式員就是這樣工作的,結果不去花時間研究系統(放着linux核心,android系統這種高品質,高複雜度的優秀源碼不看。說實話,代碼量不超過10w行以上的同志,真的懷疑能否看懂代碼的真正意圖,是以google校園招聘的時候,似乎對代碼量有一定的要求),

【這裡也請那些教育訓練學校的老師們,稍微注重下教育品質,教育訓練方法。】

我心目中的移植,是應該建立再對目前平台和目标平台的一個深刻認識上,再加上對要移植代碼本身,程式結構得深入了解上才能做的。看看上面博友的移植,如果隻會改幾個bug,何年何月才能将android系統移植到windows上呢?

下面是我之前做的一份關于完成端口到linux平台上的移植的設計文檔。這個設計由于各種原因,最終沒有被讨論過,也沒有(永遠不會)被采用。

                    <strong>完成端口在Linux平台下的實作 一 說明</strong>

Windows下的完成端口模式是一種真正的由作業系統來實作的“異步IO”,應用程式等待完成通知即可。

在面向對象的網絡庫中,包括ACE, ASIO, LibEvent, SPServer等開源公共庫中實際有類似的子產品。一般将完成端口模型稱之為Proactor模型,而select的方式稱之為Reactor模型。

簡單來說,在Reactor模型中,系統通知你可以幹什麼,然後應用程式去做對應的事情(例如send,recv,accept等)。以發送為例,流程大緻如下:

1  使用者注冊回調事件

2  Reactor等待select傳回,然後調用回調

3 使用者在回調中繼續發送操作(非異步IO)

而在Proactor模型中,系統通知你什麼已經幹完了(例如上一次send完成了,recv完成了,accept完成了)等,仍以發送為例,流程大緻如下:

1  使用者注冊回調,發送資料(異步IO)

2  Proactor檢測發送完成事件,然後調用回調

3  使用者在回調中繼續發送操作(異步IO)

從流程上看,似乎沒有差別。而關鍵點就在于IO是否為異步。Reactor模型一般是非異步的,而Proactor由于能檢測完成事件,是以IO是異步的。

在Linux平台上,由于系統并沒有支援異步IO,是以我們必須用Reactor的模式去模拟一個Proactor模式。大體流程如下;

1 使用者注冊回調,發送資料(非阻塞IO)

2 構造一個完成事件,記錄發送的結果,并添加到完成事件隊列

3 Proactor檢測完成事件隊列,然後調用回調

4 使用者繼續發送資料(非阻塞IO)

從上面可以看出,在Linux下Proactor的實作關鍵是自己構造一個完成事件隊列,并處理相關入隊和出隊的操作即可。這種實作方式與windows完成端口是一緻的。

從已知的資料來看,Proactor模型本身沒有設計缺陷。我們将參考Windows下的完成端口的原理和用法來實作一個Linux平台下的完成端口。

<strong>二 設計細節</strong>

與完成端口相關的有線程池,完成端口句柄,和完成事件隊列。

1 完成端口句柄:将與一個epoll句柄相對應

2  線程池:調用GetQueuedCompletionStatus從完成事件隊列中取事件

3  使用者調用IO操作,将事件加入完成隊列

<strong>2.1 事件入隊</strong>

先讨論事件入隊的操作。有兩種明顯的方式來完成。

這裡所有的socket或者别的檔案句柄必須都是非阻塞的。

<strong>1, 真正的異步IO</strong>

要實作真正的異步IO,需要有一個線程池來完成實際的IO工作,并且還需要有一個IO請求隊列來儲存使用者送出的IO操作請求。在Windows平台下,這個線程池和隊列是位于作業系統内部的。而在Linux平台上得自己建立相關組建。

工作流程如下:

1 使用者發起IO操作,内部建立一個IO請求,并加入到請求隊列

2  内部線程從請求隊列中取出請求,完成實際的請求,并将請求結果加入到完成端口的完成事件隊列

這種方式的優缺點有:

1  優點,很大程度上的異步IO。(其他的優點暫時未想到)

2  缺點:實作難度較大,而且資源消耗比較多

<strong>2. 利用非阻塞模式構造的僞異步IO</strong>

在這方式下将在使用者調用的IO函數中直接完成實際的IO操作,隻不過不阻塞罷了。這種方式可以不需要IO請求隊列,不需要線程池。

考慮目前的并發數量,暫時用方式2來實作。

<strong>2.2 一些不能完全實作的地方</strong>

Windows下有些用法在Linux沒有合适的模型對應,包括:

1  AcceptEx對應的先建立socket以供AcceptEx使用的方法

2  GetAcceptExSockAddr

實作過程中,将盡力保證接口的一緻。

<strong>2.3 涉及的内容</strong>

子產品上,可能包括:

1 完成端口相關函數和類設計

2 已有windows下的線程池到Linux平台下的移植

3 Socket相關庫的修改與異步IO配套功能的增加

性能考慮方面,目前可以想到的包括:

1 池的和自旋鎖的使用

2 GetQueuedCompletionStatus函數的内部實作要考慮使用剛完成工作的那個線程來繼續工作,以避免不必要的線程切換

3 同理,PostQueuedCompletionStatus函數内部要避免觸發所有線程來競争一個事件的出隊

  

回想當時做這個移植,花了很多時間來調研完成端口的實作機制,來研究proactor和reactor的差別,來研究linx平台的特性。考慮到完成端口是Win平台最有效率的IO操作,那麼在linux平台上實作自然也得考慮效率方面的問題。

雖然最終沒有實作該設想,但卻極大加深了對以上知識的認識,對以後的工作有非常大的幫助。

作為本篇的結尾,希望大家認真對待工作,從簡單的移植工作中去發掘背後那塊絕大的金礦,以提高自己的價值。

謝謝大家對《深入了解android 卷I/卷II》的支援。