说起 Wine,稍微资深一点的 Linux 用户应该都听过,但是真要说起 Wine 到底是怎么回事,可能大多数人不见得说得清。这篇文章会简单地介绍 Wine 的工作原理,以及如何开始 Wine 的开发。所以如果您属于以下三类读者之一:
想参与 Wine 开发,但是不知如何开始的。
仅仅想大致了解 Wine 是如何工作的。
只是想能够愉快的用上最新版本 Wine 的。
希望在看完本文后,能够有一些收获。
Part 1 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 的行为。
Part 2 Wine 原理介绍
本节内容相对来说稍显基础和单一啦,并且阅读时最好对操作系统有一定程度的了解哦。如果只是想编译、运行,对原理不敢兴趣的同学,可以跳过,不影响后面的阅读。
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,实现了一套 IPC 机制,完成和 API 层的交互。
有了以上这些基础,Wine 实现起各种功能就可以按部就班了,只需理解 Windows 下 API 的行为和含义,然后再重新实现一遍就行了。听起来虽然简单,实际难度不小,特别是一些未公开的行为,必须对整体有相当的了解后才能下手。甚至一些差异,比如 UI 相关的内容,由于 Windows 窗口系统和 X 在设计哲学上的不同,实现上需要有所舍取。目前 Wine 支持的平台不仅包括 Linux,还包括 BSD、Mac OS X 和 Android。
Part 3 环境
下面的开发环境都以 deepin 为例进行说明。
首先获取代码。Wine 官方代码仓库地址为
git://source.winehq.org/git/wine.git
如果你想方便打包给别人使用,又不太想折腾打包的一些细节,可以用各个发行版自己维护的 Wine。比如 Debian 维护的 Wine 仓库地址为
https://salsa.debian.org/wine-team/wine.git
这里以官方的 Wine 为例:
git clone git://source.winehq.org/git/wine.git
然后安装开发的依赖。为了简单起见,我们只编译 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
根据不同的 Wine 版本,此时可能会提示不同的 feature 支持情况。我们可以根据需求,对上面的依赖库和传入的参数进行调整,具体可以查看 configure.ac 的内容。
Wine 的源码比较大,编译有些耗时,可以根据 CPU 情况增加并行参数,比如 make -j8,进行编译。
编译完成后,运行:
./wine --version可以查看版本号。如果想安装到系统,可以运行:
sudo make install但是注意,安装后可能会修改一些文件的默认打开方式。
Part 4 使用
运行:
./wine winecfg可以对默认容器进行设置,默认的容器位于 HOME 目录下的 .wine,环境变量 WINEPREFIX 用来修改当前的容器路径。比如有一个叫 demo.exe 的可执行文件,我们想测试能否正常运行,可以运行。
WINEPREFIX=~/.demo_exe ./wine demo.exeHOME 目录下的`demo_exe`就会作为其容器目录。
Part 5 开发
编译过后的 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 的实现。
目录 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 示例:
hello.exe: hello.c i686-w64-mingw32-g++ -o hello.exe hello.c -DUNICODE -D_UNICODE -municode -lgdi32
上面的例子,定义了 UNICODE,所以使用的 UNICODE 版本的 API,入口函数为 wmain,-lgdi32 表示需要链接库 gdi32 。生成出来的 hello.exe,可以同时在 Windows 和 deepin 下运行。
Part 6 其他
本文介绍的内容只涉及到 Wine 开发的基础,Wine 本身还有很多东西值得去探索。比如 Wine 是如何使用 driver 机制让接口和实现分离的,再比如 Wine 是如何使用纯 C 实现 COM 机制的。虽然 Wine 的出现已经有一些年头了,但是目前的开发仍然比较活跃,感兴趣的同学可以加入进来,为 Linux 生态添砖加瓦,让大家能用到更多的优质应用,也算是曲线救国了。
Part 7 更新
7.1 新的 WOW64
上面编译的例子中,我们只编译了 32 位的 Wine,忽略了编译 64 位 Wine 的细节。从 Wine 8 开始,Wine 开始了一个称为 PE Convertion 的开发过程,重新实现了 WOW64 的机制。此模式下只需要编译出一份 Wine,既可以运行 32 位程序,也能运行 64 位程序。同时也不再依赖 32 位的运行时库。虽然此模式还被认为处于实验阶段,但是从 deepin-wine8 开始,我们已经默认使用此方式。
编译新的 WOW64 方式的 Wine,步骤如下:
Wine 官方的源码地址已经更改,在https://gitlab.winehq.org/wine/wine.git下载源代码。
依旧需要安装编译时依赖的开发库,不同在于我们这次只需要安装 amd64 的版本,在安装开发库时不再需要指定 :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 其他架构
在关于原理一节的描述中我们提到过,x86/x64 机器的 Linux 系统可以通过 Wine 运行 x86/x64 的 PE 程序。实际上如今通过 DBT(dynamic binary translation)技术,在 ARM 及其他架构上已经有了运行 Wine 的方案。感兴趣的读者可以查看Box64项目。
11月份,微信在 deepin 商店上架了Linux原生版本,功能与 Windows、MacOS 相差无几。Wine 版本的微信终于可以"功成身退"了,我们也期待今后有更多软件推出原生版本,让 Wine 早点完成"历史使命"。
-
Linux
+关注
关注
87文章
11310浏览量
209683 -
WINDOWS
+关注
关注
4文章
3548浏览量
88784 -
应用程序
+关注
关注
37文章
3275浏览量
57732 -
模拟器
+关注
关注
2文章
877浏览量
43248
原文标题:想开启 Wine 开发?看这篇就够了!
文章出处:【微信号:linux_deepin,微信公众号:深度操作系统】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论