现在不少搞嵌入式、边缘计算的朋友工作机上跑着 Windows目标板却是 arm64 的 Linux比如 RK3568、RK3588 这类开发板。以前最常见的做法是在 Windows 上装个虚拟机把整个 Linux 桌面跑起来再在虚拟机里交叉编译、打 deb 包最后拷到板子上 dpkg -i。老实说这套流程能用但太重而且虚拟机的 IO 和共享目录经常让人想砸键盘。我现在的习惯是用 WSL 作为主力构建环境在 WSL 里装交叉编译工具链把 arm64 的程序打成 .deb 包一条 scp 传到板子上再一行 dpkg -i 完事。这篇文章就把这套环境从零搭到最终交付的完整路径讲清楚包括中间踩过的坑和为什么每个环节要这么选。1. 为什么是“WSL 交叉编译 deb”这套组合1.1 在 Windows 上WSL 才是嵌入式开发的合理底座你当然可以在 Windows 原生的 CMD/PowerShell 里交叉编译只要装了交叉编译器命令照样能跑。但真正麻烦的是构建脚本生态Linux 下常见的 configure、autotools、Makefile 依赖链在 Windows 原生环境里动不动就报错更别说 dpkg-deb 这种工具本身就只能跑在 Linux 上。所以底层需要一个完整的 Linux 用户态环境。WSL2 的定位是一个轻量级虚拟机但它和 Windows 的集成度比传统虚拟机高一个数量级。你可以直接在 Windows 的资源管理器里访问 WSL 文件系统也可以用 wsl 命令从任意路径拉起一个 Linux shell终端里跑命令、编辑器里写代码、Windows 侧做文件管理。对比一下传统方案方案环境一致性性能与 Windows 集成日常维护成本双系统好好差文件互通麻烦高切换系统重启VirtualBox/VMware 全量虚拟机好一般一般共享目录和端口麻烦中Windows 升级易出问题WSL2好接近原生IO 略差极好文件互相可见低一条命令拉起选择 WSL 最实际的原因有三个一是构建产物和中间文件可以放在 Linux 原生文件系统里避免权限、换行符、符号链接一堆坑二是所有 Debian 系的工具链、打包工具能直接 apt 安装不用折腾 Windows 移植版三是日常代码编辑、版本管理可以继续用 Windows 侧的体验编译和打包时才进 Linux工作流很顺畅。1.2 交叉编译解决的核心矛盾板子太弱、迭代太快arm64 板子不是不能本地编译装个 gcc 完全没问题。但你有没有试过在 RK3568 这种板子上编译一个带几十个依赖的项目CPU 性能和内存摆在那里一个稍微大一点的 C 工程能编译十分钟如果你还在调代码阶段这种等待是灾难性的。交叉编译就是开发机上生成目标平台的可执行文件开发机算力强、磁盘大、内存足编译一次以秒级计极大缩短迭代反馈周期。交叉编译的底层逻辑很简单x86_64 主机上的编译器产出的是 arm64 指令集的目标文件配合对应的交叉链接器和系统库头文件就能得到能在 arm64 板子上运行的 ELF 可执行文件。典型命令是aarch64-linux-gnu-gcc前缀里的aarch64就是目标架构host 和 target 不同所以叫“交叉”。有一点要记住交叉编译不只是换个编译器那么简单。如果目标板上的程序依赖动态链接库比如 libc 以外的库你还需要目标架构下的库文件和头文件也就是 sysroot。对于 Debian/Ubuntu 衍生系统的开发板直接在 WSL 的 Ubuntu 里安装 Debian 提供的gcc-aarch64-linux-gnu工具链默认 sysroot 是/usr/aarch64-linux-gnu里面包含一套为 arm64 架构准备的基础头文件和库。如果你只是写一个不怎么依赖第三方库的小程序这个默认工具链完全够用。后续项目复杂度上来了再考虑用 crossbuild 或 Yocto SDK 的 sysroot。1.3 用 deb 交付而不是扔一个二进制很多人一开始都是把编译好的可执行文件用 scp ftp 直接传到板子上跑简单粗暴但一旦有了维护和批量部署的需求就崩了。deb 包本质上是一种自描述、可被系统包管理器统一管理的发布格式解决的不只是“文件放哪儿”的问题。第一版本可追溯。dpkg -i安装后系统知道这个包是谁、什么版本、装了什么文件。升级的时候dpkg -i 新版.deb就能原位覆盖不需要手动删旧文件。第二依赖声明。你的程序如果依赖某个 arm64 库在 deb 包的Depends字段里写清楚安装时 dpkg 会检查依赖是否满足不满足直接拒绝安装并告诉你缺什么。第三是卸载干净。deb 包安装时记录文件清单卸载时自动删除而不是靠人在板子上手动删这对产品交付、量产刷机都是必要的。还有一点很实际如果你负责维护多台板子甚至几十台把 deb 包放到内网 apt 源里所有板子一发apt update apt upgrade就能整体升级。这种交付方式裸二进制是做不到的。2. 先搞懂 deb 包的结构才知道自己在打包什么2.1 deb 文件的物理结构deb 包并不是一个“压缩包”它本质上是一个 Unix 的ar归档文件里面通常包含三个成员debian-binary、control.tar.gz和data.tar.*。用ar -t打开就能看到$ ar -t myapp_1.0.0_arm64.deb debian-binary control.tar.gz data.tar.xz其中debian-binary内容就是2.0加上一个换行表示 deb 格式版本。control.tar.gz里装的是包管理元数据至少包含一个control文件还可能包含md5sums、postinst、prerm、conffiles等维护脚本。data.tar.xz才是真正的载荷目录结构就是你安装后系统上应该出现的文件路径。理解这个结构对排查问题很重要。我见过有人手动解压 deb 再去改里面的二进制然后重新打包最后把控制文件的校验和也搞乱了安装时各种报错。用标准工具dpkg-deb --build去生成才是正道。2.2 control 文件里那些关键字段手写控制文件是打包最基本的能力。一个最小的 control 文件长这样Package: hello-service Version: 1.0.0 Architecture: arm64 Maintainer: Alice aliceexample.com Depends: libc6 ( 2.31) Section: utils Priority: optional Description: A sample service for arm64 boards This package installs a small daemon and its systemd unit.几个坑点值得展开说Architecture必须写arm64写错成amd64的话包能打出来但传到板子上dpkg -i会直接拒绝提示架构不匹配。如果你在 x86 的 WSL 里打dpkg默认不会替你纠正必须显式写。Version的字符串规则比较严格只能由数字、字母、点号、加号、减号和波浪线组成不能有空格或中文。很多自动化脚本喜欢拼时间戳比如1.0.0-20250701这种格式合法但1.0.0_2025就非法下划线是不允许出现在版本号里的。Depends字段里写库名和最低版本号比如libc6 ( 2.31)如果板子上系统库版本太低dpkg 会提示依赖不满足这时候要去解决动态库兼容问题而不是强装。Section、Priority、Maintainer这些字段看起来像填空题但它们会影响后续放到内网 apt 源里的展示和索引最好养成填写的习惯。2.3 dpkg -i 安装时到底做了什么dpkg -i的执行流程其实不复杂先解包读取 control 元数据检查依赖是否满足再解压 data 到系统根目录也就是把usr/bin/foo放到系统的/usr/bin/foo然后跑维护脚本。维护脚本中比较常用的是postinst用来做安装后的初始化比如systemctl daemon-reload、重启服务、创建用户或目录。这也就解释了为什么在 WSL 里打好的 deb 包不能直接拿到板子上跑因为目标板的/usr才是 deb 载荷写入的根。而交叉编译的过程只是生成了目标架构的文件deb 包数据结构本身与架构无关构建流程可以在任意架构的机器上做只要载荷里的文件是目标架构的就行。3. 实操全流程从 WSL 环境搭建到板子上 dpkg -i3.1 准备 WSL 环境第一步是确认 Windows 侧虚拟化已启用。Win11 大部分机器默认没问题老一点的机器需要在 BIOS 里开 SVM/VT-x。接着用管理员权限打开 PowerShell执行wsl --install -d Ubuntu-22.04这个命令会启用 WSL 功能、安装虚拟化平台然后下载 Ubuntu 发行版。如果网络比较差wsl --install卡住是常有的事可以改成手动方式先把发行版的安装包下载到本地再用wsl --install --from-file或wsl --import导入。安装完成并设置好 Linux 用户密码后进入 WSL 第一件事当然是更新软件源和系统sudo apt update sudo apt upgrade -y有一个环境规划上的建议把所有嵌入式项目放在 Linux 原生文件系统里也就是~/project这类目录而不是 Windows 的C:\盘映射出来的/mnt/c/...。WSL 访问/mnt/c走的是 DrvFSIO 慢只是其次权限模型和换行符才是大问题。我做了几年项目结论很简单开发工程放 Linux 目录最终产物通过 Windows 路径或 scp 传给板子。3.2 安装 arm64 交叉编译工具链在 WSL 的 Ubuntu 里安装工具链非常简单直接 apt 装 Debian 官方提供的包sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu dpkg-dev fakeroot其中gcc-aarch64-linux-gnu是编译器binutils-aarch64-linux-gnu提供aarch64-linux-gnu-ld、aarch64-linux-gnu-objdump等配套工具dpkg-dev和fakeroot负责打包后面都会用到。装完验证一下aarch64-linux-gnu-gcc --version aarch64-linux-gnu-gcc: fatal error: no input files arm-linux-gnueabihf-gcc --version # 这是 32 位 ARM 的别装错这里顺便给一个区分aarch64对应 64 位 arm64arm-linux-gnueabihf对应 32 位 ARM 且带硬浮点。如果你的板子是 64 位系统选 aarch64 前缀那套选错前缀编译出来的程序拿到板子上file命令一下就能看到架构不对。3.3 写一个小示例程序并完成交叉编译为了把流程走通我用一个简单的 systemd 服务来演示。程序每三秒写一条日志到系统日志没什么功能但足够覆盖“可执行文件 服务管理”的完整场景。先准备代码hello_service.c#include stdio.h #include unistd.h #include syslog.h int main(void) { openlog(hello-service, LOG_PID, LOG_DAEMON); while (1) { syslog(LOG_INFO, hello from arm64 board); sleep(3); } closelog(); return 0; }然后交叉编译aarch64-linux-gnu-gcc -O2 -Wall -o hello_service hello_service.c file hello_servicefile的输出应该显示类似ELF 64-bit LSB executable, ARM aarch64的信息。看到这个说明编译产物确实是 arm64 指令集。如果你在 x86 的 WSL 里直接敲gcc编译产物是 x86_64所以交叉编译时务必使用带aarch64-linux-gnu-前缀的编译器。3.4 使用 dpkg-deb 构建 deb 包推荐直接在 WSL 的 Linux 目录里构造打包目录不要放在/mnt/c。我习惯的目录结构是这样的hello-deb-build/ ├── DEBIAN/ │ └── control ├── usr/ │ ├── bin/ │ │ └── hello_service │ └── lib/ │ └── systemd/ │ └── system/ │ └── hello.service其中DEBIAN目录是控制区里面不能有usr这类安装路径它下面的control文件就是上面写的那个。usr目录对应的是安装后系统的路径usr/bin/hello_service最终会被安装到/usr/bin/hello_service。systemd 服务文件hello.service写[Unit] DescriptionHello service for arm64 demo Afternetwork-online.target [Service] ExecStart/usr/bin/hello_service Restartalways RestartSec3 [Install] WantedBymulti-user.target权限这一步很容易被忽略。打包前必须给DEBIAN/control设置普通文件权限即可但usr/bin/hello_service必须保留可执行权限。如果你是从 Windows 目录里拷贝过去的文件dpkg-deb打包时会自动把权限带进包里但 Windows 侧拷过去的文件经常丢执行位结果是安装完hello_service文件存在却不可执行。这里有一个血泪经验在hello-deb-build/DEBIAN写好 control 后先手动固定权限再打包chmod 755 hello-deb-build/DEBIAN/control chmod 755 hello-deb-build/usr/bin/hello_service chmod 644 hello-deb-build/usr/lib/systemd/system/hello.service接着执行打包dpkg-deb --build --root-owner-group hello-deb-build hello-service_1.0.0_arm64.deb--root-owner-group的作用是把包内文件属主统一置为 root避免以当前 Linux 用户身份打包导致安装后文件属主混乱。这个参数在老版本 dpkg 里可能没有升级一下dpkg-deb版本就好。打包完成后验证一下dpkg-deb --info hello-service_1.0.0_arm64.deb dpkg-deb --contents hello-service_1.0.0_arm64.deb--info输出 control 元数据--contents列出包内文件路径和权限。这一步能提前发现架构写错、路径不对、权限缺失等问题不用等到板子上踩雷。3.5 传到板子上并执行 dpkg -i最常见的传输方式是把 WSL 内的 deb 文件复制到 Windows 目录然后用 SSH 从你的终端工具传过去。但更省事的是在 WSL 里直接用 scp前提是板子上开了 SSH serverscp hello-service_1.0.0_arm64.deb root192.168.1.100:/tmp/如果你的板子没有网络而你用的是一根 USB 连接也可以把 deb 放到 U 盘里插板子上但流程一样最后都是dpkg -i /tmp/hello-service_1.0.0_arm64.deb安装过程如果顺利输出里可以看到Selecting previously unselected package hello-service和Setting up hello-service (1.0.0)这两行是关键。如果缺少依赖dpkg 会提示你Unmet dependencies你需要先安装依赖包。安装完顺势启用服务systemctl daemon-reload systemctl enable --now hello systemctl status hello按我上面的hello.service写法服务启起来后systemctl status应该能看到 active (running)日志里每隔三秒出现一条hello from arm64 board。到这里一条 deb 从 Windows 开发机到 arm64 板子的完整链路就算打通了。3.6 不想手写目录结构用 fpm 一行搞定如果项目比较简单不想维护一整个目录树可以试试fpm这个工具。WSL 里用 gem 安装sudo gem install fpm然后一行生成 debfpm -s dir -t deb -n hello-service -v 1.0.0 -a arm64 \ --description Hello service for arm64 \ usr/bin/hello_serviceusr/bin/hello_service \ usr/lib/systemd/system/hello.serviceusr/lib/systemd/system/hello.servicefpm 会把等号左侧的源文件、右侧的目标路径对应起来打包。但我个人的建议是如果只是三五个文件的小项目fpm 很香项目一旦开始要处理 preinst、postinst、conffiles 这种复杂逻辑手写目录结构加 dpkg-deb 反而更可控。fpm 生成的包偶尔在 depends 和脚本上有一些小问题排查起来不如 dpkg-deb 透明。4. 踩坑实录Windows 上打 arm64 deb 的高频问题4.1 在 /mnt/c 下打包导致的权限和换行问题前面多次强调不要在/mnt/c下构建这里用一个真实例子解释它有多坑。有一次我在C:\work\project下放了打包目录WSL 里到/mnt/c/work/project执行dpkg-deb --build打完传到板子上一装usr/bin下的可执行文件居然没有执行权限systemd服务起不来。后来我用dpkg-deb --contents一看包内权限果然是-rw-r--r--执行位没了。原因在于 DrvFS 对 Windows 文件没有可靠的 Unix 权限映射chmod 755在/mnt/c下经常失效或者文件从 Windows 侧新建后默认成了 0644。解决方式就是把整个打包工作区放到 WSL 的 ext4 文件系统里比如~/build/hello-deb。这一步是性价比最高的避坑手段没有之一。4.2 交叉编译的库依赖问题交叉编译最容易踩的第二个坑是目标架构的动态库缺失。示例程序只依赖 libc所以没有任何问题。但如果程序调用了libssl、libsqlite3你在 x86 的 WSL 里装了libssl-dev交叉编译时链接器默认还是去找 arm64 的库而gcc-aarch64-linux-gnu的默认 sysroot/usr/aarch64-linux-gnu里并不包含这些库。两种手段解决要么单独安装libssl-dev:arm64这类多架构包要么把依赖的库和头文件手动拷进 sysroot。使用 apt 多架构的方法是先在 WSL 里开启 arm64 架构sudo dpkg --add-architecture arm64 sudo apt update sudo apt install libssl-dev:arm64但注意在 WSL 的 x86 环境里装libssl-dev:arm64没问题因为装的是交叉开发所用的头文件和 arm64 库文件不会影响 WSL 自身运行。装了之后aarch64-linux-gnu-gcc在链接时就能找到对应库。验证动态库依赖用aarch64-linux-gnu-objdump -p hello_service | grep NEEDED如果板子上缺库可以用NEEDED列表去板子上逐一核对。想图省事也可以静态链接aarch64-linux-gnu-gcc -static编出来的二进制基本什么环境都能跑但体积大而且涉及网络库、DNS 解析之类的场景静态链接会有额外的兼容性风险建议先动态链接。4.3 包名和版本号导致 dpkg 拒绝安装还有一个高频错误是把Package字段写成大写字样如HelloService。Debian 的包名规范是小写字母、数字和-.大写字母在安装时会直接报错。版本号同理。我来列几个常见非法示例和修正场景非法写法正确写法包名大小写Package: HelloServicePackage: hello-service版本号带下划线Version: 1.0.0_betaVersion: 1.0.0-beta版本号带空格Version: 1 0 0Version: 1.0.0架构写错Architecture: amd64Architecture: arm64这类错误在dpkg-deb --build时有些会直接报错有些不会直到在板子上dpkg -i才暴露。我的习惯是把拿到板子上安装前先用dpkg-deb --info检查一遍把包名、版本、架构这三个字段过目一遍基本上能挡掉八成问题。4.4 WSL 本身的一些环境问题再补充几个 WSL 本身使用中的问题。第一个是wsl --install网络慢或卡住可以手动下载 Ubuntu 的.appx包再用wsl --import导入或者用wsl --install --web-download改走网络直连模式。第二个是路径映射问题。Windows 的C:\work在 WSL 里是/mnt/c/work大小写敏感度、文件权限都不一样构建产物如果在 Windows 工具里打开注意路径别搞混。第三个是网络模式。WSL2 默认 NAT 模式板子和 Windows 在同一局域网WSL 访问板子没问题但 Windows 侧的服务如果要在 WSL 里访问一般用localhost就能通这是 WSL2 的 localhostForwarding 特性反过来板子要访问 Windows 宿主机需要从 WSL 里拿到宿主机 IP通常是ip route show default里的网关地址。4.5 dpkg -i 成功但服务跑不起来的排查顺序最后给一个排查清单专门针对“dpkg -i 成功systemd 却起不来”的场景。按顺序查用dpkg -L hello-service确认文件是否真的装到了预期路径尤其是/usr/bin/hello_service是否存在且有执行权限。直接在板子上运行/usr/bin/hello_service如果提示Exec format error说明二进制架构不对如果提示cannot open shared object file说明动态库缺失如果直接没输出说明程序本身逻辑问题。查看 systemd 服务状态和日志journalctl -u hello -n 50重点看ExecStart路径和权限。检查systemctl daemon-reload是否执行成功有些版本的 systemd 对 unit 文件的格式敏感少一个空行都可能导致 unit 被忽略。我实际处理过的问题里有相当一部分最后都落在“文件权限没设置对”和“动态库版本不一致”这两个问题上。所以打包前dpkg-deb --contents检查权限、板子上跑前ldd检查依赖这两步建议养成肌肉记忆。5. 这套流程的扩展方向如果只是给一两块板子做交付这篇文章里的办法已经足够。但当板子数量到了几十台或者你需要持续更新版本那 deb 包最好再往前走一步在内网搭一个 apt 仓库。把 deb 包用reprepro或aptly管理板子上配置好源地址后一条apt update apt upgrade就能批量升级整个现场的板子。此时你在 WSL 里打的 deb 包就不再是一次性产物而是一个可持续交付的环节。打 arm64 deb 包这件事本身难度不高但链条很长从 Windows 到 WSL、从交叉编译到打包规范、再到板子上的系统行为任何一个环节不清楚都可能让你卡上半天。我个人实际用下来最深的体会是环境选择先于技术细节工程文件放进 Linux 目录、权限维护做扎实、架构和依赖字段一板一眼写清楚后面的路会非常顺。希望这篇流程记录能帮你少走几个弯路。 SEO 优化官网定制响应式建站教育培训建站