网络
这一部分讲 embassy-net 0.9.1(tag embassy-net-v0.9.1,2026-04-16)和它的两个驱动接口 crate:embassy-net-driver 0.2.0(2023-10-18 发布,之后没有新版本)、embassy-net-driver-channel 0.4.0。embassy-net 的 src/ 一共 3927 行,TCP/IP 协议本身一行也没写:底层是 smoltcp 0.13.1(tag v0.13.1),一个不分配内存、不依赖 async 的协议栈。embassy-net 做的是把 smoltcp 「调用方每隔一段时间来 poll 我一次」的同步接口,接到 embassy 的任务、waker 和定时器上。
三个角色:
- 驱动:实现
embassy-net-driver的Drivertrait,只管「现在能不能收一帧、能不能发一帧」,能就交出一个令牌,不能就登记 waker。网卡中断到来时叫醒的就是这个 waker。 Stack和Runner:embassy_net::new把 smoltcp 的Interface、SocketSet和几个 waker 槽装进一个RefCell<Inner>。Stack是指向它的 4 字节句柄,Runner拿着驱动,在自己的任务里反复 poll 这个Inner。- 套接字:
TcpSocket、UdpSocket只是「Stack+ smoltcp 里的一个槽号」。读写时借出RefCell,在 smoltcp 套接字里登记 waker,再叫醒Runner。
- Stack 与 Runner:
Runner::run是一个永远Pending的poll_fn;每次 poll 先收完所有帧、再发完所有能发的,最后把 smoltcp 的poll_at换成一个当场 poll、当场 drop 的Timer;实测空闲时一次也不 poll,ARP 每秒一次,DHCP 重试 10 秒一次;0.9.1 修掉的「发送队列满时忙转」,复原旧代码后 100 ms 里 poll 两万多次;两个任务一起等wait_config_up时每秒互相叫醒上千万次。 - 驱动接口:
receive返回收、发两个令牌,transmit必须在真有空间时才给令牌;stm32 以太网驱动的中断处理函数只做一件事:叫醒Runner;Capabilities::default()的 MTU 是 0,debug 下 panic、release 下帧长不受限;embassy-net-driver-channel用两条zerocopy_channel把驱动放进另一个任务;tuntap 驱动在 macOS 上编不过。 - TCP 与 UDP 套接字:缓冲区由调用方给、生命周期被
transmute成'static;每个with_mut都叫醒Runner;进程内两个栈之间的 TCP echo 和吞吐(缓冲区、MTU、驱动队列深度);drop 一个连着的TcpSocket不发任何包;UDP 报文超过 MTU 时send_to返回Ok却被丢掉;两个任务读同一个UdpSocket,一个永远收不到。
flowchart TB irq["网卡中断"] --> dw["驱动的 waker"] dw --> run["Runner 任务"] run --> inner["RefCell Inner"] inner --> iface["smoltcp Interface"] inner --> set["SocketSet"] iface --> drv["Driver 令牌"] run --> tm["Timer::at poll_at"] app["应用任务"] --> sock["TcpSocket UdpSocket"] sock --> inner sock --> rw["Inner.waker"] rw --> run
网络栈里同时走着本卷的两条主线:网卡中断叫醒 Runner 是中断主线,Runner 按 poll_at 交给时间驱动的那个到期时刻是定时器主线。应用任务和 Runner 之间靠 waker 来回叫醒,形状和跨任务主线一样,只是中间的「队列」换成了 smoltcp 的套接字缓冲区。和 tokio 对照:tokio 的网络 I/O 由运行时里的反应器统一等 epoll / kqueue 事件(见 反应器),协议栈在内核里;embassy 没有内核,协议栈本身就是一个普通任务。
本部分的实验大多在 std 上跑:platform-std 执行器上建两个 embassy-net 栈,中间用一个手写的 Driver(或 embassy-net-driver-channel)接成一根「网线」,静态 IPv4 地址 10.0.0.1 和 10.0.0.2。少数实验在 QEMU 上跑同样的两栈结构,用来看 Cortex-M4 上的内存和代码体积。main 上 embassy-net 已经从 smoltcp 换成了自家的 xarxa 协议栈(2026-07-27 的提交 680b19296「net: migrate smoltcp -> xarxa」),还没发版,本部分只讲 0.9.1。