Farseerfc的小窝 - desktop//farseerfc.me/zhs/2015-03-19T13:45:00+09:00桌面系统的混成器简史2015-03-19T13:45:00+09:002015-03-19T13:45:00+09:00farseerfctag:farseerfc.me,2015-03-19:/zhs/brief-history-of-compositors-in-desktop-os.html
<p>(原本是想写篇关于 Wayland 的文章,后来越写越长感觉能形成一个系列,
于是就先把这篇背景介绍性质的部分发出来了。)</p>
<!-- PELICAN_BEGIN_SUMMARY -->
<p>Linux 系统上要迎来 Wayland 了,或许大家能从各种渠道打听到 Wayland
是一个混成器,替代 X 作为显示服务器。
那么 <strong>混成器</strong> 是个什么东西,桌面系统为什么需要它呢?
要理解为什么桌面系统需要 <strong>混成器</strong> (或者它的另一个叫法,
<ruby><rb>混成窗口管理器</rb><rp>(</rp><rt>Compositing Window Manager</rt><rp>)</rp></ruby>
),在这篇文章中我想回顾一下历史,
了解一下混成器出现的前因后果。</p>
<p>首先介绍一下混成器出现前主要的一类窗口管理器,也就是
<ruby><rb>栈式窗口管理器</rb><rp>(</rp><rt>Stacking Window Manager</rt><rp>)</rp></ruby> 的实现方式。</p>
<!-- PELICAN_END_SUMMARY -->
<div class="label label-warning">
本文中所有桌面截图来自维基百科,不具有著作权保护。</div>
<div class="section" id="id2">
<h2><a class="toc-backref" href="#id11">早期的栈式窗口管理器</a></h2>
<div class="panel panel-default">
<div class="panel-heading">
栈式窗口管理器的例子,Windows 3.11 的桌面</div>
<div class="panel-body">
<img alt="栈式窗口管理器的例子,Windows 3.11 的桌面" class="img-responsive" src="//farseerfc.me/zhs/images/Windows_3.11_workspace.png"/>
</div>
</div>
<!-- PELICAN_BEGIN_SUMMARY -->
<p>我们知道最初图形界面的应用程序是全屏的,独占整个显示器(现在很多游戏机和手持设备的实现仍旧如此)。
所有程序都全屏并且任何时刻只能看到一个程序的输出,这个限制显然不能满足人们使用计算机的需求,
于是就有了 <a class="reference external" href="http://en.wikipedia.org/wiki/WIMP_(computing)">窗口 …</a></p></div>
<p>(原本是想写篇关于 Wayland 的文章,后来越写越长感觉能形成一个系列,
于是就先把这篇背景介绍性质的部分发出来了。)</p>
<!-- PELICAN_BEGIN_SUMMARY -->
<p>Linux 系统上要迎来 Wayland 了,或许大家能从各种渠道打听到 Wayland
是一个混成器,替代 X 作为显示服务器。
那么 <strong>混成器</strong> 是个什么东西,桌面系统为什么需要它呢?
要理解为什么桌面系统需要 <strong>混成器</strong> (或者它的另一个叫法,
<ruby><rb>混成窗口管理器</rb><rp>(</rp><rt>Compositing Window Manager</rt><rp>)</rp></ruby>
),在这篇文章中我想回顾一下历史,
了解一下混成器出现的前因后果。</p>
<p>首先介绍一下混成器出现前主要的一类窗口管理器,也就是
<ruby><rb>栈式窗口管理器</rb><rp>(</rp><rt>Stacking Window Manager</rt><rp>)</rp></ruby> 的实现方式。</p>
<!-- PELICAN_END_SUMMARY -->
<div class="label label-warning">
本文中所有桌面截图来自维基百科,不具有著作权保护。</div>
<div class="section" id="id2">
<h2><a class="toc-backref" href="#id11">早期的栈式窗口管理器</a></h2>
<div class="panel panel-default">
<div class="panel-heading">
栈式窗口管理器的例子,Windows 3.11 的桌面</div>
<div class="panel-body">
<img alt="栈式窗口管理器的例子,Windows 3.11 的桌面" class="img-responsive" src="//farseerfc.me/zhs/images/Windows_3.11_workspace.png"/>
</div>
</div>
<!-- PELICAN_BEGIN_SUMMARY -->
<p>我们知道最初图形界面的应用程序是全屏的,独占整个显示器(现在很多游戏机和手持设备的实现仍旧如此)。
所有程序都全屏并且任何时刻只能看到一个程序的输出,这个限制显然不能满足人们使用计算机的需求,
于是就有了 <a class="reference external" href="http://en.wikipedia.org/wiki/WIMP_(computing)">窗口</a>
的概念,有了 <a class="reference external" href="http://en.wikipedia.org/wiki/Desktop_metaphor">桌面隐喻</a> 。</p>
<p>在 <ruby><rb>桌面隐喻</rb><rp>(</rp><rt>Desktop Metaphor</rt><rp>)</rp></ruby> 中每个窗口只占用显示面积的一小部分,
有其显示的位置和大小,可以互相遮盖。于是栈式窗口管理器就是在图形界面中实现桌面隐喻的核心功能,
其实现方式大体就是:给每个窗口一个相对的“高度”或者说“远近”,比较高的窗口显得距离用户比较近,
会覆盖其下比较低的窗口。绘图的时候窗口管理器会从把窗口按高低排序,按照从低到高的顺序使用
<a class="reference external" href="http://zh.wikipedia.org/wiki/%E7%94%BB%E5%AE%B6%E7%AE%97%E6%B3%95">画家算法</a>
绘制整个屏幕。</p>
<!-- PELICAN_END_SUMMARY -->
<p>这里还要补充一点说明,在当时图形界面的概念刚刚普及的时候,绘图操作是非常“昂贵”的。
可以想象一下 800x600 像素的显示器输出下,每帧
<a class="reference external" href="http://zh.wikipedia.org/wiki/%E7%9C%9F%E5%BD%A9%E8%89%B2">真彩色</a>
位图就要占掉 <span class="math">\(800 \times 600 \times 3 \approx 1.4 \text{MiB}\)</span> 的内存大小,30Hz
的刷新率(也就是30FPS)下每秒从 CPU 传往绘图设备的数据单单位图就需要
<span class="math">\(1.4 \times 30 = 41 \text{MiB}\)</span> 的带宽。对比一下当时的
<a class="reference external" href="http://en.wikipedia.org/wiki/VESA_Local_Bus">VESA 接口</a> 总的数据传输能力也就是
<span class="math">\(25 \text{MHz} \times 32 \text{bits} = 100 \text{MiB/s}\)</span> 左右,
而 Windows 3.1 的最低内存需求是 1MB,对当时的硬件而言无论是显示设备、内存或是CPU,
这无疑都是一个庞大的负担。</p>
<p>于是在当时的硬件条件下采用栈式窗口管理器有一个巨大 <strong>优势</strong> :如果正确地采用画家算法,
并且合理地控制重绘时 <strong>只绘制没有被别的窗口覆盖的部分</strong> ,那么无论有多少窗口互相
遮盖,都可以保证每次绘制屏幕的最大面积不会超过整个显示器的面积。
同样因为实现方式栈式窗口管理器也有一些难以回避的 <strong>限制</strong> :</p>
<ol class="arabic simple">
<li>窗口必须是矩形的,不能支持不规则形状的窗口。</li>
<li>不支持透明或者半透明的颜色。</li>
<li>为了优化效率,在缩放窗口和移动窗口的过程中,窗口的内容不会得到重绘请求,
必须等到缩放或者移动命令结束之后窗口才会重绘。</li>
</ol>
<p>以上这些限制在早期的 X11 窗口管理器比如 twm 以及 XP 之前经典主题的 Windows
或者经典的 Mac OS 上都能看到。
在这些早期的窗口环境中,如果你拖动或者缩放一个窗口,那么将显示变化后的窗口边界,
这些用来预览的边界用快速的位图反转方式绘制。当你放开鼠标的时候才会触发窗口的
重绘事件。
虽然有很多方法或者说技巧能绕过这些限制,比如 Windows XP 上就支持了实时的
重绘事件和不规则形状的窗口剪裁,不过这些技巧都是一连串的 hack ,难以扩展。</p>
</div>
<div class="section" id="nextstep-mac-os-x">
<h2><a class="toc-backref" href="#id12">NeXTSTEP 与 Mac OS X 中混成器的发展</a></h2>
<div class="panel panel-default">
<div class="panel-heading">
NeXTSTEP 桌面</div>
<div class="panel-body">
<img alt="NeXTSTEP 桌面" class="img-responsive" src="//farseerfc.me/zhs/images/NeXTSTEP_desktop.png"/>
</div>
</div>
<p>转眼进入了千禧年, Windows 称霸了 PC 产业,苹果为重振 Macintosh 请回了 Jobs 基于 <a class="reference external" href="http://en.wikipedia.org/wiki/NeXTSTEP">NeXTSTEP</a>
开发 Mac OSX 。</p>
<p>NeXTSTEP 在当时提供的 GUI 界面技术相比较于同年代的 X 和 Windows 有一个很特别的地方:
拖动滚动条或者移动窗口的时候,窗口的内容是 <strong>实时更新</strong> 的,这比只显示一个缩放大小的框框来说被认为更直观。
而实现这个特性的基础是在 NeXTSTEP 中运用了
<a class="reference external" href="http://en.wikipedia.org/wiki/Display_PostScript">Display PostScript (DPS)</a>
技术,简单地说,就是每个窗口并非直接输出到显示设备,而是把内容输出到 (Display) PostScript
格式交给窗口管理器,然后窗口管理器再在需要的时候把 PostScript 用软件解释器解释成位图显示在屏幕上。</p>
<img alt="ditaa diagram" class="ditaa img-responsive" src="//farseerfc.me/uml/8d293c10.png"/>
<p>比起让窗口直接绘制,这种方案在滚动和移动窗口的时候不需要重新渲染保存好的 DPS ,
所以能实现实时渲染。到了实现 Mac OS X 的时候,为了同时兼容老的 Mac 程序 API (carbon)
以及更快的渲染速度,以及考虑到 Adobe 对苹果收取的高昂的 Display PostScript 授权费,
Mac OS X 的 Quartz 技术在矢量图的 PDF 描述模型和最终渲染之间又插入了一层抽象:</p>
<img alt="ditaa diagram" class="ditaa img-responsive" src="//farseerfc.me/uml/5807c26a.png"/>
<div class="panel panel-default">
<div class="panel-heading">
Mission Control</div>
<div class="panel-body">
<img alt="Mission Control" class="img-responsive" src="//farseerfc.me/zhs/images/Mac_OS_X_Lion_Preview_-_Mission_Control.jpg"/>
</div>
</div>
<p>也就是说在 Mac OS X 中无论窗口用何种方式绘图,都会绘制输出成一副内存中的位图交给混成器,
而后者再在需要的时候将位图混成在屏幕上。这种设计使得 2001年3月发布的 Mac OS X v10.0
成为了第一个广泛使用的具有软件混成器的操作系统。</p>
<p>到了 Mac OS X v10.2 的时候,苹果又引入了 Quartz Extreme 让最后的混成渲染这一步发生在
显卡上。然后在 2003年1月公开亮相的 Mac OS X v10.3 中,他们公布了 Exposé (后来改名为
Mission Control) 功能,把窗口的缩略图(而不是事先绘制的图标)并排显示在桌面上,
方便用户挑选打开的窗口。</p>
<p>由于有了混成器的这种实现方式,使得可能把窗口渲染的图像做进一步加工,添加阴影、三维和动画效果。
这使得 Mac OS X 有了美轮美奂的动画效果和 Exposé 这样的方便易用的功能。
或许对于乔布斯而言,更重要的是因为有了混成器,窗口的形状终于能显示为他
<a class="reference external" href="http://www.folklore.org/StoryView.py?story=Round_Rects_Are_Everywhere.txt">梦寐以求</a>
的 <a class="reference external" href="http://www.uiandus.com/blog/2009/7/26/realizations-of-rounded-rectangles.html">圆角矩形</a>
了!</p>
</div>
<div class="section" id="project-looking-glass-3d">
<h2><a class="toc-backref" href="#id13">插曲:昙花一现的 Project Looking Glass 3D</a></h2>
<p>在苹果那边刚刚开始使用混成器渲染窗口的 2003 年,昔日的 <ruby><rb>升阳公司</rb><rp>(</rp><rt>Sun Microsystems</rt><rp>)</rp></ruby>
则在 Linux 和 Solaris 上用 Java3D 作出了另一个炫酷到没有朋友的东西,被他们命名为
<a class="reference external" href="http://en.wikipedia.org/wiki/Project_Looking_Glass">Project Looking Glass 3D</a>
(缩写LG3D,别和 Google 的 Project Glass 混淆呀)。这个项目的炫酷实在难以用言语描述,
好在还能找到两段视频展示它的效果。</p>
<div class="well" style="padding: 0">
<div class="tab-content" id="youtubeku">
<div class="tab-pane fade active in" id="youtube_JXv8VlpoK_g">
<div align="left" class="youtube embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" src="https://www.youtube.com/embed/JXv8VlpoK_g"></iframe> </div>
</div>
<div class="tab-pane fade" id="youku_XOTEzMzM3MTY0">
<div align="left" class="youku embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" height="498" src="https://player.youku.com/embed/XOTEzMzM3MTY0" width="510"></iframe> </div>
</div>
</div>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#youtube_JXv8VlpoK_g">Youtube</a></li>
<li><a data-toggle="tab" href="#youku_XOTEzMzM3MTY0">Youku</a></li>
</ul>
</div>
<div class="well" style="padding: 0">
<div class="tab-content" id="youtubeku">
<div class="tab-pane fade active in" id="youtube_zcPIEMvyPy4">
<div align="left" class="youtube embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" src="https://www.youtube.com/embed/zcPIEMvyPy4"></iframe> </div>
</div>
<div class="tab-pane fade" id="youku_XOTEzMzQwMjky">
<div align="left" class="youku embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" height="498" src="https://player.youku.com/embed/XOTEzMzQwMjky" width="510"></iframe> </div>
</div>
</div>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#youtube_zcPIEMvyPy4">Youtube</a></li>
<li><a data-toggle="tab" href="#youku_XOTEzMzQwMjky">Youku</a></li>
</ul>
</div>
<div class="panel panel-default">
<div class="panel-heading">
LG3D</div>
<div class="panel-body">
<img alt="LG3D" class="img-responsive" src="//farseerfc.me/zhs/images/LG3D.jpg"/>
</div>
</div>
<p>如视频中展示的那样, LG3D 完全突破了传统的栈式窗口管理方式,
在三维空间中操纵二维的窗口平面,不仅像传统的窗口管理器那样可以缩放和移动窗口,
还能够旋转角度甚至翻转到背面去。从视频中难以体会到的一点是, LG3D 在实现方式上与
Mac OS X 中的混成器有一个本质上的不同,那就是处于(静止或动画中)缩放或旋转状态
下的窗口是 <strong>可以接受输入事件</strong> 的。这一重要区别在后面 Wayland 的说明中还会提到。
LG3D 项目展示了窗口管理器将如何突破传统的栈式管理的框架,可以说代表了窗口管理器的未来发展趋势。</p>
<p>LG3D 虽然以 GPL 放出了实现的源代码,不过整个项目已经停滞开发许久了。
官方曾经放出过一个
<a class="reference external" href="http://sourceforge.net/projects/lg3d-livecd/">预览版的 LiveCD</a>
。可惜时隔久远(12年前了)在我的 VirtualBox 上已经不能跑起来这个 LiveCD 了……</p>
<p>更为可惜的是,就在这个项目刚刚公开展示出来的时候,乔布斯就致电升阳,
说如果继续商业化这个产品,升阳公司将涉嫌侵犯苹果的知识产权
(时间顺序上来看,苹果最初展示 Exposé 是在 2003年6月23日的
Apple Worldwide Developers Conference ,而升阳最初展示
LG3D 是在 2003年8月5日的 LinuxWorld Expo)。
虽然和乔布斯的指控无关,升阳公司本身的业务也着重于服务器端的业务,
后来随着升阳的财政困难,这个项目也就停止开发并不了了之了。</p>
</div>
<div class="section" id="windows">
<h2><a class="toc-backref" href="#id14">Windows 中的混成器</a></h2>
<div class="panel panel-default">
<div class="panel-heading">
Longhorn 中的 Wobbly 效果</div>
<div class="panel-body">
<div class="well" style="padding: 0">
<div class="tab-content" id="youtubeku">
<div class="tab-pane fade active in" id="youtube_X0idaN0MY1U">
<div align="left" class="youtube embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" src="https://www.youtube.com/embed/X0idaN0MY1U"></iframe> </div>
</div>
<div class="tab-pane fade" id="youku_XOTEzMzY5NjQ0">
<div align="left" class="youku embed-responsive embed-responsive-16by9"> <iframe allow="fullscreen" class="embed-responsive-item" frameborder="0" height="498" src="https://player.youku.com/embed/XOTEzMzY5NjQ0" width="510"></iframe> </div>
</div>
</div>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#youtube_X0idaN0MY1U">Youtube</a></li>
<li><a data-toggle="tab" href="#youku_XOTEzMzY5NjQ0">Youku</a></li>
</ul>
</div>
</div>
</div>
<p>上面说到, Windows 系列中到 XP 为止都还没有使用混成器绘制窗口。
看着 Mac OS X 上有了美轮美奂的动画效果, Windows 这边自然不甘示弱。
于是同样在 2003 年展示的 Project Longhorn 中就演示了 wobbly 效果的窗口,
并且跳票推迟多年之后的 Windows Vista 中实现了完整的混成器
<a class="reference external" href="http://en.wikipedia.org/wiki/Desktop_Window_Manager">Desktop Window Manager (DWM)</a>
。整个 DWM 的架构和 Mac OS X 上看到的很像:</p>
<img alt="ditaa diagram" class="ditaa img-responsive" src="//farseerfc.me/uml/1763baf6.png"/>
<p>和 Mac OS X 的情况类似, Windows Vista 之后的应用程序有两套主要的绘图库,一套是从早期
Win32API 就沿用至今的 GDI(以及GDI+),另一套是随着 Longhorn 计划开发出的 WPF 。
WPF 的所有用户界面控件都绘制在 DirectX 贴图上,所以使用了 WPF 的程序也可以看作是
DirectX 程序。而对老旧的 GDI 程序而言,它们并不是直接绘制到 DirectX 贴图的。首先每一个
GDI 的绘图操作都对应一条
<a class="reference external" href="http://en.wikipedia.org/wiki/Windows_Metafile">Windows Metafile (WMF)</a>
记录,所以 WMF 就可以看作是 Mac OS X 的 Quartz 内部用的 PDF 或者 NeXTSTEP 内部用的
DPS,它们都是矢量图描述。随后,这些 WMF 绘图操作被通过一个
Canonical Display Driver (cdd.dll) 的内部组建转换到 DirectX 平面,并且保存起来交给
DWM。最后, DWM 拿到来自 CDD 或者 DirectX 的平面,把它们混合起来绘制在屏幕上。</p>
<p>值得注意的细节是,WPF 底层的绘图库几乎肯定有 C/C++ 绑定对应, Windows 自带的不少应用程序
和 Office 2007 用了 Ribbon 之后的版本都采用这套绘图引擎,不过微软没有公开这套绘图库的
C/C++ 实现的底层细节,而只能通过 .Net 框架的 WPF 访问它。这一点和 OS X 上只能通过
Objective-C 下的 Cocoa API 调用 Quartz 的情况类似。</p>
<p>另外需要注意的细节是 DirectX 的单窗口限制在 Windows Vista 之后被放开了,或者严格的说是
基于 WDDM 规范下的显卡驱动支持了多个 DirectX 绘图平面。
在早期的 Windows 包括 XP 上,整个桌面上同一时刻只能有一个程序的窗口处于 DirectX 的
<strong>直接绘制</strong> 模式,而别的窗口如果想用 DirectX 的话,要么必须改用软件渲染要么就不能工作。
这种现象可以通过打开多个播放器或者窗口化的游戏界面观察到。
而在 WDDM 规范的 Vista 中,所有窗口最终都绘制到 DirectX 平面上,换句话说每个窗口都是
DirectX 窗口。又或者我们可以认为,整个界面上只有一个真正的窗口也就是 DWM 绘制的全屏窗口,
只有 DWM 处于 DirectX 的直接渲染模式下,而别的窗口都输出到 DirectX 平面里(可能通过了硬件加速)。</p>
<p>由 DWM 的这种实现方式,可以解释为什么
<a class="reference external" href="http://gaming.stackexchange.com/questions/13066/why-is-windowed-mode-always-slower-in-games">窗口模式下的游戏总是显得比较慢</a>
,原因是整个桌面有很多不同的窗口都需要 DWM 最后混成,而如果在全屏模式下,只有游戏
处于 DirectX 的直接渲染方式,从而不会浪费对游戏而言宝贵的 GPU 资源。</p>
<p>由于 DWM 实现了混成器,使得 Vista 和随后的 Windows 7 有了
<a class="reference external" href="http://en.wikipedia.org/wiki/Windows_Aero">Aero Glass</a> 的界面风格,
有了 Flip 3D 、Aero Peek 等等的这些辅助功能和动画效果。
这套渲染方式延续到 Windows 8 之后,虽然 Windows 8 还提出了 Modern UI
不过传统桌面上的渲染仍旧是依靠混成器来做的。</p>
</div>
<div class="section" id="linux">
<h2><a class="toc-backref" href="#id15">这就结束了? Linux 桌面呢?</a></h2>
<p>别急,我写这些文章的目的是想聊聊 Linux 中的混成器,尤其是 X 下现有的混成器和 Wayland
,这篇文章只是个背景介绍。关于 X 中混成器的实现方式和限制,且听我下回分解。</p>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
var location_protocol = (false) ? 'https' : document.location.protocol;
if (location_protocol !== 'http' && location_protocol !== 'https') location_protocol = 'https:';
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = location_protocol + '//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>archlinux 上用 chrome 实现 透明计算 远程登录2015-02-13T20:39:00+09:002015-02-13T20:39:00+09:00farseerfctag:farseerfc.me,2015-02-13:/zhs/arch-chrome-remote-desktop.html
<p><a class="reference external" href="http://news.sciencenet.cn/htmlnews/2015/1/311393.shtm">透明计算</a>
具体是什么,因为他们没有公开技术细节所以我并不知道,只是看
<a class="reference external" href="http://v.qq.com/page/h/v/q/h0145ebh1vq.html">公开出来的演示视频</a>
,感觉似乎只要能从手机上远程登录系统桌面,就能算是透明计算了。
如果透明计算真是这个意思,那么我似乎已经用着这个技术很多年了嘛。</p>
<p>Xorg 上常用的远程桌面工具有很多,基于 VNC 协议的、基于NX的和基于 RDP 协议的都能找到,
直接 ssh X forwarding 效果也不错。只是这些方案的一个 <strong>不太易用</strong> 的地方在于,需要
通过 ip 访问到远程的电脑,所以在跨越 NAT 之类的情况下不太容易使用。</p>
<p>于是今天介绍一个使用方便设置也简单的方法: 通过 chrome-remote-desktop 在 archlinux
上使用远程桌面。这个方案的优势在于,借助 Google 的云端服务器(内部貌似是XMPP协议下的握手)
方便地实现了 NAT 穿透,无论什么网络环境基本都能使用。当然,要支持远程登录,
位于远端的登录的计算机必须一直开着 …</p>
<p><a class="reference external" href="http://news.sciencenet.cn/htmlnews/2015/1/311393.shtm">透明计算</a>
具体是什么,因为他们没有公开技术细节所以我并不知道,只是看
<a class="reference external" href="http://v.qq.com/page/h/v/q/h0145ebh1vq.html">公开出来的演示视频</a>
,感觉似乎只要能从手机上远程登录系统桌面,就能算是透明计算了。
如果透明计算真是这个意思,那么我似乎已经用着这个技术很多年了嘛。</p>
<p>Xorg 上常用的远程桌面工具有很多,基于 VNC 协议的、基于NX的和基于 RDP 协议的都能找到,
直接 ssh X forwarding 效果也不错。只是这些方案的一个 <strong>不太易用</strong> 的地方在于,需要
通过 ip 访问到远程的电脑,所以在跨越 NAT 之类的情况下不太容易使用。</p>
<p>于是今天介绍一个使用方便设置也简单的方法: 通过 chrome-remote-desktop 在 archlinux
上使用远程桌面。这个方案的优势在于,借助 Google 的云端服务器(内部貌似是XMPP协议下的握手)
方便地实现了 NAT 穿透,无论什么网络环境基本都能使用。当然,要支持远程登录,
位于远端的登录的计算机必须一直开着 Chrome Remote Desktop 的后台服务。</p>
<div class="panel panel-default">
<div class="panel-heading">
Chrome Remote Desktop 插件</div>
<div class="panel-body">
<img alt="Chrome Remote Desktop 插件" class="img-responsive" src="//farseerfc.me/zhs/images/chrome-remote-desktop-plugin.png"/>
</div>
</div>
<div class="section" id="chrome-remote-desktop">
<h2><a class="toc-backref" href="#id5">Chrome Remote Desktop 的客户端</a></h2>
<p>虽然可能有很多人不知道,不过 Chrome 内包括远程桌面的功能很久了。只是这个功能的界面默认
没有提供界面,要使用它需要安装 Google 官方出品的
<a class="reference external" href="https://chrome.google.com/webstore/detail/chrome-remote-desktop/gbchcmhmhahfdphkhkmpfmihenigjmpp">remote-desktop 插件</a> 。
装好之后远程桌面的客户端就准备好,可以用来远程访问别的计算机桌面了(无论是 Windows/OS X
还是 Linux 都支持)。并且不光可以自己远程访问自己账户的桌面,还可以远程协助朋友的桌面。</p>
</div>
<div class="section" id="archlinux">
<h2><a class="toc-backref" href="#id6">Archlinux 上设置远程登录的服务器</a></h2>
<p>有了客户端之后还要设置一下才能让桌面作为远程登录的服务器。Windows 和 OS X 上 Chrome
会自动下载需要的安装包,无脑下一步就能装好了。Linux上由于发行版众多,桌面配置各异,
所以需要一点手动配置。官方的设置步骤记载在 <a class="reference external" href="https://support.google.com/chrome/answer/1649523">这里</a>
其中给出了 debian 用的二进制包和 Ubuntu 12.10 上的设置方式,以下设置是参考官方步骤。</p>
<p>首先要安装 chrome-remote-desktop 这个包,这个包实际上对应了 Windows/OS X 上用安装程序
安装的 Remote Desktop Host Controller。 archlinux 上开启了
<a class="reference external" href="https://github.com/archlinuxcn/repo">[archlinuxcn]</a>
仓库的话,可以直接安装打好的包。或者可以从
<a class="reference external" href="https://aur.archlinux.org/packages/chrome-remote-desktop/">AUR</a> 装。</p>
<pre><span class="code-line">$ pacman -Ss chrome-remote-desktop<br/><span style="color:purple;font-weight:bold;">archlinuxcn/</span><span style="font-weight:bold;">chrome-remote-desktop </span><span style="color:green;font-weight:bold;">40.0.2214.44-1</span><br/>Allows you to securely access your computer over the Internet through Chrome.</span></pre><p>装好之后从会说这么一段话:</p>
<blockquote>
<p>groupadd:无效的组 ID “chrome-remote-desktop”</p>
<p>Please create ~/.config/chrome-remote-desktop folder manually, if it doesn't exist, or else you can't use CRD.
The needed files are created by the Chrome app, inside the chrome-remote-desktop folder, after Enabling Remote Connections.
To {enable,start} the service use systemctl --user {enable,start} chrome-remote-desktop</p>
<p>You may need to create a ~/.chrome-remote-desktop-session file with commands to start your session</p>
<p>Go to <a class="reference external" href="https://support.google.com/chrome/answer/1649523">https://support.google.com/chrome/answer/1649523</a> for more information.</p>
</blockquote>
<p>那句报错是 AUR 里打的包还没跟上上游 Google 的更改导致的错误,
首先我们需要把远程登录的用户添加入 chrome-remote-desktop 这个用户组里。
新版本的 chrome remote desktop 提供了一个命令做这个事情,所以执行以下命令就可以了:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> /opt/google/chrome-remote-desktop/chrome-remote-desktop --add-user</span>
</pre></div>
<p>然后我们需要手动创建 <code class="code">
~/.config/chrome-remote-desktop</code>
这个文件夹,内容是空的
就好了,随后 chrome 会往这里面放 <code class="code">
host#.json</code>
文件用于身份验证。</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> mkdir ~/.config/chrome-remote-desktop</span>
</pre></div>
<p>然后我们要创建一个 shell 脚本 <code class="code">
~/.chrome-remote-desktop-session</code>
,这是远程
登录时的 .xinitrc ,内容么就是启动你想在远程登录时用的桌面环境。
这里可以指定一个和你正在登录的 WM/DE 不同的桌面,比如我启动 xfce4:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> cat ~/.chrome-remote-desktop-session</span>
<span class="code-line"><span class="gp">#</span>!/bin/bash</span>
<span class="code-line"><span class="go">startxfce4</span></span>
<span class="code-line"><span class="gp">$</span> chmod <span class="m">755</span> .chrome-remote-desktop-session</span>
</pre></div>
<p>接下来需要从 Chrome 的插件里启用远程桌面。打开 Chrome 的 Remote Desktop 插件,这时
应该可以看到一个「启用远程链接」的按钮。</p>
<div class="figure">
<img alt="Chrome Remote Desktop 插件中「启用远程链接」的按钮" class="img-responsive" src="//farseerfc.me/zhs/images/chrome-remote-desktop-enable-button.png"/>
<p class="caption">Chrome Remote Desktop 插件中「启用远程链接」的按钮</p>
</div>
<div class="alert alert-warning compound">
<p>在撰写本文的时候, Archlinux 官方源里的 chromium 的版本和 aur/google-chrome
的版本尚且还是 40.0.2214.111 ,而 Chrome Web Store 中提供的 Chrome Remote
Desktop 的插件的版本是 41.0.2272.41 。虽然通常并不要求两者版本一致,不过貌似最近
Chrome 内部的 Remoting 功能更改了 API 导致可能出问题。如果你找不到
「启用远程链接」的按钮,请尝试一下新版本的 Chrome 比如 google-chrome-dev 。
在这一步启用之后,老版本的 chrome 应该也就能使用远程桌面了。</p>
</div>
<div class="alert alert-warning compound">
<p>在32位的 Linux 版本上,最近更新的 Chrome Remote Desktop 插件可能无法正确识别 Host
的版本,具体 <a class="alert-link reference external" href="https://code.google.com/p/chromium/issues/detail?id=332930">参考这个 bug</a> 。</p>
</div>
<p>点击「启用远程链接」,设定一个 PIN 密码(不需要很复杂,这里首先有 Google 帐号验证保证只有
你才能访问),然后就能看到这套电脑的 hostname 出现在「我的电脑」列表里。</p>
<div class="figure">
<img alt="启用远程链接之后的样子" class="img-responsive" src="//farseerfc.me/zhs/images/chrome-remote-desktop-after-enabled.png"/>
<p class="caption">启用远程链接之后的样子</p>
</div>
<p>同时,启用了远程链接之后,可以在刚刚创建的 ~/.config/chrome-remote-desktop
文件夹中找到记录了验证信息的文件。</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> ls .config/chrome-remote-desktop</span>
<span class="code-line"><span class="go">chrome-profile host#8cfe7ecfd6bb17955c1ea22f77d0d800.json pulseaudio#8cfe7ecfd6</span></span>
</pre></div>
<p>然后就可以启动对应的 systemd 用户服务了,如果想自动启动服务要记得 <code class="code">
systemctl --user enable</code>
:</p>
<div class="highlight"><pre><span class="code-line"><span></span><span class="gp">$</span> systemctl --user start chrome-remote-desktop.service</span>
</pre></div>
<p>如果上面的设置一切正常,就可以看到 chrome-remote-desktop 启动了另外一个 Xorg 执行你
刚刚指定的桌面环境:</p>
<div class="figure">
<img alt="htop 中看到的 chrome-remote-desktop 启动的另外一个 Xorg" class="img-responsive" src="//farseerfc.me/zhs/images/chrome-remote-desktop-htop.png"/>
<p class="caption">htop 中看到的 chrome-remote-desktop 启动的另外一个 Xorg</p>
</div>
<p>然后就可以试着通过 Remote Desktop 插件登录到这个新开的 Xorg 了:</p>
<div class="figure">
<img alt="「远程」登录到新的 XFCE4" class="img-responsive" src="//farseerfc.me/zhs/images/chrome-remote-desktop-xfce4.png"/>
<p class="caption">「远程」登录到新的 XFCE4</p>
</div>
</div>
<div class="section" id="linux-chrome-windows-os-x">
<h2><a class="toc-backref" href="#id7">Linux 版本的 Chrome远程桌面 和 Windows/ OS X 上的区别</a></h2>
<p>通过上面的设置步骤也可以看出,Linux版本的远程桌面会在后台开一个独立的 X 会话,而不能
复用现在已有的 X 会话。对远程登录的用法而言这还能接受,对远程协助的功能而言有点问题,
因为正在使用的人不能观察协助者做了什么,协助者也不能继续请求协助的人的操作。</p>
<p>当然目前 Chrome 远程桌面的 Linux Host Controller 还只是 beta 版本,官方只测试支持
Ubuntu 12.04 和 12.10 (14.04之后似乎有
<a class="reference external" href="https://code.google.com/p/chromium/issues/detail?id=366432">Bug</a>
),所以不能要求太多。希望以后能改善吧。</p>
</div>
<div class="section" id="bonus">
<h2><a class="toc-backref" href="#id8">Bonus: 手机远程登录</a></h2>
<div class="panel panel-default">
<div class="panel-heading">
手机上的 Chrome 远程桌面 App</div>
<div class="panel-body">
<img alt="手机上的 Chrome 远程桌面 App" class="img-responsive" src="//farseerfc.me/zhs/images/chrome-remote-desktop-android.png"/>
</div>
</div>
<p>通过上面的设置就可以从任何一个 Chrome 远程桌面客户端登录刚刚设置的这台电脑了。
因为 Chrome 在三大桌面系统 Windows / OS X / Linux 上都有,所以应该能覆盖大多数桌面
系统了。</p>
<p>除了桌面的 Chrome 之外还有一个客户端是 Android 上的
<a class="reference external" href="https://play.google.com/store/apps/details?id=com.google.chromeremotedesktop">Chrome 远程桌面 App</a> 经过上面的设置之后,从这个 App 也能看到并登录:</p>
<div class="figure">
<img alt="手机远程登录" class="img-responsive" src="//farseerfc.me/zhs/images/chrome-remote-desktop-android-logined.png"/>
<p class="caption">手机远程登录</p>
</div>
<p>好啦,开始享受国家自然科学一等奖的透明计算技术吧!</p>
</div>