当前位置: 首页 > news >正文

ev_loop_fork函数

 libev监视器介绍:libev监视器用法-CSDN博客

libev loop对象介绍:loop对象-CSDN博客

libev ev_loop_fork函数介绍:ev_loop_fork函数-CSDN博客

libev API吐血整理:https://download.csdn.net/download/qq_39466755/90794251?spm=1001.2014.3001.5503

用于解决fork函数导致子进程集成的fd集合失效问题

#include <stdio.h>
#include <unistd.h>
#include <sys/event.h>
#include <fcntl.h>void child_process(int kq) {printf("Child: Attempting to use inherited kqueue...\n");struct kevent events[1];int n = kevent(kq, NULL, 0, events, 1, NULL); // 无超时等待printf("Child: kevent returned %d events (expected: 1)\n", n);
}int main() {int kq = kqueue();int pipe_fd[2];pipe(pipe_fd);// 监控管道读端struct kevent ev;EV_SET(&ev, pipe_fd[0], EVFILT_READ, EV_ADD, 0, 0, NULL);kevent(kq, &ev, 1, NULL, 0, NULL);// 触发事件write(pipe_fd[1], "test", 5);pid_t pid = fork();if (pid == 0) {child_process(kq); // 子进程直接使用继承的 kqueue_exit(0);} else {struct kevent events[1];int n = kevent(kq, NULL, 0, events, 1, NULL);printf("Parent: kevent returned %d events\n", n);}return 0;
}

运行结果

Child: Attempting to use inherited kqueue...
Child: kevent returned 0 events (expected: 1)  # 子进程事件丢失!
Parent: kevent returned 1 events               # 父进程正常

修改代码子进程可以正常接收父进程的fd集合

#include <ev.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>// 管道读端回调
static void pipe_cb(struct ev_loop *loop, ev_io *w, int revents) {char buf[256];ssize_t n = read(w->fd, buf, sizeof(buf));printf("[%s] Received data: %.*s\n", getpid() == getppid() ? "Parent" : "Child", (int)n, buf);
}int main() {// 忽略 SIGPIPE(防止写入关闭的管道导致进程退出)signal(SIGPIPE, SIG_IGN);struct ev_loop *loop = EV_DEFAULT;int pipe_fd[2];pipe(pipe_fd);// 监控管道读端ev_io pipe_watcher;ev_io_init(&pipe_watcher, pipe_cb, pipe_fd[0], EV_READ);ev_io_start(loop, &pipe_watcher);// 写入数据(触发事件)write(pipe_fd[1], "hello", 6);pid_t pid = fork();if (pid == 0) {// ---------- 关键修复 ----------ev_loop_fork(loop);  // 重置内核状态// ------------------------------printf("Child: Started event loop\n");ev_run(loop, 0);  // 子进程现在能正常接收事件_exit(0);} else {printf("Parent: Started event loop\n");ev_run(loop, 0);}return 0;
}

运行结果

Parent: Started event loop
[Parent] Received data: hello  # 父进程正常接收
Child: Started event loop
[Child] Received data: hello   # 子进程修复后也能接收

结合libev接口,父子进程共享循环时的正确用法

struct ev_loop *loop = EV_DEFAULT;
ev_io parent_watcher;
ev_io_init(&parent_watcher, parent_cb, pipe_fd[0], EV_READ);
ev_io_start(loop, &parent_watcher);pid_t pid = fork();
if (pid == 0) {// 子进程ev_loop_fork(loop);  // 先重置后端// 添加子进程独有的监视器ev_io child_watcher;ev_io_init(&child_watcher, child_cb, another_fd, EV_WRITE);ev_io_start(loop, &child_watcher);ev_run(loop, 0);  // 现在能正确处理父/子监视器的事件
} else {// 父进程继续原逻辑ev_run(loop, 0);
}

代码解析:

        libev 使用底层机制(如 epoll/kqueue)来监听文件描述符。当调用 fork() 时,子进程会继承父进程的 epoll 实例,但该实例可能已失效(内核状态与用户态不一致)。ev_loop_fork() 会重建后端(如重新创建 epoll 实例),确保事件循环在子进程中能正常工作。因为struct ev_loop *loop = EV_DEFAULT;已经创建了底层的事件监听机制(如 epoll、kqueue 或 select 等,具体取决于系统支持)。

        即使子进程不直接使用 pipe_fd[0],事件循环本身仍需正确的后端支持。

        虽然子进程没有主动使用 parent_watcher(监视 pipe_fd[0]),但该监视器仍存在于 loop 中(因为它是父进程注册的)。未重置的事件循环可能会错误地尝试处理这些继承的监视器,导致未定义行为。

        一般情况下都是搭配libev开源库的API函数(ev_fork_init,ev_fork_start等)一起使用:

#include <ev.h>
#include <unistd.h>
#include <stdio.h>// fork 回调函数
void fork_cb(EV_P_ ev_fork *w, int revents) {printf("Child process (PID: %d) reinitializing event loop...\n", getpid());ev_loop_fork(EV_A); // 必须调用,重新初始化子进程的事件循环
}int main() {struct ev_loop *loop = EV_DEFAULT;struct ev_fork fork_watcher;// 初始化fork监视器ev_fork_init(&fork_watcher, fork_cb);ev_fork_start(loop, &fork_watcher); // 启动监视器printf("Parent process (PID: %d) started. Forking...\n", getpid());pid_t pid = fork();if (pid == 0) {// 子进程:ev_loop_fork已在回调中调用ev_run(loop, 0); // 子进程事件循环} else if (pid > 0) {// 父进程代码printf("Parent process continues (child PID: %d)\n", pid);sleep(2); // 模拟父进程工作} else {perror("fork failed");return 1;}return 0;
}

相关文章:

  • 【部署】win10的wsl环境下调试dify的api后端服务
  • 初学者入门指南:什么是网络拓扑结构?
  • Java后端开发day46--多线程(二)
  • 互联网大厂Java求职面试实战:Spring Boot微服务与数据库优化详解
  • rust-candle学习笔记12-实现因果注意力
  • 数据结构精解:优先队列、哈希表与树结构
  • 【Redis】string
  • Exploring Temporal Event Cues for Dense Video Captioning in Cyclic Co-Learning
  • 【C++指南】STL容器的安全革命:如何封装Vector杜绝越界访问与迭代器失效?
  • [论文阅读]BadPrompt: Backdoor Attacks on Continuous Prompts
  • 提高工作效率的新选择[特殊字符]——Element Plus UI库
  • HNUST湖南科技大学-软件测试期中复习考点(保命版)
  • window环境下,如何通过USB接口控制打印机
  • Spring MVC 视图解析器 (ViewResolver) 如何配置? Spring Boot 是如何自动配置常见视图解析器的?
  • idea如何快速生成测试类
  • 【DLF】基于语言的多模态情感分析
  • 如何阅读、学习 Linux 2 内核源代码 ?
  • 《AI大模型应知应会100篇》第54篇:国产大模型API对比与使用指南
  • 2025数维杯数学建模A题完整参考论文(共36页)(含模型、可运行代码、数据)
  • 内存安全暗战:从 CVE-2025-21298 看 C 语言防御体系的范式革命
  • 贵州省总工会正厅级副主席梁伟被查,曾任贵州省纪委副书记
  • “拼好假”的年轻人,今年有哪些旅游新玩法?
  • 西安机场回应航站楼“水帘洞”事件:屋面排水系统被冰雹堵塞
  • 国家税务总局泰安市税务局:山东泰山啤酒公司欠税超536万元
  • 2024年上市公司合计实现营业收入71.98万亿元
  • 上市不足一年,吉利汽车拟私有化极氪并合并:整合资源,杜绝重复投入