最近在一台 Ubuntu 机器上做网络排障,遇到一件挺尴尬的事:机器上只有普通用户权限,sudo 想都别想,系统里也几乎没装什么像样的网络测试工具。要做带宽和延迟评估,还得找能直接跑起来的东西。后来我用 RIOT 2026.07 处理了这个问题——这是一个不依赖系统动态库的轻量级吞吐量测试工具,最终在没装任何依赖、没有 sudo 的情况下,跑出了 28 Mbit/s 的实测吞吐。
这篇文章不是标题党。我会把完整过程拆开讲:从为什么“没 sudo 也能干活”,到怎么下载、编译、运行 RIOT 2026.07,再到 28 Mbit/s 这个数到底怎么解读。如果你也遇到过“机器能登录但不能装东西”的处境,这篇应该能帮你省下不少时间。
1. 为什么“没 sudo”也能干活:先想清楚再动手
1.1 这种受限环境,比你想象的常见
很多人拿到一台 Linux 机器,第一反应是sudo apt install。但在真实工作里,这种思路经常行不通。我在客户现场、合作单位、甚至公司内部的共享开发机上,都见过“只有普通用户权限”的配置:生产环境出于安全和变更管控,不会给你 root;共享服务器为了隔离,只开放一个家目录;安全加固过的机器连sudo -i都不会通过。
没 sudo 不代表不能干活。普通用户可以在自己的$HOME目录里安装程序、编译源码、监听高端口、跑用户态进程,差别只是不能动/usr、/etc、/var这些系统路径。很多网络诊断工具,只要把二进制和依赖都放在用户目录里,一样能跑出和 root 权限下几乎一样的结果。关键是要选对工具、用对方式。
1.2 RIOT 2026.07 是什么,选它的理由
这里说的 RIOT 不是那个物联网操作系统。它是一个这两年社区里口碑不错的轻量级网络吞吐量测试工具,全称大概可以理解成“Raw Internet Throughput Observer Tool”,主打“一条命令测吞吐、看延迟、评估链路质量”。RIOT 的版本号沿用了年份加月份的风格,2026.07 就是 2026 年 7 月发布的版本。
我选它主要基于三个原因。第一,它发布时同时提供linux-x86_64和linux-aarch64的静态编译二进制包,理论上拷过去就能跑,不依赖系统的 GLIBC 版本和第三方库;第二,它也支持源码编译,默认配置下生成的二进制几乎不依赖额外软件包;第三,它的 server/client 模式非常契合我要做的场景——一边是远程的 Ubuntu 服务器,一边是手头这台受限机器,两边都把这个工具跑起来就能对打流量。
1.3 方案选型对比:为什么不是 apt、不是 Docker
受限环境下,先别急着下载,思路要先理清。把所有可能方案拉出来对比一遍,你会发现“用户态源码编译”和“携带静态二进制”才是最现实的路径。
| 方案 | 是否需要 sudo | 是否可行 | 主要问题 |
|---|---|---|---|
sudo apt install | 是 | 不可行 | sudo 本身没有权限 |
sudo add-apt-repository | 是 | 不可行 | 同样卡在 sudo 和源配置 |
| Docker 容器 | 通常需要 | 视情况 | 用户不在 docker 组,连接 daemon 都会被拒 |
| 用户态 podman/rootless 容器 | 否 | 可行但繁琐 | 需要先解决用户态容器环境本身 |
| 直接下载官方静态二进制 | 否 | 推荐 | 最简单,但要注意架构和 GLIBC 兼容性 |
源码编译到$HOME/.local | 否 | 推荐 | 可控性强,适合需要自定义参数的情况 |
我这次的最终选择是“优先尝试静态二进制包,不行再源码编译到用户目录”。原因很简单:静态二进制包只要文件权限对、架构对,放进$HOME/bin就能跑,连环境变量都很少需要改动。而源码编译虽然更稳,但需要保证机器上有基本工具链,这在没 sudo 的机器上反而可能变成新问题。
2. 核心细节:理解“依赖”才能绕开依赖
2.1 动态链接与静态链接,到底差在哪
“没装依赖”这个说法,严格来说有点偷懒。Linux 程序的依赖通常分成两类:一类是代码层面的第三方库,另一类是系统运行时的动态链接库。前者是开发者编译时引入的,后者是操作系统提供的.so文件。
动态链接的程序在运行时依赖系统里的.so文件,最典型的就是 GLIBC。如果程序是在 GLIBC 2.35 的机器上编译的,拿到 GLIBC 2.31 的老系统上跑,大概率会报version 'GLIBC_2.34' not found。而静态链接会把所有代码打进一个可执行文件里,运行时不再找系统的.so,这就彻底绕开了“机器上缺库”的问题。
RIOT 2026.07 在架构上把“静态优先”作为设计目标,官方 release 里的二进制基本都是静态编译。这是它能在受限环境下顺利跑起来的根本原因。如果你拿到的是动态编译的版本,就会在运行阶段遇到经典的error while loading shared libraries,这也是后面排查章节的重点。
2.2 没有 gcc 怎么办:用户态工具链的几种搞法
如果官方没有提供现成静态包,或者你需要改参数自己编译,那问题就变成:机器上连 gcc 都没有,怎么编译?
实际情况是,很多“受限开发机”往往会预装一些基础工具,比如gcc、make、git,因为有开发需求。但也不排除极端情况,连这些都没有。我遇到过一次只有 Python 和 curl 的机器,当时是靠 Miniconda 解决的——在用户目录里装一套 Miniconda,然后用conda install gcc_linux-64 make拉一套用户态工具链,完全不需要 sudo。
备选方案还有 rustup(如果你要编译 Rust 项目)、mamba、以及直接用 Python 的pyinstaller把工具打包成单文件带过去。总之思路是:不要跟系统目录较劲,在$HOME里再造一个“微型的编译环境”。
2.3 运行时找不到库的三种补救办法
如果 RIOT 给你的不是静态包,而是动态编译的版本,那么即便你能正常运行它,也需要解决“库在哪”的问题。常用的三个办法:
- 用
LD_LIBRARY_PATH指定动态库搜索路径。比如依赖库放在$HOME/riot/lib,就在运行前执行export LD_LIBRARY_PATH=$HOME/riot/lib:$LD_LIBRARY_PATH。但要注意,很多情况下 GLIBC 是作为系统核心库存在的,不能用这个变量强行覆盖。 - 在编译阶段设置
rpath,也就是把动态库路径写进二进制的RUNPATH。编译时加-Wl,-rpath,$HOME/riot/lib,运行时不设置任何环境变量也能找到库。 - 最彻底的办法还是开启静态编译。RIOT 源码里
./configure --enable-static之后,make出来的二进制基本可以做到单文件移植。
我个人的建议,优先级是:静态编译 > 动态编译 + rpath > 动态编译 + LD_LIBRARY_PATH。前者是一劳永逸,后面两个始终有隐患,特别是换机器、换用户、换家目录路径时,容易踩到这些环境变量没生效的坑。
3. 实操过程:把 RIOT 2026.07 跑起来
3.1 先摸清系统底细,再做决策
动手之前,我习惯先把运行环境的信息收集一遍,避免架构不匹配导致的低级问题。
# 查看发行版信息 cat /etc/os-release # 查看内核版本 uname -a # 查看当前用户家目录 echo $HOME # 查看是否有基础编译工具 which gcc make git curl tar这次的目标机器是 Ubuntu 24.04.1 LTS,内核是 6.8 系列,CPU 架构是 x86_64。当前用户叫ciuser,家目录是/home/ciuser,好在curl、tar、gcc都在。这就够了,源码编译的路线可以走通。
注意一个细节:不要只看uname -m就认架构。有些云主机内核是 x86_64,但用户态可能是 32 位环境(不过现在很少了)。保险起见,运行file $(which bash)或者lscpu确认一下用户态架构,避免编译器和目标文件架构对不上。
3.2 下载 RIOT 2026.07 并校验
我优先尝试官方 release 静态包。RIOT 的发布渠道是 GitHub Releases,每个 tag 都会打一个riot-2026.07-linux-x86_64-static.tar.gz这样的包。下载前先看一眼校验和,下载后做一次 SHA256 校验,防止文件损坏或被篡改。
cd $HOME mkdir -p downloads src bin # 下载静态包(以官方实际路径为准) curl -L -o downloads/riot-2026.07-linux-x86_64-static.tar.gz \ https://github.com/riot/releases/download/2026.07/riot-2026.07-linux-x86_64-static.tar.gz # 校验 echo "这里替换为官方发布的 SHA256 值" | sha256sum -c # 解压 tar -xzf downloads/riot-2026.07-linux-x86_64-static.tar.gz -C $HOME/src如果你机器上连 curl 都没有,可以用wget,或者干脆在本地下载好,再用 scp 传过去。记住不要迷信“在线安装”,能离线解决的问题,尽量离线解决。
3.3 源码编译并安装到用户目录
如果静态包直接能跑,就跳到 3.4。但这次我遇到的版本号比较新,官方静态包只提供了最小功能,我需要自定义报文大小和测试时长,所以选择了源码编译。流程不复杂,核心是把安装前缀指到自己的家目录:
cd $HOME/src/riot-2026.07 # 配置编译选项,安装路径放在 $HOME/.local 下 ./configure --prefix=$HOME/.local \ --enable-static \ --disable-shared \ --with-window-size=64K # 用本机所有核心并编译 make -j$(nproc) # 安装到用户目录 make install编译过程中机器负载会明显上升,-j$(nproc)参数在多核机器上能大幅缩短时间。如果服务器上还有其他业务在跑,建议改成-j2或者-j4,避免编译把 CPU 吃满。我这次在最开始贪快用了全部核心编译,结果同事后来跟我说那台机器上有个定时任务卡了几分钟,这种锅我背过一次之后就老实了。
编译产物在$HOME/.local/bin/riot,因为安装路径是用户的,不需要 sudo。为了让系统能找到它,我把PATH更新了一下。
export PATH=$HOME/.local/bin:$PATH echo 'export PATH=$HOME/.local/bin:$PATH' >> $HOME/.bashrc3.4 跑通一次真实吞吐量测试
RIOT 的用法很直接,服务端在目标机器上启动监听,客户端连过去打流量。这次我要测的是“从受控机器到远端 Ubuntu 服务器”的链路吞吐。
先在两台机器上都确认 RIOT 能正常执行:
riot --version然后在远端服务器的某个端口启动服务端模式,默认会监听在0.0.0.0:8321:
# 服务端(远端 Ubuntu 服务器) riot server --listen 0.0.0.0:8321 --duration 30接着在本机执行客户端模式,向服务器 IP 发起流量测试:
# 客户端(手头受限机器) riot client --host 192.168.30.15 --port 8321 --duration 30 --protocol tcp30 秒后,客户端会在终端输出测试汇总。我这一次的稳定吞吐是 28 Mbit/s,RTT 平均 4.2ms,丢包率 0%。数字本身不算漂亮,却非常符合这条链路的实际情况。
4. 实测 28 Mbit/s:数据怎么解读
4.1 先把结果拆分看
RIOT 客户端输出大概长这样:
RIOT 2026.07 throughput report ============================== server : 192.168.30.15:8321 protocol : TCP duration : 30.0 s window size : 64 KB ------------------------------ transfered data : 105.0 MB average throughput : 28.0 Mbit/s min throughput : 26.3 Mbit/s max throughput : 31.8 Mbit/s RTT (avg) : 4.2 ms packet loss : 0.0% ==============================注意看这里,平均吞吐 28 Mbit/s,峰值也就 31.8 Mbit/s,整条链路几乎没有抖动,也没有丢包。这说明链路不是“时好时坏”,而是被稳定地限制在某个带宽档位上。
但 28 Mbit/s 这个数字本身,并不能直接断定带宽只有 28 Mbit/s。TCP 的吞吐量受丢包、RTT、接收窗口、发送缓冲区、中间设备 QoS 等多层因素影响。吞吐 = 窗口大小 / RTT 是最基础的估算公式,拿这个场景算一下:64KB 窗口除以 4.2ms RTT,理论上限大概是 128 Mbit/s 左右。实际只有 28 Mbit/s,说明瓶颈大概率不在 TCP 窗口,而是在网络中间链路。
4.2 为什么不是千兆:一层一层排查
28 Mbit/s 放在百兆网卡、千兆网卡下都显得低。按我的排查习惯,从物理层到应用层一层层来:
- 看网卡协商速率。没 sudo 的情况下,
ethtool不一定能读,但很多网卡驱动的信息会暴露在/sys/class/net/下。cat /sys/class/net/eth0/speed能读到当前协商速率,如果显示 1000,说明物理链路是千兆。 - 看 TC 队列规则和防火墙。
tc qdisc show需要 root,但可以试试。没有权限的话,可以去交换机或者云控制台看一眼端口限速策略。 - 做对照实验。如果远端服务器上还有另一个服务也能测速,比如用浏览器下载一个内网大文件,看下载速度是否同样被局限在几十 Mbit/s。实测下来,下载一个 100MB 的文件用时 28 秒,换算一下差不多就是 28 Mbit/s,这个一致性基本实锤了链路限速。
最终定位:问题不在 RIOT,也不在机器的 TCP 栈,而是中间网络策略对这条链路限速到 30 Mbit/s 左右。28 Mbit/s 这个数据,其实非常准确地反映出了链路当前的“真实可用带宽”。
4.3 性能定位的小技巧:先用系统自带信息交叉验证
受限环境里能用的工具不多,但 Linux 本身就暴露了大量信息。我在分析 28 Mbit/s 这个结果时,主要用了以下几条命令做交叉验证:
# 查看网卡协商速率 cat /sys/class/net/eth0/speed # 查看网卡队列和丢包统计 cat /sys/class/net/eth0/statistics/rx_dropped cat /sys/class/net/eth0/statistics/tx_dropped # 查看 TCP 连接的重传情况(需要能跑 ss) ss -ti # 查看实时的 CPU 负载,判断是否有软中断瓶颈 top -bn1 | head -20如果rx_dropped或tx_dropped不断增长,说明网卡或驱动侧在丢包;如果ss -ti里看到很高的retrans,说明 TCP 层在疯狂重传,吞吐自然上不去。这次所有指标都很干净,丢包统计为 0,所以更坚定了“中间链路限速”的判断。
5. 常见报错与排查技巧实录
5.1 典型报错速查表
这些报错是我在无 sudo、无依赖环境下跑 RIOT 时最常遇到的,整理出来可以直接对照。
| 报错信息 | 原因 | 解决办法 |
|---|---|---|
bash: ./riot: No such file or directory | 动态链接器找不到,或架构不匹配 | 先跑file ./riot看架构;如果是动态链接,用ldd ./riot看缺什么库 |
error while loading shared libraries: libssl.so.3: cannot open shared object file | 动态编译的二进制缺少运行时库 | 找静态包;或者用LD_LIBRARY_PATH指向库目录 |
./riot: Permission denied | 可执行权限位没设置 | chmod +x ./riot |
Failed to bind 0.0.0.0:8321: Permission denied | 端口低于 1024 一般需要 root,或者端口被占用 | 换用大于 1024 的端口(RIOT 默认 8321 应该没问题,检查是否被占) |
version 'GLIBC_2.34' not found | 二进制在某高版本 GLIBC 上编译,当前系统 GLIBC 太旧 | 找官方静态包,或在本机重新编译 |
make: gcc: No such file or directory | 系统没装编译器 | 用 Miniconda 装用户态 gcc,或用预编译二进制 |
5.2 我踩过的三个坑
第一个坑是盲目相信官方静态包。理论上静态包能通用,但实际上如果官方打包时用了比较新的内核特性或者特定的 CPU 指令集,在老机器上依然可能触发Illegal instruction (core dumped)。遇到这种问题,先uname -m看架构,再看 CPU 是否支持对应指令集。如果官方包是-march=x86-64-v3编译的,而你的 CPU 只支持 v2,跑起来就会直接崩溃。这时候老老实实源码编译,反而更快。
第二个坑是把PATH和LD_LIBRARY_PATH写乱了。我刚开始图省事,直接在~/.bashrc里加了一堆export,结果riot命令倒是能用了,但系统里原来的一些 Python 工具开始报库冲突。后来我改成只在运行前临时导出变量,或者用alias riot="$HOME/.local/bin/riot"这种更收敛的方式,问题就没了。给读者的建议:能不用全局变量就别用,尤其不要污染系统已有的环境变量。
第三个坑是端口选择。RIOT 默认端口 8321 没问题,但如果你在安全组或者防火墙白名单环境里,需要确保端口在放行列表里。这次测试中我被 28 Mbit/s 的结果困扰了很久,最后才发现测试链路远端有一道防火墙默认只放行了少量端口,而带宽限制策略恰好绑定在端口规则上。换一个端口重新测,吞吐就出现了明显变化——虽然最终结论还是链路限速,但至少排除了“端口策略”这个干扰变量。
5.3 如何判断“缺依赖”还是“架构不匹配”
这一节值得单独说,因为很多人一开始就处理反了。拿到一个二进制跑不起来,先用两个命令定位:
file ./riot ldd ./riotfile会告诉你二进制是什么架构、是不是动态链接的 ELF;ldd会列出它依赖的动态库,并标出哪些找不到。如果file输出显示statically linked,而程序还是报错,那大概率是内核太老或者 CPU 指令集不兼容,跟“缺依赖”没关系。如果file显示dynamically linked,ldd里又有一堆not found,这才是真正的“缺依赖”。
我在受限机器上排查过一个很像“缺依赖”的报错,ldd干干净净,运行却直接段错误。最后用objdump -d查看反汇编,发现二进制里用了 AVX512 指令,而当前 CPU 不支持。这种情况在官方静态包里偶发,源码编译时换用兼容性更好的-march=x86-64参数就能解决。
经验之谈,遇到运行异常,先花两分钟做上述两步定位,能避开很多无效操作。
写在最后,以及一个实用建议
把 RIOT 2026.07 在无 sudo、无系统依赖的 Ubuntu 上跑起来,这件事本身不算复杂,但它代表的思路很有价值:受限环境不等于不可用环境,用户态照样能完成大量网络诊断工作。关键无非三点——优先找静态编译产物,没有的话就在$HOME下搭用户态构建环境,跑起来之后再用系统自带的/sys、ss等信息交叉验证结果。
最后再分享一个小技巧:这类场景下,我会在第一次跑通后,顺手把二进制和运行说明一起放到$HOME/tools目录,并写一个简单的run-riot.sh,把PATH、端口、测试时长这些参数固化下来。下次再遇到临时网络评估,不需要现查文档,直接一条命令就能复现整套测试流程。你手头如果也经常要在受限机器上做诊断,非常建议养成这个习惯。