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

Linux `epoll` 机制的入口——`epoll_create`函数

<摘要>

epoll_create 是 Linux 高性能 I/O 多路复用机制 epoll 的初始化函数。它的核心作用是创建一个新的 epoll 实例,并返回一个指向该实例的文件描述符。这个实例本质上是一个内核中的数据结构,用于存储和管理后续通过 epoll_ctl 添加的、需要被监控的文件描述符及其事件。它是使用 epoll API 的第一步,是调用 epoll_ctlepoll_wait 的基础。该函数通常用于构建高性能、高并发的网络服务器。


<解析>

你可以将 epoll 实例理解为一个哨所,而 epoll_create 就是负责搭建这个哨所的命令。哨所本身并不直接监视道路(文件描述符),但它为后续安排哨兵(epoll_ctl)和等待报告(epoll_wait)提供了指挥中心。

1) 函数的概念与用途
  • 功能:创建一个 epoll 实例,并返回一个与之关联的文件描述符。
  • 场景:在任何打算使用 epoll 机制来监视大量文件描述符(尤其是网络套接字)的程序中。通常是程序初始化阶段的第一步。例如,Nginx 或 Redis 服务在启动时就会调用此函数来创建它们的主要事件循环实例。
2) 函数的声明与出处

epoll_create 定义在 <sys/epoll.h> 头文件中,是 Linux 内核系统调用的一部分。

int epoll_create(int size);

还有一个更新的版本:

int epoll_create1(int flags);

(我们将主要介绍 epoll_create,并在最后说明 epoll_create1 的差异)

3) 返回值的含义与取值范围
  • 成功:返回一个新的、非负的文件描述符。这个描述符代表新创建的 epoll 实例。
  • 失败:返回 -1,并设置相应的错误码 errno
    • EINVAL:参数 size 不是正数。
    • EMFILE:已达到进程可打开的文件描述符数量上限。
    • ENFILE:已达到系统可打开的文件描述符总数上限。
    • ENOMEM:没有足够的内存来创建新的 epoll 实例。
4) 参数的含义与取值范围
  1. int size
    • 作用向内核提供一个提示,表示期望监控的文件描述符的大致数量
    • 历史与现状:在最初的内核实现中,这个参数决定了 epoll 实例内部数据结构的大小。然而,在新版本的内核中,这个大小提示并不是强制性的限制,内核会动态调整所需的空间。但为了向后兼容,此参数必须大于零。
    • 取值范围:任何大于 0 的整数。常见的做法是传入一个预期的合理数值,例如 11001000,但即使传入 1,之后也能添加远超于此数量的监控描述符。
5) 函数使用案例

示例 1:基础创建并检查
此示例展示了如何创建一个最简单的 epoll 实例并进行错误检查。

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h> // for close()int main() {int epoll_fd;// 创建一个 epoll 实例。// 参数 1 是一个历史遗留的提示,现在只要大于0即可,内核会动态调整。epoll_fd = epoll_create(1);if (epoll_fd == -1) {perror("epoll_create");exit(EXIT_FAILURE);}printf("epoll instance created successfully. File descriptor: %d\n", epoll_fd);// 重要:使用完毕后,必须关闭 epoll 实例的文件描述符以释放资源。if (close(epoll_fd) == -1) {perror("close");exit(EXIT_FAILURE);}printf("epoll instance closed.\n");return 0;
}

示例 2:集成到简易服务器框架中
此示例展示了 epoll_create 在一个完整服务器程序中的典型位置和作用。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>#define MAX_EVENTS 10
#define PORT 8080int create_and_bind_socket() {int server_fd;struct sockaddr_in address;// 创建套接字if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);// 绑定套接字到端口if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}// 开始监听if (listen(server_fd, SOMAXCONN) < 0) {perror("listen");close(server_fd);exit(EXIT_FAILURE);}return server_fd;
}int main() {int epoll_fd, server_fd;struct epoll_event event, events[MAX_EVENTS];// 1. 创建 epoll 实例 (这是使用epoll的第一步)epoll_fd = epoll_create1(0); // 使用更现代的epoll_create1if (epoll_fd == -1) {perror("epoll_create1");exit(EXIT_FAILURE);}// 2. 创建并设置服务器监听套接字server_fd = create_and_bind_socket();printf("Server listening on port %d\n", PORT);// 3. 设置事件并添加到epoll(这里才用到epoll_ctl)event.events = EPOLLIN; // 监听可读事件(新连接)event.data.fd = server_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event) == -1) {perror("epoll_ctl: add server_fd");close(server_fd);close(epoll_fd);exit(EXIT_FAILURE);}printf("Entering main event loop...\n");// 4. 事件主循环(这里才用到epoll_wait)while(1) {int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);if (nfds == -1) {perror("epoll_wait");break;}for (int n = 0; n < nfds; ++n) {// ... 处理事件,例如accept新连接或读写数据 ...printf("Event occurred on fd: %d\n", events[n].data.fd);// 实际项目中这里会有复杂的业务逻辑}}// 5. 清理资源close(server_fd);close(epoll_fd);return 0;
}

示例 3:错误处理与资源管理
此示例重点展示创建多个实例和严格的错误处理与资源清理。

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <unistd.h>#define NUM_INSTANCES 3int main() {int epoll_fds[NUM_INSTANCES];int success_count = 0;printf("Attempting to create %d epoll instances...\n", NUM_INSTANCES);for (int i = 0; i < NUM_INSTANCES; i++) {epoll_fds[i] = epoll_create(1); // 尝试创建第i个实例if (epoll_fds[i] == -1) {perror("epoll_create failed");printf("Stopped after creating %d instances.\n", success_count);break;} else {success_count++;printf("Created instance %d with fd: %d\n", i, epoll_fds[i]);}}// 无论创建成功了多少个,都安全地关闭所有已成功创建的描述符printf("\nCleaning up: closing all created instances...\n");for (int i = 0; i < success_count; i++) {if (close(epoll_fds[i]) == -1) {perror("close failed");} else {printf("Closed instance with fd: %d\n", epoll_fds[i]);}}printf("Total instances created and closed: %d\n", success_count);return (success_count == NUM_INSTANCES) ? EXIT_SUCCESS : EXIT_FAILURE;
}
6) 编译方式与注意事项

编译命令:

gcc -o epoll_create_demo epoll_create_demo.c
# 编译示例2(网络相关)
gcc -o epoll_server epoll_server.c

注意事项:

  1. 资源管理epoll_create 返回的文件描述符必须在不再需要时通过 close() 系统调用来关闭,否则会导致文件描述符泄漏。
  2. size 参数:虽然现代 Linux 内核会忽略 size 参数的大小提示(只要它大于 0),但为了可移植性和代码清晰,仍然应该传递一个合理的预期值。
  3. 更现代的 epoll_create1
    • int epoll_create1(int flags);
    • 此函数是 epoll_create 的扩展版本。flags 参数可以设置为 0EPOLL_CLOEXEC
    • EPOLL_CLOEXEC 标志表示在执行 exec() 系列函数时自动关闭这个 epoll 文件描述符,这是一种良好的安全实践。
    • 在新的代码中,推荐使用 epoll_create1(0) 来代替 epoll_create(size)
7) 执行结果说明
  • 示例1:运行后,会打印出成功创建的 epoll 实例的文件描述符编号(例如 3),然后打印关闭信息。
  • 示例2:运行后,服务器启动并打印监听端口信息。它会阻塞在 epoll_wait 调用上。虽然当前没有真正的处理逻辑,但程序框架已经搭建完成。
  • 示例3:运行后,会尝试创建 3 个 epoll 实例,并打印每个实例对应的文件描述符。最后,程序会按顺序关闭所有已创建的实例。如果系统资源充足,会看到创建和关闭了 3 个实例。
8) 图文总结:epoll_create 在 epoll 工作流中的角色
应用程序启动
调用 epoll_create()
创建成功?
获得 epoll 实例文件描述符 (epfd)
处理错误 (检查 errno)
并退出或重试
后续操作: epoll_ctl(epfd, ...)
添加/修改/删除监控的描述符
后续操作: epoll_wait(epfd, ...)
等待事件就绪
处理就绪事件
应用程序退出
close(epfd)
释放内核资源
http://www.xdnf.cn/news/1445167.html

相关文章:

  • 自由学习记录(92)
  • 图像正向扭曲反向扭曲
  • 关于缓存的一些思考?
  • 从Java全栈到前端框架:一次真实的面试对话
  • 自动化运维-ansible中对于大项目的管理
  • HTML第八课:HTML4和HTML5的区别
  • 网络通信与协议栈 -- OSI,TCP/IP模型,协议族,UDP编程
  • Linux 网络编程中核心函数`recv`。
  • Qt6用Chart模块做数据可视化?别再用老套路,看看这套35张图背后的秘密
  • MySQL :索引原理
  • 【面试题】BPE和WordPiece的区别?
  • Anaconda3出现Fatal error in launcher: Unable to create process using.....问题
  • STM32CubeMX + HAL 库:基于 I²C 通信的 BMP280气压海拔测量
  • 【超详细】别再看零散的教程了!一篇搞定Gitee从注册、配置到代码上传与管理(内含避坑指南最佳实践)
  • PS大神级AI建模技巧!效率翻倍工作流,悄悄收藏!
  • Wan系列模型解析--详细架构图
  • 机器学习在Backtrader多因子模型中的应用
  • 美团龙猫利用expat库实现的保存xml指定范围数据到csv的C程序
  • TypeScript 泛型入门(新手友好、完整详解)
  • XSENS VISION NAVIGATOR助力智能城市自动化清洁机器人精确导航
  • TLSF内存算法适配HTOS
  • 【Unity UGUI Canvas(画布)(1)】
  • 【音视频】FMP4 介绍
  • 【正点原子K210连载】第三十一章 音频FFT实验 摘自【正点原子】DNK210使用指南-CanMV版指南
  • 【论文阅读】-《THE JPEG STILL PICTURE COMPRESSION STANDARD》
  • Android 接入deepseek
  • 关于ES中文分词器analysis-ik快速安装
  • k8s使用StatefulSet(有状态)部署单节点 MySQL方案(使用本地存储)
  • 【Bug】Nexus无法正常启动的五种解决方法
  • SuperMap GIS基础产品FAQ集锦(20250901)