epoll_ctl – 控制epoll实例
函数介绍
epoll_ctl
系统调用用于控制epoll实例,可以添加、修改或删除要监视的文件描述符。
函数原型
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能
对epoll实例进行控制操作,管理要监视的文件描述符。
参数
int epfd
: epoll实例的文件描述符int op
: 操作类型EPOLL_CTL_ADD
: 添加文件描述符到监视集合EPOLL_CTL_MOD
: 修改已监视文件描述符的设置EPOLL_CTL_DEL
: 从监视集合中删除文件描述符
int fd
: 要控制的文件描述符struct epoll_event *event
: 指向epoll_event结构体的指针
返回值
- 成功时返回0
- 失败时返回-1,并设置errno
特殊限制
- 需要有效的epoll文件描述符
- 文件描述符必须是有效的
- 某些操作需要文件描述符已在监视集合中
相似函数
epoll_wait()
: 等待epoll事件poll()
: 传统轮询控制
示例代码
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
int main() {
int epfd, sockfd;
struct epoll_event ev;
printf("=== Epoll_ctl 函数示例 ===\n");
// 创建epoll实例
epfd = epoll_create1(EPOLL_CLOEXEC);
if (epfd == -1) {
perror("epoll_create1 失败");
exit(EXIT_FAILURE);
}
printf("创建epoll实例: %d\n", epfd);
// 创建测试用的socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("创建socket失败");
close(epfd);
exit(EXIT_FAILURE);
}
printf("创建测试socket: %d\n", sockfd);
// 设置socket为非阻塞模式
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
printf("设置socket为非阻塞模式\n");
// 示例1: 添加文件描述符到epoll监视集合
printf("\n示例1: 添加文件描述符到epoll\n");
ev.events = EPOLLIN | EPOLLOUT | EPOLLET; // 读事件、写事件、边缘触发
ev.data.fd = sockfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
perror("EPOLL_CTL_ADD 失败");
} else {
printf("成功添加socket %d 到epoll监视集合\n", sockfd);
printf("监视事件: EPOLLIN | EPOLLOUT | EPOLLET\n");
}
// 示例2: 修改已监视的文件描述符
printf("\n示例2: 修改已监视的文件描述符\n");
ev.events = EPOLLIN | EPOLLET; // 只监视读事件和边缘触发
ev.data.fd = sockfd;
if (epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev) == -1) {
if (errno == ENOENT) {
printf("修改失败,文件描述符未在监视集合中: %s\n", strerror(errno));
} else {
perror("EPOLL_CTL_MOD 失败");
}
} else {
printf("成功修改socket %d 的监视设置\n", sockfd);
printf("新监视事件: EPOLLIN | EPOLLET\n");
}
// 示例3: 删除文件描述符
printf("\n示例3: 删除文件描述符\n");
if (epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL) == -1) {
if (errno == ENOENT) {
printf("删除失败,文件描述符不在监视集合中: %s\n", strerror(errno));
} else {
perror("EPOLL_CTL_DEL 失败");
}
} else {
printf("成功从epoll监视集合中删除socket %d\n", sockfd);
}
// 示例4: 错误处理演示
printf("\n示例4: 错误处理演示\n");
// 使用无效的epoll文件描述符
if (epoll_ctl(-1, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
if (errno == EBADF) {
printf("无效epoll文件描述符错误处理正确: %s\n", strerror(errno));
}
}
// 使用无效的操作类型
if (epoll_ctl(epfd, 999, sockfd, &ev) == -1) {
if (errno == EINVAL) {
printf("无效操作类型错误处理正确: %s\n", strerror(errno));
}
}
// 使用无效的文件描述符
if (epoll_ctl(epfd, EPOLL_CTL_ADD, -1, &ev) == -1) {
if (errno == EBADF) {
printf("无效文件描述符错误处理正确: %s\n", strerror(errno));
}
}
// 示例5: epoll_event结构说明
printf("\n示例5: epoll_event结构说明\n");
printf("struct epoll_event 结构体:\n");
printf(" uint32_t events; // 事件类型\n");
printf(" epoll_data_t data; // 用户数据\n\n");
printf("常用事件类型:\n");
printf(" EPOLLIN: 可读事件\n");
printf(" EPOLLOUT: 可写事件\n");
printf(" EPOLLPRI: 紧急数据可读\n");
printf(" EPOLLERR: 错误条件\n");
printf(" EPOLLHUP: 挂起事件\n");
printf(" EPOLLET: 边缘触发模式\n");
printf(" EPOLLONESHOT: 一次性事件\n");
printf(" EPOLLRDHUP: 对端关闭连接\n\n");
// 示例6: epoll_data_t联合体说明
printf("epoll_data_t联合体(可存储不同类型的数据):\n");
printf(" void *ptr; // 指针\n");
printf(" int fd; // 文件描述符\n");
printf(" uint32_t u32; // 32位无符号整数\n");
printf(" uint64_t u64; // 64位无符号整数\n\n");
// 演示使用用户数据
printf("示例6: 使用用户数据\n");
struct epoll_event event_with_data;
event_with_data.events = EPOLLIN;
event_with_data.data.fd = sockfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event_with_data) == 0) {
printf("使用文件描述符作为用户数据: %d\n", event_with_data.data.fd);
// 删除文件描述符
epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
}
// 示例7: 实际应用场景
printf("\n示例7: 实际应用场景\n");
printf("服务器程序中的典型使用流程:\n");
printf("1. 创建epoll实例\n");
printf("2. 添加监听socket到epoll\n");
printf("3. 在事件循环中:\n");
printf(" a. 调用epoll_wait等待事件\n");
printf(" b. 处理就绪事件\n");
printf(" c. 根据需要添加/修改/删除监视的文件描述符\n\n");
printf("常见操作模式:\n");
printf("监听socket: EPOLLIN | EPOLLET\n");
printf("客户端socket: EPOLLIN | EPOLLOUT | EPOLLET\n");
printf("一次性事件: EPOLLIN | EPOLLONESHOT\n\n");
// 清理资源
close(sockfd);
close(epfd);
printf("总结:\n");
printf("epoll_ctl是管理epoll监视集合的核心函数\n");
printf("支持添加、修改、删除三种基本操作\n");
printf("正确使用事件类型和用户数据很重要\n");
printf("需要妥善处理各种错误情况\n");
return 0;
}