• 软件测试技术
  • 软件测试博客
  • 软件测试视频
  • 开源软件测试技术
  • 软件测试论坛
  • 软件测试沙龙
  • 软件测试资料下载
  • 软件测试杂志
  • 软件测试人才招聘
    暂时没有公告

字号: | 推荐给好友 上一篇 | 下一篇

发行版迁移及二进制兼容性注意事项

发布: 2007-5-25 23:14 | 作者: 佚名 | 来源: 互连网 | 查看: 131次 | 进入软件测试论坛讨论

领测软件测试网

级别: 初级

John Engel
Linux 技术顾问, IBM
2005 年 3 月 24 日
2005 年 7 月 更新

了解二进制兼容性,因为它关系到运行在 POWER™ 上 Linux® 中的不同操作环境。研究 IBM® 支持的两个 Linux on POWER 发行版本,Red Hat Enterprise Linux(RHEL)和 SUSE LINUX Enterprise Server(SLES),注意各版本之间的二进制兼容性。通常,可以从基于 2.4 内核的 RHEL3 向基于 2.6 内核的 RHEL4 进行顺利迁移,因为这两个版本之间维护了稳定的应用程序二进制接口(Application Binary Interface,ABI),并将 RHEL4 的很多特性反向移植到了 REHL3 中。尽管基于 2.4 内核的 SLES8 与基于 2.6 内核的 SLES9 的线程模型有所不同,但在很多情形下,这两个版本之间仍保持了二进制兼容性。了解可以提高 Linux on POWER 应用程序性能的新技术,将来可以遵循这些步骤来确保多个版本之间的二进制兼容性。

简介
当前有很多不同的 Linux 发行版本可用。虽然 Red Hat 和 SUSE LINUX 是受 IBM 支持的 Linux on POWER 解决方案提供商,不过,其他发行版本,比如 Gentoo 和 Debian,也同样正在被 Linux on POWER 所接受。应用程序开发人员通常希望确保他们的应用程序能在多种发行版本上运行,在给定发行版本的不同版本上运行。通过了解二进制兼容性所涉及的问题,可以达成这些目标。本文定义了二进制兼容性,讨论了尝试保持兼容性时出现的考虑事项,并介绍了不同版本之间的迁移,即 Red Hat Enterprise Linux 版本 3 和版本 4,以及 SUSE LINUX Enterprise Server 版本 8 和版本 9。文中还包含了一些惯例,在多种发行版本中运行某个应用程序时,可以遵循这些惯例来确保兼容性。

表 1 提供了一些软件级别的消息,以及从 RHEL3、RHEL4、SLES8 和 SLES9 中获得的受支持特性,本文中对这些进行了详细介绍:

表 1. RHEL 和 SLES 发行版本中受支持的特性和代码级别

SLES8 SP4 RHEL3 U4 SLES9 RHEL4
内核 2.4.21 2.4.21 2.6.5 2.6.9
glibc 2.2.5 2.3.2 2.3.3 2.3.4
SMT
NPTL
NUMA
JDK IBM 1.3.1 IBM 1.4.2¹ IBM 1.4.2 IBM 1.4.2
Apache 1.3.26 2.0.46 2.0.49 2.0.52

¹RHEL3 没有附带 IBM Developer Kit for Linux,Java™ Technology Edition,但是可以从 IBM 下载它。(请参阅 参考资料。)

您可以使用图 1 所示的流程图来确定某个应用程序在 RHEL or SLES 中是否具备二进制兼容性。

图 1. 确定应用程序的二进制兼容性
确定应用程序的二进制兼容性

二进制兼容性概述
应用程序二进制接口(ABI)的应用促成了 Linux on POWER 二进制兼容性。编译过的二进制代码可以通过 ABI 接口访问某个操作系统及其服务。当多个操作系统支持相同的 ABI 时,就使得相同的二进制代码可以在各种环境中运行。在“64-bit PowerPC ELF Application Binary Interface Supplement 1.7”文档(IBM)中可以找到针对 PowerPC® Executable and Linking Format(ELF)ABI 的补充支持的更多信息(请参阅 参考资料。)

二进制兼容性是获得二进制代码并在给定处理器家族的多种环境中运行它的能力。这些环境可以是同一 Linux 发行版本的不同版本,也可以是完全不同的发行版本。一个示例是,在使用 SLES9 的基于 POWER4™ 处理器的系统中编译并运行的二进制代码,并在同样使用 SLES9 的基于 POWER5™ 处理器的系统中去执行它。另一个示例是,取得在使用 RHEL3 的基于 POWER4™ 处理器的系统中编译并运行的二进制代码,然后在使用 SLES9 的基于 POWER5™ 处理器的系统中去执行相同的二进制代码。

处理器兼容性
处理器兼容性是与 Linux on POWER 的二进制兼容性关系密切的一个话题。这一节将介绍不同的 64-位 POWER 处理器之间的兼容性,以及 32-位 PowerPC 处理器与 64-位 POWER 处理器之间的兼容性。

POWER 处理器兼容性
“二进制兼容性概述”中的最后一个示例涉及到了在不同的处理器类型中运行二进制代码 —— POWER4 处理器和 POWER5 处理器。POWER5 处理器包含了对 POWER4 架构的改进,同时保持了两者间的二进制兼容性,这就使得您可以在这两个平台上运行相同的应用程序。同样的二进制代码也可以运行在 IBM® eServer™ BladeCenter™ JS20 上,它使用的是基于 PowerPC® 970 的处理器。这种处理器是 POWER4 处理器的单核心(single-core)版本,它保持了与 Power 架构家族其他成员的二进制兼容性。

PowerPC 与 POWER 处理器兼容性
在本地 32-位 PowerPC 环境以及 64-位 POWER 环境中运行的 32-位应用程序之间存在二进制兼容性。也有可能在 64-位 Linux on POWER 系统中所生成的本地 Linux PowerPC 环境中执行 32-位二进制代码。这一兼容性可能要归于以下原因:

  • 64-位 Power 架构包含对完整的 32-位PowerPC 架构的支持。
  • 64-位 Linux 内核包含对处理 32-位系统调用的支持。
  • 32-位运行时环境包含所需的 32-位程序库。
  • 64-位 运行时环境包含所需的 32-位和 64-位程序库。

 

有一些生成 32-位和 64-位 Linux 二进制代码的不同方法(取决于开发平台):

  • 32-位 PowerPC 平台上的本地 GNU Compiler Collection(GCC)C 编译器,比如运行 Linux 的 Apple Powerbook,生成的 32-位二进制代码可以在其本地 32-位平台或者在包含有适当的 32-位用户空间程序库的 64-位 Linux on POWER 平台上执行。
  • IBM XL C/C++ 版本 7.0 以及用于 64-位 Linux on POWER 的 GCC C 编译器可以生成 32-位和 64-位的二进制可执行代码,该代码在 32-位和 64-位运行时环境中都可以执行。
  • 在 32-位 PowerPC Linux 系统和 64-位 Linux on POWER 系统中都有可用的交叉编译器。这些交叉编译器既可生成 32-位的二进制代码,也可以生成 64-位的二进制代码。不管二进制代码是在哪里构建的,32-位二进制代码都可以运行在 32-位 Linux 平台或 64-位平台上,而生成的 64-位代码只可以运行在 64-位 Linux on POWER 系统中。Crosstool 是交叉编译器的一个示例,在 Dan Kegel 的“Building and Testing gcc/glibc cross toolchains”文档的“Downloads”中可以找到它。(请参阅 参考资料。)

 

表 2 展示了如何为各种开发平台生成 32-位和 64-位 Linux 二进制代码:

表 2. 生成 32-位和 64-位 Linux 二进制代码

开发平台 编译器 生成的 Linux 二进制代码
32-bit Linux on PowerPC Native GCC C 编译器 32-bit
64-bit Linux on POWER Native XL C/C++ or GCC C compiler 32-bit 或者 64-bit
32-bit Linux on PowerPC 或者
64-bit Linux on POWER
crosstool 等交叉编译器 32-bit 或者 64-bit

虽然已经展示了 32-位和 64-位环境之间的兼容性,但这并不意味着发行版本官方支持此类兼容性。Red Hat 支持 RHEL3 和 RHEL4 之间的 32-位和 64-位向前和向后兼容性,而当从 SLES8 向 SLES9 迁移时,SLES8 只支持 32-位向前兼容性。

表 3 展示了 RHEL 和 SLES 的不同版本上 32-位和 64-位应用程序的向后和向前兼容性:

表 3. RHEL 和 SLES 发行版本中的 32-位和 64-位兼容性

发行版本 32-bit 64-bit
RHEL3 RHEL4
RHEL4 RHEL3
SLES9 SLES8
SLES8 SLES9

优化性能
2.6 内核和 POWER5 架构都具有可以提高应用程序性能的特性。这些提高来自于不同的程序库、处理器特性以及得到更新的编译器。有些情况下可以直接提高性能,不需要对应用程序进行任何修改,不过,在其他情况下,性能提高需要重新编译源代码。重要的是要紧记:通过重新编译来获得性能提升可能会影响某些环境中的二进制兼容性。这一节将给出 2.6 内核以及 POWER5 架构的新特性的一些示例,这些新特性有益于提高应用程序的性能。

NUMA
非一致内存访问(Non-uniform Memory Access,NUMA)是用于 Linux on POWER 的 2.6 内核所支持的新特性。NUMA 解决的是系统中特定的处理器(取决于它们在总线上的位置)访问内存中特定区域所需时间比其他处理器更长的问题。通过使用更多的内存总线,并令每条总线上处理器更少,NUMA 减少了系统共享内存总线的冲突。当系统的处理器较少时,这不是什么问题,但当系统中有很多处理器时,这样可以提高性能。由于 POWER5 架构扩展到 64 个处理器,Linux on POWER 可以受益于 2.6 内核的这一新特性。大部分应用程序将受益于 2.6 内核级 NUMA 支持。希望能进一步提高性能的应用程序可以使用用户空间 NUMA API。 RHEL4 附带了用户空间 NUMA API,在 NUMA Group 主页上可以找到关于如何使用这些 API 的更多资料。(请参阅 参考资料。)

编译器改进
您可能会考虑通过重新编译,借助用于 Linux on POWER 的最新编译器的新特性来获得性能提高。高性能编译器 IBM XL C/C++ Version 7.0 可以用于 RHEL3、RHEL4 和 SLES9,可以进一步提高基于 POWER5 的系统的性能。使用 -qarch-qtune 选项来为其相应的架构进行优化。例如,要为 POWER5 进行优化,要使用下面的选项: -qarch=pwr5-qtune=pwr5

XL C/C++ 编译器也包含了对 PowerPC 970 处理器上向量多媒体扩展(Vector Multimedia Extensions,VMX)的支持,使用的是 -qaltivec选项。

GNU Compiler Collection 包括很多语言的编译器。相对于版本 3.2,版本 3.3 已经得到了改进,包括对它的 C 编译器(gcc)的特定于 POWER5 处理器的优化。-mcpu=power5-mtune=power5 标记已经得到了支持,结果是能够支持以 POWER5 架构为目标的寄存器使用和指令调度参数。另外也有用于 IBM PowerPC 970 处理器的 VMX 向量扩展,可以提高向量化代码的性能。虽然这些优化在它们各自的架构上提高了性能,但是可能会影响运行在其他平台上的应用程序的二进制兼容性。

在 developerWorks 文章“How to use IBM XL C/C++ Advanced Edition V7.0 for Linux on POWER: A guide for GCC users”中可以找到关于如何使用针对 Linux on POWER 的 XL C/C++ 编译器的更多资料。

SMT
在迁移到基于 2.6 的 Linux 内核时,同步多线程(Simultaneous Multi-threading,SMT)带来了提高性能的另一种可能。 SMT 得到了 POWER5 架构的支持,极大地提高了重量级多线程应用程序的性能。POWER5 芯片有两个硬件指令线程,在每个周期都能够执行多个指令,这可以提高性能。不过,对于某些非重量级线程的应用程序,SMT 可能会导致性能的下降。在引导时向 Linux 内核传递 smt-enabled=off 可以禁用 SMT。

其他 2.6 内核改进
RHEL4 和 SLES9 中所使用的 2.6 内核具有这些其他方面的性能改进:

  • 可扩展性得到了改进,能够支持 64 路 SMP POWER5 处理器。
  • 为需要内存很多的应用程序提供了大页(large page)支持,允许使用 16MB 的页大小,同时仍支持传统的 4KB 页大小。
  • 在 2.6 中增加了 PCI 热插拔特性,允许在运行着的系统中添加或者删除 PCI 适配器,而无需关闭系统。
  • 通过使用反向映射算法,虚拟内存子系统已经得到了提高,这为内存受限的系统带来了改进。
  • 块 I/O 和异步 I/O 是其他得到改进的方面,它们提高了性能。

 

从 RHEL3 迁移到 RHEL4
从 RHEL3 到 RHEL4,Red Hat 维持了一个稳定的 ABI,使得应用程序在这两个版本之间可以顺利迁移。不过,为了确保二进制兼容性, Red Hat 建议您将应用程序接口链接到他们定义的核心程序库。这个列表中包括的程序库有:

  • libc、libgcc、libstdc++、libdl、libm、libutil、libcrypt、libz、libpthread、libncurses
  • libX11、libXext、libXt、libICE、libSM、libGL
  • libgtk、libgdk、libgdk_pixmap、libpango、libatk、libglib、libgmodule、libgthread、libgnomeprint、libgnomeprintui、libgconf、libglade

 

在“Red Hat Enterprise Linux 4 Application Compatibility”一文中描述了核心程序库以及其他兼容性问题(请参阅 参考资料)。如果某个应用程序使用了此核心程序库集之外的程序库,可能仍然会保持兼容性,但应该进行进一步的回归测试。应用程序可能无法保持二进制兼容性的其他情况包括:应用程序静态链接到任何程序库、依赖内核级接口,或者与 POSIX 标准或者 64-位 POWER ABI 定义相冲突。

不仅 ABI 在 RHEL3 和 RHEL4 之间是稳定的,而且 RHEL4 中可以找到的 2.6 内核的很多特性也已经反向移植到了 RHEL3。这使得 RHEL3 应用程序可以利用同步多线程(SMT)和 Native Posix Thread Library(NPTL)等特性来获得 RHEL4 中所包含的性能提高,而无需重新编译它们的源代码。这些应用程序也会获得如本文前面所描述的伴随 2.6 内核而来的性能提高。

不过,在两种情形下,在 RHEL4 中重新编译可以提高应用程序的性能:

  • 在拥有大量处理器的系统中,可能会发现借助 RHEL4 中所增加的 NUMA 用户空间 API 进行重新编译会有好处。虽然通常可以发现应用程序可以因内核级 NUMA 支持而获得性能提高,但是使用这些用户空间 API 进行编译能够获得进一步的提高。使用处理器较多而且频繁访问内存的应用程序将最为受益,因为 NUMA 减少了某个处理器访问内存中某个区域的时间。
  • 其他应用程序当使用最新的 RHEL4 编译器进行重新编译时,可能也会发现性能的提高。这些编译器已经增加了可以利用 POWER5 优化的标记,方法如本文前面所述。

 

当为了性能的提高而重新编译您的应用程序时,必须要考虑影响二进制兼容性的风险。

不过,在大部分情况下,当从 RHEL3 迁移到 RHEL4 时,不必付出额外的工作。

从 SLES8 迁移到 SLES9
由于两个版本间的一些重要变化,从 SLES8 到 SLES9 的迁移需要考虑稍微更多一些事项。具体地说,SLES8 基于 2.4.21 内核,使用的是 2.2.5 版本的 glibc,而 SLES9 基于 2.6 内核,使用的是 2.3 版本的 glibc。另外,SLES9 已经转移到了 NPTL 线程模型,而 SLES8 仍然在使用较老的 LinuxThreads 模型。在下面的“线程模型”中对线程模型的变化所带来的问题进行了描述。虽然使用通用程序库的非线程化应用程序在 SLES 版本间保持二进制兼容性的几率更大,但是也应该进行回归测试以保证质量

如 RHEL,在某些情形下,重新编译源代码可能会为将要在 SLES9 上运行的应用程序带来好处。例如,通过利用 SLES9 中得到增强的编译器,应用程序可能会获得性能的提高。SMT 也是 SLES9 中的一个新特性,可以提高重量级线程应用程序的性能。

通常,大部分非线程的应用程序在 SLES8 和 SLES9 中是二进制兼容的。不过,由于程序库和内核版本的重要变化,对尝试优化自己应用程序性能的开发人员来说,重新编译源代码可能是最好的选择。

确保发行版本间的兼容性
编译能够跨多种 Linux 发行版本移植的应用程序可能看起来是一项艰难的任务,不过,遵循一些简单的惯例就可以完成它。大部分发行版本的最新版本通常包含相同版本号的程序库的内核版本。大部分发行版本还会使用类似的配置和数据文件的格式。当尝试编写跨不同发行版本移植的应用程序时,可以遵循下面的部分所给出的一些指导方针。

很多 Linux 发行版本都包含相同的 ABI 和通用的核心程序库集。不过,每个发行版本对核心的定义会稍有不同,这就意味着当宣称您的应用程序支持某个给定的发行版本上时,总是需要进行回归测试。例如,如果您仔细研究 RHEL 和 SLES 发行版本所包含的 glibc 程序库,您会发现 RHEL3 所包含的是版本 2.3.2,SLES9 是 2.3.3,而 RHEL4 所附带的是 2.3.4,都非常类似。辅助版本号的区别通常是缺陷的修复,而没有新加的特性。RHEL3、RHEL4 和 SLES9 也使用了类似的线程模型,所以,任何链接到通用程序库的应用程序都应该能够在所有这三个操作环境中运行。您也会在 Gentoo 和 Debian 等其他发行版本中找到通用程序库。

文件层级标准(File Hierarchy Standard,FHS)定义了在通常的类-UNIX® 系统中文件和目录的布局方式。如果您的应用程序要依赖于其他配置和数据文件,那么应该使用 FHS。FHS 的主要用途是为应用程序提供一个能找到标准配置文件的通用位置。在文件系统层级标准(Filesystem Hierarchy Standard)主页上可以找到关于 FHS 的更多资料。(请参阅 参考资料。)

虽然通常不可能声称能够确保跨所有 Linux 发行版本的二进制兼容性,但是,通过遵循下面这些惯例,您可以声明支持大部分当前发行版本。

二进制兼容性方面的考虑事项
尽管已经从 ABI 和处理器家族的角度定义了二进制兼容性,但是当确定某个应用程序是否能够跨多种环境运行时,还有其他兼容性需要考虑。有一些示例,比如线程模型、底层内核依赖、中间件以及核心程序库。这一节将介绍这些需要考虑的事项以及 Linux on POWER 如何处理它们。

线程模型
随着 glibc 2.3 的发布和 Linux 内核从版本 2.4 到版本 2.6 的变迁,线程模型已经发生了变化。glibc 2.2 版本和 2.4 内核中所使用的传统 LinuxThreads 模型已经被 Native Posix Thread Library(NPTL)所取代。NPTL 已经彻底重写,这为重量级线程应用程序带来了显著的性能提高。在 Red Hat 文章“The Native POSIX Thread Library for Linux”中可以找到关于 NPTL 规范的更多细节。(请参阅 参考资料。)

SLES8 基于 2.4 内核,使用的是 Linux Thread 模型,当尝试在使用 NPTL 线程模型的 SLES9 上运行线程化的应用程序时,这会引发问题。这个问题有两个解决方案:

  • 对源代码进行少量修改,并使用基于 NPTL 的程序库重新编译,就可以受益于 NPTL 带来的性能提高。在 LinuxDevices.com 的文章 “Migrating applications to the 2.6 kernel and NPTL”中可以找到关于从 LinuxThreads 模型迁移到基于 NPTL 的模型的更多资料。 (请参阅 参考资料。)
  • 设置 LD_ASSUME_KERNEL 环境变量,它在 SLES9 中提供了与 LinuxThreads 模型的向后兼容性。通过设置 这个变量,链接器会认为运行的是使用较老的 LinuxThreads 模型的 2.4 内核。Red Hat 开发者 Ulrich Drepper 详细地解释了 LD_ASSUME_KERNEL(请参阅 参考资料)。

 

Red Hat RHEL3 使用的也是 2.4 内核,不过它从 2.6 内核反向移植了 NPTL 线程支持。这就使得线程化的应用程序可以在 RHEL 的不同版本之间顺利进行迁移。

底层内核依赖
当在不同的操作环境中运行应用程序时,底层内核依赖是要考虑的另一个方面。作为一个示例,如果应用程序编写时使用的是某个文件系统中的固定编码的值,那么当信息从 /proc 文件系统移到 sysfs 文件系统时可能会不兼容。先前的 /proc 文件系统是一个处于内存中的文件系统,让用户空间能够访问内核数据结构,这些数据结构中包含有系统和设备状态信息。这些信息从 /proc 文件系统迁移到 sysfs 文件系统中。/proc 文件系统仍然存在,不过只是包含进程信息。

系统信息从 /proc 文件系统移到 sysfs 文件系统的一个示例是通用串行总线(Universal Serial Bus,USB)设备列表的存储。在基于 2.4 内核的系统上编写的应用程序会发现,这个设备列在 /proc/bus/usb/devices 中。随着向 2.6 内核和 sysfs 文件系统的迁移,此信息已经移到了 /sys/bus/usb/devices。此信息的移动可能会导致应用程序不能正常工作。虽然这并不常见,但您需要意识到内核级中这种潜在问题。

中间件和应用程序兼容性
当前很多应用程序都不是自包含的,而是依赖于其他应用程序中间件。中间件是位于两个应用程序之间或者应用程序与操作系统之间的一部分软件。中间件的示例包含 IBM DB2® Universal Database for Linux、Java™ Development Kit(JDK)以及 IBM WebSphere® 应用程序。开发人员可以确定他们的应用程序支持哪个版本的中间件,但是中间件的提供者决定了他们的产品能够运行于哪个 Linux 发行版本。在用于 Linux 的 IBM 软件 Web 站点有用于 Linux 的 IBM 中间件列表(请参阅 参考资料)。 连同中间件一起,应用程序可能会依赖于发行版本所附带的其他应用程序。例如 Apache web 服务器数据库系统(比如 mysql 和 postgresql)以及解释语言(perl 和 python)。

作为示例,让我们来仔细研究 Java,看它是如何影响应用程序开发人员的。Java 是一个广受关注的通用的中间件软件包,这要归因于因为其平台独立性而存在的大量 Java 应用程序。JDK 不仅有不同的版本和提供者,用于 Linux on POWER 的还有 32-位和 64-位的版本。RHEL3 不附带 JDK,但是它支持 IBM Developer Kit for Linux、Java 2 Technology Edition,Version 1.4.2,而 RHEL4 既附带 IBM 32-bit SDK for Linux V1.4.2 也附带 IBM 64-bit SDK for Linux V1.4.2。SLES8 Update4 附带 IBM 32-bit SDK for Linux V1.3.1,而 SLES9 附带 IBM SDK V1.4.2 的 32-位和 64-位版本。当自己的应用程序依赖于只有在版本 1.4.2 中才可用的特性时,应用程序开发人员必须意识到这些区别,因为这些特性在 SLES8 中是不可用的。

作为另一个示例,Apache web 服务器是一个不被认为是中间件的应用程序,但是很多应用程序都依赖它。Apache 的最新版本是版本 2.0, RHEL3、RHEL4 和 SLES9 都附带它,而 SLES8 附带的是版本 1.3。如果某个应用程序使用了只有在 Apache 2.0 中才可用的新特性,那么开发人员应该意识到他们的应用程序可能无法与 SLES8 附带的版本 1.3 的 Apache 相兼容。

核心程序库
当尝试获得二进制兼容性时,核心程序库的重要变化也会引发问题。程序库的重要变化之间保持了向后兼容性。这就使得使用给定程序库的较老版本编译的应用程序可以使用同一程序库的新版本运行。如果修订版本间有重要的变化,那么使用给定程序库的最新版本编译的应用程序将不能使用同一程序库的较老版本运行。例如,在 SLES8 系统中使用 glibc 版本 2.2.5 编译的二进制代码,将可以在 SLES9 系统中使用 glibc 版本 2.3 运行,因为 2.3 版本是向后兼容的。不过,在 SLES9 上编译的同样的源文件将因较老的 glibc 而不能在 SLES8 系统中运行。只有当您在当前发行版本上开发应用程序,并且希望在发行版本的较老版本上(不提供较老的兼容程序库)支持那个应用程序时,这才会引发问题。

延伸阅读

文章来源于领测软件测试网 https://www.ltesting.net/


关于领测软件测试网 | 领测软件测试网合作伙伴 | 广告服务 | 投稿指南 | 联系我们 | 网站地图 | 友情链接
版权所有(C) 2003-2010 TestAge(领测软件测试网)|领测国际科技(北京)有限公司|软件测试工程师培训网 All Rights Reserved
北京市海淀区中关村南大街9号北京理工科技大厦1402室 京ICP备2023014753号-2
技术支持和业务联系:info@testage.com.cn 电话:010-51297073

软件测试 | 领测国际ISTQBISTQB官网TMMiTMMi认证国际软件测试工程师认证领测软件测试网