热度 3
2021-12-14 15:13
1701 次阅读|
0 个评论
B y Toradex 胡珊逢 Torizon 是一种基于容器形式开发应用的操作系统。这不仅是指用户的应用在容器里面运行,应用的编译和调试也是需要借助容器。为了便于用户开发, Toradex 推出的 V isual Studio Code/ V isual Studio T orizon Extension 能够完成后台的容器配置,便于用户使用。本文将介绍 T orizon Extension 后台实现的流程,从而帮助用户更好地调试应用程序。 总体来讲,开发语言可以分为两类,一种需要编译后在目标设备上运行,例如 C/ C++ 、 Qt 等 ,另外一种是可以直接解释运行,如 Python 。这里我们以需要编译的开发语言 C /C++ 为例进行说明。对于传统的嵌入式 L inux C /C++ 应用开发,首先要在开发电脑上安装交叉编译工具或者包含交叉编译工具的 SDK ,然后编写应用代码,用编译工具进行交叉编译,最后把编译好的二进制文件部署到目标设备上进行调试,常见的工具如 GDB 。 Torizon 的总体流程也是如此,但最大的区别是交叉编译工具的安装、编译和调试并不需要用户手动运行相关指令,而是 T orizon Extension 会在开发电脑上自动创建一个 SDK 容器,里面包含所需的交叉编译工具和头文件等,用户的代码在容器里面进行编译并通过 GDB 调试。由于采用的是容器形式,因此 Torizon 的开发电脑可以是 L inux ,也可以是 Windows 系统, Windows 需要安装 WSL 。 构建 SDK 容器 当用户在 V isual Studio Code/ V isual Studio 上安装了 T orizon Extension 之后,总体情况如下。在开发电脑和计算机模块上都将运行 Docker 。 T orizon Extension 会根据用户代码类型,如 C / C++ 、 . Net 自动生成合适的 SDK 容器。用户代码文件通过 T orizon Extension 在 SDK 容器中使用合适的编译工具进行编译。然后 T orizon Extension 再生成一个可以部署到计算机模块上运行的 release 或者 debug 版本容器。对于 debug 版本容器, T orizon Extension 通过 SDK 容器对其进行调试,如使用 GDB 工具。 下面我们将使用 V isual Studio Code 创建一个单 C 文件的 Hello World 工程,完成编译、调试和部署流程,并介绍这背后 T orizon Extension 是如何生成 SDK 容器、目标设备的 release / debug 版本容器以及如何执行编译、调试和部署任务。 首先在 V isual Studio Code 中创建一个 C/C++ 项目,选择 arm64v8-debian-no-ssh_bullseys 平台。 创建完毕后,点击 V isual Studio Code 中 Terminal → New Terminal , 点击右边 Torizon IDE backend process 标签 。在这个终端中会输出 SDK 容器构建时后台日志。 当 SDK 容器构建成功后,可以在开发电脑的终端上输入下面命令,查看正在运行的容器。 -------------------------------------- $ docker container ls -------------------------------------- 这里名为 keen_yalow 即为 Torizon Extension 生成的 SDK 容器。使用下面命令进入 SDK 容器,在容器的 /workspaces/HelloWorld 位置可以看到 Hello World 项目文件,包括 C 文件等。 -------------------------------------- $ docker exec -it keen_yalow bash root@dell-5400:/# cd /workspaces/HelloWorld/ root@dell-5400:/workspaces/HelloWorld# ls HelloWorld.c appconfig_0 -------------------------------------- 除了开发电脑的终端里使用 docker 命令进入 SDK 容器外,还可以在 Visual Studio Code 中 Terminal → New Terminal 新建一个 bash 终端直接进入 SDK 容器,同样也可以看到项目文件。 这些项目文件同样也位于 Visual Studio Code 的文件浏览器中,用户可以直接对其进行修改。 在开发电脑的终端中使用下面命令,看到 SDK 容器中 /workspaces/HelloWorld 目录实际上 bind 到开发电脑上创建项目时选择的保存目录。 -------------------------------------- $ docker inspect keen_yalow -------------------------------------- 回到 Visual Studio Code 中打开的终端中,可以看到 S DK 容器中已经安装了 aarch64-linux-gnu-gcc 等交叉编译工具。这是 Torizon Extension 在创建 SDK 容器时自动安装的工具。用户无需手动安装。 那么 Torizon Extension 是如何构建该 SDK 容器呢?得益于 Torizon Extension 的开源 ,我们可以一窥其中究竟。在一开始使用 V isual Studio Code 创建一个 C/C++ 项目,我们选择了 arm64v8-debian-no-ssh_bullseys 平台, T orizon Extension 则使用 对应的模板文件 。其中的 sdk.dockerfile 就是用于生成 SDK 容器的 docker file 文件,以及 config.yaml 配置文件。 sdk.dockerfile 中的 FROM #%platform.sdkbaseimage%# 指定 SDK 容器构建的基础容器镜像, sdkbaseimage 的配置位于 config.yaml 文件,即使用 torizon/debian:2-bullseye 。 -------------------------------------- baseimage: common: - "torizon/debian" - "2-bullseye" -------------------------------------- 然后通过 apt-get 命令继续安装其他调试所需的软件,如 gdb-multiarch 等。在 sdk.dockerfile 中还有一些其他参数,如 #%application.sdkpreinstallcommands%# ,这就允许用户使用 dockerfile 文件编写语法在构建 SDK 容器时能够添加其他的软件和第三方库文件,甚至修改 SDK 容器本身的配置,如 更改 de bian 下载源 。这些具体的参数可以参考 这里 。当然用户并不需要直接修改这里的配置文件,在 V isual Studio Code 的 T orizon Extension 插件中可以十分方便地进行更改。 至此,我们已经阐释了 T orizon Extension 是如果构建 SDK 容器。用到的工具大致和传统嵌入式 L inux 开发类似,只是这操作都由 T orizon Extension 在容器内完成,并为用户提供灵活的配置功能。上述过程总体如下图。 接下我们将看看编译是如何完成的。 应用程序编译 点击 Ter build_debug 即开始编译 Debug 版本的应用编译。编译的时候在 V isual Studio Code 可以看到下面输出日志。 在 SDK 容器中实际上执行了 aarch64-linux-gnu-gcc -g HelloWorld.c -o HelloWord 命令对 C 进行编译。本文创建项目是选了单个 C 文件的项目类型,所以编译时仅使用 HelloWorld.c 。 进入 SDK 容器的 /workspaces/HelloWorld 目录,我们可以看到编译生成的二进制可执行文件 HelloWorld 。它是针对 ARM aa rch64 处理器进行交叉编译的。 之所以能够完成上述编译任务,是由于在创建项目时, Torizon Extension 自动生成了 V isual Studio Code 配置文件 tasks.json 。这里定义了 build_debug 任务具体执行的命令参数。用户因此只需要像上面提到的步骤,点击 build_debug 即可开始编译。 编译总体过程如下图。 完成编译后,接下来就是开始调试。 应用程序调试 在编译完成后点击 F 5 开始调试用于程序。在 Torizon 上应用是以容器形式运行的,调试过程实际上将应用打包成一个可以在目标设备上运行的容器,并在容器里面启动调试工具。所以在按 F5 之后, Torizon Extension 会开始构建一个包含应用的新容器。 当容器构建完毕并部署到目标设备后在 V isual Studio Code 会进入单步调试界面。 此时我们查看设备上的的镜像 Images 和容器 Containers 会发现一个 debug 版本的 helloworld 镜像和一个正在运行的 debug 版本的 helloworld 容器。这些就是刚才按 F 5 开始调试时, Torizon Extension 自动部署过来的。 目标设备上的容器和开发电脑上的 SDK 容器之间通过 gdb 实现调试功能。使用 SSH 登录目标设备,运行下面命令查看正在运行的容器信息。 -------------------------------------- $ docker container ls -------------------------------------- 目标设备上 0.0.0.0 :3 2768 映射到容器内部的 6502 端口。 查看容器 ID 并进入容器。通过 ps 命令列出容器中所有运行的进程。这里可以看到有个 gdbserver 进程正在运行并监听 6502 端口。通过该进程,连接开发电脑上 SDK 容器中的 gdb 客户端从而实现 Visual Studio Code 的调试功能。 同样在开发电脑查看 SDK 容器内运行的进程,其中另一个是 gdb-multiarch 就是 gdb 客户端。 那么现在我们将揭示 Tor izon Extension 是如何为目标设备构建 debug 版本容器,以及在 VS Code 中是如何启动调试任务的。我们回到 Tor izon Extension 的开源项目中的 arm64v8-debian-no-ssh_bullseye 模板。其中 debug.dockerfile 就是用于生成 debug 版本容器的文件。 首先, FROM --platform=#%platform.architecture%# #%platform.baseimage%# 指定了 debug 版本容器使用的基础镜像,对应在 config.yaml 中配置属性参数。 EXPOSE 6502 指定 gbdserver 监听端口。接下来 apt -get 命令安装 gdbserver 。这期间还可以通过 #%application.extrapackages%# 来安装第三方软件和库文件,这些具体的参数可以参考 这里 。最后设置 debug 版本容器启动时运行的命令, CMD stdbuf -oL -eL gdbserver 0.0.0.0:6502 /#%application.appname%#/#%application.exename%# #%application.appargs%# 。这里看到启动 gdbserver ,并可以接收执行应用程序的参数。最终通过该容器即可在目标设备上启动一个 debug 版本容器,并在容器内使用 gdbserver 与 VS C ode 连接,调试用户应用程序。 仔细的你可能发现, debug.dockerfile 中并没有将编译好的应用二进制文件包含进来。使用 SSH 登录目标设备,运行下面命令查看正在运行的容器的详细信息。 ec85215a7553 为正在运行的 debug 版本容器 ID 。 -------------------------------------- $ docker container inspect ec85215a7553 -------------------------------------- Torizon 系统的 /home/torizon/HelloWorld 目录被映射到 debug 版本容器的 /HelloWorld 目录。 T orizon Extensio n 通过 rsync 将开发电脑上编译好的二进制文件 HelloWorld 复制到目标设备上默认用户 toriozn 的用户目录下 /home/torizon/HelloWorld 。因此在上面的截图中我们能够看到 debug 版本容器中 gdbserver 其指向的应用路径位于 /HelloWorld/HelloWorld 。之所以不将二进制应用直接放入容器的原因是,在调试期间用户应用代码可能会经常做修改,采用这样的方式只需要重新编译应用代码本身,而不必重新构建和部署整个 debug 版本容器。重新编译生成的二进制文件可以重新映射到原来的容器,这样的设计能够极大提高了调试效率。 在 Visual Studio Code 上按 F 5 启动调试时,该任务是在 launch.json 文件中配置的。这里指定 g db 作为调试器,具体为 gdbmultiarch 。详细说明可以参考 VS Code Configuring C/C++ debugging 。 调试总体过程如下。 最终应用部署 当完成应用调试后,通常生成一个 release 版本最终发布容器。在上面的 debug 版本容器虽然已经包含了用户开发的应用,但也包含了 gdbserver 等额外的工具,并且应用通过 gdbserver 启动。这样的容器显然不适合用于最终的发布。因此我们需要在 VS Code 依次点击 Torizon: Deploy release container 生成一个包含用户应用的最终发布版本容器。 进行上面步骤前,在 Torizon Extension 插件中我们先删除之前已经部署和运行的 de bug 版本镜像和容器。 然后完成上述构建并部署 发布版本容器。在开发电脑的终端上使用 docker images 可以查看到生成的发布版本容器。相比于 debug 版本容器,由于不再安装部分软件,因此发布版本容器体积也会小一些。 部署完毕后,在目标设备上也可以看到这个 发布版本容器。 如果你已经看到这里,恭喜,你已经了解 Torizon Extension 后台运行的大部分过程。现在只剩下发布版本容器是如何构建的。这部分则相对简单些。继续回到 Tor izon Extension 的开源项目中的 arm64v8-debian-no-ssh_bullseye 模板。其中 release.dockerfile 就是用于生成发布版本容器的文件。发布版本容器使用的基础镜像和 debug 版本一样, FROM --platform=#%platform.architecture%# #%platform.baseimage%# ,但不再使用 apt-get 安装 gdbserver 等工具。用户指定的第三方软件和库通过 "#%application.extrapackages%#" 仍可以安装。 COPY work/#%application.appname%# /#%application.appname%# 将之前编译好的二进制应用文件复制到容器中。最后设置容器启动时运行的命令 CMD /#%application.appname%#/#%application.exename%# #%application.appargs%# 。这里我们看到发布版本容器已经不再像 debug.dockerfile 使用 gdbserver 启动应用,而是直接执行用户的应用。这是发布版本容器和 debug 版本容器最大的不同。 当通过 VS Code 运行发布版本容器后,在 Torizon Extension 界面中可以看到该容器相关信息。由于演示代码中执行完 printf 函数后程序会直接退出,因此这里看到的容器是停止状态。在下方容器信息窗口中,默认运行的程序是 /HelloWorld/HelloWorld ,即上通过 release.dockerfile 生成容器时所添加的二进制文件。 构建和部署发布版本容器总体过程如下。 总结 Torizon Extension 简化了在嵌入式系统上使用容器进行应用开发,在代码编写、调试、部署各个阶段都提供了丰富的功能,采用容器形式的开发方式也有别于传统嵌入式 L inux ,希望通过本文的介绍能够使您了解 Torizon Extension 背后的任务流程,从而更好地使用 Torizon 。当然 Torizon Extension 的功能也远不止如此。