在 OpenPower 上兜兜风,第 3 部分: 如何避免必须移植代码

为什么移植非常困难?在 OpenPower 上兜兜风 系列文章的最后这篇文章中,Peter Seebach 将介绍从一种体系结构移植到另外一种体系结构上时涉及哪些问题,并对 API 和 硬件接口进行比较。

Peter Seebach, 自由作家, Plethora.net

Peter Seebach1999 年,Peter Seebach 首次在 Alpha 上测试了自己的代码已为 64 位环境做好了准备。对于这个问题来说,再仔细也不过分。当然,那些代码都可以工作,因此让我们开始吧。



2010 年 9 月 21 日

我们在 第 2 部分 中使用的例子都是人为的。为什么会这样呢?因为很难碰到一个很好的例子,它需要很多没有明显的错误、做作或低级之处的可移植性代码,只有这样才能很好地应用于非内核开发。设备驱动程序及其类似程序都有足够的理由来了解这些问题;用户应用程序则没有这么大的必要。

多年以来,软件工程师已经学习了很多有关可移植性的问题,其中的很多知识已经渗透到了语言的设计以及标准编码实践当中。如果您遵循了语言规范并使用了合理的实践,您应该很少会在意您的主机硬件是高位优先还是低位优先,或者最大的类型到底是什么。当然,这要假设其他人也全部遵循这种实践;实际上,他们通常都不会这样做,因此仍然有很多可能会碰到这种问题。

不过,问题并非在于可移植性问题很难解决;而在于可移植性很难翻新什么花样。很多商业代码在编写时都没有考虑将来的可移植性问题。 因此,当您使用真实的代码时,就可能会碰到可移植性问题 —— 不过大部分问题都是由于意外造成的困难,而不是因为问题本身很难解决。这些问题是错误编码的结果,在大部分情况中,很容易写出没有问题的代码。

可移植性问题通常都与 API 的交互有关,而不是与硬件有关。和 API 相比,处理器体系架构在编译代码时不会造成那么多的区别。如果您希望将一个本地 UNIX® 应用程序移植到 Microsoft® Windows® 系统上,就需要一个支持库;如果您需要将一个本地 x86 应用程序移植到 PowerPC® 系统上,但依然使用相同的操作系统,那么您的问题可能已经解决好了。

不过,实际上有些代码就是不能顺利完成转换,但是这里面有一些原因。因此,这最后一篇文章将介绍一些在实验中学习到的关于软件可移植性的各种问题,例如 “将源目录从基于 Pentium 的笔记本上复制到一台 OpenPower™ 服务器上”。

信任 API

如果您希望看到不能顺利移植的代码,那么请教那些自认为比库作者知识还丰富的人吧。让我根据自己十年左右的经验来给出一个例子;这到目前为止仍然是一个非常好的例子。它涉及到 PINE 的邮件读者。由于某些超出我的知识范围的原因,PINE 是使用一组复杂的库和封装程序设计的。在编译时,您会告诉它是在什么平台上进行编译的,然后再进行一些奇妙的处理。其中最奇妙的事情之一是,在几乎每个平台上都为计划要处理的每个库函数和系统调用都提供自己的声明,而不是包括标准的头文件。根据 PINE 开发人员的说法,之所以这样做是由于有一个平台上标准库的定义都是错的;我认为这可能是 Ultrix 或其他平台。(我从来没有见过这种平台,因此可能猜测的并不对。)

这就是说在每个新平台上,PINE 需要对标准头文件中的内容提供自己的精确定义。如果您要将它移植到一种新平台上,这需要自行更新定义并修正。

我们要使用的例子是在一个高位优先系统上编译 PINE。4.4BSD 系统,例如 NetBSD 和 FreeBSD,都具有 64 位文件偏移量。PINE 并没有包括系统头文件,而是对相关函数提供了自己的声明,所有这些都是使用 32 位偏移量来定义的。因此,在高位优先系统上,它认为每个文件都是空的。数据破坏是最坏的结果,不过更加实际的问题是从来没有考虑有任何 e-mail。

教训非常简单:如果有人认为自己比库或系统供应商了解的内容更多,那就随他去吧。如果您必须要修改代码,那么就期望这是一个恶梦好了。然后指定一些计划来迁移到其他平台上。这种代码就是随时可能爆炸的一颗定时炸弹。

对 64 位文件偏移量会产生问题的旧式 UNIX 程序的数量可能并不会像我们想象的那样少;不过,其中大部分都只对那些大小超过 2GB 或 4GB 的文件有问题;只有那些主动破坏所提供的 API 和标准 C 获取函数声明方法的程序才会受到小文件的困扰。类似地,如果磁盘具有超过 2GB 的空闲空间,那么很多更老的 Windows 安装程序会产生一些恐怖的后果(最好的情况是仅仅拒绝安装)。

人们很容易忘记 C 和 UNIX API 都不仅仅是一组函数;它们还是一组正确获得函数及其相关类型的方法。试图超越这些方法是愚蠢的。我知道只有一个例外:尽管 Perl 的配置时诊断(“Your stdio isn't very std”)不能判断 stdio 库内部明显出现什么错误之外,Perl 都可以很好地处理这种情况。(实际上,我认为这些消息都是一次从配置脚本中提取出来的,不过之后又恢复了,这可能是由那些认为这些消息很有用而且并不关心它并不正确的人使用的。)

在大部分情况中,如果您希望可以使用更高级的库(举例来说,使用 stdio,而不是原始的读写操作),就应该这样做;这可以帮您更好地回避这些问题。


我们输入了 “make”

某个主要的数据库厂商流传着一个早期 Linux® 移植的传说(可能是假的):他们被要求说明移植到 Linux 上所需要的过程。“我们输入了 make”。这是对在具有类似 API 的平台上进行移植的经验的一个很好总结。不过并非所有的平台都是这样兼容的。一个程序员幽默站点上刊登了一个有趣的故事,说有人用基于线程的代码不能很好地进行编译,是因为他在 Windows 系统上是使用 MFC 库来编写的程序,然后在某些 UNIX 系统上试图编译这个程序。这种事情实在有点疯狂。

即使在一个系统上,区分 API 也非常重要。要提供一个更容易移植的 API 有很多东西都可以做,举例来说,OpenGL 就比 DirectX 的可移植性要好。不幸的是,您并不能对自己希望的所有东西都获得一个可移植的 API,这些可移植的 API 有时可能会缺少一些特性。移植成本仍然会很大,但是可以移植到多个系统上的努力是值得的。

交叉平台开发 API 和工具包有很多。通常,它们都是通过尝试将 API 特性抽象到一个新层次上,然后在此基础上提供一个通用的 API。有时这种方法可以很好地工作;有时效果却很差。解释语言,以及诸如 Java™ 之类的字节编码语言,通过对整个机器进行抽象而更进了一步。实际上,这通常是一个很好的选择;很多程序都是将时间都花在开发人员身上,而不是 CPU 身上;即使对于 CPU 绑定的操作来说,一台具有 8 个处理器、32GB 内存的服务器,与一台单处理器的工作站上补充一点造成性能降低的资源相比,程序在前者上运行速度可能会比在后者上运行还要慢。(这对我来说实在感到有些羞愧,因为我发现在 OpenPower 服务器上一个处理器上关联的物理内存比我自己的任何一台机器上的物理内存都要多。)

对于 C 语言来说,您最好的赌注是 UNIX/POSIX API 已经存在至少 20 年了;其他没有任何一种语言能够具有类似级别的可移植性或灵活性。难点是寻找合适的 GUI API;它们看起来存在时间都非常短,至少相对而言是如此。很多 GUI 工作现在都完全用其他形式来实现了 —— 举例来说,Web 页面和 Java —— 这并不是偶然的。


那么,什么最重要呢?

因此,如果到新体系架构的可移植性是如此平凡,那么为什么它会如此重要呢?这是由于几个方面的原因。一个原因是现在从实际受影响的少量代码集进行高度移植的趋势。我们一定听说过有关 Linux 向新体系架构的移植的新闻,这是因为它做了很多工作来在新体系架构上启动内核(更不要说编译器工具链和其他东西了)。我们没听说过的是有数百人在报告特定的包已经成功移植到 Linux 上了。举例来说,我喜欢在嵌入式系统上使用的 “Zork test” 对于 OpenPower 项目来说只是微不足道的。我需要在 makefile 中修改一行内容来编译一个 32 位的版本,修改 4 行内容来编译 64 位版本。这两种情况都不需要修改一行代码就能很好地完成我们的工作。

对于软件移植的很多讨论都需要解决两个主要业界中的问题:桌面计算机和视频游戏控制台。在这两种情况中,主要问题是 API 和开发环境。实际上,视频游戏控制台对于现代代码来说通常都是一个例外,不需要太多移植。因为它们拥有相当大的市场,采用完全一致的硬件,并且趋向于奖励和鼓励这种和硬件非常协调的代码的开发。开发人员不需要担心其他用户可能会使用稍微低端的系统,他们可以充分利用给定硬件的所有性能。尽管如此,这种方式也已经开始日渐没落了;开发人员越来越喜欢使用 API 而不是直接访问硬件了。

在大部分情况中,如果您希望在新硬件上运行应用程序,NetBSD 和 Linux 可以在这种硬件上运行的话,那么您的程序移植就已经完成。只有在需要进行内核开发时,才会遇到一些挑战。但是这正是让 Slashdot 和其他人认为向新系统的移植真正困难的地方。除非您从事的编译器或内核方面的开发工作,否则这对您来说基本上不是什么问题。如果说其他方面的东西,需要了解主机硬件的应用程序十分罕见。您可以简单地找一些例子,例如 X11 或 Java,但是您看到的 90% 的包都没有理由需要特别关心它们的主机平台。

这并不是说这些程序并不想尝试了解这方面的东西;如果您浏览一下 autoconf 脚本运行时的情况,就会发现人们正在检查 sizeof(char) 的大小,目的是为了尝试一下自己的 C 代码是否碰巧在其他非 C 编译器的工具下运行的。大部分时候,这些检查并不会实际做什么事情,实际上,有时它们只是为现有 bug 创造一些令人兴奋的新机会,举例来说,在交叉编译时就需要如此。

实际上,这要归结于我们对 “平台” 这个单词的理解。随着时间的流逝,UNIX 已经逐渐模糊了 “体系架构”(例如 PowerPC 或 x86) 和 “平台”(例如 UNIX、OS X 或 Windows)之间的关系。平台和体系结构这两个概念通常都非常紧密地联系在一起使用;举例来说,SPARC 就意味着 SunOS。现在 UNIX 是一个平台,体系架构已经几乎成为一种回忆了。您可以将一堆闲置的配件堆在一起运行 UNIX,也可以花 10,000 美金购买一个精心配置好的工作站,而且同样也运行 UNIX。


OpenPower:有趣,值得付出时间

访问 OpenPower 之类的系统的动机并不是需要了解一个程序能否在 POWER5 系统上成功运行。这可能会对某些性能测试非常重要;一个给定的系统能否管理某个给定的任务,结果并不总是显而易见的;尤其是在您有非常紧迫的时间要求时更是如此。举例来说,如果您需要一个渲染工厂,那么基准测试可能会需要花上很长时间。

我模糊地记得在 90 年代早期,有人曾告诉我说免费的操作系统永远都不会达到我们对商业 UNIX 所要求的标准,在具有 RAID 磁盘阵列、大量内存并且可以运行数月的多处理器系统上运行。现在呢,我却正在一台具有 RAID 磁盘、32GB 内存(对我来说这可真大)并运行了 2 个月的 8 路多处理器系统上使用一个 shell 提示符。

如果您正试图确认自己的代码可以在这个硬件上运行,可以尝试运行 make,然后回答 “好吧,它可以工作”,这可能会使整个问题变得更加简单。但您并不能变得在 “移植” 到 “新体系架构” 时不遇到任何困难,不过至少可以将工作简化一些。有些人可能会担心某个 “新” 体系架构上的 Linux 发行版也许还不完全成熟,或者尚不十分稳定,不过在 Power 体系架构的系统上,这已经不再是什么问题了;PowerPC Linux 已经非常稳定了,并且已经使用了很长时间。我认为它还有一点不足是因为在使用 Apple 的大量创新不兼容组件时还有一些困难,例如声卡和散热管理系统。如果您没有使用这些组件,那么 PowerPC Linux 已经非常不错了。这与您在最新硬件上尝试运行 x86 Linux 所碰到的问题是不同的,尤其是在笔记本或桌面系统上更是如此。由于找出这些问题也是一种练习,因此我想我会把这也叫做成功。

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux, Multicore acceleration
ArticleID=230961
ArticleTitle=在 OpenPower 上兜兜风,第 3 部分: 如何避免必须移植代码
publish-date=09212010