說起wine,稍微資深一點的linux 使用者應該都聽過,但是真要說起wine到底是怎麼回事,可能大多數人不見得說得清。這篇文章會簡單的介紹wine的工作原理,以及如何開始wine 的開發。
所以如果您屬於以下三類讀者之一:*想參與wine開發,但是不知如何開始的。
*不認同wine技術,僅僅想大致了解wine是如何工作的。
*只是想能夠愉快的用上最新版本wine的。
希望在看完本文後,能夠有一些收穫。
wine是 「wine is not an emulator」的遞迴縮寫,如同「gnu」一樣(gnu』s not unix),字面意思就是wine不是乙個模擬器。這裡的模擬器主要是指wine並不是乙個虛擬機器,而是乙個windows api實現相容層。
這麼說可能不太好理解,大家可以把windows應用程式模擬成android應用程式,而wine 的角色就和android很像了,將作業系統提供的各種功能封裝成api,並讓應用程式在隔離的環境內執行。至於api長成啥樣,隔離的力度如何,這些都是實現相關的,和本質並不衝突。另一方面,wine其實又是乙個模擬器,不過模擬的物件不是硬體cpu,而是windows 的行為。
本節內容較為枯燥,需要對作業系統有一定的了解。如果只是想編譯、執行,對原理不敢興趣的同學,可以跳過,不影響後面的閱讀。
wine的目的是執行windows上的可執行程式(pe,portable executable)。我們知道,可執行程式的本質其實就是按照某一規則排列的機器碼,而機器碼是指令集相關的。得益於常見的pc機一般是x86/x64的,因此windows應用程式從指令集的角度看,是完全可以在 x86/x64的linux機器上直接執行,而不需要硬體層模擬的。
但是為了能夠直接載入執行pe檔案,需要滿足一些abi相容。最基本的,windows pe程式,會假定自己被載入到位址0x400000處,因此wine實現自己的loader時,需要保證將pe映象載入到同樣的位置。對於靜態鏈結的程式,需要做的事情可能不是太多,但是對於動態鏈結的程式,wine需要模仿windows loader的行為,載入依賴的庫,並進行相應的重定位工作。
為了最大程式上減少對二進位制層面的依賴,wine決定實現至少gdi32,kernel32,user32三個動態庫,因為其他庫都是建立在這三個庫的基礎之上的。所以理論上來說,除此之外的其他動態庫是可以直接使用windows上面現有的庫的,但由於各種原因,wine還是傾向於盡量實現所有的api。
我們把wine自己實現的api庫稱作builtin,把windows上現成的庫稱作native。當wine在載入builtin動態庫的同時,還會在記憶體中建立pe header,用來模仿windows上的記憶體布局。更加詳細的實現,有機會可以寫一篇wine loader相關的文章來介紹wine本身、pe 程式和動態庫是如何被載入的。
除開loader的功能外,wine還需要解決程序間通訊(ipc)的問題。wine的實現方式是將所有跨程序的物件和機制,比如gdi物件,比如訊號量,全部實現在wine server中。同時 wine允許系統執行多個wine server的例項。
這樣存在於同乙個wine server中的物件自然是可以相互通訊,好像在同乙個空間內;而不同 wine server下的物件,是相互隔離的,這種架構使得不同容器之間的程式相互沒有影響。wine server的具體實現是通過unix socket,實現了一套lpc機制,完成和api層的互動。
有了以上這些基礎,wine實現起各種功能就可以按部就班了,只需理解windows下api的行為和含義,然後再重新實現一遍就行了。聽起來雖然簡單,實際難度不小,特別是一些未公開的行為,必須對整體有相當的了解後才能下手。甚至一些差異,比如ui相關的內容,由於 windows視窗系統和x在設計哲學上的不同,實現上需要有所捨取。目前wine支援的平台不僅包括linux,還包括bsd、mac os x和android。
下面的開發環境都以deepin為例進行說明。
首先獲取**。wine官方**倉庫位址為git:如果你想方便打包給別人使用,又不太想折騰打包的一些細節,可以用各個發現版自己維護的wine。
這裡以官方的wine為例,git clone git:然後安裝開發的依賴。為了簡單起見,我們只編譯32位的wine,因為64位的wine只支援64位的pe程式,而目前windows上仍有大量的程式只提供了32位的版本。
接著執行指令碼,./configure --with-gnutls --without-hal --without-oss,根據不同的 wine版本,此時可能會提示不同的feature支援情況。我們可以根據需求,對上面的依賴庫和傳入的引數進行調整,具體可以檢視configure.ac的內容。
wine的原始碼比較大,編譯有些耗時,可以根據cpu情況增加並行引數,比如make -j8,進行編譯。編譯完成後,執行 ./wine --version可以檢視版本號。如果想安裝到系統,可以執行sudo make install,但是注意,安裝後可能會修改一些檔案的預設開啟方式。
執行 ./wine winecfg可以對預設容器進行設定,預設的容器位於home 目錄下的.wine,環境變數wineprefix用來修改當前的容器路徑。
比如有乙個叫demo.exe 的可執行檔案,我們想測試能否正常執行。
可以執行wineprefix=~/.demo_exe ./wine demo.exe,home目錄下的demo_exe 就會作為其容器目錄。
編譯過後的wine原始碼目錄結構如下:
*目錄dlls按照模組存放了所有api的實現。
*目錄loader是和wine啟動、載入相關的**。
*目錄programs存放了外部程式的**,比如登錄檔管理工具regedit。
*目錄server顧名思義,是wine server的實現。
接下來需要做的就和普通開發沒什麼兩樣了。比如說我們發現某個應用存在字型相關的 bug,可以首先根據經驗判斷在windows上,該程式是如何實現的,然後檢視對應的實現。例如gdi相關的字型實現,位於dlls/gdi32/font.c和dlls/gdi32/freetype.c。修改完**後,在所在模組的目錄,比如上例就是dlls/gdi32下重新make就可以快速驗證了。
對於複雜的問題,不太好直接定位的,可以通過輸出日誌的方式來除錯,環境變數 winedebug指定了需要輸出的日誌。更加詳細的說明可以檢視這篇文章。
有時我們可能需要把複雜的情況簡單化,這時候難免會寫一些小的demo程式來重現問題。如果不想到windows上面編譯,可以使用mingw直接在deepin下編譯出exe檔案。方法很簡單,首先安裝mingw,sudo apt install mingw-w64,接著正常利用windows api實現程式,最後利用mingw編譯工具鏈生成檔案即可,makefile示例:
上面的例子,定義了unicode,所以使用的unicode 版本的api,入口函式為wmain,-lgdi32 表示需要鏈結庫gdi32。生成出來的hello.exe,可以同時在windows和deepin下執行。
本文介紹的內容只涉及到wine開發的基礎,wine本身還有很多東西值得去探索。比如wine 是如何使用driver機制讓介面和實現分離的,再比如wine是如何使用純c實現com 機制的。雖然wine的出現已經有一些年頭了,但是目前的開發仍然比較活躍,感興趣的同學可以加入進來,為linux生態添磚加瓦,讓大家能用到更多的優質應用,也算是曲線救國了:)
【相關鏈結】
canvas Android遊戲開發座標系統
1 android中的座標系統 在android系統中,螢幕的左上角是座標系統的原點 0,0 座標。原點向右延伸是x軸正方向,原點向下延伸是y軸正方向。2 螢幕的寬和高 為了在螢幕中的合適位置繪製圖形,我們需要使用螢幕的寬和高作為參考,來確定繪製圖形的位置。要獲得螢幕的寬和高,首先從activity...
VirtualBox安裝linux作業系統
1.應用場景 有時,物理機並不能用於安裝linux作業系統,因為你還需要在windows進行一些日常操作,或者硬碟上存了很多東西,怕有個閃失,得不償失,可是又想領略linux的魅力,或者說就是想敲幾個linux命令找找感覺,最簡單的方式就是安裝虛擬機器 好處 簡單,方便,成本低 2.環境 tbd3....
如何安裝Manjaro Windows雙系統
今天實在是不想使用window系統來開發了,所以希望使用manjaro系統來代替我的 生產工具,目前來說linux發展迅捷,功能已經相當強大。經過ubuntu,deepin最終開始走向manjaro gnome 的系統,不得不說這是目前的linux桌面系統老大,穩定和社群都很強大,下面展示我的安裝成...