第一百二十一章:信号旗
问道期涉及内核源码:
一
林小源在网络之海的深处发现了一座奇怪的建筑——它不是港口,不是灯塔,而是一座高耸的旗塔。塔顶飘着无数彩色的旗帜,每一面旗帜都在不断变化图案。
"这些旗帜是什么?"
"Netlink 消息。"一个威严的声音从塔顶传来。
林小源抬头,看到一个身穿铠甲的将军站在塔顶。铠甲上刻着复杂的消息结构—— 的字段在甲片上闪闪发光:消息长度、消息类型、标志、序列号、进程 ID。
"我是 Netlink socket。"将军说,"内核和用户空间之间的通信通道。你看到的这些旗帜——每一面都是内核发给用户空间的消息。"
"内核主动发消息?"
"对。"将军挥了挥手,一面旗帜从塔顶飘下,上面写着"网络接口 eth0 已上线"。"比如网卡上线、路由变化、设备插入——这些事件发生时,我会主动通知用户空间。用户空间不需要轮询,只需要监听 Netlink socket。"
林小源接过那面旗帜,感到上面的信息在跳动:"那用户空间也能给内核发消息?"
"当然。ip 命令配置网络、ss 命令查看 socket 统计、 配置防火墙——都是通过 Netlink 发消息给内核。双向通信,异步推送。"
二
"你和 ioctl 有什么区别?"林小源问。
将军的表情变得不屑:"ioctl?那是老旧的方式——同步、单向、只能由用户空间发起。你发一个 ioctl 请求,等待内核返回结果。一次只能查一个东西。"
"你呢?"
"我是异步的。"将军说,"内核可以随时推送消息——不需要用户空间先问。你可以一次性查所有网络接口的状态,不用一个一个查。而且我的消息是结构化的—— 加上属性(),比 ioctl 的二进制数据好解析多了。"
林小源看着塔上飘扬的旗帜:"你有哪些类型?"
"按协议族分。"将军从铠甲上摘下一枚徽章,"——路由和网络配置,ip 命令用的。——设备事件, 用的。——防火墙配置, 用的。还有 ——通用的,任何子系统都可以用。"
"udev 也用 Netlink?"
"对。当你插入一个 USB 设备,内核通过 发消息给 udev,udev 根据规则创建设备节点。整个过程不需要轮询——事件驱动。"
三
将军从塔顶走下来,带着林小源走进旗塔的内部。这里是一间巨大的仓库,堆满了旗帜的原料——消息缓冲区。
"Netlink 也有容量限制。"将军指着仓库,"每个 Netlink socket 有一个接收缓冲区。如果消息太多、处理太慢,缓冲区会溢出,消息会被丢弃。"
"丢弃?"
"对。NETLINK_NO_OMEVENT 标志可以控制溢出时的行为——是丢弃新消息还是丢弃旧消息。"将军说,"所以监听 Netlink 的程序要足够快——不能在处理一条消息时阻塞太久,否则会丢事件。"
林小源看着仓库里的旗帜,忽然明白了——Netlink 不仅仅是一个通信机制,它是内核和用户空间之间的桥梁。通过这座桥,用户空间可以配置内核、监听事件、查询状态。它是 Linux 网络管理的基石。
"ip 命令、ss 命令、iptables——它们的底层都是 Netlink。"将军说,"你看到的那些网络管理工具,本质上都是 Netlink 的客户端。"
/*
* Netlink socket:
*
* 1. 用途
* 内核 → 用户空间: 事件通知
* 用户空间 → 内核: 配置命令
*
* 2. 类型
* NETLINK_ROUTE — 路由和网络配置
* NETLINK_KOBJECT_UEVENT — 设备事件
* NETLINK_NETFILTER — netfilter
* NETLINK_GENERIC — 通用
*
* 3. 工具
* ip 命令使用 Netlink
* ss 命令使用 Netlink
* udev 使用 Netlink
*
* Netlink 的优势:
* 异步通信
* 可以从内核推送消息
* 比 ioctl 更灵活
*/
printf("=== Netlink — 内核与用户空间的桥梁 ===\n\n");
printf("Netlink socket:\n");
printf(" 内核和用户空间之间的通信通道\n");
printf(" 用于配置网络、管理设备\n\n");
printf("--- 通信方向 ---\n");
printf("用户空间 → 内核:\n");
printf(" 配置命令\n");
printf(" 查询信息\n\n");
printf("内核 → 用户空间:\n");
printf(" 事件通知\n");
printf(" 状态变化\n\n");
printf("--- Netlink 类型 ---\n");
printf("NETLINK_ROUTE:\n");
printf(" 路由和网络配置\n");
printf(" ip 命令使用\n\n");
printf("NETLINK_KOBJECT_UEVENT:\n");
printf(" 设备事件\n");
printf(" udev 使用\n\n");
printf("NETLINK_NETFILTER:\n");
printf(" netfilter 配置\n");
printf(" iptables 使用\n\n");
printf("--- 使用 Netlink 的工具 ---\n");
printf("ip: 网络配置\n");
printf("ss: socket 统计\n");
printf("udev: 设备管理\n");
printf("iptables: 防火墙\n\n");
printf("--- Netlink vs ioctl ---\n");
printf("ioctl:\n");
printf(" 同步\n");
printf(" 只能用户空间发起\n\n");
printf("Netlink:\n");
printf(" 异步\n");
printf(" 双向通信\n");
printf(" 可以推送消息\n\n");
printf("--- Netlink 消息 ---\n");
printf("struct nlmsghdr {\n");
printf(" __u32 nlmsg_len; // 消息长度\n");
printf(" __u16 nlmsg_type; // 消息类型\n");
printf(" __u16 nlmsg_flags; // 标志\n");
printf(" __u32 nlmsg_seq; // 序列号\n");
printf(" __u32 nlmsg_pid; // 进程 ID\n");
printf("};\n");#include <stdio.h>
/*
* Netlink socket:
*
* 1. 用途
* 内核 → 用户空间: 事件通知
* 用户空间 → 内核: 配置命令
*
* 2. 类型
* NETLINK_ROUTE — 路由和网络配置
* NETLINK_KOBJECT_UEVENT — 设备事件
* NETLINK_NETFILTER — netfilter
* NETLINK_GENERIC — 通用
*
* 3. 工具
* ip 命令使用 Netlink
* ss 命令使用 Netlink
* udev 使用 Netlink
*
* Netlink 的优势:
* 异步通信
* 可以从内核推送消息
* 比 ioctl 更灵活
*/
int main() {
printf("=== Netlink — 内核与用户空间的桥梁 ===\n\n");
printf("Netlink socket:\n");
printf(" 内核和用户空间之间的通信通道\n");
printf(" 用于配置网络、管理设备\n\n");
printf("--- 通信方向 ---\n");
printf("用户空间 → 内核:\n");
printf(" 配置命令\n");
printf(" 查询信息\n\n");
printf("内核 → 用户空间:\n");
printf(" 事件通知\n");
printf(" 状态变化\n\n");
printf("--- Netlink 类型 ---\n");
printf("NETLINK_ROUTE:\n");
printf(" 路由和网络配置\n");
printf(" ip 命令使用\n\n");
printf("NETLINK_KOBJECT_UEVENT:\n");
printf(" 设备事件\n");
printf(" udev 使用\n\n");
printf("NETLINK_NETFILTER:\n");
printf(" netfilter 配置\n");
printf(" iptables 使用\n\n");
printf("--- 使用 Netlink 的工具 ---\n");
printf("ip: 网络配置\n");
printf("ss: socket 统计\n");
printf("udev: 设备管理\n");
printf("iptables: 防火墙\n\n");
printf("--- Netlink vs ioctl ---\n");
printf("ioctl:\n");
printf(" 同步\n");
printf(" 只能用户空间发起\n\n");
printf("Netlink:\n");
printf(" 异步\n");
printf(" 双向通信\n");
printf(" 可以推送消息\n\n");
printf("--- Netlink 消息 ---\n");
printf("struct nlmsghdr {\n");
printf(" __u32 nlmsg_len; // 消息长度\n");
printf(" __u16 nlmsg_type; // 消息类型\n");
printf(" __u16 nlmsg_flags; // 标志\n");
printf(" __u32 nlmsg_seq; // 序列号\n");
printf(" __u32 nlmsg_pid; // 进程 ID\n");
printf("};\n");
return 0;
}道藏笔记
内核启示
Netlink socket 是内核与用户空间的通信通道。
Netlink 的用途:
- 用户空间 → 内核:配置命令
- 内核 → 用户空间:事件通知
Netlink 类型:
- — 路由和网络配置
- — 设备事件
- — netfilter
Netlink 的优势:
- 异步通信
- 双向通信
- 可以推送消息
Netlink 是"信号旗"——让内核和用户空间对话。
Netlink 之试
Netlink 消息太多导致缓冲区溢出时,本章提到控制溢出行为的标志是什么?