說起 Wine,稍微資深一點的 Linux 用戶應該都聽過,但是真要說起 Wine 到底是怎么回事,可能大多數(shù)人不見得說得清。這篇文章會簡單地介紹 Wine 的工作原理,以及如何開始 Wine 的開發(fā)。所以如果您屬于以下三類讀者之一:
想?yún)⑴c Wine 開發(fā),但是不知如何開始的。
僅僅想大致了解 Wine 是如何工作的。
只是想能夠愉快的用上最新版本 Wine 的。
希望在看完本文后,能夠有一些收獲。
Part 1 Wine 是什么
Wine 是 "Wine Is Not an Emulator" 的遞歸縮寫,如同 "GNU" 一樣(GNU's Not Unix),字面意思就是 Wine 不是一個模擬器。這里的模擬器主要是指 Wine 并不是一個虛擬機,而是一個 Windows API 實現(xiàn)兼容層。這么說可能不太好理解,大家可以把 Windows 應用程序類比成 Android 應用程序,而 Wine 的角色就和 Android 很像了,將操作系統(tǒng)提供的各種功能封裝成 API,并讓應用程序在隔離的環(huán)境內運行。至于 API 長成啥樣,隔離的力度如何,這些都是實現(xiàn)相關的,和本質并不沖突。另一方面,Wine 其實又是一個模擬器,不過模擬的對象不是硬件 CPU,而是 Windows 的行為。
Part 2 Wine 原理介紹
本節(jié)內容相對來說稍顯基礎和單一啦,并且閱讀時最好對操作系統(tǒng)有一定程度的了解哦。如果只是想編譯、運行,對原理不敢興趣的同學,可以跳過,不影響后面的閱讀。
Wine 的目的是運行 Windows 上的可執(zhí)行程序(PE,portable executable)。我們知道,可執(zhí)行程序的本質其實就是按照某一規(guī)則排列的機器碼,而機器碼是指令集相關的。得益于常見的 PC 機一般是 x86/x64 的,因此 Windows 應用程序從指令集的角度看,是完全可以在 x86/x64 的 Linux 機器上直接運行,而不需要硬件層模擬的。
但是為了能夠直接加載運行 PE 文件,需要滿足一些 ABI 兼容。最基本的,Windows PE 程序會假定自己被加載到地址 0x400000 處,因此 Wine 實現(xiàn)自己的 loader 時,需要保證將 PE 鏡像加載到同樣的位置。對于靜態(tài)鏈接的程序,需要做的事情可能不是太多,但是對于動態(tài)鏈接的程序,Wine 需要模仿 Windows loader 的行為,加載依賴的庫,并進行相應的重定位工作。
為了最大程度上減少對二進制層面的依賴,Wine 決定實現(xiàn)至少 GDI32,KERNEL32,USER32 三個動態(tài)庫,因為其他庫都是建立在這三個庫的基礎之上的。所以理論上來說,除此之外的其他動態(tài)庫是可以直接使用 Windows 上面現(xiàn)有的庫,但由于各種原因,Wine 還是傾向于盡量實現(xiàn)所有的 API。我們把 Wine 自己實現(xiàn)的 API 庫稱作 builtin,把 Windows 上現(xiàn)成的庫稱作 native。當 Wine 在加載 builtin 動態(tài)庫的同時,還會在內存中建立 PE header,用來模仿 Windows 上的內存布局。更加詳細的實現(xiàn),有機會可以寫一篇 Wine loader 相關的文章來介紹 Wine 本身、PE 程序和動態(tài)庫是如何被加載的。
除開 loader 的功能外,Wine 還需要解決進程間通信(IPC)的問題。Wine 的實現(xiàn)方式是將所有跨進程的對象和機制,比如 GDI 對象,比如信號量,全部實現(xiàn)在 Wine server 中。同時 Wine 允許系統(tǒng)運行多個 Wine server 的實例。這樣存在于同一個 Wine server 中的對象自然是可以相互通信,好像在同一個空間內;而不同 Wine server 下的對象,是相互隔離的,這種架構使得不同容器之間的程序相互沒有影響。Wine server 的具體實現(xiàn)是通過 unix socket,實現(xiàn)了一套 IPC 機制,完成和 API 層的交互。
有了以上這些基礎,Wine 實現(xiàn)起各種功能就可以按部就班了,只需理解 Windows 下 API 的行為和含義,然后再重新實現(xiàn)一遍就行了。聽起來雖然簡單,實際難度不小,特別是一些未公開的行為,必須對整體有相當?shù)牧私夂蟛拍芟率帧I踔烈恍┎町?,比?UI 相關的內容,由于 Windows 窗口系統(tǒng)和 X 在設計哲學上的不同,實現(xiàn)上需要有所舍取。目前 Wine 支持的平臺不僅包括 Linux,還包括 BSD、Mac OS X 和 Android。
Part 3 環(huán)境
下面的開發(fā)環(huán)境都以 deepin 為例進行說明。
首先獲取代碼。Wine 官方代碼倉庫地址為
git://source.winehq.org/git/wine.git
如果你想方便打包給別人使用,又不太想折騰打包的一些細節(jié),可以用各個發(fā)行版自己維護的 Wine。比如 Debian 維護的 Wine 倉庫地址為
https://salsa.debian.org/wine-team/wine.git
這里以官方的 Wine 為例:
git clone git://source.winehq.org/git/wine.git
然后安裝開發(fā)的依賴。為了簡單起見,我們只編譯 32 位的 Wine,因為 64 位的 Wine 只支持 64 位的 PE 程序,而目前 Windows 上仍有大量的程序只提供了 32 位的版本。
sudo apt install gcc-multilib flex bison libx11-dev:i386 libfreetype6-dev:i386 libxcursor-dev:i386 libxi-dev:i386 libxshmfence-dev:i386 libxxf86vm-dev:i386 libxrandr-dev:i386 libxfixes-dev:i386 libxinerama-dev:i386 libxcomposite-dev:i386 libglu1-mesa-dev:i386 libosmesa6-dev:i386 ocl-icd-opencl-dev:i386 libpcap-dev:i386 libdbus-1-dev:i386 libgnutls28-dev:i386 libncurses-dev:i386 libsane-dev:i386 libv4l-dev:i386 libgphoto2-dev:i386 liblcms2-dev:i386 libpulse-dev:i386 libgstreamer-plugins-base1.0-dev:i386 libudev-dev:i386 libcapi20-dev:i386 libcups2-dev:i386 libfontconfig1-dev:i386 libgsm1-dev:i386 libkrb5-dev:i386 libtiff-dev:i386 libmpg123-dev:i386 libopenal-dev:i386 libldap2-dev:i386 libxrandr-dev:i386 libxml2-dev:i386 libxslt1-dev:i386 libjpeg62-turbo-dev:i386 libusb-1.0-0-dev:i386 gettext libsdl2-dev:i386 libvulkan-dev:i386接著運行腳本:
./configure --with-gnutls --without-hal --without-oss
根據(jù)不同的 Wine 版本,此時可能會提示不同的 feature 支持情況。我們可以根據(jù)需求,對上面的依賴庫和傳入的參數(shù)進行調整,具體可以查看 configure.ac 的內容。
Wine 的源碼比較大,編譯有些耗時,可以根據(jù) CPU 情況增加并行參數(shù),比如 make -j8,進行編譯。
編譯完成后,運行:
./wine --version可以查看版本號。如果想安裝到系統(tǒng),可以運行:
sudo make install但是注意,安裝后可能會修改一些文件的默認打開方式。
Part 4 使用
運行:
./wine winecfg可以對默認容器進行設置,默認的容器位于 HOME 目錄下的 .wine,環(huán)境變量 WINEPREFIX 用來修改當前的容器路徑。比如有一個叫 demo.exe 的可執(zhí)行文件,我們想測試能否正常運行,可以運行。
WINEPREFIX=~/.demo_exe ./wine demo.exeHOME 目錄下的`demo_exe`就會作為其容器目錄。
Part 5 開發(fā)
編譯過后的 Wine 源碼目錄結構如下:
├── aclocal.m4 ├── ANNOUNCE ├── AUTHORS ├── config.log ├── config.status ├── configure ├── configure.ac ├── COPYING.LIB ├── dlls ├── documentation ├── fonts ├── include ├── libs ├── LICENSE ├── LICENSE.OLD ├── loader ├── MAINTAINERS ├── Makefile ├── Makefile.in ├── po ├── programs ├── README ├── server ├── tools ├── VERSION └── wine -> tools/winewrapper
目錄 dlls 按照模塊存放了所有 API 的實現(xiàn)。
目錄 loader 是和 Wine 啟動、加載相關的代碼。
目錄 programs 存放了外部程序的代碼,比如注冊表管理工具 regedit 。
目錄 server 顧名思義,是 Wine server 的實現(xiàn)。
接下來需要做的就和普通開發(fā)沒什么兩樣了。比如說我們發(fā)現(xiàn)某個應用存在字體相關的 BUG,可以首先根據(jù)經(jīng)驗判斷在 Windows 上,該程序是如何實現(xiàn)的,然后查看對應的實現(xiàn)。例如 GDI 相關的字體實現(xiàn),位于 dlls/gdi32/font.c 和 dlls/gdi32/freetype.c 。修改完代碼后,在所在模塊的目錄,比如上例就是 dlls/gdi32 下重新 make 就可以快速驗證了。
對于復雜的問題,不太好直接定位的,可以通過輸出日志的方式來調試,環(huán)境變量 WINEDEBUG 指定了需要輸出的日志。
有時我們可能需要把復雜的情況簡單化,這時候難免會寫一些小的 demo 程序來重現(xiàn)問題。如果不想到 Windows 上面編譯,可以使用 mingw 直接在 deepin 下編譯出 exe 文件。方法很簡單,首先安裝 mingw:
sudo apt install mingw-w64接著正常利用 Windows API 實現(xiàn)程序,最后利用 mingw 編譯工具鏈生成文件即可,Makefile 示例:
hello.exe: hello.c i686-w64-mingw32-g++ -o hello.exe hello.c -DUNICODE -D_UNICODE -municode -lgdi32
上面的例子,定義了 UNICODE,所以使用的 UNICODE 版本的 API,入口函數(shù)為 wmain,-lgdi32 表示需要鏈接庫 gdi32 。生成出來的 hello.exe,可以同時在 Windows 和 deepin 下運行。
Part 6 其他
本文介紹的內容只涉及到 Wine 開發(fā)的基礎,Wine 本身還有很多東西值得去探索。比如 Wine 是如何使用 driver 機制讓接口和實現(xiàn)分離的,再比如 Wine 是如何使用純 C 實現(xiàn) COM 機制的。雖然 Wine 的出現(xiàn)已經(jīng)有一些年頭了,但是目前的開發(fā)仍然比較活躍,感興趣的同學可以加入進來,為 Linux 生態(tài)添磚加瓦,讓大家能用到更多的優(yōu)質應用,也算是曲線救國了。
Part 7 更新
7.1 新的 WOW64
上面編譯的例子中,我們只編譯了 32 位的 Wine,忽略了編譯 64 位 Wine 的細節(jié)。從 Wine 8 開始,Wine 開始了一個稱為 PE Convertion 的開發(fā)過程,重新實現(xiàn)了 WOW64 的機制。此模式下只需要編譯出一份 Wine,既可以運行 32 位程序,也能運行 64 位程序。同時也不再依賴 32 位的運行時庫。雖然此模式還被認為處于實驗階段,但是從 deepin-wine8 開始,我們已經(jīng)默認使用此方式。
編譯新的 WOW64 方式的 Wine,步驟如下:
Wine 官方的源碼地址已經(jīng)更改,在https://gitlab.winehq.org/wine/wine.git下載源代碼。
依舊需要安裝編譯時依賴的開發(fā)庫,不同在于我們這次只需要安裝 amd64 的版本,在安裝開發(fā)庫時不再需要指定 :i386 后綴。此外 mingw-w64 在此模式下成為必須項。
在 Wine 源碼目錄的同級目錄下新建 build-wine 目錄,并進入此目錄。
運行 ../wine/configure --prefix=/opt/wine-newwow64 --enable-archs=i386,x86_64。這一步如果有任何提示,比如缺少編譯期依賴,可以在安裝后再次運行。
make -j8,進行編譯。
sudo make install 安裝,Wine 會安裝在第 4 步通過 prefix 指定的位置,即 /opt/wine-newwow64 。當然也可以選擇不安裝,直接在編譯目錄下運行。
7.2 其他架構
在關于原理一節(jié)的描述中我們提到過,x86/x64 機器的 Linux 系統(tǒng)可以通過 Wine 運行 x86/x64 的 PE 程序。實際上如今通過 DBT(dynamic binary translation)技術,在 ARM 及其他架構上已經(jīng)有了運行 Wine 的方案。感興趣的讀者可以查看Box64項目。
11月份,微信在 deepin 商店上架了Linux原生版本,功能與 Windows、MacOS 相差無幾。Wine 版本的微信終于可以"功成身退"了,我們也期待今后有更多軟件推出原生版本,讓 Wine 早點完成"歷史使命"。
-
Linux
+關注
關注
87文章
11312瀏覽量
209711 -
WINDOWS
+關注
關注
4文章
3552瀏覽量
88810 -
應用程序
+關注
關注
37文章
3277瀏覽量
57737 -
模擬器
+關注
關注
2文章
877瀏覽量
43254
原文標題:想開啟 Wine 開發(fā)?看這篇就夠了!
文章出處:【微信號:linux_deepin,微信公眾號:深度操作系統(tǒng)】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論