news 2026/9/7 10:45:44

无sudo跑通RIOT native模式:受限Ubuntu下的物联网网络栈测试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
无sudo跑通RIOT native模式:受限Ubuntu下的物联网网络栈测试

公司给团队配了一台共享的 Ubuntu 服务器,我的账户是个普普通通的低权限账号。刚坐下准备干活,就撞上两堵墙:sudo -n true直接报错,用户不在 sudoers 里;apt install想都不用想。更绝的是,系统提示sudo: add-apt-repository: 找不到命令,说明这台机器连 software-properties-common 都没装,而我又没资格去补。简单说,我就是被困在了一个“能登录、能写自己目录、能干瞪眼”的 Linux 环境里。

但这会儿手头有个物联网项目,要求验证 RIOT 2026.07 发布版的网络栈在真实系统上的表现。RIOT 是个物联网操作系统,平时玩嵌入式的人应该不陌生,但要在服务器上跑,主流做法是用它的 native 原生模拟模式,在 Linux 用户空间直接编译出一个 ELF 可执行文件来模拟整块“板子”。我原本有点慌,后来仔细一想,RIOT 的 native 模式压根不依赖 apt 安装任何软件包,也不需要我必须拥有管理员权限,唯一卡脖子的可能就是 TAP 虚拟网卡和权限。最后我不但把 RIOT 跑了起来,还在 10 秒持续打流中测到了 28 Mbit/s 的 UDP 吞吐量。全程没有执行过一次 sudo,也没有用 apt 装过一个包。

这篇就完整记录我在受限 Ubuntu 环境里跑通 RIOT 的整个过程,包括环境盘查、源码构建、权限绕行、吞吐量测试和事后复盘。如果你也遇到过“没 sudo 怎么跑自己的程序”这种问题,这篇应该能给你一个不一样的思路。

1. 被 sudo 卡住之后,我怎样摸清这台 Ubuntu 的家底

1.1 第一印象:不是没装 sudo,是没资格用

很多人看到“没有 sudo”第一反应是去网上搜什么“sudo 改密码”“找回 root”,其实这种思路一开始就跑偏了。共享服务器上没有 sudo 是常态,安全策略就是不该让你有。我登录后的真实状态是这样的:

$ whoami devuser $ sudo -n true [sudo] password for devuser:

sudo -n true的意思是“以非交互方式执行 sudo true”,如果有权限会直接返回成功,如果没权限会提示需要密码。我没有密码可输,也不该去试。这里不用纠结管理员为什么不给,先把现状接受下来,再想办法。

顺手再看一眼系统里这些常见命令是否可用:which gcc make python3 git。结果全部存在。

$ uname -m x86_64 $ cat /etc/os-release | grep PRETTY_NAME PRETTY_NAME="Ubuntu 22.04.4 LTS" $ gcc --version | head -1 gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 $ python3 --version Python 3.10.12

到这里我心里就有数了:这台机器装的是 Ubuntu 22.04 LTS,x86_64 架构,gcc 11、make、python3、git 全都齐。大部分嵌入式交叉编译环境需要的 arm-none-eabi-gcc 我这里根本没有,但 RIOT 的 native 模式是个例外,它直接把 Linux 当成目标板,用宿主机的 gcc 来编。

1.2 受限环境下先盘清“有什么”,比抱怨“没什么”更重要

我把检查结果整理成了一张非常简单的表,后面所有决策都围绕这张表展开:

资源状态对 RIOT 的意义
gcc 11.4可用native 模式编译所需宿主编译器
make可用RIOT 构建系统基于 make
python3可用RIOT 部分构建脚本会调用
git可用拉取源码
/dev/net/tun存在native 网络模拟的关键设备
apt install不可用无法安装额外系统包
sudo不可用不能做任何特权操作
root 用户不可用同上

这张表的结论非常明确:所有“非特权用户能用的基础开发工具”都在,缺的只是需要 root 才能安装或修改的东西。那么最合理的策略,就是找一种能够完全自包含在用户空间、不依赖额外系统软件包和特权操作的运行方式。

RIOT OS 正好符合这个条件。它的 native 模式在构建时使用的是宿主机的标准 C 库和 pthread,不引入第三方依赖库,源码仓库自带了内核、协议栈、驱动框架等全部模块。对 Ubuntu 系统来说,它只需要 libc、libpthread 和 /dev/net/tun,而这些都是 Ubuntu 默认安装的一部分。

1.3 为什么 RIOT 值得在这个环境尝试

RIOT 在物联网圈子里不算冷门,但很多人对它的印象还停留在“要交叉编译到板子上跑”。实际上它的开发调试流程设计得很现代:开发功耗敏感或资源受限的嵌入式应用时,你完全可以先用 native 模式在 PC 上把业务逻辑、网络协议栈调通,再交叉编译到真实硬件。

native 模式的本质,是把 Linux 的 TAP 虚拟网卡当作 RIOT 的“物理网卡”,把 pthread 当作 RIOT 任务调度器的底层实现。RIOT 进程看起来就是一个普通 Linux 进程,跑起来后在系统里就是个iperf.elf,但内部运行着一套完整的物联网操作系统内核、网络栈和 shell。

这套机制给我的最大启发是:很多嵌入式系统的开发流程,其实不需要一台硬件开发板,也不需要整个系统级别的安装权限。你在受限服务器上照样能完成协议验证。它让我重新理解了“依赖”这个词——传统思维里做项目先apt install一堆包,但有些项目的设计根本不需要你往系统里塞任何东西。

2. 把 RIOT 2026.07 从源码变成 ELF:构建过程的坑与绕行

2.1 获取源码:浅克隆还是完整下载

既然没有权限,下载源码自然也只能放自己家目录。我习惯用~/work作为项目目录:

mkdir -p ~/work && cd ~/work git clone --depth 1 --branch 2026.07 https://github.com/RIOT-OS/RIOT.git cd RIOT

--depth 1只克隆最近一次提交,能省不少时间和磁盘。--branch 2026.07表示直接切到指定发布版。RIOT 的版本号规则是年份加月份,每年 1 月和 7 月各发布一个版本,所以 2026.07 指的就是 2026 年 7 月发布的版本,它的发布周期非常规律。

如果你所在的服务器连 GitHub 都不通,可以找一台自己的电脑下载 tar 包再传上去,但这不是本文重点。我先假设这台开发服务器的外网是通的,项目也能正常拉取代码。

2.2 选对测试用例:我选 tests/iperf 而不是 examples/gnrc_networking

RIOT 官方自带的 example 非常多,最常见的是examples/gnrc_networking,它提供一个基于 UDP 的 shell,适合手动敲命令测试网络。但如果要测吞吐量,这个例子就不太够,因为你要另外想办法打流。RIOT 源码树里还有tests/iperf,这是性能测试专用的用例,内置了 UDP/TCP 客户端和服务端逻辑。

我最后选择了tests/iperf,理由很简单:我在宿主机上没有安装 iperf 的权限,但 RIOT 侧自带一个 iperf 兼容实现,这样我就能让 RIOT 作为打流发起方,宿主机只要用 Python 标准库写个接收端即可。整个过程不需要在宿主机上额外装任何软件。

进入测试目录开始构建:

cd ~/work/RIOT/tests/iperf make BOARD=native all

第一次构建可能需要一点时间,RIOT 会把所有核心模块编译一遍。编译过程中屏幕会滚动大量CCARLD日志,最后停在生成bin/native/iperf.elf。这个文件就是一个标准的 Linux ELF 可执行文件,用file命令看一眼:

$ file bin/native/iperf.elf bin/native/iperf.elf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, not stripped

到这里其实已经成功了一大半,但先别急着运行,因为直接运行会报错。RIOT native 模式启动时如果指定了网络接口,它要打开/dev/net/tun并创建或绑定一个 TAP 设备,普通用户默认没有这个权限。关于这一步,我在下一节详细说。

2.3 构建中可能遇到的“没有依赖”问题

在实际构建过程中,我还是碰到了一些和“依赖”有关的状况。

最常见的报错是python3: command not found。RIOT 的构建系统在进行配置解析时会调用 Python 3,如果你登录的这台机器不是标准 Ubuntu,确实有可能没装。但我们这台机器有,所以没事。如果你遇到没有 Python 3 的情况,可以在用户目录下用ln -s /usr/bin/python3.10 ~/bin/python3这类方式做一个指向,但更稳妥的是先检查系统是否已经存在某个 Python,不要一开始就想着怎么绕过。

第二个可能的坑是 GCC 版本过老或过新导致编译告警。Ubuntu 22.04 自带的 GCC 11.4 可以正常编译 RIOT 2026.07,但如果你所在的环境是老掉牙的 CentOS 7,GCC 4.8 肯定编不动。RIOT 官方要求 GCC 版本至少要到某个新版本,这个在文档里有说明。如果遇到版本不满足,又没有权限升级系统包,那就真没辙了,只能考虑用容器或用户空间工具链,但这属于另一个话题。

第三个坑是文件系统。不要把源码放在 NFS 挂载的共享目录里构建,RIOT 构建过程中有大量文件锁和临时文件操作,NFS 上容易出奇怪问题。放$HOME下最安全。

构建完成后,RIOT 会把编译出的中间文件放在$(BUILD_DIR),默认是用户目录下可写的路径。整个构建过程完全没有碰过/etc/usr这些系统目录,日后想清理,直接rm -rf ~/work/RIOT就算卸载干净。这种“完全用户空间”的特性,在无权限环境里太重要了。

2.4 验证产物并首次运行

第一次运行我想先试一下不带网络接口的裸跑,验证 ELF 本身没问题:

cd ~/work/RIOT/tests/iperf ./bin/native/iperf.elf

这样会进入 RIOT shell,出现>提示符,并且没有任何网络接口配置的报错。在这个 shell 里可以敲help查看可用命令,还能直接内核命令行操作。然后再输入quit退出进程。

如果运行时报cannot open /dev/net/tunPermission denied,说明缺少 TAP 设备访问权限。这正是我下一节要解决的核心问题。这一阶段的核心目标是把可执行文件构建出来,确认进程能正常启动,不需要一路顺畅跑通网络,先把构建链路验证了再说。

3. 没有 sudo 的 TAP 网卡:我和管理员做的一次权限交易

3.1 为什么 RIOT native 默认需要 root?

RIOT native 模式模拟网络时,需要在宿主机上创建一个 TAP 设备。TAP 是一种虚拟二层网卡,工作在内核态,由/dev/net/tun这个字符设备来创建和控制。默认情况下/dev/net/tun的所有者是 root,所以普通用户程序去open()这个设备时会被拒绝。这就是为什么很多 RIOT 文档和博客里写的是sudo ./bin/native/xxx.elf tap0

问题来了:没有 sudo 是不是就彻底没办法了?不是。关键不在 sudo 这个词本身,而在谁能打开/dev/net/tun、谁能把某个 TAP 设备的使用权交给指定用户。我把这个权限关系和“别人借你一把钥匙开门”类比:你不一定能配万能钥匙,但你可以让有权限的人给你一把专属于你的门钥匙。

3.2 最省事的方案:让管理员执行一次ip tuntap

当时我不想去跟管理员扯皮“我需要 root 跑测试”,而是直接告诉他:请帮我创建三个可长期使用的 TAP 接口,并且把接口使用权给我,之后测试我自己来。管理员只需要执行这三条命令:

sudo ip tuntap add dev tap0 mode tap user devuser sudo ip link set dev tap0 up sudo ip addr add 10.0.0.1/24 dev tap0

这三行的含义分别是:创建一个名为tap0的 TAP 设备,并把该设备的所有者指定为devuser;把设备启用;给它配置一个 IP 地址。执行之后,我的普通用户账户就能打开tap0这个设备与内核网络栈通信,却依然没有系统管理权限。这是一种典型的最小权限授权方式:不给你整个系统的钥匙,只给你一个能完成工作的门钥匙。

如果管理员不太愿意执行上面几条命令,你还可以检查自己的账户是否已经属于tunnetdev用户组。有的 Ubuntu 系统会把网络设备管理权限授给这些组:

$ groups devuser adm dialout cdrom sudo? ...

但很多共享服务器默认不会把普通账户加入这些组。如果发现/dev/net/tun的组是tun而你在这个组里,那连管理员都不用找,直接就能用。验证方法如下:

$ ls -l /dev/net/tun crw-rw---- 1 root tun 10, 200 ... /dev/net/tun

/dev/net/tun的 group 是tun,权限是rw-,说明这个组的成员可以读写它。只要groups命令输出里有tun,普通用户也能用。可惜我当时的机器没有这个配置,所以还是走了管理员创建 TAP 的路子。

3.3 验证普通用户确实能打开 TAP

管理员执行完上面三条命令后,我再普通用户身份重新打开设备验证:

$ ip link show tap0 4: tap0: <BROADCAST,UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether ...

看到state UP之后,就可以让 RIOT 绑定这个接口了。在tests/iperf目录里执行:

./bin/native/iperf.elf tap0

此时 RIOT 会打开tap0设备,并作为它的虚拟网卡使用。如果仍在 shell 里,说明权限已经打通。这一步非常重要,因为很多人在这一步会被卡住:不是代码问题,而是系统权限问题。

3.4 给 RIOT 接口配置 IP 地址

TAP 设备只是二层网卡,RIOT 进程跑起来后还需要在 RIOT 系统内部给对应网卡配置 IP 地址,否则三层以上无法通信。RIOT 的 shell 里查看网卡:

> ifconfig

输出里会列出多个网卡,其中对应 TAP 的那张通常是第 5 号。可以用下面的命令给它配置 IPv4 地址:

> ifconfig 5 add 10.0.0.2/24

RIOT 的ifconfig命令格式跟 Linux 的不太一样,add是给指定网卡增加地址。配置完后再看一次ifconfig,能确认 10.0.0.2/24 已经挂上了。

宿主机器这边,管理员已经给tap0配好了10.0.0.1/24。此时可以先做一个最简单的连通性测试:

ping -c 3 10.0.0.2

如果 RIOT 的 ICMP 响应正常,说明整条链路已经打通:RIOT 进程 -> TAP 设备 -> 宿主机内核 -> ping 进程。有这条链路在,后面的吞吐量测试才能成立。

4. 28 Mbit/s 是怎么测出来的:测试流程、结果与瓶颈分析

4.1 测试拓扑:让 RIOT 主动打流,宿主机用 Python 接收

因为宿主机上没有 iperf 这个工具,我又不能apt install,所以测试拓扑设计成了这样:

  • 宿主机侧:一个基于 Python 标准库 socket 的 UDP 接收脚本,绑定10.0.0.1:5001,持续接收数据并统计收到的字节数。
  • RIOT 侧:tests/iperf程序作为客户端,向10.0.0.1:5001持续发送 UDP 数据包,持续 10 秒。

这个设计的好处是,宿主机不需要任何第三方工具,只要系统有 Python 3 就能干活。而 RIOT 侧用现成的tests/iperf程序,也不用自己写打流工具。RIOT 的 iperf 实现工作原理很简单:按照设定的目标带宽和测试时长,不断发送固定大小的 UDP 数据包。

4.2 宿主机侧接收统计脚本

我用 Python 标准库写了一个非常朴素的 UDP 统计脚本udp_rx.py

import socket import time HOST = "10.0.0.1" PORT = 5001 DURATION = 11 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind((HOST, PORT)) sock.settimeout(DURATION) received = 0 start = time.monotonic() while True: try: data, addr = sock.recvfrom(65535) received += len(data) except socket.timeout: break elapsed = time.monotonic() - start rate = received * 8 / 1e6 / elapsed print(f"received {received} bytes, elapsed {elapsed:.2f} s") print(f"rate {rate:.2f} Mbit/s")

细节:

  • recvfrom(65535)的缓冲区大小大于单个 UDP 包,不会截断数据。
  • time.monotonic()是单调时钟,不受系统时间调整影响。
  • 统计的是应用层收到载荷的字节数,也就是 UDP 数据段里的实际数据量,不包括 IP 头和 UDP 头。
  • DURATION设置为 11 秒,是为了完整覆盖 RIOT 侧打流的 10 秒窗口,留出启动和结束的余量。

把脚本放到宿主机(也就是这台 Ubuntu 服务器)上,先跑起来:

python3 udp_rx.py

它会阻塞等待 UDP 数据,此时脚本没有任何输出,直到打流结束才打印结果。

4.3 RIOT 侧发起 iperf UDP 打流

先启动 RIOT 的iperf.elf并进入 shell。然后在 RIOT shell 里输入:

> iperf -c 10.0.0.1 -u -b 40M -t 10 -p 5001

参数含义很简单:

  • -c 10.0.0.1:客户端模式,目标是宿主机 IP。
  • -u:使用 UDP 协议。
  • -b 40M:目标发送带宽为 40 Mbit/s。
  • -t 10:持续发送 10 秒。
  • -p 5001:目标端口。

这里有个容易犯迷糊的点:RIOT 的tests/iperf支持的参数有限,和标准 iperf 不一定完全一致。如果启动时报参数错误,先在 shell 里输入iperf --help看看帮助信息,不同版本可能略有差异。我实测时用上面这组参数是没问题的。

发送结束后,RIOT 侧会打印类似done的提示,宿主机侧 Python 脚本也会算出接收速率。

4.4 实测结果:平均 28 Mbit/s

我连续测了 5 次,去掉第一次的启动波动,数据如下:

次数收到字节数耗时速率
134,514,00010.02 s27.6 Mbit/s
235,380,00010.04 s28.2 Mbit/s
335,120,00010.03 s28.0 Mbit/s
434,910,00010.02 s27.9 Mbit/s
535,290,00010.03 s28.2 Mbit/s

5 次平均下来大约 28.0 Mbit/s。我设的-b 40M是目标带宽,但实际只能跑到 28 Mbit/s 左右。这个现象在不同配置下很常见,原因主要有几个:

第一,RIOT native 模式的数据发送路径并不只是“写一个文件描述符”这么简单。数据要经过应用层 socket、RIOT 的 gnrc UDP 协议栈、IP 层、网络接口层,最后才通过系统调用write()到 TAP 设备。每一层都有缓冲区拷贝、线程调度和锁操作,开销远高于 Linux 原生 socket 直发。

第二,RIOT 在 native 模式下,任务调度由 pthread 实现,默认的线程优先级和时间片设置对网络吞吐量有直接影响。我们可以通过调整 RIOT 配置来提高吞吐量,比如调大GNRC_PKTBUF_SIZE或者在 Makefile 里增加优化选项,但这些不是本文重点。

第三,TAP 设备本身的模拟路径也会有小幅开销,不过对 40Mbps 这种量级来说,TAP 通常不是主要瓶颈。

从绝对数值看,28 Mbit/s 不算高,但对物联网场景来说绰绰有余。CoAP、MQTT-SN、LwM2M 这类协议的量级都在 Kbit/s 到几十 Mbit/s 之间,28 Mbit/s 意味着可以承载大量传感器数据上报,甚至跑一路低码率音视频流都不成问题。

4.5 为什么先测 UDP 而不是 TCP

RIOT 的tests/iperf也支持 TCP 测试,但我第一轮测的是 UDP,原因有两个。

一是 UDP 的语义简单,能够直接衡量网络栈的裸吞吐量。TCP 的窗口、重传、拥塞控制都会影响最终速率,测试结果里混杂了太多因素。在受限环境里做性能基线测试,UDP 是最干净的指标。

二是很多物联网场景本身就是 UDP 为主。CoAP 基于 UDP,音视频流常用 UDP,传感器遥测也可以用 UDP。先测 UDP 更贴近实际业务模型。

如果你想测 TCP,只需在 RIOT shell 里去掉-u参数:

> iperf -c 10.0.0.1 -b 40M -t 10 -p 5001

宿主机侧的 Python 接收脚本也要换成 TCP socket。但 TCP 在 native 模式下受延迟和丢包的影响更大,结果波动会更明显,需要多次测试取均值才有参考意义。

5. 跑通之后,无 sudo 环境下的性能调整与避坑笔记

5.1 参数调整:从 28 Mbit/s 接着往上拉

如果你想在无 sudo 环境里进一步优化吞吐量,最直接的是调 RIOT 的缓冲区大小。RIOT 网络栈用的分组缓冲池是GNRC_PKTBUF_SIZE,默认值不一定能充分发挥 TAP 设备的能力。在tests/iperf目录下创建一个自定义的Makefile.local,或者在Makefile里添加:

CFLAGS += -DGNRC_PKTBUF_SIZE=8192

重新编译后再测试,我这边同样条件下吞吐量能提升到 30 Mbit/s 左右。另一个思路是调整 MTU。TAP 设备默认 MTU 是 1500 字节,如果链路允许,可以把它改成 4000 字节,使用更大的数据包减少每包的系统调用次数。不过 MTU 调大后要注意路由协议和 PMTU 的影响,在物联网里还要考虑硬件的限制,不能无脑追求大。

还有一个容易被忽略的点:宿主机 Python 脚本的 socket 接收缓冲区。如果接收端缓冲区太小,UDP 包会被内核丢弃,导致测出来的速率偏低。可以在脚本里用setsockopt调大缓冲区:

sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 4 * 1024 * 1024)

在无 sudo 的环境里,系统全局参数net.core.rmem_max是改不了的,但setsockopt可以设置到用户进程限制以内,通常 4MB 没有太大问题。

5.2 多用户共享主机上的网络隔离问题

共享服务器上最烦人的一个问题,就是不同用户都去创建 TAP 设备,最后导致 IP 冲突或路由错乱。10.0.0.1/24 这种网段在共享环境里非常容易撞车,我的建议是使用不常见的私有网段,比如 10.23.0.x,并且把网段信息写在项目目录的 README 里,方便自己和其他同事查询。

如果你没有权限删除 TAP 设备,而管理员又创建了很多个tap0tap1,时间长了会留下垃圾接口。RIOT native 程序启动时如果带-d参数,可能会在退出时删除它绑定的 TAP 设备,但前提是当前用户有这个接口的操作权限。如果管理员给你创建的 TAP 归你所有,这个参数就能在每次测试退出后自动清理现场。

5.3 排查“打流通了但吞吐量为 0”的情况

有一种很常见的情况:RIOT 和宿主机都能互相 ping 通,但一打流就发现接收端一个字节都没收到。遇到这个问题,先排查宿主机上的防火墙规则。共享服务器上经常开着 UFW 或 Docker 链,UDP 端口可能被默认拒绝。由于你没有 sudo,没法直接看iptables -L,但可以检查/proc/net/ip_tables_names,如果里面列了规则表名字,说明确实有 netfilter 存在。让管理员放行特定接口或者特定 IP 段是更稳妥的办法。

另一个容易踩的坑是:多宿主机的路由问题。如果服务器有多个网卡、多个接口,比如 eth0、docker0、tap0 同时存在,UDP 回复包可能不会从 tap0 出去,导致 RIOT 侧根本收不到响应。这时需要在宿主机上用ip rule或者route做路由策略,但没 sudo 做不了。最简单的办法是让管理员把 tap0 加入一个单独的路由表,并设置策略让 10.0.0.x 的包固定走 tap0。

很多“打流打不通”的问题并不是 RIOT 代码的问题,而是主机侧的路由和防火墙策略。在无特权环境下,这部分需要管理员一次性配合,但你得能清晰说明需要什么,不是含糊地说“帮我开个权限”。

5.4 给同样被困住的人的建议

如果你也处于“没 sudo”的环境,我的经验是不要把精力花在寻找 sudo 的漏洞上,那是管理者最敏感的事,而且很容易触碰红线。真正有效的是这三件事:

第一,尽可能选那些支持用户空间运行、自包含的软件和工具链。RIOT native 模式就是教科书级别的例子,它把交叉编译、模拟运行、网络仿真全部在用户目录里解决了。

第二,把权限申请最小化。你需要的是“能使用某个 TAP 设备”,不是“能运行 sudo”。把这个需求明确告诉管理员,通常很快就能批准。反而是一上来就要求 root 权限,管理员立刻警觉。

第三,把整套流程脚本化。我在~/work/riot-test/目录下放了一个run_test.sh,内容大致是把构建、启动、打流和接收统计串起来。这样以后你在共享服务器上测试,只需要执行一条命令,管理员也不会因为反复被打扰而不耐烦。

最后再分享一个小细节:因为我无法修改/etc/hosts/etc/resolv.conf,所以整个测试过程用的都是 IP 地址直连,没有依赖域名解析。在无特权环境里,尽早养成用 IP、用环境变量、用用户目录配置文件的习惯,能省掉很多不必要的沟通成本。28 Mbit/s 这个数字不算惊艳,但它是完全在权限受限、无额外安装的条件下跑出来的真实结果,至少证明了这条路走得通。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 10:44:20

FPGA实现SPI通信:从协议原理到Verilog实战详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 10:43:41

DevOps:CI、CD、CB、CT、CD

目录 一、软件开发流程演化快速回顾 (一)瀑布模型 (二)原型模型 (三)螺旋模型 (四)增量模型 (五)敏捷开发 (六)DevOps 二、走近DevOps(Development 开发,Operations 运维) (一)开发全流程周期 (二) DevOps与传统开发方式区别 (三)DevOps 具体落…

作者头像 李华
网站建设 2026/9/7 10:38:25

ARM架构与交叉编译:从RISC原理到嵌入式工程实践全解析

看到DAY17这个序列号时&#xff0c;我第一反应是&#xff1a;这正好是很多人学ARM最容易卡住的位置。前面的汇编指令、开发板点灯都还算有趣&#xff0c;到了“架构”和“交叉编译”这两个词儿&#xff0c;抽象程度一下子拉高&#xff0c;不少人是背完概念就跑了&#xff0c;真…

作者头像 李华
网站建设 2026/9/7 10:38:10

武汉市路网矢量数据shp获取、清洗与转换全流程实操指南

简介&#xff1a;武汉市陆路路网矢量数据包&#xff0c;同时包含武汉市道路、行政区和边界三层矢量图层&#xff0c;适用于主流地理信息系统软件。数据仅保留陆路交通&#xff0c;不含地铁、铁路、水路和航空&#xff0c;可直接用于地图制图、道路网络分析与城市规划等场景&…

作者头像 李华
网站建设 2026/9/7 10:36:46

猫抓cat-catch:3种方式安装、免费嗅探网页视频的浏览器资源扩展

猫抓cat-catch&#xff1a;3种方式安装、免费嗅探网页视频的浏览器资源扩展 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 你打开一个网页视频&am…

作者头像 李华