netlink_unicast 是Linux内核中通过Netlink协议向特定用户空间进程发送单播消息的核心函数
一、函数作用
1. 单播消息传输
将Netlink消息(封装在sk_buff中)发送到指定的用户空间进程(通过目标portid标识,通常为目标进程的PID)
2. 异步非阻塞发送
函数通过内核的Netlink套接字队列发送消息,无需等待用户空间确认,直接返回发送状态。
3. 跨用户/内核空间通信
支持内核模块向用户空间程序发送事件通知、响应请求或传输数据(如路由表更新、设备状态变更等)。
二、函数原型
#include <linux/netlink.h>
int netlink_unicast(
struct sock *ssk, // 内核 Netlink 套接字(由 netlink_kernel_create 创建)
struct sk_buff *skb, // 待发送的 sk_buff(包含 Netlink 消息)
u32 portid, // 目标用户空间进程的端口 ID(PID)
int nonblock // 是否非阻塞(通常为 0)
);
- 参数:
- ssk:内核netlink套接字,通常由netlink_kernel_create初始化时创建
- skb:包含完整Netlink消息的sk_buff缓冲区
- protid:目标用户空间进程的端口ID,即接收方PID
- nonblock:若设置为1,当发送队列满时立即返回-EAGAIN;否则阻塞等待
- 返回值
- 成功:发送的字节数
- 失败:负数错误码(如-ENOBUFS表示缓冲区不足)
三、内核自动释放sk_buff原因
1. 所有权转移
- 调用netlink_unicast后,sk_buff的所有权从调用者庄毅给内核Netlink子系统
- 无论发送成功与否,内核会负责释放sk_buff,调用者无需手动释放,否则会导致双重释放崩溃
2. 发送完成后的释放逻辑
- 发送成功:消息被加入接收方(用户空间)的套接字缓冲区后,用户空间通过recvmsg读取数据,内核在用户空间完成读取后释放sk_buff
- 发送失败
- 若因目标不存在或缓冲区满导致无法发送,内核直接释放sk_buff
- 若因临时错误(如资源不足),内核可能重试或丢弃并释放
3. 引用计数机制
- sk_buff内部维护引用计数(skb->users),netlink_unicast调用时会增加引用计数
- 当消息被成功传输或丢弃时,引用计数减为0,触发kfree_skb释放内存。
四、使用示例
内核模块发送消息
// 创建并填充 sk_buff
struct sk_buff *skb = nlmsg_new(NLMSG_ALIGN(payload_len), GFP_KERNEL);
struct nlmsghdr *nlh = nlmsg_put(skb, 0, 0, NLMSG_NOTIFY, payload_len, 0);
memcpy(nlmsg_data(nlh), data, payload_len);
// 发送到用户空间进程(PID=123)
int ret = netlink_unicast(nl_sk, skb, 123, 0);
if (ret < 0) {
// 错误处理(注意:skb 已由内核释放,无需手动释放)
pr_err("发送失败: %d\n", ret);
}
五、关键注意事项
1. 禁止手动释放skb
调用netlink_unicast后,任何对skb的操作(包括kfree_skb)都是危险的,可能导致内存错误
2. 错误处理
发送失败时,错误码仅用于日志记录或重试逻辑,无需处理skb
3. 多线程/上下文安全
若在原子上下文(如中断处理)中调用,需确保使用GFP_ATOMIC分配skb,且nonblock=1
六、对比其他发送函数
函数 |
作用 |
skb 释放方 |
使用场景 |
netlink_unicast |
单播发送到指定 PID |
内核自动释放 |
定向通知、响应请求 |
netlink_broadcast |
广播到所有监听进程 |
内核自动释放 |
事件广播(如网络接口状态变更) |
nlmsg_free |
手动释放 skb |
调用者释放 |
构造消息失败时取消发送 |
七、总结
netlink_unicast时内核通过Netlink协议向用户空间单播消息的核心接口,其自动释放sk_buff的机制通过所有权转移和引用计数实现,确保内存安全。开发者只需关注消息构造和发送逻辑,无需手动管理内存,极大降低了资源泄漏风险。