linux之buildroot(2)配置toolchain

Linux之buildroot(2)配置toolchain

Author:Onceday Date:2023年11月27日

漫漫长路,才刚刚开始…

全系列文章请查看专栏: buildroot编译框架_Once_day的博客-CSDN博客

参考文档:

1. 构建配置
1.1 配置config生成

可参考文档:

诸如make menuconfig/make nconfig等配置工具都有一个帮助文档,提供有关于选项的细节信息。也包括一个搜索工具,可以搜索对应的配置项信息。不同的配置前端,其使用方式也有区别,需要根据当前的前端的帮助文档确定使用方式。

在menuconfig中(使用/健进行配置项搜索),左列中的数字提供一个对应条目的快捷方式。只需键入此数字即可直接跳转到该条目,或者在由于缺少依赖项而无法选择该条目的情况下跳转到包含该条目的菜单。

在这里插入图片描述

如上图所示,在搜索FS之后,便出现上面界面,然后键入数字2便可以跳转到对应的配置项上了。

1.2 交叉编译工具链(Cross-compilation toolchain)

可参考文档: Linux编译之(1)C语言基础_Once_day的博客-CSDN博客

交叉编译工具链是一套被设计用来在一个类型的操作系统或硬件结构中编译代码,然后在另一个不同的目标系统中运行。这种方法主要被用于嵌入式系统开发,其中目标系统可能没有足够的资源来支持软件的编译。交叉编译工具链主要包括以下组件:

  1. 交叉编译器(Cross-Compiler):它可以在一个平台(主机)上将源代码编译成另一个平台(目标)的可执行代码。

  2. 交叉链接器(Cross-Linker):它可以链接在主机上编译的目标代码,并生成可以在目标平台上运行的可执行文件。

  3. 库(Libraries):这些是预编译的代码块,它们可以被链接到你的程序中,以提供一些通用的功能。交叉编译工具链应包含可以在目标平台上运行的库。

  4. 头文件(Header Files):这些文件包含源代码中使用的函数、变量和数据结构的定义。头文件应该是针对目标平台的。

  5. 调试器(Debugger):在开发过程中,调试器用于测试和调试在主机上编译,但在目标平台上运行的代码。

  6. 二进制工具(Binutils):这些工具用于处理目标平台的二进制文件。它们包括诸如 objdump(用于显示二进制文件信息)、readelf(用于显示 ELF 文件信息)等工具。

一个常见的例子是使用 GNU Compiler Collection (GCC) 构建交叉编译工具链。例如,如果你正在一个 x86 架构的 Linux 系统上开发,而你的目标平台是 ARM 架构的嵌入式设备,你就可能需要一个交叉编译工具链。你可以使用 GCC 构建这样一个工具链,它包括 ARM 交叉编译器 (arm-linux-gnueabi-gcc),交叉链接器 (arm-linux-gnueabi-ld),ARM 库,以及适用于 ARM 平台的头文件。

这里有一些很重要的术语,如下:

  • host(宿主机)和target(目标机),对于上面的例子,X86架构的Linux系统即为宿主系统(host system),其运行的编译器有两种,本地编译器(the regular compilation toolchain)和交叉编译器(the cross-compilation toolchain),它们都运行在宿主系统(x86-linux)中,但前者用于生成在宿主机上运行的二进制文件,后者用于生成在目标机(target system)上运行的程序。

    需要注意,GNU语境里Host(宿主机)等价于target(目标机),其含义和上述完全相反。此时,build system则代表编译环境所运行的系统

buildroot提供两种交叉工具链解决方法,如下:

  • Buildroot内部工具链(Internal toolchain backend),Buildroot 可以自己生成一套完整的交叉编译工具链。这个工具链是根据你在 Buildroot 配置中选择的选项(例如,目标架构、目标处理器、C 库类型等)定制的。这种方法的优点是你可以完全控制工具链的配置,包括使用的版本、补丁等。但是,生成工具链的过程可能会需要一些时间。

    当使用make clean时,会重新编译整个工具链,这会浪费一些时间,而外部工具链则不会

  • 外部工具链(external toolchain backend):Buildroot 也可以使用预先生成的交叉编译工具链。有多个组织(如 Linaro、ARM、CodeSourcery 等)提供了预编译的工具链,这些工具链可以直接用于开发。使用外部工具链的优点是可以节省生成工具链的时间,而且这些工具链通常由专业人员维护和优化。但是,它们可能不像 Buildroot 内部工具链那样具有高度的定制性。

1.3 内部工具链

如下所示,Toolchain选择Buildroot toolchain,配置界面如下所示(make nconfig):

在这里插入图片描述

(1) 对于Linux上的C library,buildroot有三种可供选择(选择不同的C库,可供选择的配置项也不一样):

  • uClibc-ng:uClibc-ng 是一个为嵌入式系统设计的轻量级 C 库。它的目标是提供比 glibc 更小、更简单的实现,同时仍然提供大部分的 POSIX 和 ISO C 标准功能。uClibc-ng 支持多种处理器架构,并且可以进行高度的配置以适应不同的应用需求。
  • glibc:glibc(GNU C Library)是 GNU 项目的 C 库,也是大多数 Linux 发行版的标准 C 库。它提供了丰富的功能和强大的性能,但相比于其他的 C 库,它的体积更大,更复杂。glibc 支持所有主要的处理器架构,并且包含许多 GNU 扩展。
  • musl:musl 是一个轻量级的 C 库,它的目标是提供简单、高效、标准兼容的 C 库实现。musl 的设计目标是在保持接口兼容性的同时,提供更高的性能、更好的资源管理,以及更少的软件复杂性。musl 支持多种处理器架构,并且在使用标准 Linux API 的应用中,它可以作为 glibc 的替代品。

(2) 启用兼容性垫片以在旧的内核上运行Enable compatibility shims to run on older kernels): 如果你正在开发的软件需要在多种版本的 Linux 内核上运行(包括旧版本),你可能需要使用“兼容性垫片”(compatibility shims)。兼容性垫片是一段代码,它为新的系统调用或功能和可能不直接支持它们的旧内核之间提供了桥梁。例如,你可能有一个程序使用了在内核版本 3.10 中引入的系统调用。如果你试图在内核版本为 3.0 的系统上运行此程序,则不会识别系统调用,程序无法按预期工作。在这种情况下,你可以使用兼容性垫片在运行时检测内核版本,如果它早于 3.10,使用替代方法来达到相同的结果。启用兼容性垫片通常涉及条件编译,根据目标内核版本在构建中包含不同的代码部分。

(3) 安装 glibc 实用程序(Install glibc utilities): glibc 是 GNU 项目的 C 库,提供了一套丰富的、用于实现各种功能的 API。glibc 实用程序是一组包含在 glibc 中的工具和命令,它们提供了许多有用的功能,如字符串操作、数学计算、日期和时间处理、文件和目录访问等。安装 glibc 实用程序通常意味着你可以在你的系统中使用这些工具和命令。这些实用程序可能包括(但不限于)getconf(用于查询系统配置变量)、iconv(用于转换字符编码)、locale(用于获取或设置区域设置信息)等。

(4) 选择用于构建工具链的Linux内核头文件的版本。在构建交叉编译工具链的过程中,也需要构建C库。这个库提供用户空间应用程序和Linux内核之间的接口。为了知道如何与Linux内核“对话”,C库需要访问Linux内核头文件(即内核的H文件),它定义了用户空间和内核之间的接口(系统调用,数据结构等)。由于此接口是向后兼容的,因此用于构建工具链的Linux内核头文件的版本不需要与在嵌入式系统上运行的Linux内核的版本完全匹配。它们只需要具有与运行的Linux内核版本相等或更老的版本。如果buildroot环境使用的内核头文件比在嵌入式系统上运行的Linux内核更新,那么C库可能正在使用嵌入式设备上Linux内核没有提供的接口。

1.4 外部工具链

如下所示,Toolchain选择External toolchain,配置界面如下所示(make nconfig):

在这里插入图片描述

外部工具链后端允许使用现有的预构建交叉编译工具链。对于著名的交叉编译工具链(来自ARM的Linaro、ARM的Sourcery CodeBench、x86-64、PowerPC和MIPS),可以自动下载它们,也可以指向一个定制的工具链,有三种解决方案可供选择:

  • 使用预定义的外部工具链配置文件,让builroot下载、解压缩并安装工具链。builroot已经知道一些CodeSourcery和Linaro工具链。只需在toolchain中从可用的工具链配置文件中选择工具链配置文件。这绝对是最简单的解决方案。
  • 使用预定义的外部工具链配置文件,但不用Buildroot下载并提取工具链,可以告诉Buildroot工具链已经安装在系统上的什么位置。只需通过可用的工具链在toolchain中选择工具链配置文件,取消自动选择下载工具链,并用交叉编译工具链的路径填充工具链路径文本条目(Toolchain path)。
  • 使用完全自定义的外部工具链。这对于使用crosstool-NG或使用builroot本身生成的工具链特别有用。为此,在toolchain列表中选择Custom工具链解决方案。需要填充工具链路径Toolchain path、工具链前缀Toolchain prefix和外部工具链c库External toolchain C library选项。然后,需要告诉builroot你的外部工具链支持什么。如果外部工具链使用glibc库,只需要告诉您的工具链是否支持c++,以及它是否具有内置RPC支持。如果外部工具链使用uClibc库,那么必须告诉builroot它是否支持RPC, wide-char, locale,程序调用,线程和c++。在执行开始时,builroot将判断所选择的选项是否与工具链配置不匹配。

官方文档表明,buildroot不支持非pure的工具链,buildroot更像是一个从头开始构建的编译环境,而不是一个集成的软件开发环境。下面是翻译的文本:

我们的外部工具链支持已经通过CodeSourcery和Linaro的工具链、crosstool-NG生成的工具链和Buildroot本身生成的工具链进行了测试。一般来说,所有支持sysroot特性的工具链都可以工作。如果没有,请毫不犹豫地联系开发者。

我们不支持由OpenEmbedded或Yocto生成的工具链或SDK,因为这些工具链不是纯粹的工具链(即只是编译器、binutils、C和c++库)。相反,这些工具链附带了非常大的预编译库和程序集。因此,Buildroot不能导入工具链的系统根,因为它将包含数百兆字节的预编译库,这些库通常由Buildroot构建。

我们也不支持使用发行版工具链(即发行版安装的gcc/binutils/C库)作为工具链来为目标构建软件。这是因为你的发布工具链不是一个“纯”的工具链(也就是说,只与C/ c++库一起),所以我们不能将它正确地导入到builroot构建环境中。因此,即使您正在为x86或x86_64目标构建系统,也必须使用builroot或crostool-ng生成交叉编译工具链。

如果您想为您的项目生成一个自定义工具链,它可以用作Buildroot中的外部工具链,我们的建议是使用Buildroot本身或使用crostool-ng来构建它。

1.5 工具链的通用参数

这些选项是在构建一个交叉编译工具链(Toolchain)时的通用配置选项:

  • Copy gconv libraries:这个选项决定是否复制 gconv 库到目标系统。gconv 库是 glibc(GNU C Library)的一部分,用于进行字符集转换。如果你的应用程序需要进行字符集转换(如从 UTF-8 转换为 ISO-8859-1),你应该启用这个选项。注意,这个选项可能会增加你的系统的大小。

  • Extra toolchain libraries to be copied to target:这个选项允许你指定一些额外的库,这些库将被复制到目标系统。你应该在这里输入你需要的库的名字,库名之间用空格分隔。例如,如果你需要复制 libmlibpthread,你可以输入 m pthread

  • Target Optimizations:这个选项允许你指定用于目标代码优化的编译器选项。这些选项将被传递给编译器,用于生成优化的代码。例如,你可以输入 -O2 -march=armv7-a 来启用等级 2 的优化并指定目标架构为 ARMv7-A。

  • Target linker options:这个选项允许你指定传递给链接器的选项。这些选项可以影响如何将你的对象文件链接成可执行文件或库。例如,你可以输入 -Wl,-rpath,/usr/lib 来设置运行时库搜索路径为 /usr/lib

1.6 构建交叉工具链

builroot内部工具链选项可用于创建外部工具链。创建一个新的builroot配置,详细信息如下:

  • 为目标CPU体系结构选择适当的Target options
  • 工具链菜单(Toolchain) 中为 工具链类型(Toolchain type) 保留默认的Buildroot工具链,并根据需要配置工具链。
  • 系统配置(System configuration) 菜单中,Init System选择None/bin/sh选择None
  • Target packages菜单中,禁用BusyBox
  • 文件系统映像(Filesystem images)菜单中,禁用打包根文件系统(tar the root filesystem)

然后可以触发构建,并要求Buildroot生成SDK。这将方便地为我们生成一个包含工具链的tarball:

make sdk

这将在$(O) /images中生成SDK压缩包,其名称类似于arm- builroot -linux-uclibcgnueabi_sdk- build保存此tarball,因为它现在是工具链,所以可以在其他Buildroot项目中作为外部工具链来使用。

在那些其他的Buildroot项目中,在make nconfig中如下配置:

  • 工具链类型(Toolchain type)选择外部工具链(External toolchain)
  • 工具链(Toolchain)选择自定义工具链(Custom toolchain)
  • Toolchain origin选择待下载安装的Toolchain(Toolchain to be downloaded and installed
  • 将工具链URL设置为file:///path/to/your/sdk/tarball.tar.gz

当使用外部工具链时,buildroot生成一个包装程序(External toolchain wrapper),它透明地将适当的选项(根据配置)传递给外部工具链程序。如果需要调试此包装器以检查传递的参数,可以将环境变量BR2_DEBUG_WRAPPER设置为以下任意一种:

  • 0,空或不设置:不调试。
  • 1,在一行上跟踪所有参数。
  • 2,每行跟踪一个参数。

最终经过复杂而漫长的编译过程,生成如下的编译工具包(包括host system工具和交叉编译器):

ubuntu->buildroot:$ tree -L 1 output/build/
output/build/
├── buildroot-config
├── build-time.log
├── gcc-final-12.3.0
├── glibc-2.38-27-g750a45a783906a19591fb8ff6b7841470f1f5701
├── host-autoconf-2.71
├── host-autoconf-archive-2023.02.20
├── host-automake-1.16.5
├── host-binutils-2.40
├── host-bison-3.8.2
├── host-expat-2.5.0
├── host-gawk-5.2.2
├── host-gcc-final-12.3.0
├── host-gcc-initial-12.3.0
├── host-gdb-12.1
├── host-gmp-6.3.0
├── host-isl-0.25
├── host-libffi-3.4.4
├── host-libiberty-2.41
├── host-libtool-2.4.6
├── host-libzlib-1.3
├── host-m4-1.4.19
├── host-mpc-1.2.1
├── host-mpfr-4.1.1
├── host-ncurses-6.4-20230603
├── host-patchelf-0.13
├── host-pkgconf-1.6.3
├── host-python3-3.11.6
├── host-skeleton
├── host-zlib
├── ifupdown-scripts
├── linux-headers-4.14.329
├── packages-file-list-host.txt
├── packages-file-list-staging.txt
├── packages-file-list.txt
├── skeleton
├── skeleton-init-common
├── skeleton-init-none
├── toolchain
└── toolchain-buildroot

可以看到默认情况编译的工具包还是挺多的,特别是gcc,编译了三个版本,这可能和GCC的编译流程有关,因为第一个host-gcc-initial是使用host system上的glibc库编译的,然后再编译glibc,然后继续编译host-gcc-final,并且最终编译出gcc-final,整个过程也算是一波三折

最终编译出来的toolchain工具包在output/images目录下面,如下:

ubuntu->buildroot:$ ll output/images/
total 271492
drwxr-xr-x 2 ubuntu ubuntu      4096 Nov 29 01:26 ./
drwxrwxr-x 6 ubuntu ubuntu      4096 Nov 29 01:26 ../
-rw-r--r-- 1 ubuntu ubuntu 277999412 Nov 29 01:27 aarch64-onceday-linux-gnu_sdk-buildroot.tar.gz
2. 备注信息
2.1 musl C库介绍

Musl 是一个为 Linux-based 系统设计的轻量级 C 标准库,最初由 Rich Felker 在 2010 年开发。该库的目标是提供简单、高效、标准兼容的 C 库实现,同时保持小巧和轻量级。Musl 是以 MIT 许可证发布的自由和开源软件。

Musl 的设计和实现秉持以下主要原则:

  • 简单性:Musl 的源代码被设计得易于阅读和理解。清晰的代码结构和详细的注释使得开发者能够更容易地理解和修改库的实现。

  • 标准兼容性:Musl 旨在遵循 ISO C 和 POSIX 标准,这使得使用 musl 的程序能够在其它遵循这些标准的系统上运行。

  • 性能:Musl 的实现高度关注效率,包括运行时性能和内存使用。许多功能都经过优化,以减少 CPU 使用和内存占用。

  • 小巧:Musl 小巧的体积使其适合在资源受限的环境中使用,如嵌入式系统和小型设备。

Musl 已经在 Alpine Linux(一个专为安全、简洁和资源效率设计的轻量级 Linux 发行版)中作为默认的 C 库被广泛采用,也被许多其他嵌入式 Linux 发行版和项目所使用。

2.2 GCC的OpenMP和graphite支持
  1. 启用编译器 OpenMP 支持:OpenMP (Open Multi-Processing) 是一个支持多平台共享内存并行编程的 API。在 C,C++ 和 Fortran 语言中都有广泛应用。当你启用编译器的 OpenMP 支持时,你的代码就可以使用 OpenMP 的指令,这些指令可以使得你的程序在多核或多线程环境中运行,并且可能会显著提高程序的性能。在 GCC(GNU Compiler Collection)中,你可以通过添加 -fopenmp 选项来启用 OpenMP 支持。

  2. 启用 Graphite 支持:Graphite 是 GCC 中的一项用于高级循环优化的功能。它可以分析你的代码,找出可以并行化或向量化的循环,并自动进行优化。Graphite 通过将循环转换为一种叫做 Static Single Assignment(SSA)的形式,然后使用一种叫做 Polyhedral model 的模型进行分析和优化。启用 Graphite 支持可能会提高程序的性能,特别是对于那些包含大量复杂循环的代码。在 GCC 中,你可以通过添加 -fgraphite-identity-floop-nest-optimize 等选项来启用 Graphite 支持。

请注意,虽然这些优化可以提高程序的性能,但是它们也可能会使得编译过程变得更复杂,并且可能会引入新的编译依赖。因此,在启用这些选项之前,你应该仔细考虑你的需求和约束。

OpenMP 是为 C,C++ 和 Fortran 语言设计的,它提供了一组编译器指令,库函数和环境变量,以便开发者可以指定并行区域(即应该并行执行的代码段)。这些并行区域可以由多个线程同时执行,线程的数量可以在运行时动态调整。

下面是一些 OpenMP 的主要特性:

  • 并行化循环:OpenMP 可以自动将循环的迭代分配给多个线程,以便它们可以并行执行。这对于大量数据的计算任务非常有用。
  • 任务并行:OpenMP 还支持创建并行执行的任务,这些任务可以在多个线程间自由调度。
  • 同步原语:OpenMP 提供了一组原语用于同步线程,如临界区,屏障,锁,以及一些更高级的原语如 orderedatomic
  • 数据环境管理:OpenMP 允许开发者指定变量的作用域,如 privatesharedfirstprivatelastprivate 等,以便更好地控制数据在并行区域中的行为。
  • 运行时库:OpenMP 还提供了一个运行时库,用于动态控制和查询并行执行的各种参数,如线程数量,嵌套并行度等。

OpenMP 的这些特性使其成为在科学计算,机器学习,图像处理等领域进行高性能并行计算的重要工具。

以下是一个使用 OpenMP 的简单 C 语言程序示例,它并行计算一个数组的所有元素的和。这个程序将一个大循环分配给多个线程进行处理,每个线程计算部分元素的和,最后再将这些部分和加起来得到总和。

#include <omp.h>
#include <stdio.h>

int main() {
    int i;
    int num = 1000;
    int a[num];
    int sum = 0;
    
    // 初始化数组
    for(i = 0; i < num; i++)
        a[i] = i;

    // 并行计算数组的所有元素的和
    #pragma omp parallel for reduction(+:sum)
    for(i = 0; i < num; i++)
        sum += a[i];

    printf("Sum: %d\n", sum);
    return 0;
}

在这个例子中,#pragma omp parallel for reduction(+:sum) 指示编译器创建一个并行区域,其中 for 循环的迭代被分配给多个线程执行。reduction(+:sum) 表示每个线程都有自己的局部 sum 变量,线程结束时,这些局部 sum 变量将被加起来(通过 + 操作)形成全局 sum 变量。

要编译这个程序,你需要使用支持 OpenMP 的编译器,并开启 OpenMP 支持,例如用 GCC 编译器,命令行参数添加 -fopenmp 选项:gcc -fopenmp sum.c -o sum

2.3 GNU binutils二进制工具包

GNU Binutils 是一个由一系列二进制工具组成的包,这些工具主要用于处理二进制和对象文件。下面是一些 GNU Binutils 主要的组成部分:

  • as:GNU 组装器,它将汇编语言代码转换为目标文件。

  • ld:GNU 链接器,它将多个目标文件链接为一个可执行文件或库。

  • ar:用于创建,修改和提取静态库的工具。

  • nm:显示二进制文件(如库或可执行文件)中的符号表。

  • objdump:显示二进制文件的详细信息,如指令,符号表,编译器,链接器选项等。

  • ranlib:生成静态库的索引,以加速链接过程。

  • strip:从二进制文件中移除符号信息,以减小文件大小。

  • gprof:性能分析工具,用于分析程序的运行时性能。

  • addr2line:将程序计数器(PC)地址转换为文件名和行号,用于调试。

  • c++filt:解码 C++ 符号名。

  • size:列出二进制文件中各个部分的大小。

  • strings:从二进制文件中提取可打印的字符串。

  • readelf:显示 ELF 格式的二进制文件的详细信息。

这些工具是在开发和调试 C,C++,以及其他使用 GNU 工具链的项目时非常重要的。它们让开发者能够控制和理解编译和链接过程,并能够分析和优化生成的二进制文件。

2.4 Crosstool-NG交叉工具链生成器

参考文档:crosstool-NG

crosstool-NG 是一个用于构建交叉编译工具链的开源项目。交叉编译工具链是一个编译器集合,可以在一个平台(通常是开发者的机器)上编译出在另一个不同的平台(通常是目标设备)上运行的代码。crosstool-NG 支持多种目标架构,并且可以高度自定义。

crosstool-NG 的主要特性包括:

  • 多种架构支持:crosstool-NG 支持多种处理器架构,如 x86,ARM,MIPS,PowerPC 等。

  • 高度可配置:crosstool-NG 提供了一个菜单驱动的配置系统,你可以在这个系统中选择和配置你的工具链的各个部分,如 gcc,glibc,binutils 等。

  • 自动化构建:crosstool-NG 可以自动下载源码,解压,配置,编译,安装,并最后生成一个完整的交叉编译工具链。

  • 工具链构建的隔离:crosstool-NG 在构建工具链时会创建一个隔离的环境,以避免与你的系统的其他部分产生冲突。

crosstool-NG 的基本使用流程如下:

  1. 配置:运行 ct-ng menuconfig 命令打开配置菜单,然后选择和配置你的工具链的各个部分。

  2. 构建:运行 ct-ng build 命令开始构建过程。crosstool-NG 会自动下载所需的源码并开始编译过程。这个过程可能需要一些时间。

  3. 使用:构建完成后,你会得到一个完整的交叉编译工具链,你可以使用这个工具链来编译你的代码。

crosstool-NG 是一个非常强大的工具,它使构建和管理交叉编译工具链变得更加简单。

2.5 toolchain RPC远程调用支持

Remote Procedure Call(RPC)是一种编程模型,用于在分布式系统中实现进程间通信。在 RPC 中,一个进程(客户端)可以在不了解网络细节的情况下请求另一个进程(服务器)执行某个过程或函数。这就像调用本地的函数或过程一样。

在编译工具链的上下文中,RPC 支持通常指的是工具链能够生成能够进行 RPC 通信的代码。例如,编译器可能会包含一些可以生成 RPC stubs 的工具或特性。这些 stubs 是客户端和服务器用于进行 RPC 通信的代码。

在 C 和 C++ 中,RPC 通常通过如下方式实现:

  • 使用某些专用的工具(如 rpcgen)来从一个描述文件(如 .x 文件)生成 RPC stubs。这个描述文件定义了要远程调用的过程的接口。

  • 在你的代码中调用这些 stubs,就像调用普通的函数一样。

  • 编译你的代码,生成客户端和服务器的可执行文件。

  • 在运行时,客户端和服务器通过网络进行通信,客户端发送请求,服务器执行过程并返回结果。

请注意,具体的 RPC 实现和工具可能会有所不同,你应该查阅你的工具链的文档以获取详细的信息。
PC 通信的代码。例如,编译器可能会包含一些可以生成 RPC stubs 的工具或特性。这些 stubs 是客户端和服务器用于进行 RPC 通信的代码。

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值