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