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