ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Windows上打出arm64 deb包:三处易错点与完整避坑指南

Windows上打出arm64 deb包:三处易错点与完整避坑指南 1. 先搞清楚我要打的到底是什么deb 包里的架构藏在哪三层说出来你可能不信我是在一台 Windows 11 办公机上给一台 arm64 的 Linux 服务器打出了这辈子第一个 arm64 的 .deb 安装包。听起来不算难但真正做完回头看我发现整个过程踩掉的坑几乎都集中在三件事上——而且这三件事我当时在开始前全都想错了。先说背景。目标机器是一台 ARM 架构的 Linux 云主机跑的是 Debian 系系统交付方式要求是 .deb 包。而我手头没有可以直接登录的 ARM 机器只在 Windows 上有代码仓库和构建脚本。原本以为“windows 上打出 arm64 的 deb 包”就是开个 Linux 容器、把二进制交叉编译一下、塞进包里改个架构字段一个下午肯定能搞定。结果折腾到晚上才发现我还是太乐观了。问题出在哪在于很多人对“deb 包”的理解仍然停留在“一个安装包”的层面。实际上 deb 并不是某种编译产物它只是一个 ar 格式的归档容器里面装着两份 tar 打包的文件一份是控制信息control.tar.包括 control、postinst、preinst 这些一份是实际要安装的文件data.tar.也就是 usr/bin、etc/systemd/system 这些路径。所谓“这是 arm64 的包”并不是由包后缀名决定的也不是由包目录名决定的而是同时由好几个层面的信息共同决定。我习惯把这件事拆成三层来看这也是排查时最省心的框架层面看什么常见命令期望结果包描述层control 文件里的 Architecture 字段dpkg-deb -I xxx.debArchitecture: arm64二进制层可执行文件的 ELF 机器类型file xxxreadelf -h xxxARM aarch64Machine: AArch64动态依赖层二进制依赖的 so 库以及后续 dpkg 的依赖计算ldd xxxdpkg-shlibdeps依赖全部指向 arm64 库三层之间会互相影响但又是独立的。第一层错了包在安装时就会被 dpkg 拒收第二层错了包能装上但一运行就报 Exec format error第三层错了包装上后可能在运行时崩溃或者依赖计算完全对不上。我当时犯的每一个错误基本都对应了这三层之间的某一张“认知断层”。1.1 你眼中的“架构”和打包器眼中的“架构”不是一回事先说一个特别容易误导新手的点deb 包的架构打包器其实默认只认 control 文件里写的 Architecture 字段。这句话有两层意思。第一层意思是如果你用 dpkg-deb --build 打包而 control 文件里没有写 Architecture那么 dpkg-deb 会自动用当前构建机器的架构来填。也就是说你在 Windows 上的 amd64 Debian 容器里打包什么都不写最后得到的包几乎一定是 amd64 的。这不是 bug是 dpkg 的默认行为。第二层意思是dpkg 在安装时检查架构也是看这个字段。它并不关心 data.tar 里那个二进制文件到底是不是 arm64 的。包内二进制是什么架构dpkg 根本不管它只负责把文件解压放到指定路径、跑控制脚本。所以在“包管理器视角”下Architecture: arm64 的包一定能装到 arm64 系统上哪怕包里的二进制实际上还是 x86_64。这就会产生两个截然不同的失败模式。我后面会专门讲但你得先意识到deb 这个格式本身没有“编译”的概念它只负责“搬运”文件。你真正要保证的是搬运进去的文件确实是 arm64 可执行文件。1.2 为什么在 Windows 上做这件事更容易想错还有一个容易被忽略的差异在 Windows 宿主机上你根本没办法直接执行 file、readelf、ldd 这些检查工具甚至连 dpkg-deb 都没有。所以你所有的判断都必须绕一层先进 Linux 容器或者 WSL2 再操作。中间多了一层“环境翻译”就多了一层想当然的空间。我当时的第一反应是“先把包打出来再说”。这个顺序本身就错了。正确顺序应该是先在交叉编译或模拟构建阶段确认二进制架构再把打包当成一个收尾动作。换句话说二进制架构是源头deb 包只是上游产物。你在 Windows 上折腾一阵子最后发现打出的是 amd64 包问题基本都出在“源头上没管好架构”而不是“打包命令写错了”。这个认知不摆正后面三个坑你一个都躲不掉。2. 第一个想错的地方Architecture 写对了不代表二进制就是 arm64我第一次打包时做法非常简单粗暴用 gcc 默认编译出 Linux x86_64 的二进制然后手工写了 Architecture: arm64 的 control 文件用 dpkg-deb --build 打包文件名也改成了 myapp_1.0.0_arm64.deb。我当时觉得这就是 arm64 的 deb 包了。传过去之后安装确实成功了因为 dpkg 只看 control 字段。结果运行的时候直接报cannot execute binary file: Exec format error这条报错基本就是“你手上的文件不是本机架构能执行的”最直白的翻译。我当时第一反应是检查 Docker 环境、检查依赖库结果都没问题。最后用 file 看了一眼那个可执行文件瞬间破防myapp: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2好家伙打包了半天打了个寂寞。问题的根子在于我把“deb 是 arm64 的”和“里面的二进制是 arm64 的”这两件事混为一谈了。deb 只是壳壳上的标签写错了会立刻被拆穿标签写对了但内容不对反而更隐蔽——它能装上能让你觉得“成功了一半”然后在你真正跑起来的时候给你致命一击。2.1 两种失败模式越隐蔽越危险我把这个阶段的失败分成两种。第一种是 Architecture 字段还留在 amd64 没改安装时 dpkg 会直接拒绝dpkg: error: package architecture (amd64) does not match system (arm64)这种失败虽然让你白忙一趟但至少报错清晰马上能定位。第二种就是我上面踩到的Architecture 改成 arm64 了但二进制没变安装完全顺利跑的时候才炸。这种失败更阴险因为报错出现在运行时而且 Exec format error 这种提示很容易让人误判成“权限问题”“文件损坏”或“动态库缺失”。另外还有一种更冷门但真实存在的情况postinst、prerm 这类控制脚本本身是 shell 脚本脚本解释器是 /bin/sh脚本内容不挑架构所以脚本本身没问题。但脚本里如果执行了包内安装的二进制那这个二进制就会立刻触发架构问题。我在另一个项目里就见过 postinst 里调用了包内的 helper 工具做数据库迁移结果核心包能装脚本跑到中途才 Exec format error排查起来比二进制直接跑不了还费劲。所以检查范围不能只看主程序包内所有 ELF 文件都要查。2.2 把架构检查做成构建的一部分在 Windows 上绕一层容器做检查其实不麻烦关键是顺序要对。我现在的做法是在构建容器里编译完二进制之后紧接着就执行三连检查file bin/myapp readelf -h bin/myapp | grep -E Class|Machine|Type期望的结果分别是ELF 64-bit LSB executable, ARM aarch64Class: ELF64Machine: AArch64如果确认没问题再把它塞进 deb 包的目录结构里去打包。检查脚本直接嵌进打包脚本里只要查出二进制不是 aarch64整个打包过程就中断。不要依赖肉眼去认名字也不要依赖自己“记得”设置了 --host 参数。机器不会骗人file 的输出才是唯一真相。打包目录最小结构长这样myapp-1.0.0/ ├── DEBIAN/ │ └── control └── usr/ └── bin/ └── myappcontrol 里关键字段Package: myapp Version: 1.0.0 Architecture: arm64 Maintainer: Your Name youexample.com Description: myapp for arm64然后打包dpkg-deb --build --root-owner-group myapp-1.0.0 myapp_1.0.0_arm64.deb这里--root-owner-group很关键。Windows 和 Linux 容器里的用户 ID 通常不一致不加这个参数打包包内文件属主可能变成乱七八糟的 uid传到目标机上安装后文件的 owner 全是错的。加上这个参数dpkg 会强制把包内文件 owner 设为 root:root这是发布 deb 包的常规操作。2.3 控制脚本不挑架构但包里的程序挑再强调一次这次的教训核心架构标签是给包管理器看的真正决定能不能跑的是 ELF 文件本身。deb 的 format 是平台无关的但它的内容载荷是平台强相关的。你可以在 x86 机器上轻松制作一个 arm64 的包前提是你塞进去的二进制必须是 arm64 编译产物或者交叉编译产物。改了 Architecture 而不改二进制等于给罐头贴错了保质期吃坏肚子是迟早的事。3. 第二个想错的地方Docker 能跑 arm64 镜像不是 Docker 的默认能力解决了二进制架构问题之后我以为剩下的就是“交叉编译一下就行”。但很快遇到了第二个认知坑环境。我在 Windows 上做 Linux 构建默认都是用 Docker 开一个 Linux 容器因为这条路最省事。一开始我甚至想用现成的 arm64 容器直接编译当时心想 Docker 不是号称跨架构吗于是直接在命令行里敲了docker run --rm --platform linux/arm64 -t debian:bookworm-slim bin/bash如果是 Docker Desktop这命令确实能跑容器里的 uname -m 会告诉你 aarch64。但请注意这件事并不是“Docker 天然支持”。它背后藏着一套机制binfmt_misc 和 QEMU 用户态模拟。Docker Desktop 在启动自带的 Linux 虚拟机时会自动注册 qemu-aarch64 到系统的 binfmt_misc 里。内核看到一个 ELF 文件是 AArch64就会自动把它交给 qemu-aarch64 来翻译执行。你感知不到这层翻译你只知道容器成功启动了程序的行为跟原生 Linux arm64 一模一样。问题是“自动注册”只在 Docker Desktop 这种完整封装环境里存在。换成别的 Windows Docker 运行时就不是这么回事了。3.1 Docker Desktop 替你做的事binfmt_misc 与 QEMU你可以把 binfmt_misc 理解成内核的一个“文件格式路由表”。正常情况下Linux 内核遇到可执行文件会看它的 ELF header 来解析但不会执行其他架构的 ELF。binfmt_misc 允许你注册一个“解释器”当内核发现某个文件的开头特征匹配就把文件整体交给指定的解释器程序执行。QEMU 用户态模拟qemu-aarch64 / qemu-aarch64-static就是这个解释器。它被注册到 binfmt_misc 之后你在 x86_64 的 Linux 内核里执行 arm64 ELF内核见怪不怪直接把执行权交给 QEMUQEMU 在用户态翻译执行 arm64 指令。容器里的视角是“我就是一个普通的 arm64 Linux 环境”实际上翻译发生在 QEMU 层面。这就是为什么--platform linux/arm64在 Docker Desktop 上“有效”。有效的前提是注册了 binfmt。如果没注册Docker 能拉取镜像、能解包、能尝试启动容器但容器里第一个进程/bin/sh就已经是 arm64 ELF 了内核根本不认识启动瞬间就是standard_init_linux.go: exec: /bin/sh: exec format error我当时第一次在非 Desktop 环境里看到这行报错时第一反应是拉去重装 Docker。后来冷静下来才意识到不是 Docker 坏了而是 Docker 背后的“翻译层”没了。3.2 换成 Docker Engine、CI Agent 就翻车这句话对很多 Windows 用户来说不容易有体会因为大家用的基本都是 Docker Desktop。但只要环境一变问题就来了。比如在一个 Windows Server 上直接装了 Docker Engineexperimental 的 Windows 容器模式在 Jenkins agent 上装了 Docker但后端是一个自定义的 Linux 虚拟机镜像仓库又是内网的用了 docker-machine 或者远程 Docker context连接到一个精简 Linux 主机。这些环境里绝大多数没有自动注册 binfmt_misc也没有 qemu-aarch64。你执行同样的docker run --platform linux/arm64大概率就是报错或直接拉取失败。另一个常见报错是no matching manifest for linux/arm64/v8 in the manifest list entries这说明镜像仓库里根本没有对应 arm64 架构的镜像层。官方 Debian/Ubuntu/alpine 镜像都做了 multi-arch manifest所以不报这个错但很多公司内网镜像、第三方老镜像只有 amd64 版本这时候光有 binfmt 也没用因为找不到 arm64 层可拉。我当时就陷入了这种尴尬Windows 办公机上的 Docker Desktop 没办法直接连公司内网仓库CI agent 上又有全套 Docker Engine 但没有 binfmt。结果在办公机本地跑 arm64 容器玩得飞起一到 CI 上马上崩。这种环境差异带来的割裂感比打包本身还折磨人。3.3 5 秒钟判定当前环境能不能跑 arm64 容器现在我不猜了先跑一条命令判定docker run --rm --platform linux/arm64 alpine uname -m如果环境正常输出是aarch64。如果输出报exec format error就说明这个 Docker 环境没有 binfmt 翻译层。如果是no matching manifest说明镜像仓库缺少 arm64 的 manifest。需要补翻译层的话在 Linux 环境里可以手动注册docker run --privileged --rm tonistiigi/binfmt --install arm64这个命令会让一个特权容器帮你往宿主内核的 binfmt_misc 里写入 qemu-aarch64 注册项。执行完之后再跑上面的 uname 判断命令通常就能通了。注意--privileged在有些受限环境里不一定能跑如果连这个容器都执行不起来那基本可以放弃在这个环境模拟 arm64 容器了。如果 binfmt 注册后还是拉不到 arm64 层那问题就在于镜像源。这时候要么换官方 multi-arch 镜像要么自己准备一个包含基础工具链的 arm64 镜像推到内网仓库。没有其他捷径。3.4 实在不行直接退回 WSL2 里的一套 Docker还有一个经常被 Windows 用户忽略的“保底方案”WSL2。WSL2 本身就是跑在 Windows 里的轻量级 Linux 虚拟机内核是真实的 Linux 内核。在 WSL2 的发行版里安装 Docker Engine然后通过 WSL2 的 Docker context 让 Windows 上的 docker 命令指向它本质上你就拥有了一台“不折腾 binfmt 也随时能自己装解释器”的 Linux 主机。具体做法不复杂WSL2 里apt install docker.io qemu-user-static binfmt-support然后启动 Docker。以后从 Windows 侧执行docker --context wsl或者直接wsl docker ...都能调用到 WSL2 里的 Docker。这样即使 Docker Desktop 出幺蛾子你还是有一条完整的 Linux 构建通道。代价是文件挂载路径稍微绕一点但换来的是架构模拟和工具链安装的自由度值。我当时因为公司策略不能随便用 Docker Desktop最后就是靠着 WSL2 里自装 Docker 解决了 buildx 和 binfmt 的一致性问题。Windows 表面上还是 Windows但背后的活全是 Linux 干的。4. 第三个想错的地方交叉编译不是万能钥匙复杂 C/C 会被依赖反噬环境通道打通了我当时觉得自己已经是个成熟的跨架构工程师了。于是开始想交叉编译。交叉编译这事不同语言之间的体验差距堪比“开车”和“骑马”。4.1 交叉编译最顺的和最不顺的项目最顺的是 Go。只要你的项目没有启用 cgo交叉编译基本就是一条命令GOOSlinux GOARCHarm64 CGO_ENABLED0 go build -o myapp .在 Windows 的 PowerShell 里写法略有不同$env:GOOSlinux $env:GOARCHarm64 $env:CGO_ENABLED0 go build -o myapp .Go 静态链接的特性在这里简直太救了只要 CGO_ENABLED0生成的可执行文件不依赖目标系统的动态库拷到任何 Linux arm64 系统都能跑。deb 包里甚至不需要额外声明 Depends安装路径丢进去就能用。Rust 的体验也类似加一个 target 就行rustup target add aarch64-unknown-linux-gnu cargo build --target aarch64-unknown-linux-gnu --release但一旦项目是 C/C事情就没这么优雅了。我当时手里正好有一个用了 libcurl、openssl 和一堆 autotools 的 C 项目。我以为装个交叉编译器就能搞定apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu ./configure --hostaarch64-linux-gnu make结果 configure 阶段就开始出问题。autotools 项目里很多AC_RUN_IFELSE宏会在配置期间编译一个小程序并实际运行它用来探测系统属性。交叉编译时这些小工具是 arm64 的在 x86_64 的构建机上根本跑不起来。configure 脚本拿不到探测结果只能默默当作“不支持”处理各种宏定义错位最后生成出来的代码行为跟你在 x86_64 上原生编译完全不是一回事。这是交叉编译最操蛋的地方不是编译不通过而是编译“假通过”。你以为你用了 aarch64 编译器实际上整个构建系统因为无法运行目标程序已经静默地砍掉了一堆功能。4.2 三个典型的交叉编译翻车点一个一个说第一configure 探测程序执行不了。刚才说了autotools 会在配置阶段运行交叉编译出来的可执行文件。交叉编译配置项里有个--build和--host的概念build 表示当前构建机架构host 表示目标运行架构。configure 需要同时知道这两个才能在必要时用 build 架构的编译器编译“探测程序”用 host 架构的编译器编译“最终程序”。很多老项目、老版本的构建脚本对多架构支持并不好只给你留了--host没有--build的自动推导结果探测程序是 arm64 的执行时直接被 binfmt 拒之门外——除非你的构建机自己也注册了 qemu那就另说。但这就绕回第二节的坑了。第二依赖库版本不对。C 项目几乎不可能不依赖第三方库。你 apt 安装的是 amd64 的 libcurl-dev、libssl-dev头文件倒是有但链接时交叉编译器按自己的 sysroot 找 .so/.a大概率找到的是/usr/lib/x86_64-linux-gnu下的宿主库或者在交叉 sysroot 下找不到任何库。这时候常见的误导是编译期靠头文件混过去了链接期也可能因为各种隐式路径瞎凑合过去最后产物的 dynamic section 里多了一条指向 x86 库的 NEEDED。等到 arm64 目标机上安装完一跑就是error while loading shared libraries。第三dpkg-shlibdeps 的依赖计算会骗人。如果你用 dpkg-buildpackage 构建它会跑 dh_shlibdeps通过读取二进制文件的动态段来计算 Depends 字段。交叉编译产物如果混进了 x86 库或者文件里记录的 GLIBC 版本符号和 arm64 版本对不上生成的依赖清单就完全是错的。轻则多余的依赖重则缺依赖、也没法在 arm64 系统上正确安装。4.3 我最后用的其实是模拟构建这条路在交叉编译和模拟构建之间反复横跳之后我的结论很简单对于没有复杂外部依赖的小型项目交叉编译很香对于有大量 C/C 依赖的复杂项目与其在交叉编译里跟依赖库死磕不如直接在 arm64 容器里做“模拟原生构建”。模拟原生构建的方式很简单假设你已经确认 Docker 环境能跑 arm64 容器按第三节那条 uname 命令测直接进容器docker run --rm --platform linux/arm64 -v $(pwd):/build -w /build \ debian:bookworm-slim bash进容器后你的感觉就是一台原生 arm64 Debian 机器。apt 装什么依赖装的就是 arm64 版本的库configure 的探测程序要运行就运行QEMU 在底下兜着不会报 Exec format errordpkg-shlibdeps 算出来的依赖也天然和 arm64 目标对得上。付出的代价无非是 QEMU 翻译层的性能损失大概比原生慢 5 到 10 倍编译个大项目确实要等但换来的是“行为和最终环境一致”。如果你没有 Docker Desktop、也没有能跑 arm64 容器的环境还有一个很冷门的备选自己用 debootstrap 做一个 arm64 的 rootfs配合 qemu-aarch64-static 进 chroot 构建。大致流程是# 在任意 Debian/Ubuntu x86_64 容器或 WSL2 里执行 apt install debootstrap qemu-user-static binfmt-support debootstrap --archarm64 bookworm /arm64-rootfs http://deb.debian.org/debian cp /usr/bin/qemu-aarch64-static /arm64-rootfs/usr/bin/ chroot /arm64-rootfs /bin/bash进了 chroot 之后同样是一个“原生 arm64 用户态”。这种方式适合在没有 Docker 的受限环境里搭一套离线构建环境。麻烦是文件系统和网络隔离很原始不如容器干净但原理完全一样。我最后在 Windows 上的组合拳就是Go 的小服务全部走交叉编译C/C 大项目进 arm64 容器模拟原生构建所有产物最后统一走同一个验证流程。这是试错三天后沉淀下来的最稳路径。5. 我现在在 Windows 上打 arm64 deb 的固定流程可直接抄前面讲了那么多坑我把最终沉淀下来的标准流程完整放一遍你照着走基本不会再踩我再踩过的雷。5.1 准备一个干净的构建容器我会在 WSL2 里先启动一个 amd64 的 Debian 容器作为“打包器宿主”。这个容器负责所有打包工具链dpkg-dev、dpkg-deb、file、readelf。目标机如果是 Debian 12基础镜像我就用debian:bookworm-slim如果是 Ubuntu就选对应版本的 Ubuntu。docker run -it --rm -v $(pwd):/build -w /build debian:bookworm-slim bash容器里装齐工具apt update apt install -y dpkg-dev file binutils build-essential # 如果走交叉编译额外装这个 apt install -y gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu libc6-dev-arm64-cross为什么这里我刻意用 amd64 容器做“打包宿主”而不是直接用 arm64 容器很简单打包动作dpkg-deb、tar、control 文件组织本身不依赖目标架构用原生 amd64 容器跑更快。真正需要 arm64 用户态的操作是“编译和验证”放到单独的 arm64 容器里做。职责分离速度和正确性都不耽误。5.2 一个最小但完整的打包目录如果是手工打包一个简单单文件程序目录结构保持这个最小形态就行myapp-1.0.0/ ├── DEBIAN/ │ ├── control │ └── postinst └── usr/ └── bin/ └── myappcontrol 文件Package: myapp Version: 1.0.0 Architecture: arm64 Maintainer: Your Name youexample.com Depends: libc6 ( 2.36) Description: myapp for arm64注意 Depends 别乱写。如果二进制是动态链接的在 arm64 容器里先用readelf -d myapp | grep NEEDED看一下依赖了哪些 so再对应到 Debian 的包名。如果是 Go 静态编译的Depends 可以不写或者只写一个基础libc6。目录组织好后在 amd64 的打包容器里执行dpkg-deb --build --root-owner-group myapp-1.0.0 myapp_1.0.0_arm64.deb如果你用的是 debhelper / dpkg-buildpackage 流程打包命令就换成dpkg-buildpackage -us -uc -aarm64手工 dpkg-deb 适合简单包debhelper 会自动生成 md5sums、做依赖检查、压缩控制适合有完整 debian/ 目录的正式项目。5.3 安装验证用 arm64 容器模拟目标机这一步是我最强调的。打出包后不要直接往生产环境传。先在 arm64 容器里装一遍、跑一遍docker run --rm --platform linux/arm64 -v $(pwd):/pkg -w /pkg \ debian:bookworm-slim bash -c \ dpkg -i myapp_1.0.0_arm64.deb myapp --version如果这一条命令能在本地 arm64 容器里顺利结束至少证明包的结构、依赖、文件路径、动态库链接都没问题。如果容器里缺依赖会当场报错你也能借此精确补 Depends 字段。这比“传到服务器再试”成本低太多也是纠错最狠的一个环节。如果你的环境连 arm64 容器都跑不起来那就只能退到 qemu-system-aarch64 起一台全系统模拟的 ARM64 虚拟机。但这条路重、慢、还要配内核和 rootfs优先级放到最后。99% 的日常验证一个 arm64 容器足够。5.4 一些收尾经验和容易漏掉的细节最后再整理几个文档里不会写、但实战中真正影响成败的小经验。base 镜像和最终目标系统的 Debian/Ubuntu 大版本尽量保持一致。在 bookworm 容器里编译的二进制链接的是 glibc 2.36如果目标机是更老的 bullseyeglibc 2.31装上去很可能会因为 GLIBC 版本问题跑不起来。反过来在更老的系统里编译则相对安全不要太追求环境太新。静态编译是 arm64 deb 包的“免死金牌”。只要是 Go 项目能 CGO_ENABLED0 就绝不打开C/C 项目如果允许优先尝试静态链接能省掉后面 80% 的依赖问题。需要用动态库的话只能老老实实走模拟原生构建别想交叉编译一把梭。包里的文件属主一定要用 root:root。Windows 和容器之间文件挂载后 uid 经常是乱的打包时记得带--root-owner-group否则安装后一堆系统路径下出现奇怪属主服务一启动就权限报错。postinst / prerm 脚本记得加可执行权限chmod 0755。deb 不会自动帮你 chmod 这些脚本权限不对安装时控制脚本直接跳过服务没注册你还一脸懵。我把这套流程走顺之后再回头想标题里那三个“想错了的地方”其实都是同一个根源我一开始把“arm64 的 deb 包”当成了一种静态标签觉得贴上就行。但真正可交付的跨架构包是“构建架构、打包架构、安装架构”三重巡检之后才成立的产物。在 Windows 上做到这件事最重要的是别被 Windows 这个表层迷惑尽早把注意力放到容器和二进制检查里去。
返回列表