简要分析NLMSG_DONE参数
NLMSG_DONE是Linux Netlink协议中用于标记
多部分消息传输结束的标志类型。当内核需要返回大量数据时,会分多个消息块(chunk)发送,最后通过NLMSG_DONE通知用户态“所有数据已发送完毕”。以下是其核心要点:
一、定义与值
- 头文件:定义在<linux/netlink.h>中:
#define NLMSG_DONE 0x3 /* 多部分消息传输结束 */
- 作用:标记分块传输的终止,用户态收到此消息后停止等待更多数据。
二、核心作用
1. 分块传输终结符
- 大数据场景:当内核需返回的数据量较大(如路由表、接口列表等),无法通过单个Netlink消息承载时,会将数据分块发送。
- 结束标志:最后一个消息块会携带NLMSG_DONE,告知用户态无需继续等待后续数据。
2. 流式传输控制
- 资源优化:避免一次性分配超大内存,降低内核和用户态的内存压力。
- 可靠性:若传输中途发生错误,用户态可通过未收到NLMSG_DONE判断数据不完整。
三、数据结构
NLMSG_DONE消息的负载(Payload)通常
为空,仅通过消息头标记结束:
struct nlmsghdr {
__u32 nlmsg_len; // 消息总长度(头 + 负载)
__u16 nlmsg_type; // 消息类型(此处为 NLMSG_DONE)
__u16 nlmsg_flags; // 标志位(如 NLM_F_MULTI)
__u32 nlmsg_seq; // 序列号(与请求匹配)
__u32 nlmsg_pid; // 发送方端口 ID
};
- 关键字段:
- nlmsg_type:设为NLMSG_DONE。
- nlmsg_flags:若消息分块传输,需包含NLM_F_MULTI标志
四、使用场景示例
1. 用户态接收分块数据
用户态循环接收数据,直到检测到NLMSG_DONE:
while (1) {
// 接收消息
len = recv(fd, buf, sizeof(buf), 0);
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
// 遍历消息链(可能多个消息粘包)
for (; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
if (nlh->nlmsg_type == NLMSG_DONE) {
printf("所有数据接收完毕\n");
return 0;
}
// 处理其他消息(如 NLMSG_ERROR 或实际数据)
}
}
2. 内核发送分块数据
内核分块发送数据,最后发送NLMSG_DONE:
// 内核模块代码示例
void send_chunked_data(struct sk_buff *skb, u32 portid) {
// 分块发送数据
for (int i = 0; i < total_chunks; i++) {
struct sk_buff *chunk = alloc_skb(...);
struct nlmsghdr *nlh = nlmsg_put(chunk, portid, seq, NLMSG_DONE, 0, NLM_F_MULTI);
// 填充数据到 chunk
netlink_unicast(nl_sk, chunk, portid, 0);
}
// 最后发送 NLMSG_DONE 结束
struct sk_buff *done_skb = nlmsg_new(0, GFP_KERNEL);
struct nlmsghdr *done_nlh = nlmsg_put(done_skb, portid, seq, NLMSG_DONE, 0, 0);
netlink_unicast(nl_sk, done_skb, portid, 0);
}
五、与其他消息类型的区别
消息类型 | 值 | 作用 |
NLMSG_DONE | 3 | 多部分消息结束标志,无负载数据。 |
NLMSG_ERROR | 2 | 错误响应或 ACK 确认,携带错误码或成功标志( error=0 )。 |
NLMSG_NOOP | 1 | 空操作,无实际行为。 |
NLM_F_MULTI | - | 标志位(非消息类型),表示消息是分块传输的一部分。 |
六、注意事项
- 负载数据:NLMSG_DONE通常不携带负载数据(nlmsg_len = sizeof(struct nlmsghdr))
- 标志位:
- 分块传输时,除最后一个消息外,其他消息需设置NLM_F_MULTI
- NLMSG_DONE消息本身不设置NLM_F_MULTI
- 序列号:所有分块消息和NLMSG_DONE需使用相同的nlmsg_seq,以匹配原始请求
七、总结
- 本质:Netlink协议中用于标记分块数据传输结束的标志。
- 用途:
- 通知用户态分块数据已全部发送完毕。
- 优化大数据传输的内存和可靠性。
- 关键行为:
- 分块消息需设置NLM_F_MULTI
- 最后一个消息为NLMSG_DONE,不携带负载