Linux基础 -- 用户态Generic Netlink库高性能接收与回调框架

用户态Generic Netlink库高性能接收与回调框架

一、概述

在 Linux 系统中,Netlink 是用户态与内核态通信的强大机制。libnl 是一个专为简化 Netlink 编程而设计的库,提供了接收和处理 Netlink 消息的高级接口。libnl-genl 是其通用 Netlink (Generic Netlink) 扩展库,专门用于处理自定义协议。本文将详细介绍如何通过 nl_socket_modify_cbnl_cb_set 两种方法实现高效的 Netlink 消息接收和处理框架。

Netlink 使用回调机制来处理接收到的消息。常用的回调设置方法有两种:

1. nl_socket_modify_cb

  • 仅在指定的 Netlink 套接字上设置回调。
  • 适合在每个套接字上使用不同回调。

2. nl_cb_set

  • 在回调上下文 (struct nl_cb *) 上设置回调。
  • 可以在多个套接字之间共享。

1. epoll + 非阻塞 + 批量接收

  • 使用 epoll 进行非阻塞监听。
  • 通过大缓冲区批量接收消息。
  • 结合 libnllibnl-genl 实现高效的 Netlink 消息接收。
c 复制代码
#include <netlink/netlink.h>
#include <netlink/msg.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#define MY_GENL_FAMILY "my_custom_family"

// 自定义消息处理回调
int my_genl_handler(struct nl_msg *msg, void *arg) {
	struct nlmsghdr *nlh = nlmsg_hdr(msg);
	struct genlmsghdr *gnlh = nlmsg_data(nlh);
    
	printf("[Genl Handler] Received a Netlink message.\n");
	printf("Command: %d, Version: %d\n", gnlh->cmd, gnlh->version);
	
	struct nlattr *attrs[GENL_MAX_ATTRS + 1] = {};
	nla_parse(attrs, GENL_MAX_ATTRS, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
	
	if (attrs[GENL_ATTR_MSG]) {
		printf("Message: %s\n", nla_get_string(attrs[GENL_ATTR_MSG]));
	}
	
    return NL_OK;
}

// 设置非阻塞模式
void set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int main() {
    struct nl_sock *sock = nl_socket_alloc();
    nl_connect(sock, NETLINK_GENERIC);

    // 查找 Generic Netlink family
    int family_id = genl_ctrl_resolve(sock, MY_GENL_FAMILY);
    if (family_id < 0) {
        printf("[Error] Could not resolve Generic Netlink family\n");
        return -1;
    }

    struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
    nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_genl_handler, NULL);
    nl_socket_set_cb(sock, cb);

    int fd = nl_socket_get_fd(sock);
    set_nonblocking(fd);

    int epfd = epoll_create1(0);
    struct epoll_event ev, events[10];
    ev.events = EPOLLIN;
    ev.data.fd = fd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);

    while (1) {
        int nfds = epoll_wait(epfd, events, 10, -1);
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == fd) {
                nl_recvmsgs(sock, cb); // 使用 libnl + epoll 处理消息
            }
        }
    }

    nl_cb_put(cb);
    nl_close(sock);
    nl_socket_free(sock);
    close(epfd);
    return 0;
}

3. 多线程处理(可选)

  • 通过线程池并行处理接收到的 Netlink 消息。
  • 每个线程从 epoll 事件队列中读取并解析 Netlink 消息。

四、最佳实践

  • 通过 nl_socket_modify_cb 灵活调整特定套接字的回调。
  • 通过 nl_cb_set 设置全局回调,并在多套接字间复用。
  • 对于高频 Netlink 通信,采用非阻塞模式 + epoll + 批量处理。
  • 使用 libnl-genl 处理 Generic Netlink,简化自定义协议实现。

五、总结

本框架提供了一种高效的 Netlink 消息接收和处理方式,适用于高并发和高性能场景,并支持自定义协议的灵活扩展。

相关推荐
Nerd Nirvana4 小时前
15个提升开发效率的VS Code插件推荐
linux·vscode·开发工具·嵌入式软件开发·插件使用·智能采集设备·边缘终端
v***16024 小时前
Linux安装Redis以及Redis三种启动方式
linux·redis·bootstrap
AI视觉网奇4 小时前
vscode 远程失败
linux·服务器
好奇的菜鸟5 小时前
WSL 2 安装 Ubuntu 24.04 及系统迁移到非系统盘
linux·运维·ubuntu
z***56565 小时前
如何在 Ubuntu 22.04 上安装 MySQL
linux·mysql·ubuntu
好奇的菜鸟5 小时前
在WSL Ubuntu 24中设置root密码并默认使用root登录
linux·运维·ubuntu
偶像你挑的噻5 小时前
15-Linux驱动开发-PWM子系统
linux·驱动开发·stm32·嵌入式硬件
礼拜天没时间.5 小时前
《Grafana 企业级可视化监控实战指南:从安装、配置到智能告警》:Grafana 简介
linux·运维·信息可视化·zabbix·grafana·监控
礼拜天没时间.5 小时前
《Grafana 企业级可视化监控实战指南:从安装、配置到智能告警》:Grafana 环境搭建
linux·运维·信息可视化·zabbix·grafana·监控
边疆.5 小时前
【Linux】基础IO
linux·运维·服务器·io·文件操作·重定向