Linux基础 -- epoll监听Netlink并实现

server/2024/12/15 19:45:41/

使用epoll监听Netlink并实现高级用法

本文档主要介绍如何使用 epoll 监听 Netlink 消息,包括基础实现与高级用法。

epoll监听Netlink的基础实现

以下示例展示了如何通过 epoll 监听 Netlink 消息并处理收发。

功能说明

  • 创建一个 Netlink 套接字。
  • 使用 epoll 监听来自 Netlink 的消息。
  • 收到消息后进行处理并回送消息。

示例代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
#include <sys/epoll.h>#define NETLINK_USER 31
#define MAX_PAYLOAD 1024  // 最大负载
#define EPOLL_MAX_EVENTS 10int main() {struct sockaddr_nl src_addr, dest_addr;struct nlmsghdr *nlh = NULL;struct iovec iov;struct msghdr msg;int sock_fd, epoll_fd;struct epoll_event ev, events[EPOLL_MAX_EVENTS];// 创建netlink套接字sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);if (sock_fd < 0) {perror("socket creation failed");return -1;}memset(&src_addr, 0, sizeof(src_addr));src_addr.nl_family = AF_NETLINK;src_addr.nl_pid = getpid();  // 用户进程IDsrc_addr.nl_groups = 0;     // 不订阅多播组// 绑定套接字if (bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)) < 0) {perror("bind failed");close(sock_fd);return -1;}// 初始化epollepoll_fd = epoll_create1(0);if (epoll_fd < 0) {perror("epoll_create1 failed");close(sock_fd);return -1;}ev.events = EPOLLIN;  // 可读事件ev.data.fd = sock_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock_fd, &ev) < 0) {perror("epoll_ctl failed");close(sock_fd);close(epoll_fd);return -1;}// 设置netlink消息结构nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);nlh->nlmsg_pid = getpid();nlh->nlmsg_flags = 0;iov.iov_base = (void *)nlh;iov.iov_len = nlh->nlmsg_len;memset(&msg, 0, sizeof(msg));msg.msg_name = (void *)&dest_addr;msg.msg_namelen = sizeof(dest_addr);msg.msg_iov = &iov;msg.msg_iovlen = 1;// 循环监听事件while (1) {int nfds = epoll_wait(epoll_fd, events, EPOLL_MAX_EVENTS, -1);for (int i = 0; i < nfds; i++) {if (events[i].data.fd == sock_fd) {// 接收消息recvmsg(sock_fd, &msg, 0);printf("Received message payload: %s\n", (char *)NLMSG_DATA(nlh));// 回送消息strcpy((char *)NLMSG_DATA(nlh), "Reply from user space");sendmsg(sock_fd, &msg, 0);}}}// 清理资源close(sock_fd);close(epoll_fd);free(nlh);return 0;
}

epoll的高级用法

1. 使用 EPOLLET(边沿触发模式)

边沿触发模式只在状态发生变化时触发事件,适合高性能场景。

代码示例
ev.events = EPOLLIN | EPOLLET;  // 边沿触发
ev.data.fd = sock_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock_fd, &ev);while (1) {int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);for (int i = 0; i < nfds; i++) {if (events[i].events & EPOLLIN) {char buf[1024];while (1) {ssize_t n = read(events[i].data.fd, buf, sizeof(buf));if (n <= 0) {if (errno == EAGAIN) break;perror("read");close(events[i].data.fd);break;}printf("Read %ld bytes: %s\n", n, buf);}}}
}

2. 使用 EPOLLONESHOT

EPOLLONESHOT 避免同一事件被多个线程处理,触发后需要重新注册。

代码示例
ev.events = EPOLLIN | EPOLLONESHOT;
ev.data.fd = sock_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock_fd, &ev);void handle_event(int epoll_fd, int fd) {printf("Handling event for fd %d\n", fd);struct epoll_event ev;ev.events = EPOLLIN | EPOLLONESHOT;ev.data.fd = fd;epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
}

3. 并发处理(多线程)

通过线程池分发事件,提高处理效率。

代码示例
void *worker_thread(void *arg) {int fd = *(int *)arg;char buf[1024];read(fd, buf, sizeof(buf));printf("Worker thread handled fd %d: %s\n", fd, buf);
}void epoll_main_loop(int epoll_fd) {struct epoll_event events[MAX_EVENTS];while (1) {int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);for (int i = 0; i < nfds; i++) {pthread_t tid;pthread_create(&tid, NULL, worker_thread, &events[i].data.fd);pthread_detach(tid);}}
}

4. 使用定时器

结合 timerfd 实现高效的超时处理。

代码示例
#include <sys/timerfd.h>int tfd = timerfd_create(CLOCK_MONOTONIC, 0);
struct itimerspec new_value = {.it_value = {5, 0},  // 5秒后触发.it_interval = {0, 0}
};
timerfd_settime(tfd, 0, &new_value, NULL);struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = tfd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, tfd, &ev);if (events[i].data.fd == tfd) {uint64_t expirations;read(tfd, &expirations, sizeof(expirations));printf("Timer expired %llu times\n", expirations);
}

总结

epoll 是高性能 I/O 多路复用的强大工具,结合高级用法如 EPOLLETEPOLLONESHOT、线程池和定时器,可以实现更复杂和高效的网络或系统事件处理。


http://www.ppmy.cn/server/150421.html

相关文章

2024年全国仿真创新应用大赛 | MWORKS助力“复杂系统数字仿真”赛道,获奖名单公布

2024年全国仿真创新应用大赛全国总决赛于近日圆满落幕。大赛由工业和信息化部人才交流中心主办&#xff0c;以“创新引领&#xff0c;铸就未来”为主题&#xff0c;来自全国的参赛院校、企业、医学科学单位、军事科学单位及仿真领域的科研院所共计422家、近1300余人参加了此次总…

“深化国际合作,共绘深空蓝图” | 同元软控受邀为亚太空间合作组织进行协同仿真设计培训

2024年12月2-6日&#xff0c;由亚太空间合作组织&#xff08;APSCO&#xff09;、中国国家航天局探月与航天工程中心联合主办的“协同仿真设计”短期培训项目在北京举行。该培训旨在提高空间探索项目开发人员的数字建模和仿真能力&#xff0c;从而实现基于模型驱动系统工程的数…

jvm结构介绍

Java虚拟机&#xff08;JVM&#xff09;是Java平台的核心组件&#xff0c;它负责将Java字节码转换为机器码 1. 类加载子系统&#xff08;Class Loading Subsystem&#xff09;&#xff1a; • 负责将Java类加载到JVM中。这包括从文件系统、网络或其他来源读取.class文件&#x…

linux在没网的情况下如何校验时间 超详细拿来即用

一、没有校时服务器的话 1、手动修改 sudo date --set"2024-06-17 13:44:00"二、有校时服务器的话 1、手动校时 ntpdate 14.193.73.22、自动校时 写一个校时服务脚本 14.193.73.2 是校验时间服务器 #!/bin/sh while true dontpdate 14.193.73.2sleep 5;hwclock…

HTML、CSS表格的斜表头样式设置title 画对角线

我里面有用到layui框架的影响&#xff0c;实际根据你自己的框架来小调下就可以 效果如下 上代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…

C# 实现 10 位纯数字随机数

本文将介绍如何用 C# 实现一个生成 10 位纯数字随机数的功能。以下是完整的代码示例&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace RandomTset {class Program{// 使用GUID作为种子来创建随机数生成器static…

MAVEN--Maven的生命周期,pom.xml详解,Maven的高级特性(模块化、聚合、依赖管理)

目录 &#xff08;一&#xff09;Maven的生命周期 1.Maven的三套生命周期 2.Maven常用命令 &#xff08;二&#xff09;pom.xml详解 &#xff08;三&#xff09;Maven的高级特性&#xff08;模块化、聚合、依赖管理&#xff09; 1.Maven的依赖范围 2.版本维护 3.依赖传…

使用DuckDB 加载和清洗数据

DuckDB CLI是允许用户直接从命令行与DuckDB交互的工具。前文你看到了如何使用Python与DuckDB交互。但是&#xff0c;有时你只是想直接使用数据库—例如在创建新表、从不同数据源导入数据以及执行与数据库相关的任务时。在这种情况下&#xff0c;直接使用DuckDB CLI要有效得多。…