返回

Windows交叉编译Linux GCC/NVCC详解: MinGW, Docker, WSL

windows

Windows环境下交叉编译 Linux GCC 和 NVCC

构建可以在Windows上运行并生成Linux可执行文件的GCC或NVCC,是许多开发人员面临的挑战。此过程并不简单,因为它涉及到目标平台的体系结构和库兼容性。此文章讨论此问题,分析根本原因,并给出相应的解决方案。

挑战:直接在Windows上重编译Linux版GCC

使用MinGW G++ 或 VSC++编译Linux版GCC的源代码,并生成可在Windows下执行的编译器,从概念上讲是可行的,但实际上很复杂。GCC是一个庞大的项目,其编译过程高度依赖于目标平台(此处即 Linux)特定的系统库和头文件。

直接用Windows的编译器来编译 GCC,会导致很多错误,例如缺少必要的POSIX兼容层、路径处理差异以及体系结构兼容性问题。最重要的是,生成的可执行文件运行的目的是调用 Linux 操作系统服务,而在 Windows 环境中这些服务不可用。简单来说,Windows 版的编译器不可能生成原生在Linux上跑的应用。

可选方法:交叉编译环境

解决这一问题,关键是搭建一个交叉编译环境

交叉编译意味着在一个平台(构建主机)上生成另一个平台(目标主机)的可执行代码。 对于我们的情况,构建主机是Windows,目标主机是Linux。这里主要涉及两种常用的交叉编译方法:

  • MinGW-w64 工具链: MinGW-w64项目提供了一组针对Windows的工具链,可以生成适用于不同目标体系结构(包括Linux x86_64)的可执行文件。

  • 使用 Docker: 通过Docker创建Linux容器,其中包含了需要的编译工具链,并将代码在Linux环境下编译。

解决方案: MinGW-w64 交叉编译

使用 MinGW-w64 提供的工具链, 可以方便地在Windows环境下编译出可在Linux上运行的GCC。 流程如下:

  1. 安装 MinGW-w64: 从 MinGW-w64 的官方网站下载适合你的 Windows 平台的版本。安装时,注意选择正确的体系结构(x86_64 for Linux 64-bit), 并选择 'posix' 或 'seh' 线程模型。 此外, 安装目标环境需设置为x86_64-w64-mingw32或者x86_64-w64-mingw32,具体取决于构建目标。

  2. 设置环境变量: 将 MinGW-w64 安装目录中的bin 文件夹添加到系统的 PATH 环境变量中。这样,就可以直接在命令行使用交叉编译工具链。

  3. 编写简单的测试代码(hello.c):

    #include <stdio.h>

    int main() {
        printf("Hello from Linux!\n");
        return 0;
    }
  1. 使用交叉编译器编译: 在命令提示符下,运行以下指令:
    x86_64-w64-mingw32-gcc hello.c -o hello-linux
这行指令使用`x86_64-w64-mingw32-gcc` (为Linux x86-64 平台设计的 GCC 交叉编译器),编译`hello.c` 文件并生成可执行文件`hello-linux` 。这个文件是为Linux平台构建的,不应在Windows直接运行。
  1. 验证(在Linux系统下): 将编译出来的hello-linux文件传送到Linux环境下,添加执行权限(chmod +x hello-linux) ,执行文件,即可看到预期的输出。

    该方法提供了一个可靠的途径来生成能在Linux环境下运行的可执行文件, 而无需在Linux系统中安装编译环境。

额外安全提示:

确保下载 MinGW-w64 工具链的来源可靠,防止下载恶意软件。此外,应当小心管理生成的 Linux 可执行文件,确保它们的来源可靠,避免运行时执行恶意代码。

挑战:NVCC 在Windows环境下的局限

NVCC是NVIDIA CUDA 编译器的驱动程序,它将 CUDA C/C++ 代码编译为能在NVIDIA GPU上运行的指令。 Windows 版本的NVCC 通常依赖于 Visual Studio 的 cl.exe 编译器, 而且,其核心组件构建时也会对 Windows 特定路径有依赖。试图将其链接至其他编译器(如 MinGW G++),往往会引发兼容性问题,因为它和 Linux 版本的 CUDA Toolkits 的编译逻辑完全不同。

此外, NVCC 本身也会调用底层的CUDA工具链。 NVCC 的目标就是产生一个可执行程序。 所以,如果要用 Windows 环境下NVCC编译出的文件能在 Linux 下运行,就需要找到一个 Linux 环境下能够产生可在Linux 上跑的可执行程序编译器(通常是 gcc),并且告诉Windows下的NVCC使用Linux下的gcc来编译,但这并非易事。

解决思路:利用 WSL 或 Docker

Windows 下 NVCC 为何不能编译Linux下的可执行程序,本质上是缺少 Linux 的执行环境。 所以解决方案的方向便是搭建一个模拟Linux环境进行编译。 目前推荐的主要思路就是利用 Windows 下的 WSL(Windows Subsystem for Linux) 或者 Docker 环境:

  1. 安装 WSL 或 Docker: 两种方式都可以构建一个隔离的Linux环境。 WSL安装配置相对简单,可以直接访问Windows下的文件系统。Docker需要自行构建一个含有cuda的镜像,稍复杂一些,但是更灵活和可移植。

  2. 在 WSL 或 Docker 环境安装 Linux 版本的 CUDA Toolkits:
    下载并安装与 Linux 发行版匹配的CUDA Toolkit。

  3. 在 WSL 环境或 Docker容器内 使用NVCC编译: 使用 Linux 版本的 NVCC 和对应的编译工具链, 可以直接编译并产生为 Linux 目标设计的 CUDA 代码。 比如在 Docker 中,可执行命令:

docker run --gpus all -it --rm -v $(pwd):/app -w /app  nvidia/cuda:12.0.1-devel-ubuntu22.04 \
 bash -c "nvcc your_cuda_file.cu -o your_cuda_executable -arch=sm_70 && ./your_cuda_executable"

 # 或者简化写法:
 docker run --gpus all -it --rm -v $(pwd):/app -w /app nvidia/cuda:12.0.1-devel-ubuntu22.04 nvcc your_cuda_file.cu -o your_cuda_executable -arch=sm_70
在上面命令中:
* `--gpus all`  指定 Docker 可以访问宿主机的全部 GPU 资源。
*  `-it --rm`  启用交互模式并用完后自动移除容器。
*  `-v $(pwd):/app -w /app` 将主机当前目录挂载到 Docker 容器的 `/app` 目录并切换工作目录。
*   `nvidia/cuda:12.0.1-devel-ubuntu22.04` 使用 Nvidia 官方提供的包含了 CUDA 环境和 Ubuntu 22.04 的镜像,请根据你的需求选择正确的 CUDA 镜像版本和 Linux 系统版本。
*   `nvcc your_cuda_file.cu -o your_cuda_executable -arch=sm_70` 利用 Linux 版 NVCC 编译。
 其中`-arch=sm_70`  指明了目标 GPU 的架构, 根据自己GPU 的架构进行相应修改。
* `&& ./your_cuda_executable`  表示编译完后执行程序 (只有测试需要, 正式应用可以去掉).

 同样, 你可以在 WSL 环境执行编译过程:

 ```bash
 nvcc your_cuda_file.cu -o your_cuda_executable -arch=sm_70
 ./your_cuda_executable
 ```

  以上代码中,`your_cuda_file.cu` 为你的CUDA源代码文件。 `your_cuda_executable`为可执行文件的名称。

额外安全提示:

务必从官方网站下载Docker镜像并避免使用不明来源的第三方镜像。

综上, 直接在Windows环境中编译 Linux 的 GCC 或者NVCC 是比较复杂的,我们需要理解背后核心原理。 使用交叉编译工具链 或者 模拟 Linux 环境的方式是较可行的解决方案, 在实践过程中也应该时刻保持安全意识。