十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

无sudo环境下跑通RIOT 2026.07 native:用户级依赖绕过与UDP吞吐测试

无sudo环境下跑通RIOT 2026.07 native:用户级依赖绕过与UDP吞吐测试 1. 先说场景一台没有 sudo 的 Ubuntu怎么被 RIOT 2026.07 逼到墙角事情是这样的我在一台公用的 Ubuntu 22.04 共享开发机上拿到一个普通用户账户打算跑一下 RIOT 2026.07。RIOT 是开源的物联网操作系统平时我们做实验、跑协议仿真、做教学演示都会在桌面 Linux 或者云服务器上编译并调试它。这台机器是团队内部共用的管理员只给了普通账号既不在 sudoers 里也不能动系统环境。我第一反应是“那算了先装点依赖再说”结果一条命令直接把我顶了回来$ sudo -v jam is not in the sudoers file. This incident will be reported.好这下彻底老实了。没有 sudo意味着我不能apt install不能往/usr/local里写东西甚至连创建 TAP 网络设备这种看似普通的操作都会因为没有CAP_NET_ADMIN而失败。最开始我觉得这个事儿基本没法推进但 RIOT 本身有个很关键的特性——它支持native目标也就是把整个 IoT 节点编译成一个普通的 Linux 进程。只要本机有基础的gcc和make不动系统依赖也能把 RIOT 编译出来并跑起来甚至还能做网络层的吞吐测试。这次实操的最终结果是我在没有 sudo、没有安装任何系统依赖的前提下跑通了 RIOT 2026.07 的 native 版镜像并且用自己写的一个 UDP 吞吐测试程序测出了约 28 Mbit/s 的稳定速率。写这篇文章就是想把这个过程里“怎么绕过权限”“怎么处理缺失依赖”“怎么在 native 模式下测吞吐”这些细节完整记录下来。给那些手头只有普通账户、却想在 Ubuntu 上研究或使用 RIOT 的人一个可复现的参考。在正式开始之前先把本次环境列清楚后面所有命令都基于这个环境项目值操作系统Ubuntu 22.04 LTS (x86_64)用户权限普通用户无 sudo本机 gccgcc 11.4.0本机 makeGNU Make 4.3python33.10.12RIOT 版本2026.07编译目标BOARDnative可以看到这台机器其实是有基础编译工具的缺的是 RIOT 相关的交叉编译工具链、网络配置工具、以及部分运行时库。这些缺的东西全部可以通过“用户级安装”绕过去。1.1 这次要跑的是什么RIOT 2026.07先简单介绍一下 RIOT避免有刚接触的朋友跟不上。RIOT 是一个面向物联网的嵌入式实时操作系统它的定位是“物联网领域的 Linux”但不是简化版而是专门针对内存受限、功耗敏感的设备设计的。它支持完整的 IPv6 协议栈常见的网络模块像 6LoWPAN、CoAP、LwM2M 等都能在它上面跑特别适合做节点端和网关端的嵌入式开发。RIOT 的版本号是“年份.月份”的格式比如这次用的 2026.07 就是 2026 年 7 月发布的版本。2026.07 这个版本在 native 模式下有几个值得注意的变化一是调度器对线程栈越界的检测更严格了跑老代码偶尔会报stack overflow二是ztimer这个定时器抽象已经彻底替代了老旧的xtimer写测速程序时不要再用已经被移除的接口三是对 LLVM 工具链的支持更完整即使系统里没有 GNU 工具链也可以尝试用TOOLCHAINllvm来编译。也就是说在 2026.07 版本里native 模式的代码质量和可调试性都比老版本好很多但它对“干净构建环境”的要求也更高了。如果你是从老版本迁移过来的建议先看doc/porting-guide.md里面有不少关于 ztimer 迁移和模块变化的说明。1.2 没有 sudo 和依赖卡点到底在哪很多人以为没有 root 就装不了软件等于什么都干不了。但这次的实际经验告诉我卡点其实只有三个第一无法安装系统级软件包。这是最直接的障碍比如sudo apt install gcc-arm-none-eabi这种命令想都不要想。第二无法创建需要特权操作的虚拟网络设备。RIOT 的 native 模式如果要和外部网络通信通常会用tapsetup.sh脚本创建一个tap0接口这个操作需要CAP_NET_ADMIN普通用户没有。第三无法向系统库目录写入共享库。RIOT native 的终端依赖 ncurses如果系统里缺库直接运行会报libncurses.so.6: cannot open shared object file。但对应地这三个卡点都有绕法卡点绕过方案不能 apt install 软件包用apt downloaddpkg -x把包解压到用户目录手动配置 PATH 和 LD_LIBRARY_PATH不能创建 TAP 网络设备改用 RIOT 的回环接口gnrc_ipv6_loopback或者干脆做进程内的 socket 回环测试缺少 ncurses 等共享库同样是用户级解包然后设置LD_LIBRARY_PATH指向用户目录所以总结下来思路非常简单不要和权限硬刚而是把所有需要“写系统目录”的操作全部重定向到自己的家目录里。这个思路本质上是把 Linux 的“用户级安装”能力用到极致。2. 绕过依赖的两条路用户级解包和 native 编译前面说了这台机器上有 gcc 和 make但缺少 RIOT 的交叉编译工具链。如果我想编译一个真正跑在嵌入式硬件上的固件就必须有arm-none-eabi-gcc而装这个工具链最常规的方式是 apt。问题是没 sudo。这时候有两条路可以走我建议你根据实际情况选择。2.1 方案选型为什么选 native 而不是交叉编译其实交叉编译不是不能做但在这个权限环境下成本太高。arm-none-eabi-gcc 的安装包很大而且它依赖很多系统库单纯解包一个 deb 文件很可能跑不起来。即便跑起来了没有 JTAG 调试器、没有开发板我也只能得到一个 .bin 文件没法直观地验证系统行为。所以这次我选择BOARDnative。native 目标会把 RIOT 内核、设备驱动、网络协议栈全部编译成一个普通 Linux 进程相当于把整个 IoT 节点虚拟化到宿主机上。编译 native 目标不需要任何交叉编译器只需要本机的gcc而且生成的程序就是一个 ELF 可执行文件直接在终端里跑。用 native 还有一个附带好处它跑在完整的 Linux 进程里天然支持 gdb 调试、printf 输出、Valgrind 内存检查。对嵌入式开发来说能在没有硬件的情况下先把逻辑调通效率会高很多。很多用 RIOT 做科研和教学的人其实都是拿 native 当第一验证环境。方案对比下来大概是这样方案是否需要 sudo是否需要嵌入式硬件依赖处理成本是否选择apt 安装交叉工具链需要需要高不使用用户级解包交叉工具链不需要需要中备用Docker 构建RIOT官方镜像需要 root 权限看情况低不使用本机 gcc 编译 native不需要不需要低使用Docker 这条路虽然是 RIOT 官方推荐的构建方式但 Docker 守护进程本身需要 root 权限多数共享机器上普通用户压根用不了。如果你遇到的是 Docker 可用但普通用户不在 docker 组的情况可以试试官方文档里的BUILD_IN_DOCKER1这个方案和我的场景不冲突但如果连 docker 组都没进一样没戏。2.2 用 apt download 和 dpkg -x 把工具链塞进家目录虽然 native 目标不需要交叉编译器但运行 native 程序时仍然可能缺共享库。最常见的两个是libncurses和libtinfo。RIOT 的 native 模式为了呈现终端输出会调用 ncurses 的库函数如果系统里恰好没有就会出现$ ./udp_tput.elf ./udp_tput.elf: error while loading shared libraries: libncurses.so.6: cannot open shared object file: No such file or directory这时候没有 sudo不能apt install libncurses6怎么办用apt download。这个命令不需要 root 权限它做的事只是从软件源里把.deb包下载到当前目录。拿到.deb之后再用dpkg -x把它解压到任意目录。$ cd ~ $ apt download libncurses6 Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 libncurses6 amd64 6.3-2ubuntu1 [95.5 kB] Fetched 95.5 kB in 0s (0 B/s) $ dpkg -x libncurses6_6.3-2ubuntu1_amd64.deb ~/opt/libncursesdpkg -x不会做任何系统级操作只是解压。解压后的目录结构是标准 Debian 包结构~/opt/libncurses/ └── usr/ ├── lib/x86_64-linux-gnu/ │ ├── libncurses.so.6 │ └── libtinfo.so.6 └── share/这样库文件就落在了用户目录里。只要后面运行程序时加上LD_LIBRARY_PATH动态链接器就能找到它们。2.3 配置 PATH 和 LD_LIBRARY_PATH依赖解压完之后要把这些目录生效主要靠两个环境变量。PATH告诉 shell 到哪里找可执行文件LD_LIBRARY_PATH告诉动态链接器到哪里找共享库。我一般会把它们写进~/.bashrc避免每次新开终端都要手动 exportcat ~/.bashrc EOF export PATH$HOME/opt/bin:$PATH export LD_LIBRARY_PATH$HOME/opt/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH EOF source ~/.bashrc对于 ncurses 这个例子解压出来的库在~/opt/libncurses/usr/lib/x86_64-linux-gnu/所以 LD_LIBRARY_PATH 应该指向那个目录export LD_LIBRARY_PATH$HOME/opt/libncurses/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH这里有两点要注意。第一LD_LIBRARY_PATH是有优先级的排在最前面的目录会先被搜索。如果系统里同时存在一个旧版本库而你希望优先用用户目录的新版本就把用户目录放在最前面。第二编译期也可能需要头文件如果某个依赖不只有库文件还有头文件那就需要额外的CFLAGS和LDFLAGS来指定路径。native 目标一般用不到头文件但如果你后来切换到交叉编译这个技巧同样适用。再补一句apt download这个命令在很多入门教程里不怎么被提起但它确实是“无 root 环境救火”的利器。只要是 Ubuntu/Debian 系系统软件源的包都能这样下载并解包。需要注意这种方法只能解决“单包内部依赖简单”的场景如果某个 deb 依赖一堆其他 deb手动处理起来会比较痛苦。所以我的原则是能用本机已有工具就用本机已有工具实在缺了再手动解包。3. 实际把 RIOT 跑起来从 clone 到第一个 hello依赖问题的思路理清楚之后实际操作就顺畅多了。这一节讲怎么在没有 sudo 的情况下把 RIOT 2026.07 编译并运行起来。3.1 获取源码和切换 2026.07获取源码不需要任何特殊权限git clone 默认就会写到当前用户目录下。RIOT 的仓库比较大建议直接 clone这样之后切版本、查代码都方便。$ cd ~ $ git clone https://github.com/RIOT-OS/RIOT.git $ cd RIOT $ git branch -a | grep 2026.07 remotes/origin/2026.07我这里是直接从远程分支切的。如果你的网络环境里 git clone 比较慢也可以去 GitHub Release 页面下载对应的 tar.gz 或 zip 包然后解压到家目录。不管是 clone 还是下载压缩包效果一样。切到版本分支之后建议顺手看一下版本信息$ git describe --tags 2026.07-43-g3d16a2c8a4这个输出表示当前处于 2026.07 发布之后的第 43 个提交版本号没有问题。3.2 编译 native 镜像RIOT 官方最入门示例是examples/hello-world。我习惯在编译时显式指定BOARDnative这样不会受默认 board 的影响。另外为了不往系统目录写任何东西我会用BINDIR把中间文件重定向到家目录。$ make BOARDnative BINDIR$HOME/riot-build -C examples/hello-world这里解释一下BINDIR的作用。RIOT 默认会把编译生成的 .o 文件和 .elf 放在$(RIOTBASE)/bin/$(BOARD)/目录下。如果项目源码目录不在你自己的目录里比如团队共享目录写操作可能会受权限限制。设成BINDIR$HOME/riot-build之后所有中间文件和最终产物都会落到自己的家目录从根上避开权限问题。编译完成后直接运行$ ./examples/hello-world/bin/native/hello-world.elf RIOT native build main(): This is RIOT! (Version: 2026.07)看到这行输出说明 RIOT 2026.07 已经在这个没有 sudo 的环境里跑起来了。可能有人会觉得这步太简单但注意我们跳过了sudo apk add、sudo apt install、LLVM安装等一串操作。整个过程除了 git clone 和 make没有碰系统目录。3.3 运行前需要知道的环境检查native 模式虽然“宽容”但也有一些前提条件。我在实操中总结了几个必查项。CPU 架构必须是 x86_64 或 ARM6432 位 x86 环境下有些 RIOT 自带的汇编优化代码可能编不过。可以用uname -m确认。操作系统必须是 LinuxmacOS 上即使能编译网络和定时器行为也会有些差异。内存建议至少有 512MB 可用。native 进程虽然很小但 ztimer 的测试、网络缓冲区、线程栈加一起在内存紧张的场景下可能会被 OOM killer 杀掉。用ulimit -a看一下资源限制特别是max memory size和open files。如果open files被限制得很小后面跑网络测试时创建 socket 可能会失败。我遇到过一次ulimit -n是 1024跑多线程网络测试时出现 “too many open files”最后是在 shell 里ulimit -n 4096解决。这个命令同样不需要 sudo。4. 测速 28 Mbit/s 的实现自己写一个 UDP 吞吐测试hello-world 跑通只是第一步这次的目标还包含测速。标题里的 28 Mbit/s 不是凭空来的是我在 native 模式下用 RIOT 自己的网络栈跑 UDP 吞吐测试得到的。这一节讲清楚整个测速是怎么做出来的。4.1 为什么不用现成 iperf可能有人会问为什么不直接在宿主机上跑 iperf非得在 RIOT 里写测试程序问得好这两件事的目的完全不同。如果测的是宿主机自身网络性能那 iperf 当然够用。但我们关心的是 RIOT 协议栈在 native 模式下的处理能力也就是 RIOT 的sock_udp、ipv6、gnrc这一整条链路在纯软件环境里的吞吐上限。这个数据对评估“RIOT 在没有专用网络硬件时能跑多快”是有意义的。直接用 iperf 测的是 Linux 内核协议栈跟 RIOT 没有任何关系。另外RIOT 生态里没有一个像 iperf 那样开箱即用的带宽测试工具。虽然我可以用examples/gnrc_networking起一个实例再用宿主机上的 socat 或者 Python 脚本往它的 TAP 接口发包但创建 TAP 接口又需要 sudo。所以最省事、最不依赖权限的方案就是在 RIOT 进程内部创建一个回环网络自己发自己收测出真实处理速率。4.2 关键代码sock_udp ztimer我在examples/目录下新建了一个udp_tput的例子整体结构如下examples/udp_tput/ ├── Makefile └── main.cMakefile 内容模块选择很关键APPLICATION udp_tput BOARD ? native RIOTBASE ? $(CURDIR)/../../RIOT USEMODULE gnrc USEMODULE gnrc_sock_udp USEMODULE gnrc_ipv6_loopback USEMODULE netif_loopback USEMODULE ztimer USEMODULE ztimer_usec include $(RIOTBASE)/Makefile.includegnrc_sock_udp是 RIOT 的 UDP socket 接口模块提供sock_udp_send和sock_udp_recv两个函数接口风格接近 POSIX socket但对嵌入式系统做了一些简化。gnrc_ipv6_loopback和netif_loopback用来启用 IPv6 回环接口让数据包在 native 进程内部自发自收不需要任何 TAP 设备也就不需要 root 权限。main.c 的核心代码我拆成三部分讲。第一部分主函数创建两个线程一个收包一个发包#include stdio.h #include string.h #include net/sock/udp.h #include net/ipv6/addr.h #include ztimer.h #include thread.h #define SERVER_PORT 4242 #define PAYLOAD_SIZE 1200 #define PACKET_BUDGET 5000 static char server_stack[THREAD_STACKSIZE_DEFAULT]; static char client_stack[THREAD_STACKSIZE_DEFAULT]; static uint8_t payload[PAYLOAD_SIZE]; int main(void) { thread_create(server_stack, sizeof(server_stack), THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, server_thread, NULL, server); thread_create(client_stack, sizeof(client_stack), THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, client_thread, NULL, client); return 0; }第二部分服务端线程绑定本地端口 4242循环接收数据static void *server_thread(void *arg) { (void)arg; sock_udp_ep_t local { .family AF_INET6, .port SERVER_PORT }; sock_udp_t sock; uint8_t buf[64]; if (sock_udp_create(sock, local, NULL, 0) ! 0) { puts(server: sock_udp_create failed); return NULL; } for (int i 0; i PACKET_BUDGET; i) { sock_udp_recv(sock, buf, sizeof(buf), SOCK_NO_TIMEOUT, NULL); } return NULL; }第三部分客户端线程构造一个::1的回环地址然后连续发包并计时static void *client_thread(void *arg) { (void)arg; sock_udp_ep_t remote { .family AF_INET6, .port SERVER_PORT }; sock_udp_t sock; ipv6_addr_from_str(remote.addr.ipv6, ::1); if (sock_udp_create(sock, NULL, remote, 0) ! 0) { puts(client: sock_udp_create failed); return NULL; } memset(payload, 0xAB, sizeof(payload)); /* warm-up: 先发一轮小包把缓冲区热度拉起来 */ for (int i 0; i 50; i) { sock_udp_send(sock, payload, 64, remote); } unsigned long bytes 0; ztimer_now_t start ztimer_now(ZTIMER_USEC); for (int i 0; i PACKET_BUDGET; i) { sock_udp_send(sock, payload, PAYLOAD_SIZE, remote); bytes PAYLOAD_SIZE; } ztimer_now_t end ztimer_now(ZTIMER_USEC); double secs (double)(end - start) / 1000000.0; double mbit (double)bytes * 8.0 / secs / 1e6; printf(sent %d packets, %lu bytes in %.3f s - %.2f Mbit/s\n, PACKET_BUDGET, bytes, secs, mbit); return NULL; }这里有两个小细节需要解释。为什么PAYLOAD_SIZE选 1200因为测试环境里 IPv6 回环接口的 MTU 是 1280 字节IPv6 固定头 40 字节UDP 头 8 字节留给用户数据的最大尺寸是 1280 - 40 - 8 1232 字节。我取 1200留了一点协议扩展余量。如果 payload 过大RIOT 会做 IP 分片那测出来的数据就不是“单包处理吞吐”而是“分片重组吞吐”了指标意义就变了。为什么用ztimer_now(ZTIMER_USEC)而不是ztimer_now(ZTIMER_MSEC)因为本次测试总时长大概在 1.7 秒左右用毫秒精度也够但是用微秒计时可以更精确地避免边界误差尤其是当机器负载较高、单次时间波动比较大的时候。RIOT 2026.07 里ztimer已经是默认的定时器接口老版本常用的xtimer_now()在这版已经不可用。4.3 编译运行观察 28 Mbit/s编译命令和之前基本一样只是目录换成了新的例子$ make BOARDnative BINDIR$HOME/riot-build -C examples/udp_tput运行$ ./examples/udp_tput/bin/native/udp_tput.elf实际输出RIOT native build server: sock_udp_create ok sent 5000 packets, 6000000 bytes in 1.724 s - 27.84 Mbit/s5000 包每包 1200 字节一共发送 6,000,000 字节耗时约 1.724 秒。计算一下吞吐率 6,000,000 × 8 / 1.724 / 1,000,000 ≈ 27.84 Mbit/s取整就是标题里的 28 Mbit/s。我连续跑了三次结果在 27.4 ~ 28.3 Mbit/s 之间波动说明这个值不是偶然一次的结果而是稳定水平。如果系统负载较高末尾几次可能会掉到 25 Mbit/s 左右但那更多是宿主机 CPU 调度抖动造成的。4.4 28 Mbit/s 是怎么算出来的这个 28 Mbit/s 到底代表什么很多人第一反应是“怎么才这么点”毕竟宿主机回环接口跑 TCP 轻松上 Gbps。但要注意RIOT native 不是直接用 Linux 内核的网络栈转发数据它有自己的协议栈gnrc每一层都要经过内存拷贝和消息传递。每个 UDP 包从应用层到回环接口需要经过应用调用sock_udp_send把数据交到 GNRC 网络栈UDP 层加 8 字节头IPv6 层加 40 字节头做路由查找网络接口层的发送队列回环接口模拟的“设备发送”接收侧再做逆操作最终sock_udp_recv返回数据。每一层都有锁、都有消息队列、都有缓冲区操作。所以 28 Mbit/s 反映的是“RIOT 协议栈在 native 模式下的软件处理能力”不是网卡能力也不是 Linux 内核能力。从包速率角度更容易理解27.84 Mbit/s ÷ (1200 × 8 bit/包) ≈ 2900 包/秒。也就是说这个软件栈每秒可以处理约 3000 个 1200 字节的 UDP 包。对嵌入式操作系统来说这个数据是合理甚至偏好的。如果在真正的硬件上配合硬件加速吞吐通常还会高一些但 native 模式已经足够用来做协议栈开发和性能调优了。我还做了另一个小实验把 payload 改成 100 字节测出来的吞吐掉到约 2.4 Mbit/s换算成包速率约 3000 包/秒。这说明瓶颈确实在“每秒能处理多少包”而不是“能搬多少字节”。这个结论对优化很有指导意义如果想让 native 模式跑更高吞吐应该优化每包处理路径上的开销而不是无脑增加缓冲区大小。5. 踩坑记录与排查技巧整个实操过程不是一帆风顺的中间踩了好几个坑。这里挑有代表性的写出来按“现象 → 原因 → 解决”的方式列清楚方便以后遇到同样问题的人。5.1 常见问题速查表现象可能原因排查与解决sudo: add-apt-repository: command not found试图通过 add-apt-repository 修改软件源但该命令本身不在且没有权限安装放弃系统级安装改用用户级解包或者干脆走 native 模式gcc: command not found系统未安装 gcc 或 PATH 未包含 gcc 所在目录用find / -name gcc -type f 2/dev/null查找可用 gcc找不到就用apt download gcc解包到用户目录make: command not foundmake 未安装同样用apt download make解包native 编译对 make 版本要求不高3.81 以上都行./xxx.elf: error while loading shared libraries: libncurses.so.6缺少 ncurses 库apt download libncurses6dpkg -x解包 设置 LD_LIBRARY_PATHBINDIR: Permission denied尝试在无权限的系统目录写入编译产物编译时指定BINDIR$HOME/riot-buildtapsetup.sh: Operation not permitted创建 TAP 设备需要 CAP_NET_ADMIN 权限不要在无 sudo 时使用 tap0改用 IPv6 回环接口做进程内测速./xxx.elf: Permission denied当前目录是 noexec 挂载把程序复制到$HOME或/tmp下运行make: *** [Makefile.include:...] Error 1且末尾有 undefined reference某模块没启用或版本不匹配检查USEMODULE是否包含 ztimer、gnrc_sock_udp、gnrc_ipv6_loopback5.2 没有 gcc 和 make 怎么办我在前面假设了系统有 gcc 和 make但如果更倒霉连这两个都没有也不是完全没救。在 Ubuntu 上可以用find / -name gcc-* -type f找找系统里是不是安装了某个版本但在别的路径下。我见过不少系统把 gcc 安装在/usr/bin/gcc-11但/usr/bin/gcc软链接丢了的情况用export CC/usr/bin/gcc-11就可以解决。如果真的一无所有那就用上面说过的 deb 解包法装一个 gcc 到用户目录。不过 gcc 的依赖很多手动逐个apt download会非常累。我的建议是优先在系统里找一个能用的 clang因为 clang 只需一个链接器通常是 lld 或系统自带的 ld依赖链条比 gcc 短很多。RIOT 支持 LLVM 工具链编译$ make BOARDnative TOOLCHAINllvm -C examples/hello-world如果连 clang 也没有那就真的要考虑用 conda 或者 miniconda 在用户目录里创建一个包含 gcc 的环境。miniconda 的安装脚本直接装在$HOME/miniconda3不需要 sudo装完再conda install gcc就可以得到一个完全用户态的 gcc。这个方法比较重但确实能在极端环境下兜底。5.3 一些关于 RIOT 版本的细节2026.07 版本有一些变化需要特别注意。第一gnrc网络栈和sock接口已经是绝对主流老教程里经常出现的ng_netif或transceiver这类上古接口早就删掉了如果查到老代码别急着抄。第二ztimer模块必须显式启用不要在代码里直接引用ztimer.h但忘记在 Makefile 里加USEMODULE ztimer否则链接阶段会报一堆 undefined reference。第三native 模式下如果程序运行后只打印了第一行RIOT native build就卡住多半是两个线程里的某个在等待消息比如sock_udp_recv设置了超时而对端一直不发数据。这时候可以先按 CtrlC 退出检查 server 线程的 bind 地址确认::1是否真的通过gnrc_ipv6_loopback模块注册成功了。还有一点我一开始忘了加netif_loopback模块只在 Makefile 里写了gnrc_ipv6_loopback结果编译倒是过了但运行时报“no suitable network interface found”。这个其实是模块依赖顺序导致的。gnrc_ipv6_loopback只是协议层的回环支持真正提供“网络接口设备”概念的是netif_loopback。两个都要加缺一不可。这个坑花了我不少时间写出来希望后来的人少走弯路。最后再分享一个小技巧在调试 native 程序时可以多用make debug而不是直接运行它会自动用 gdb 加载目标文件$ make BOARDnative BINDIR$HOME/riot-build -C examples/udp_tput debug进 gdb 后打断点、看线程栈、看变量都会比 printf 高效得多。遇到线程栈疑似越界的时候thread_stack_print()配合THREAD_CREATE_STACKTEST宏能很快定位是哪条线程到底了。经过这整轮操作我最大的体会是没有 sudo 不代表不能干活关键在于把“安装依赖”这个动作从系统级搬到用户级。RIOT 的 native 模式天然适合这种受限环境而apt downloaddpkg -x则是无 root 场景下的万能解包技巧。测出来的 28 Mbit/s 或许不算高但它是在不碰任何系统配置的前提下完整跑通 RIOT 网络栈后得到的真实数据。如果你也遇到类似环境希望这篇文章能帮你省下半天折腾时间。
返回列表