epoll_pwait系统调用及示例

epoll_pwait – 带信号掩码的epoll等待

函数介绍

epoll_pwaitepoll_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;
}
此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注