epoll_pwait – 带信号掩码的epoll等待
函数介绍
epoll_pwait
是epoll_wait
的扩展版本,支持在等待期间设置临时的信号掩码,提供了更精确的信号控制能力。
函数原型
#include <sys/epoll.h>
#include <signal.h>
int epoll_pwait(int epfd, struct epoll_event *events,
int maxevents, int timeout,
const sigset_t *sigmask);
功能
等待epoll事件,同时可以指定在等待期间临时应用的信号掩码。
参数
int epfd
: epoll实例的文件描述符struct epoll_event *events
: 用于存储就绪事件的数组int maxevents
: events数组的最大元素数int timeout
: 超时时间(毫秒)const sigset_t *sigmask
: 临时信号掩码(NULL表示不改变)
返回值
- 成功时返回就绪事件的数量
- 失败时返回-1,并设置errno
特殊限制
- 需要Linux 2.6.19以上内核支持
- 原子性地设置信号掩码和等待事件
相似函数
epoll_wait()
: 基础版本pselect()
: 带信号掩码的select版本ppoll()
: 带信号掩码的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>
#include <signal.h>
int main() {
int epfd, sockfd, nfds;
struct epoll_event ev, events[10];
sigset_t mask, orig_mask;
printf("=== Epoll_pwait 函数示例 ===\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);
// 添加socket到epoll监视集合
ev.events = EPOLLIN | EPOLLOUT;
ev.data.fd = sockfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {
perror("epoll_ctl 添加失败");
close(sockfd);
close(epfd);
exit(EXIT_FAILURE);
}
printf("添加socket到epoll监视集合\n");
// 示例1: 基本使用(不改变信号掩码)
printf("\n示例1: 基本使用(不改变信号掩码)\n");
nfds = epoll_pwait(epfd, events, 10, 1000, NULL);
if (nfds == -1) {
if (errno == EINTR) {
printf("等待被信号中断\n");
} else {
perror("epoll_pwait 失败");
}
} else {
printf("等待完成,就绪事件数: %d\n", nfds);
}
// 示例2: 设置临时信号掩码
printf("\n示例2: 设置临时信号掩码\n");
// 初始化信号掩码
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);
printf("设置临时阻塞SIGUSR1和SIGUSR2信号\n");
nfds = epoll_pwait(epfd, events, 10, 2000, &mask);
if (nfds == -1) {
if (errno == EINTR) {
printf("等待被未阻塞的信号中断\n");
} else {
perror("epoll_pwait 失败");
}
} else {
printf("等待完成,就绪事件数: %d\n", nfds);
}
// 示例3: 与普通epoll_wait对比
printf("\n示例3: 与epoll_wait对比\n");
printf("epoll_wait vs epoll_pwait的区别:\n");
printf("epoll_wait:\n");
printf(" - 使用当前进程的信号掩码\n");
printf(" - 无法在调用中临时改变信号掩码\n\n");
printf("epoll_pwait:\n");
printf(" - 可以指定临时信号掩码\n");
printf(" - 原子性地设置掩码和等待\n");
printf(" - 避免竞态条件\n\n");
// 示例4: 原子性操作演示
printf("示例4: 原子性操作演示\n");
printf("说明epoll_pwait的原子性优势:\n");
printf("传统方式(非原子性):\n");
printf(" 1. sigprocmask(SIG_BLOCK, &mask, &orig_mask);\n");
printf(" 2. epoll_wait(epfd, events, 10, -1);\n");
printf(" 3. sigprocmask(SIG_SETMASK, &orig_mask, NULL);\n");
printf(" 问题: 在步骤1和2之间可能收到信号\n\n");
printf("epoll_pwait方式(原子性):\n");
printf(" epoll_pwait(epfd, events, 10, -1, &mask);\n");
printf(" 优势: 设置掩码和等待是原子操作\n\n");
// 示例5: 错误处理演示
printf("示例5: 错误处理演示\n");
// 使用无效的信号掩码
nfds = epoll_pwait(epfd, events, 10, 1000, (sigset_t*)-1);
if (nfds == -1) {
if (errno == EINVAL) {
printf("无效信号掩码错误处理正确: %s\n", strerror(errno));
}
}
// 其他错误与epoll_wait相同
nfds = epoll_pwait(-1, events, 10, 1000, &mask);
if (nfds == -1) {
if (errno == EBADF) {
printf("无效epoll文件描述符错误处理正确\n");
}
}
// 示例6: 实际应用场景
printf("\n示例6: 实际应用场景\n");
printf("多线程服务器中的信号处理:\n");
printf("场景: 主线程处理网络事件,信号处理线程处理信号\n");
printf("要求: 主线程在epoll_wait期间不被某些信号中断\n\n");
printf("实现方案:\n");
printf("sigset_t mask;\n");
printf("sigemptyset(&mask);\n");
printf("sigaddset(&mask, SIGUSR1); // 阻塞用户信号\n");
printf("sigaddset(&mask, SIGALRM); // 阻塞定时器信号\n");
printf("epoll_pwait(epfd, events, MAX_EVENTS, -1, &mask);\n\n");
// 示例7: 信号掩码操作演示
printf("示例7: 信号掩码操作演示\n");
// 创建复杂的信号掩码
sigset_t complex_mask;
sigemptyset(&complex_mask);
// 添加多个信号到掩码
int signals[] = {SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGALRM};
const char *signal_names[] = {"SIGINT", "SIGTERM", "SIGUSR1", "SIGUSR2", "SIGALRM"};
printf("创建包含以下信号的掩码:\n");
for (int i = 0; i < 5; i++) {
sigaddset(&complex_mask, signals[i]);
printf(" %s\n", signal_names[i]);
}
printf("使用复杂信号掩码进行epoll_pwait\n");
nfds = epoll_pwait(epfd, events, 10, 1000, &complex_mask);
if (nfds != -1) {
printf("epoll_pwait成功完成\n");
}
// 示例8: 线程安全考虑
printf("\n示例8: 线程安全考虑\n");
printf("在多线程环境中的使用:\n");
printf("1. 每个线程可以有自己的epoll实例\n");
printf("2. 每个线程可以设置不同的信号掩码\n");
printf("3. 避免线程间的信号处理冲突\n");
printf("4. 提高信号处理的精确性\n\n");
printf("线程特定的信号掩码设置:\n");
printf("线程1: 阻塞SIGUSR1, SIGUSR2\n");
printf("线程2: 阻塞SIGALRM, SIGVTALRM\n");
printf("线程3: 不阻塞任何信号\n\n");
// 示例9: 性能和安全性
printf("示例9: 性能和安全性\n");
printf("epoll_pwait的优势:\n");
printf("1. 原子性操作,避免竞态条件\n");
printf("2. 更精确的信号控制\n");
printf("3. 提高程序的可靠性\n");
printf("4. 简化信号处理逻辑\n\n");
printf("使用建议:\n");
printf("1. 在需要精确信号控制时使用epoll_pwait\n");
printf("2. 合理设计信号掩码\n");
printf("3. 注意信号处理的线程安全性\n");
printf("4. 在多线程环境中谨慎使用\n\n");
// 清理资源
epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
close(sockfd);
close(epfd);
printf("总结:\n");
printf("epoll_pwait是epoll_wait的增强版本\n");
printf("支持临时信号掩码设置\n");
printf("提供原子性的信号控制\n");
printf("适用于需要精确信号处理的场景\n");
printf("在现代Linux系统中推荐使用\n");
return 0;
}