驱动-热插拔-Netlink广播监听内核状态
Netlink广播监听内核状态
文章目录
- 前言
- 参考资料
- 一、Netlink 知识点
- 核心特点
- 与其他机制的对比
- 在 Linux 中的主要应用
- 网络配置 (Routing, Addressing, Links)
- 网络防火墙与过滤 (Netfilter/IPTables)
- 策略路由和高级路由
- 设备与内核事件通知
- 进程管理与审计
- 内核与用户空间自定义通信
- 其他系统任务
- Nitlink 知识点小结
- 二、Nitlink 实现内核事件
- 源码程序
- 配置交叉编译器
- 测试实验
- 相关知识点小结
- 函数 bzero
- 总结
前言
前面了解过uevent
事件,内核发送事件到用户空间。 监听方式 使用 udevadm
命令 后台监听事件,查看打印内核信息。
那么 我们看看其它方式来监听 内核信息
参考资料
驱动-热插拔-内核发送事件到用户空间-uevent
驱动-热插拔-kset_uevent_ops知识点
netlink监听广播信息
热插拔
一、Netlink 知识点
Netlink
是 Linux
内核提供的一种用于内核与用户空间进程之间进行双向通信的 IPC
(进程间通信)机制。它构建在标准的 Unix
套接字(socket
)接口之上,专门设计用来传输各种类型的网络和系统信息。
你可以把它想象成一条专门用于“系统管理”的高速数据总线,用户空间的程序(如网络配置工具、监控工具)可以通过这条总线向内核发送指令或请求,内核也可以通过它向用户空间主动发送事件通知(如设备热插拔、网络链路状态变化)。
它本质上面我理解就是一个: socket
。
核心特点
- 全双工通信:通信是双向的,用户空间可以发消息给内核,内核也可以发消息给用户空间。
- 异步机制:通信通常是异步的,发送方不需要等待接收方的即时响应。
- 面向数据报:基于消息(
message
)而非字节流,每个消息都是一个自包含的数据包,简化了协议解析。 - 协议族:
Netlink
不是一个单一的协议,而是一个协议族。每个子系统(如路由、防火墙、设备通知)都有自己独特的Netlink
协议号(NETLINK_ROUTE, NETLINK_NFLOG
等),这使得通信井井有条,互不干扰。 - 支持多播:内核可以将一条消息多播给多个用户空间的监听者,这对于事件通知(如“有一个新
USB
设备插入”)非常高效。
与其他机制的对比
机制 | 描述 | 缺点(与 Netlink 相比) |
---|---|---|
IOCTL | 通过设备文件进行控制,是传统的控制方式。 | 单向(只能用户空间发起),需要定义复杂的数据结构,扩展性差。 |
/proc 或 /sys | 通过读写虚拟文件来获取信息或设置参数。 | 单向(通常为用户空间轮询),适用于输出信息,但不擅长复杂的控制和事件通知。 |
系统调用 | 用户空间调用内核函数。 | 需要预先定义,功能固定,不适合传输大量或动态变化的数据。 |
Netlink
克服了这些缺点,提供了更现代、灵活和强大的通信方式。
在 Linux 中的主要应用
网络配置 (Routing, Addressing, Links)
这是 Netlink
最经典的应用场景。用于配置网络接口、IP地址、路由表、邻居表(ARP/NDP
)等。
工具:iproute2
工具集(如 ip addr, ip link, ip route, ip neigh
)完全替代了古老的 ifconfig, route, arp
等net-tools
工具集,其背后就是通过 Netlink 套接字(NETLINK_ROUTE 协议)与内核通信。
示例:
当你执行ip addr add 192.168.1.10/24 dev eth0
时,ip
命令会通过 Netlink
发送一个 RTM_NEWADDR
消息给内核,内核收到后为 eth0
接口添加这个 IP
地址。
当你执行 ip link set eth0 up
时,会发送 RTM_NEWLINK
消息来启动接口。
网络防火墙与过滤 (Netfilter/IPTables)
Netlink
用于与内核的 Netfilter
子系统交互,管理防火墙规则。
工具: iptables, nftables
(下一代防火墙工具)。
**机制:**虽然传统的 iptables
使用模块和内核系统调用,但 nftables
及其用户空间工具 nft
严重依赖 Netlink(NETLINK_NETFILTER)
来传递规则集和计数器信息,效率更高。
策略路由和高级路由
对于复杂的网络环境,如多路由表、基于策略的路由,配置完全依赖于 Netlink
。
设备与内核事件通知
内核可以通过 Netlink 多播组向用户空间主动发送事件消息。
协议: NETLINK_KOBJECT_UEVENT
(最重要的应用之一)
工具: udev
(设备管理器)
**机制:**当内核中发生设备事件(如U盘插入、硬盘热拔插)时,内核会向 NETLINK_KOBJECT_UEVENT
多播组发送一条消息(uevent
)。udevd
守护进程一直在监听这个组,收到消息后,会根据 /lib/udev/rules.d/
下的规则来创建设备节点、加载驱动、设置权限等。这是现代 Linux
桌面和服务器自动识别设备的基础。
进程管理与审计
协议: NETLINK_AUDIT, NETLINK_CONNECTOR(NETLINK_USERSOCK)
工具: auditd
(审计守护进程)
机制: 内核的审计子系统可以通过 Netlink
将安全审计信息(如谁调用了某个系统调用、文件访问记录等)发送给用户空间的 auditd
进程进行记录和分析。
内核与用户空间自定义通信
开发者可以创建自己的 Netlink 协议号(NETLINK_USER
或更高)来编写内核模块和对应的用户空间程序,实现自定义的、高性能的内核-用户通信。这在某些特殊需求的驱动或内核模块开发中非常有用。
其他系统任务
IPC 命名空间: ipcrm, ipcs
等工具使用 Netlink
来管理 System V IPC
对象。
SELinux:SELinux
安全策略的某些通信也通过 Netlink
进行。
Nitlink 知识点小结
Netlink
是现代 Linux
系统中内核与用户空间进行“管理信息”交换的基石级机制。 它使得功能强大的用户空间工具(如 iproute2, udev, nftables, auditd
)能够高效、灵活地控制和监控内核的各个方面,特别是在网络栈和设备管理领域。可以说,没有 Netlink
,就没有现代 Linux
灵活而强大的网络配置和设备管理功能。
二、Nitlink 实现内核事件
上面介绍了nitlink 知识点,其实就是如何监听 内核事件
源码程序
#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>int main(int argc, char *argv[])
{int ret;struct sockaddr_nl *nl; // 定义一个指向 struct sockaddr_nl 结构体的指针 nlint len = 0;char buf[4096] = {0}; // 数据接收缓冲区int i = 0;bzero(nl, sizeof(struct sockaddr_nl)); // 将 nl 指向的内存区域清零, 确保结构体的字段初始化为 0nl->nl_family = AF_NETLINK; // 设置 nl 结构体的 nl_family 字段为 AF_NETLINK, 指定地址族为 Netlinknl->nl_pid = 0; // 设置 nl 结构体的 nl_pid 字段为 0, 表示目标进程 ID 为 0, 即广播给所有进程nl->nl_groups = 1; // 设置 nl 结构体的 nl_groups 字段为 1, 表示只接收基本组的内核事件int socket_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT); // 创建一个 Netlink套接字if (socket_fd < 0){printf("socket error\n");return -1;}ret = bind(socket_fd, (struct sockaddr *)nl, sizeof(struct sockaddr_nl)); // 使用 bind 函数将 socket_fd 套接字与 nl 地址结构体绑定在一起if (ret < 0){printf("bind error\n");return -1;}while (1){bzero(buf, 4096); // 将缓冲区 buf 清零, 确保数据接收前的初始化len = recv(socket_fd, &buf, 4096, 0); // 从 socket_fd 套接字接收数据, 存储到缓冲区 buf 中,最大接收字节数为 4096for (i = 0; i < len; i++){if (*(buf + i) == '\0'){buf[i] = '\n'; // 如果接收到的数据中有 '\0' 字符, 将其替换为 '\n', 以便在打印时换行显示}}printf("%s\n", buf); // 打印接收到的数据}return 0;
}
配置交叉编译器
之前 HelloWorld驱动编写和加载驱动实验 篇有详细介绍,这里暂不再次说明:
测试实验
首先 交叉编译器 生成 可执行文件:
aarch64-linux-gnu-gcc -o netlink netlink.c
卸载驱动时候,也会有相关的 netlink 监听到的内核相关信息打印:
相关知识点小结
函数 bzero
功能: bzero(代表 “byte zero”)是一个传统的 C 库函数,用于将一段指定长度的内存块的所有字节设置为零(\0)。
参数:
- 第一个参数 void *s: 指向要清零的内存块的起始地址的指针。
- 第二个参数 size_t n: 要清零的字节数。
现状:
- 这个函数起源于 BSD 系统,目前已经被标记为废弃。在现代编程中,更推荐使用标准 C 库中的 memset 函数来实现相同的功能。
- 等价于: memset(nl, 0, sizeof(struct sockaddr_nl));
总结
Netlink
作用蛮多,这里举例用Netlink
来实现 内核监听的一个实例,实际开发当中使用很多,能够快速获取内核相关信息,调试、开发等。