第2章 通信协议栈概览:物理层、MAC层、网络层在多核上的映射关系

做通信DSP这些年,我见过不少团队把协议栈跑在多核上,结果性能反而比单核还差。为啥?说白了就是没搞清楚每一层到底该往哪个核上放。

今天咱们就聊聊这个映射关系。我会结合自己踩过的坑,把物理层、MAC层、网络层在多核上的分工讲明白。

2.1 协议栈的三层结构

通信协议栈,我习惯把它看成三个独立的"车间":

  • 物理层:负责信号的收发、调制解调、编解码。这是最"重"的活儿,计算量最大。
  • MAC层:负责调度、重传、资源分配。实时性要求高,但计算量相对小。
  • 网络层:负责路由、寻址、数据包转发。处理的是控制面,对时延没那么敏感。

你想想看,这三层的特性完全不同。物理层是"体力活",MAC层是"精细活",网络层是"管理活"。把它们硬塞到同一个核上,那肯定出问题。

核心观点:多核映射的关键,是把计算密集的任务和实时性敏感的任务分开。物理层占计算,MAC层占实时,网络层占吞吐。

2.2 物理层:独占一个核,甚至多个核

我在项目中遇到过,物理层的处理量能占到整个协议栈的70%以上。尤其是OFDM调制、LDPC译码这些模块,动不动就是几百万次乘加运算。

我的建议是:物理层最好独占一个DSP核。如果带宽高,比如5G NR的100MHz带宽,那得用两个核甚至四个核来分担。

举个例子,一个典型的物理层处理流程:

// 伪代码:物理层接收处理
void phy_rx_process(core_id) {
    while(1) {
        // 步骤1:FFT解调(计算密集)
        fft_demod(rx_buffer, freq_domain);
        
        // 步骤2:信道估计(需要查表)
        channel_estimation(freq_domain, channel_matrix);
        
        // 步骤3:LDPC译码(迭代计算)
        ldpc_decode(channel_matrix, decoded_bits);
        
        // 步骤4:传给MAC层(通过共享内存)
        send_to_mac(decoded_bits);
    }
}

你看,每个步骤都是计算密集型。如果这时候MAC层来抢核,那FFT还没算完就被打断了,整个链路就卡住了。

避坑指南:我曾经把一个LTE的物理层和MAC层放在同一个核上,结果吞吐量直接掉了40%。后来把物理层单独放到一个核上,问题就解决了。记住,物理层不要跟别人共享核。

2.3 MAC层:放在控制核上,但要靠近物理层

MAC层的活儿,说白了就是"调度"。它要决定:

  • 什么时候发数据
  • 用哪个资源块发
  • 发错了要不要重传

这些决策对实时性要求极高。比如5G里的HARQ重传,必须在几个毫秒内完成。如果MAC层离物理层太远,数据传来传去就超时了。

我个人的做法是:把MAC层放在一个独立的控制核上,但这个核跟物理层的核之间要有高速互联。比如共享L2缓存,或者用硬件队列通信。

来看一个MAC层的调度流程:

// 伪代码:MAC层调度
void mac_scheduler(core_id) {
    while(1) {
        // 从物理层拿数据(通过共享内存)
        rx_data = get_from_phy();
        
        // 判断是否需要重传
        if (rx_data.crc_error) {
            schedule_retransmission(rx_data.ue_id);
        }
        
        // 分配新资源
        allocate_resource(next_tti);
        
        // 把调度结果发给物理层
        send_to_phy(schedule_info);
    }
}

注意:MAC层和物理层之间的通信延迟,最好控制在微秒级。如果超过10微秒,HARQ的时序就可能出问题。我见过一个项目,就是因为共享内存的访问延迟太大,导致重传总是超时。

2.4 网络层:放在通用核上,或者单独的控制核

网络层处理的是IP包的路由和转发。这个活儿的特点是:

  • 计算量不大
  • 对实时性要求不高
  • 但需要处理复杂的协议逻辑

所以,网络层最适合放在通用处理器核上,比如ARM核或者RISC-V核。如果DSP芯片里没有通用核,那就放在一个专门的控制核上,优先级设低一点。

嗯,这里要注意:网络层的数据包可能很大,比如一个IP包有1500字节。如果频繁地在核之间搬运这些数据,会浪费大量带宽。我建议用DMA来搬运,别让CPU去拷贝。

2.5 多核映射的典型架构

说了这么多,咱们画个图来总结一下。下面这张SVG图,展示了我个人最常用的三层映射架构:

协议栈三层多核映射架构 物理层(DSP核0) FFT / 信道估计 / LDPC译码 / 调制解调 计算密集,独占核,建议使用SIMD指令加速 与MAC层通过共享内存 + 硬件队列通信 共享内存 / 硬件队列 MAC层(控制核1) 调度 / HARQ重传 / 资源分配 实时性要求高,独立控制核,靠近物理层 与网络层通过消息队列通信 消息队列 / DMA搬运 网络层(通用核2 / ARM核) IP路由 / 数据包转发 / 协议处理

这张图里,物理层在最下面,独占DSP核0。MAC层在中间,用控制核1。网络层在最上面,用通用核2。每一层之间通过共享内存或消息队列通信。

2.6 实际项目中的映射策略

我参与过一个5G小基站的项目,用的是4核DSP。当时我们是这样分配的:

核编号 负责模块 优先级 备注
核0 物理层接收(FFT + 信道估计 + 译码) 最高 独占,不允许被抢占
核1 物理层发送(编码 + 调制 + IFFT) 与核0并行工作
核2 MAC层调度 + HARQ 与核0/1通过硬件队列通信
核3 网络层 + 控制面处理 可被其他任务抢占

你看,物理层用了两个核(一个收一个发),MAC层用一个核,网络层用一个核。这样分工明确,每个核的负载都比较均衡。

小技巧:如果物理层的计算量特别大,比如做64QAM的LDPC译码,我建议把译码模块单独放到一个核上。这样其他物理层处理(比如FFT)可以跟译码并行,整体吞吐量能提升不少。

2.7 常见问题与避坑

最后,我总结几个常见的坑,都是我在项目中亲身经历过的:

  • 坑一:物理层和MAC层共用核。我曾经这么干过,结果MAC层的调度延迟导致HARQ总是超时。后来分开就好了。
  • 坑二:核间通信用全局变量。多核环境下,全局变量不加锁会导致数据错乱。我建议用硬件队列或者原子操作。
  • 坑三:网络层数据直接拷贝。大包拷贝会占用大量CPU时间。用DMA搬运,或者用零拷贝技术。
  • 坑四:优先级设置不合理。物理层优先级一定要最高,MAC层次之,网络层最低。否则高优先级任务会被低优先级任务阻塞。

嗯,今天就聊到这儿。记住一句话:物理层占计算,MAC层占实时,网络层占吞吐。把这个原则记牢了,多核映射就不会出大问题。


公众号:蓝海资料掘金营,微信deep3321