AUTOSAR以太网发送链路深度解析:SOME/IP TP报文到底拷贝了几次?SomeIpXf、PduR、SoAd、TcpIp、EthIf谁在偷偷开Buffer?
在传统 CAN 通信中,一帧报文最大也就几十个字节,开发人员很少会认真思考:
这几十个字节到底被 memcpy 了几次?
但进入以太网时代之后,情况完全不同了。
SOME/IP 服务的数据可能是:
- 几百字节
- 几KB
- 十几KB
- 甚至几十KB
如果这些数据在 AUTOSAR 通信栈中被重复复制:
APP Buffer
↓ Copy
SomeIpXf Buffer
↓ Copy
SomeIpTp Buffer
↓ Copy
PduR Buffer
↓ Copy
SoAd Buffer
↓ Copy
TcpIp Buffer
↓ Copy
Eth Buffer
那么一次发送可能就产生大量 CPU memcpy 和 RAM 占用。
于是很多工程中都会出现几个非常实际的问题:
SOME/IP TP 发送到底经过哪些模块?
SomeIpXf 会不会额外复制一次数据?
PduR 会不会保存完整 Payload?
SoAd 有没有自己的发送 Buffer?
TcpIp 和 EthIf 谁负责真正的数据 Buffer?
TC397 最终是怎么把数据交给 Ethernet DMA 的?
这篇文章就以 Vector MICROSAR CP + Infineon TC397 + SOME/IP TP 为背景,从真实的数据流角度,把整个发送链路拆开。
需要说明的是:不同Autosar软件包、不同 TCP/UDP 配置以及具体 Eth 驱动实现,实际 Copy 次数会存在差异。本文重点分析 AUTOSAR 架构下最典型的数据流和 Buffer 生命周期。
一、先别急着看模块,先看数据到底怎么走
如果工程中配置了 SomeIpXf,一个典型的发送路径如下:
Application
│
▼
RTE
│
▼
SomeIpXf
│
▼
SomeIpTp
│
▼
PduR
│
▼
SoAd
│
▼
TcpIp
│
▼
EthIf
│
▼
Eth Driver
│
▼
TC397 Ethernet DMA
│
▼
Ethernet MAC
如果没有配置 SomeIpXf:
看起来只是少了一个模块。
但实际上:
SomeIpXf 是否存在,很可能决定整个发送链路中是否多了一份完整 Payload Buffer。
而真正决定性能的,并不是模块数量,而是:
谁拥有Payload?
谁申请Buffer?
谁执行Copy?
什么时候释放Buffer?
二、SomeIpXf到底干了什么?
很多开发人员第一次看到 SomeIpXf 时,会认为它只是:
给 SOME/IP 数据做一个格式转换。
实际上它的作用远不只是简单转换。
假设应用层有一个结构体:
typedef struct
{
uint32 Speed;
uint16 RPM;
uint8 Status;
} VehicleData;
应用层内存中可能是:
Application RAM
+----------------------+
| VehicleData |
| |
| Speed |
| RPM |
| Status |
+----------------------+
但 SOME/IP 网络上传输的并不是 MCU 内存中的结构体。
因为还涉及:
- 字节序
- 数据类型
- Array
- String
- Struct
- Optional Data
- Dynamic Length
- 序列化格式
所以必须经过:
Application Data
│
│ Serialize
▼
SOME/IP Payload
最终可能变成:
00 00 00 64
00 00 13 88
01
...
这意味着:
SomeIpXf 很可能成为第一份完整 Payload Buffer。
三、有SomeIpXf时,第一处大数据搬运出现了
整个过程可以理解成:
┌─────────────────────────┐
│ Application Buffer │
│ │
│ struct / array / signal │
└────────────┬────────────┘
│
│ Serialize
▼
┌─────────────────────────┐
│ SomeIpXf Buffer │
│ │
│ Serialized Payload │
└─────────────────────────┘
这里发生的不是简单的指针传递。
因为:
Application Struct
和:
Network Payload
通常并不是同一种数据布局。
因此 SomeIpXf 的主要成本包括:
CPU:
Serialize
Endian Conversion
Memory Copy
RAM:
Serialized Payload Buffer
如果你的 SOME/IP Payload 是:
8 KB
并且每 10ms 发送一次:
8KB × 100 = 800KB/s
这还只是一次序列化产生的数据搬运。
如果后面还有两三次 Copy,CPU 和 Memory Bandwidth 就开始明显增加。
四、SomeIpTp会不会再开一个完整Buffer?
这是一个很容易误解的地方。
实际上,对于 SOME/IP TP 来说,SomeIpTp 的核心职责是:
对大 Payload 进行 TP 分段发送。
例如:
10KB Payload
需要发送成:
Segment 1
Segment 2
Segment 3
...
因此 SomeIpTp 更需要维护的是:
Current Offset
Remaining Length
Segment Number
Transmission State
例如:
┌─────────────────────────┐
│ SomeIpTp Tx Context │
├─────────────────────────┤
│ TotalLength │
│ CurrentOffset │
│ RemainingLength │
│ SegmentNumber │
│ TxState │
└─────────────────────────┘
它不一定需要:
10KB SomeIpTp Payload Buffer
更理想的方式是:
SomeIpXf Buffer
│
│ Offset + Length
▼
SomeIpTp
SomeIpTp 只记录:
现在发送到哪里
还剩多少
下一段从哪里开始
而真正的数据仍然在:
SomeIpXf Buffer
五、TP发送的关键:不是Push,而是Pull
理解 SOME/IP TP 数据流最关键的一点就是:
下层通常不是等上层把完整数据推下来,而是在需要发送数据时,主动向上层取数据。
可以理解为:
TcpIp:我需要1000 Byte
│
▼
SoAd:我去拿
│
▼
PduR:我继续转
│
▼
SomeIpTp:Offset是多少?
│
▼
SomeIpXf Buffer
最终形成:
Lower Layer
│
│ Need Data
▼
SoAd_CopyTxData()
│
▼
PduR_CopyTxData()
│
▼
SomeIpTp_CopyTxData()
│
▼
Payload Source
因此在一个优化较好的 TP 链路中:
SomeIpTp
PduR
SoAd
可能主要承担:
状态管理
Offset管理
PduId转换
API转发
CopyTxData转发
而不是每个模块都保存一份完整 Payload。
六、PduR真的会开Buffer吗?
答案是:
不一定。
PduR 的主要职责是:
Routing
而不是:
Payload Storage
例如:
SomeIpTp
│
▼
PduR
│
▼
SoAd
很多情况下只是:
PduId转换
↓
调用对应模块接口
↓
转发CopyTxData请求
因此:
PduR ≠ 一定存在完整Payload Buffer
但是如果涉及:
- TP Gateway
- Queue
- FIFO
- 多路由
- Buffer管理
那么 PduR 也可能配置自己的 Buffer。
所以分析一个真实工程时,不能简单认为:
每经过一个模块 = Copy一次
这是错误的。
真正应该看的是:
这个模块是否拥有Payload Buffer?
七、SoAd:看起来只是Socket适配层,但它其实很关键
SomeIpTp 再往下到SoAd:
实际上从 TP 发送角度,它还是一个非常重要的:
Transmission State Manager
典型过程更可能是:
SoAd_TpTransmit()
↓
保存Transmission Request
↓
SoAd MainFunction
↓
下层开始请求数据
↓
SoAd_CopyTxData()
SoAd 可能维护:
┌─────────────────────────┐
│ SoAd Tx Context │
├─────────────────────────┤
│ SocketId │
│ TxPduId │
│ TotalLength │
│ CurrentOffset │
│ Transmission State │
└─────────────────────────┘
所以通常:
SoAd 更可能保存发送上下文,而不是完整 SOME/IP Payload。
当然,如果配置了某些 Queue 或 Buffering 机制,也可能产生额外 Buffer。
八、真正的分水岭:UDP还是TCP
这里是整个 Buffer 分析中最重要的部分。
因为:
SomeIpTp
下面可能走:
UDP
也可能走:
TCP
两条路径的数据生命周期完全不同。
九、UDP:可能是一条非常高效的链路
典型路径:
SomeIpTp
│
▼
PduR
│
▼
SoAd
│
▼
TcpIp_UdpTransmit()
│
▼
EthIf_ProvideTxBuffer()
│
▼
Eth Tx Buffer
│
▼
EthIf_Transmit()
│
▼
DMA
其中最关键的是:
EthIf_ProvideTxBuffer()
这个接口的意义非常重要。
它意味着:
下层可以直接提供一个用于发送的 Ethernet Buffer。
例如:
Eth Tx Buffer
地址被返回:
BufPtr
然后上层开始填数据:
SomeIpTp
│
│ CopyTxData
▼
Eth Tx Buffer
最后:
EthIf_Transmit()
触发发送。
因此,一个理想的 UDP TP 路径可能是:
SomeIpXf Buffer
│
│ CopyTxData
▼
TC397 Eth Tx Buffer
│
│ DMA Read
▼
Ethernet MAC
中间:
PduR
SoAd
甚至不需要完整 Payload Copy。
十、没有SomeIpXf时,链路可能进一步减少一次Copy
如果没有 SomeIpXf,可以理解为:
应用层已经准备好了可以作为 SOME/IP Payload 使用的数据。
于是:
Application Buffer
│
▼
SomeIpTp
│
▼
PduR
│
▼
SoAd
│
▼
TcpIp
│
▼
Eth Tx Buffer
最理想的数据搬运:
Application Buffer
│
│ Copy
▼
Ethernet Tx Buffer
│
│ DMA
▼
MAC
即:
APP → Eth Tx Buffer → MAC
其中:
APP → Eth Buffer
可能是 CPU Copy。
而:
Eth Buffer → MAC
通常是 DMA 完成。
十一、有SomeIpXf和没有SomeIpXf的最大区别
我们把两种架构直接放在一起。
方案一:有SomeIpXf
Application Data
│
│ ① Serialize / Copy
▼
SomeIpXf Buffer
│
│ ② CopyTxData
▼
Eth Tx Buffer
│
│ DMA
▼
Ethernet MAC
方案二:没有SomeIpXf
Application Buffer
│
│ ① CopyTxData
▼
Eth Tx Buffer
│
│ DMA
▼
Ethernet MAC
理论上:
CPU Copy减少一次
因此:
SomeIpXf 的主要代价并不是模块调用本身,而是序列化和 Payload Buffer。
尤其是:
大Payload
高频发送
多Service
多实例
同时存在时,这个影响会更加明显。
十二、TCP为什么会多出一层Buffer?
如果 SOME/IP TP 走 TCP:
SomeIpTp
│
▼
SoAd
│
▼
TcpIp TCP
│
▼
EthIf
和 UDP 最大的不同是:
TCP需要维护发送状态,并且数据不能发送后立即丢弃。
因为:
发送
↓
等待ACK
↓
收到ACK
↓
释放Buffer
因此 TcpIp TCP 通常需要维护自己的发送数据。
典型逻辑:
Payload
│
▼
TcpIp Tx Buffer
│
▼
TCP Segment Queue
│
▼
Eth Tx Buffer
│
▼
DMA
所以 TCP 场景下:
Application
↓
SomeIpXf Buffer
↓
TcpIp Tx Buffer
↓
Eth Tx Buffer
如果 Payload 很大、发送频率很高,TCP 的 RAM 占用通常更值得关注。
十三、TC397最底层到底有什么?
最终,无论上面经过多少模块,数据都会进入:
Ethernet Tx Buffer
TC397 Ethernet MAC 的发送通常依赖 DMA。
概念上:
Tx Descriptor Ring
┌────────────────────┐
│ Tx Descriptor 0 │
│ │
│ Buffer Address ────┼──────► Tx Buffer 0
└────────────────────┘
┌────────────────────┐
│ Tx Descriptor 1 │
│ │
│ Buffer Address ────┼──────► Tx Buffer 1
└────────────────────┘
内存可能是:
RAM
TxBuffer0
TxBuffer1
TxBuffer2
TxBuffer3
...
发送时:
CPU
│
│ 设置Descriptor
▼
Tx Descriptor
│
│ Buffer Address
▼
Ethernet DMA
│
▼
Ethernet MAC
│
▼
PHY
因此最终:
真正的 Payload 硬件发送资源,一定会落在 Tx Buffer 和 DMA Descriptor 上。
注意:
DMA Descriptor
主要保存的是:
Buffer Address
Length
Control
Status
它本身不是完整 Payload Buffer。
十四、到底哪些模块可能开Buffer?
直接给一个工程分析表。
| 模块 | 可能存在的数据 | 是否可能保存完整Payload |
|---|---|---|
| Application | 原始业务数据 | 是 |
| RTE | Sender/Receiver Data | 看配置而定 |
| SomeIpXf | Serialized Payload | 通常会 |
| SomeIpTp | TP Context/Segment状态 | 通常不一定 |
| PduR | Routing/Queue/Gateway Buffer | 看配置 |
| SoAd | Socket/TP Tx Context | 通常不是完整Payload |
| TcpIp UDP | 临时发送数据/头部 | 依实现 |
| TcpIp TCP | TCP发送队列 | 通常会 |
| EthIf | Tx Buffer管理 | 可能 |
| Eth Driver | DMA Tx Buffer | 一定存在发送资源 |
| DMA Descriptor | Descriptor Ring | 不保存完整Payload |
所以:
AUTOSAR 模块数量 ≠ 数据 Copy 次数。
真正需要分析的是:
谁申请Buffer?
谁拥有Buffer?
谁调用memcpy?
谁只是传递指针?
谁只是转发CopyTxData?
十五、结尾
理解 AUTOSAR 以太网通信性能,不能只看:
模块调用链
而应该换成另一个视角:
数据到底在哪里?
每经过一个模块,都应该问四个问题:
① 这个模块有没有自己的Payload Buffer?
② 数据是Copy,还是传递指针?
③ Buffer什么时候释放?
④ 下层是Push数据,还是Pull数据?
并不一定意味着每经过一层就复制一次完整 Payload。
所以最终可以总结成一句话:
AUTOSAR以太网发送链路真正消耗CPU的,不是模块多,而是Payload到底被复制了多少次;真正消耗RAM的,不是调用链长,而是谁长期持有了完整Payload Buffer。
更多推荐

所有评论(0)