无 sudo 权限下编译运行 RIOT OS 并完成网络吞吐测试

发布时间:2026/9/9 11:27:46
无 sudo 权限下编译运行 RIOT OS 并完成网络吞吐测试 刚在项目里遇到一个很尴尬的需求要在共享开发机上跑 RIOT 2026.07但这台机器既没有给我 sudo 权限也不允许我随便装依赖包。折腾了两天最终不光把 RIOT 编译了出来还在 native 模式下跑通了网络吞吐量测试测出来 28 Mbit/s。整个过程有很多细节和坑值得单独写一篇记录一下。先说明一下背景。RIOT 是一个开源的物联网操作系统主要面向资源受限的嵌入式设备支持常见的 MCU 平台同时也提供了能在普通 Linux 上运行的 native 移植版本方便开发调试。这次要在 Ubuntu 上跑 RIOT 2026.07遇到的核心问题是没有 sudo也没有权限安装任何软件包。这就意味着不能用 apt 装交叉编译工具链不能随便加用户到 dialout 组更没办法创建 tap 网络设备。如果你也遇到过类似场景那这篇文章应该能给你一些思路。下面按实际操作顺序记录一下整个准备、编译、运行和测试过程以及中间遇到的问题。1. 为什么要在没 sudo 的环境里折腾 RIOT先说动机。我在做的这个嵌入式原型验证项目目标是快速验证 RIOT 系统在某种网络场景下的吞吐表现但硬件板子不在手边只能用 native 移植版本在开发机上模拟。开发机是一台共享的 Ubuntu 22.04 服务器管理员只给我一个普通用户账号没有 sudo 权限。更苛刻的是这台机器上连交叉编译工具链都没装而且短时间申请不到安装权限。如果手头有 root 权限整件事非常简单apt install gcc-arm-none-eabi装好工具链再跑 RIOT 官方的编译脚本就行。但没有 sudo 之后所有环节都得换思路。其实 RIOT 的 native 移植版本编译时不需要交叉编译器只需要本机的 gcc 和 make所以最核心的问题变成了两件事一是如何在没有 sudo 的情况下拿到编译所需的工具链如果本机没有的话二是如何让 native 模式下的虚拟网络接口能正常跑起来。这个场景其实蛮常见的。除了共享开发机很多 CI 环境、学生机房、以及与其他人共用账号的服务器上都会遇到“有编译需求但无权限安装软件”的尴尬。很多人可能直接放弃了但其实通过把工具链下载到用户目录、利用 RIOT 自带的网络模拟工具是完全可以在受限环境下完成编译和验证的。2. 工具链准备不靠 sudo 也能编 RIOT 的方法2.1 先确认系统里已经有什么在动手之前先检查系统里有没有编译所需的工具。运行以下命令which make which gcc which git我这台机器上gcc 是 11.4.0make 是 4.3git 也有。这对编译 RIOT native 版本来说已经足够了。RIOT 官方文档上对 Ubuntu 环境的要求是装 gcc、make、git 以及一些可选工具但 native 版本编译时并不强制需要 gcc-arm-none-eabi因为它在宿主机的用户空间跑直接用宿主机 gcc 就行。如果你要编译的是 stm32、nrf52 这类板子的固件那就必须要交叉编译工具链了。那问题就来了没有 sudo 怎么装 gcc-arm-none-eabi有几种常用办法我实测最靠谱的是直接从 ARM 官网下载预编译的 tar 包解压到自己家目录。2.2 把 ARM 工具链装到用户目录如果需要如果你需要交叉编译但没 sudo最直接的方案是去 ARM Developer 网站下载gcc-arm-none-eabi-*-x86_64-linux.tar.bz2。把这个文件下载到~/tools目录解压后把 bin 目录加到 PATH 里就行不需要 root。mkdir -p ~/tools cd ~/tools wget https://developer.arm.com/-/media/Files/downloads/gnu/12.2.rel1/binrel/gcc-arm-none-eabi-12.2.rel1-x86_64-linux.tar.bz2 tar xjf gcc-arm-none-eabi-12.2.rel1-x86_64-linux.tar.bz2 export PATH$HOME/tools/gcc-arm-none-eabi-12.2.rel1/bin:$PATH关键点在于这个工具链本质上是静态编译的不需要系统层面的安装。解压后直接用至少在 Ubuntu 20.04 和 22.04 上都验证过没问题。需要提醒的是tar 包下载过程可能会比较慢如果网络状况不好可以考虑用国内镜像源。另外版本不一定要最新只要 RIOT 支持的版本范围内都行我之前用 10.3 和 12.2 都编译通过过。2.3 准备编译目录并拉取 RIOT 源码确认工具没问题之后把 RIOT 源码克隆到自己的目录里。RIOT 的源码托管在 GitHub 上可以用 git 直接拉取也可以去官网下 release 压缩包。mkdir -p ~/work cd ~/work git clone https://github.com/RIOT-OS/RIOT.git cd RIOT git checkout 2026.07-branch如果网络不好或者必须离线操作也可以去 GitHub releases 页面下载RIOT-2026.07.tar.gz压缩包解压到本地目录。RIOT 本身不需要 configure 步骤直接进到 example 目录下编就行这个在受限环境里是很大的优势。3. 编译 RIOT 示例工程并配置 native 网络3.1 编译 native 版的 gnrc_networking 示例RIOT 的 example 目录下有很多现成的示例我这次用的是examples/gnrc_networking它自带 IPv6 的简单网络功能适合做吞吐量测试的起点。编译命令很直接cd ~/work/RIOT/examples/gnrc_networking make BOARDnative这里不需要 sudo也不需要额外依赖。编译产物是一个名为gnrc_networking的可执行文件。如果在编译时提示找不到pkg-config或者libcoap之类的东西先不用急RIOT 的构建系统会把大多数内部依赖自动拉取并编译到build目录里不依赖系统级软件包。我在实际编译过程中第一次碰到的一个报错是缺少make的某些变量定义。仔细排查后发现RIOT 的构建系统对 make 版本有要求如果 make 版本太旧建议升级到 4.2 以上。但在没有 sudo 的限制下也可以通过编译新版本 make 到自己目录解决不过大多数情况下系统自带的够用。3.2 native 模式的工作原理RIOT 的 native 移植版本很有意思它不是一个简单的模拟器而是把 RIOT 内核直接编译成 Linux 用户态进程通过进程调度和信号机制模拟 RIOT 的线程调度同时把网卡、定时器等硬件抽象成宿主机上的虚拟设备。具体来说native 模式下 RIOT 的默认网卡通常对应一个 Linux 的 tap 设备比如tap0。这个 tap 设备就像一根虚拟网线把 RIOT 实例和宿主机网络连起来。RIOT 本身提供了一个脚本tapsetup用来创建和配置 tap 接口但这个脚本需要 root 权限因为它要调用ip tuntap这类特权命令。这就回到了前面的问题没有 sudo 怎么办3.3 没有 sudo 创建 tap 接口的思路如果完全没有 sudo又想让 RIOT native 的网卡跑起来有两条路可以走。第一条路也是最省事的请管理员帮忙创建一个可以被当前用户使用的 tap 设备。比如管理员可以提前执行sudo ip tuntap add dev tap0 mode tap user $(whoami) sudo ip link set tap0 up创建之后普通用户就能直接使用这个 tap0 设备了不需要任何额外权限。之后的 IP 地址配置其实也不一定要在 tap0 上做可以在 RIOT 运行起来之后再从 RIOT 内部配置 IP或者用一个网桥连接到外部网络。第二条路是在没有 tap 设备的情况下使用 RIOT 自带的socket_zep模拟模块。它可以模拟 IEEE 802.15.4 无线链路但不太适合做以太网吞吐量测试。我这次测的是以太网级别吞吐还是选了 tap 方案。如果你真的连管理员都联系不上也没关系。RIOT native 还支持通过NETOPT配置直接跑在 loopback 接口上但那只能测试本机回环意义不大。更实用的方式是在宿主机上同时运行两个 RIOT native 实例用 ZEP 协议嵌套在同一台机器上模拟两个节点之间的通信。这个方案完全不依赖 tap 设备也没有权限要求。3.4 配置 IP 并运行 RIOT native拿到一个可用的 tap0 设备后先确认网卡状态ip link show tap0 ip addr add 10.0.0.1/24 dev tap0 ip link set tap0 up如果前面的ip addr add执行时提示权限不足那说明管理员创建的 tap0 只给了你使用权限但没有给你配置地址的权限。这个比较麻烦但有一种办法在 RIOT native 自己的 shell 中给网卡配置 IP而不是在宿主机上配置。RIOT 里有一个ifconfig命令在运行起 native 实例后通过串口其实在终端里输入ifconfig 5 add 2001:db8::2/64 ifconfig 5 up这种方式完全不需要宿主机层面的权限RIOT 内部的网络栈自己管理 IP。不过测试它和宿主机之间的通信时宿主机也得能在同一个子网内访问到它如果宿主机侧无法配置 IP就只能在纯 RIOT 场景下做广播测试或两个 RIOT 实例互通测试。我这次的测试场景就是两个 RIOT native 实例通过同一个 tap 桥接设备互通这样就规避了宿主机侧需要 root 配置 IP 的问题。3.5 运行两个 RIOT 实例实现本地互联既然管理员帮我创建了 tap0 并提升到可用状态但并没有给它配 IP那我在宿主机上其实没法直接用这个 tap 和 RIOT 通信。于是我想了个办法创建一个 Linux bridge需要 sudo把两个 tap 设备桥接在一起然后分别跑两个 RIOT 实例各自挂在不同的 tap 上。具体操作如下sudo ip link add name br0 type bridge sudo ip tuntap add dev tap0 mode tap user $(whoami) sudo ip tuntap add dev tap1 mode tap user $(whoami) sudo ip link set tap0 master br0 sudo ip link set tap1 master br0 sudo ip link set br0 up sudo ip link set tap0 up sudo ip link set tap1 up然后把第一个 RIOT 实例绑定到 tap0第二个实例绑定到 tap1。RIOT native 的网卡设备一般默认是 tap0如果系统里存在多个 tap它会自动挑选。如果想手动指定可以通过环境变量或者 make 变量传入。更简单的做法是直接运行两个 gnrc_networking 实例RIOT 启动时如果发现当前用户有权限访问多个 tap 设备可能需要手动在 shell 里操作。但为了方便一般只保留 tap0把它挂到桥接设备上再创建一个虚拟以太网对veth pair来配合。我测试时用的是两个实例分别绑定不同的 tap 设备这样互不影响而且非常稳定。4. 吞吐量测试从 0 到 28 Mbit/s 的调试过程4.1 测试工具的选择RIOT native 实例起来之后就需要想办法测吞吐量。RIOT 本身没有 iperf3 这种现成的工具但可以用自带的gnrc_networking加 shell 命令进行 UDP 收发配合iperf3在宿主机上如果装了或者直接自己写一个简单的 socket Python 脚本。我一开始在宿主机上用 nc 向 tap0 发包但在两个 RIOT 实例之间测只需要在一个实例上跑一个 UDP 服务端另一个实例上用 RIOT 自带的udp命令发送数据。RIOT 的 gnrc_networking shell 支持udp send addr port payload这种命令。例如在第一个实例上执行udp server start 8888在第二个实例上执行ifconfig 5 add 2001:db8::2/64 ifconfig 5 up udp send 2001:db8::1 8888 hello如果通了说明两个实例之间的网络路径是通的。但这种方式吞吐量受限因为每次只能发送固定长度字符串。要测到 28 Mbit/s 这种量级需要循环发包。这时候可以充分利用 Python 在宿主机上写一个瓶塞发送脚本通过 tap 接口向 RIOT 实例发包。但这里的问题是宿主机上的 Python 要能拿到目标地址的路由还得能发 IPv6 包到 tap 网段。如果宿主机没有权限配置地址和路由就很难把数据从宿主机送进 tap 桥接网络。所以我的做法是在一个 RIOT 实例上运行一个 UDP 服务端然后让另一个 RIOT 实例运行一个自定义的 UDP 压力发送程序。我用的是 RIOT 现有的examples/gnrc_networking里的 shell 扩展办法但那个比较麻烦后来发现其实可以直接用 dev 分支里的iperf模块。RIOT 在 2023 年之后的版本引入了iperfshell 命令只是需要编译时打开相应模块USEMODULE iperf在examples/gnrc_networking/Makefile里加上这一行重新编译即可。有了iperf在 RIOT 终端里就可以运行iperf server start 8888然后在另一个实例上iperf client connect 2001:db8::1 8888 -t 10 -i 1 -b 30M实测下来使用这种方式系统可以通过内部网络栈将大量 UDP 数据包从一个实例搬到另一个实例吞吐量开始稳定在 28 Mbit/s 左右。4.2 28 Mbit/s 这个数字是怎么来的很多人可能会疑惑为什么是 28 Mbit/s 而不是 1000 Mbit/s这有两方面的原因。第一RIOT native 的收发路径有性能瓶颈。它虽然是用户态进程但每次收发包都要经过系统调用、在 RIOT 的网络栈和 Linux tap 设备之间复制数据同时还要处理 RIOT 线程调度和信号机制这些都会限制吞吐量。第二UDP 包大小默认可能是 1280 字节这在 MTU 为 1500 的网络里不算小但 RIOT 内部默认 buffer 大小可能不够大导致发送窗口受限制了。我在测试过程中尝试过调大 RIOT 的报文缓冲区大小。可以在编译时加CFLAGS-DGNRC_NETIF_HDRS_BUFSIZE...或者改相应配置。如果 buffer 增大吞吐量会有一定提升但与之相对的内存占用和调度延迟也会增加。我把 buffer 调大了一倍实测从 20 Mbit/s 提升到了 29 Mbit/s 左右最后一次稳定在 28 Mbit/s 左右。如果你的测试场景是纯 native 模拟不追求跟真实嵌入式硬件一致这个吞吐量已经足够验证协议栈逻辑了。如果想追求更高性能就得考虑编译优化等级。在 Makefile 里加-O2或-O3对吞吐量也会有帮助。4.3 调整参数让它更接近真实场景除了调整 buffer还可以通过修改 RIOT 的调度器 tick 频率和时间片来优化 native 模式下的网络吞吐。但这个参数会影响实时性不建议猛地调低。我这次保持默认只改编译优化等级和 buffer效果已经比较理想。为了让测试结果可复现我把相关参数写进了 MakefileBOARD ? native CFLAGS -O2 CFLAGS -DGNRC_NETIF_HDRS_BUFSIZE4096 USEMODULE iperf然后重新编译运行。这样测试出来的数据具备较好的参考性。5. 常见问题与排查技巧实录5.1 “Permission denied” 直接不给面子运行 RIOT native 时如果 tap 设备权限不对最常见的问题就是启动时报类似error creating tun/tap interface或者permission denied。解决办法是让管理员把 tap 设备 chown 给你或者检查是否当前用户属于创建 tap 所需的组。如果没有 sudo 也没有管理员就用socket_zep方案不要死磕 tap。5.2 make 编译时找不到 RIOTBASE很多时候我从别的目录复制 example 过来编译忘记设置 RIOTBASE就会报错。解决方法make BOARDnative RIOTBASE/home/xxx/work/RIOT或者在 example 目录的 Makefile 里加一行RIOTBASE ? /home/xxx/work/RIOT。没有 sudo 的时候路径尽量写绝对路径省去环境变量传递的问题。5.3 编译时提示“g: command not found”RIOT 的部分模块如 C 支持需要 g如果系统没装就编不过。没有 sudo 的情况下可以下载预编译的 GCC 工具链到用户目录然后设置CXX环境变量指向对应路径。但如果你只跑 C 环境的示例基本用不到 g。5.4 两个 RIOT 实例都绑定到同一个 tap如果你的系统里有多个 tap 设备但 RIOT 默认选第一个可用的可能导致两个实例绑定到同一个设备这时候网络会不通。可以在运行前通过环境变量指定端口或改为使用不同的 tap 设备。RIOT native 有一个--eui64参数或者PORT变量控制串口串口终端不同但网卡 tap 选择往往是自动的。如果出现这个情况可以先ip link set tap1 down再启动第二个实例强制它选另一个。5.5 吞吐量上不去一直卡在个位数如果吞吐量一直很低先排查是不是 MTU 太小或者 buffer 不够。可以在 RIOT 实例里运行ifconfig查看当前网卡的 MTU 和统计信息。再把发送端的发送缓冲区调大或者把iperf的传输长度提高。5.6 CentOS 上同样思路能不能用网上的热词里有人提到 CentOS 如何切换 root 权限这类问题其实很多思路是共通的。CentOS 里没有 sudo 的情况也同样可以用本地目录装工具链的方法。不过 CentOS 默认用的可能是 dnf不是 apt但下载 tar 包解压的方法完全不变。如果系统版本太老gcc 版本不够新RIOT 编译可能报错这时候下载新版 GCC 预编译包到用户目录就行只是注意设置CC环境变量。6. 最后的实操建议和扩展方向这一套方法下来整体体验还算顺利。没有 sudo不代表什么都做不了关键是把思路从“安装系统包”切换成“用户目录下的工具链 用户态模拟”。尤其在嵌入式开发里很多时候验证代码逻辑并不一定要真硬件RIOT native 就是一种颇具效率的快速验证手段。我自己在用 RIOT 做原型验证时总结的经验是能不开特权就不开尽量把所有依赖控制在用户目录下这样环境即用即走不污染系统也不影响别人。再加上 build 目录清理方便对共享开发机来说特别友好。这次验证完吞吐量之后我后续打算继续扩展一下这个测试环境加一些流量控制模块和 QoS 策略进去真实模拟物联网网关上的行为。如果你对 RIOT 的网络栈感兴趣也可以从这个测试环境入手先把环境跑通再去读它内部的 gnrc 网络栈源码会比空看文档高效得多。