第2章 mbuf:存储器缓存
所属:TCP/IP详解 卷2:实现 来源:TCP/IP详解 卷2:实现
本章描述mbuf(memory buffer)的结构和操作,mbuf是内核中用于存储网络数据的基本单元。
2.1 引言
什么是mbuf
mbuf(memory buffer):
- 内核中用于存储网络数据的结构
- 内存缓冲区
- 灵活的内存管理
- 可以链接成链表
- 避免大量数据拷贝
mbuf是网络数据的基本存储单元。
为什么需要mbuf
mbuf的作用:
-
存储网络数据
- 存储接收和发送的数据
- 存储协议首部
-
灵活管理
- 可以动态分配和释放
- 可以链接起来
- 支持各种长度的数据
-
提高效率
- 避免数据拷贝
- 直接操作指针
- 高效的首部添加和移除
mbuf设计的目标是高效和灵活。
2.2 mbuf的结构
mbuf结构定义
mbuf的结构:
struct mbuf {
struct mbuf *m_next; /* 下一个mbuf */
u_char *m_data; /* 数据起始位置 */
u_int m_len; /* 数据长度 */
u_char m_type; /* 类型 */
u_char m_flags; /* 标志 */
/* ... 其他字段 ... */
union {
struct {
u_char *m_pktdat; /* 分组数据 */
} M_dat;
struct {
struct mbuf *m_fre; /* 空闲链表 */
} M_fre;
/* ... 其他联合体 ... */
} M_dat;
};字段说明
| 字段 | 说明 |
|---|---|
| m_next | 指向下一个mbuf,链表结构 |
| m_data | 指向当前数据的起始位置 |
| m_len | 当前mbuf中数据的长度 |
| m_type | mbuf的类型 |
| m_flags | 标志位 |
m_next把多个mbuf链接成链表。
mbuf的类型
常见的mbuf类型:
| 类型 | 说明 |
|---|---|
| MT_FREE | 空闲 |
| MT_DATA | 数据 |
| MT_HEADER | 协议首部 |
| MT_SOCKET | 套接字 |
| MT_PCB | 协议控制块 |
| MT_RTABLE | 选路表 |
| … | 其他类型 |
不同类型用于不同的用途。
mbuf的标志
常见的mbuf标志:
| 标志 | 说明 |
|---|---|
| M_PKTHDR | 分组首部 |
| M_EOR | 记录结束 |
| M_BCAST | 广播发送 |
| M_MCAST | 多播发送 |
| M_EXT | 外部存储 |
标志描述mbuf的属性。
2.3 mbuf的链表结构
三种链表结构
mbuf有三种链表结构:
-
简单链表(m_next)
- 单链表
- 一个接一个
- 存储连续的数据
-
分组链表(m_nextpkt)
- 整个分组的链表
- 每个分组是一个mbuf链
- 用于队列
-
mbuf队列(ifqueue)
- 接口的发送/接收队列
- 多个分组排队
mbuf可以组成各种链表结构。
分组的结构
一个IP分组的mbuf结构:
mbuf 1 mbuf 2 mbuf 3
+--------+ +--------+ +--------+
| m_next |----->| m_next |----->| m_next |-----> NULL
+--------+ +--------+ +--------+
| m_data | | m_data | | m_data |
+--------+ +--------+ +--------+
| m_len | | m_len | | m_len |
+--------+ +--------+ +--------+
| IP首部 | | TCP首部 | | 数据 |
| | | | | |
+--------+ +--------+ +--------+
一个分组可以由多个mbuf组成。
2.4 mbuf的操作
分配和释放
分配mbuf:
- m_get:分配一个mbuf
- m_gethdr:分配一个带分组首部的mbuf
- m_clget:分配外部存储
释放mbuf:
- m_free:释放一个mbuf
- m_freem:释放整个mbuf链
分配和释放是基本操作。
数据操作
常用的数据操作:
| 函数 | 功能 |
|---|---|
| m_copy | 复制mbuf链 |
| m_pullup | 把数据拉到前面,确保连续 |
| m_prepend | 在前面添加数据 |
| m_adj | 调整指针,跳过数据 |
| m_cat | 连接两个mbuf链 |
| m_dup | 复制整个分组 |
各种操作方便数据处理。
m_pullup
m_pullup的作用:
- 确保前N字节是连续的
- 方便访问协议首部
- 如果不连续,就重新排列
为什么需要:
- 协议首部通常在最前面
- 需要连续访问
- mbuf可能不连续
m_pullup确保首部连续。
m_prepend
m_prepend的作用:
- 在数据前面添加空间
- 用于添加协议首部
- 发送时每层都要加首部
例子:
- TCP发送时,IP层要加IP首部
- 调用m_prepend预留空间
- 然后写入IP首部
m_prepend在前面添加空间。
m_adj
m_adj的作用:
- 调整m_data和m_len
- 跳过前面的数据
- 用于移除首部
例子:
- IP接收时,处理完IP首部
- 调用m_adj跳过IP首部
- 然后交给TCP
m_adj调整指针,不拷贝数据。
2.5 外部存储
什么是外部存储
外部存储(external storage):
- mbuf本身很小,只有128字节左右
- 大数据用外部存储
- mbuf指向外部的大缓冲区
- M_EXT标志表示有外部存储
大数据用外部存储,小数据用mbuf本身。
为什么需要外部存储
好处:
- 支持大数据
- 减少mbuf的数量
- 提高效率
- 减少链表长度
代价:
- 管理更复杂
- 需要引用计数
- 分配释放开销大
大小数据用不同的方式。
2.6 mbuf的统计和监控
统计信息
mbuf的统计:
- 总数
- 空闲数
- 各种类型的数量
- 等待数
- 失败数
可以监控mbuf的使用情况。
调优
mbuf的调优:
- 调整mbuf的数量
- 调整外部存储的大小
- 根据负载调整
- 避免mbuf不足
合理配置mbuf可以提高性能。
2.7 小结
mbuf概述
-
什么是mbuf
- 内存缓冲区
- 存储网络数据
- 内核网络的基本单元
-
为什么需要
- 灵活管理
- 避免拷贝
- 高效处理
mbuf结构
-
结构定义
- m_next:下一个
- m_data:数据指针
- m_len:数据长度
- m_type:类型
- m_flags:标志
-
类型和标志
- 各种类型
- 各种标志
链表结构
-
简单链表
- m_next链接
- 存储数据
-
分组链表
- 整个分组的链
- 用于队列
-
队列
- 多个分组排队
- 接口队列
mbuf操作
-
分配释放
- m_get / m_free
- m_gethdr / m_freem
-
数据操作
- m_copy:复制
- m_pullup:拉平
- m_prepend:前面加
- m_adj:调整
- m_cat:连接
- m_dup:复制分组
外部存储
-
什么是外部存储
- 大数据用
- mbuf指向外部
- M_EXT标志
-
优缺点
- 优点:支持大数据,减少mbuf
- 缺点:管理复杂
关键概念
-
mbuf
- 网络数据的基本存储单元
- 灵活高效
-
链表结构
- 多个mbuf链接
- 支持任意长度
-
操作函数
- 各种操作函数
- 方便数据处理
-
外部存储
- 大数据用
- 提高效率