You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下如何等待网络套接字流量或SIGWINCH信号?

同时监听网络套接字与SIGWINCH信号的实现方案

你完全可以通过创建内存级的成对文件描述符实现需求,不需要生成实际磁盘文件。常用的两种方式是socketpair或pipe,核心思路是把信号事件转换成文件描述符的可读事件,让epoll/select能统一监听。

核心方案:用socketpair/pipe桥接信号与IO多路复用

1. 创建成对的内存文件描述符

这两种方式都完全基于内存,不会在磁盘上生成任何文件:

  • socketpair:创建一对双向连通的UNIX域套接字,两个描述符可互相读写,适合需要双向通信的场景(单向需求下也能使用,灵活性更高)。
    调用示例:
    int fd_pair[2];
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd_pair) == -1) {
        perror("socketpair");
        exit(EXIT_FAILURE);
    }
    
  • pipe:创建一对单向管道描述符,fd[0]为读端,fd[1]为写端,实现更轻量。
    调用示例:
    int pipe_fd[2];
    if (pipe(pipe_fd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    

2. 绑定SIGWINCH信号处理函数

注册SIGWINCH的信号处理程序,在信号触发时往成对描述符的写端写入一个标记字节(比如单个字符)。注意信号处理函数必须使用异步安全的系统调用(比如write,不能用printf这类非安全函数)。

示例(以socketpair为例):

#include <unistd.h>
#include <signal.h>

int fd_pair[2]; // 全局变量,供信号处理函数访问

void sigwinch_handler(int sig) {
    char trigger = 'W';
    // 忽略短写或非阻塞错误,确保信号事件被传递即可
    write(fd_pair[1], &trigger, 1);
}

// 注册信号
void setup_sigwinch() {
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = sigwinch_handler;
    sa.sa_flags = SA_RESTART; // 自动重启被中断的系统调用
    if (sigaction(SIGWINCH, &sa, NULL) == -1) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }
}

3. 用epoll统一监听

把网络套接字(监听可读事件)和成对描述符的读端(监听可读事件)都加入epoll实例,调用epoll_wait即可同时等待两种事件:

  • 当有网络流量时,网络套接字触发EPOLLIN;
  • 当SIGWINCH触发时,信号处理函数往写端写入数据,读端触发EPOLLIN。

示例代码:

#include <sys/epoll.h>

void setup_epoll(int sock_fd) {
    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    struct epoll_event ev;
    // 添加网络套接字
    ev.events = EPOLLIN;
    ev.data.fd = sock_fd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock_fd, &ev) == -1) {
        perror("epoll_ctl add sock");
        exit(EXIT_FAILURE);
    }

    // 添加socketpair读端
    ev.data.fd = fd_pair[0];
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd_pair[0], &ev) == -1) {
        perror("epoll_ctl add pipe");
        exit(EXIT_FAILURE);
    }

    // 事件循环
    struct epoll_event events[10];
    while (1) {
        int n = epoll_wait(epoll_fd, events, 10, -1);
        if (n == -1) {
            if (errno == EINTR) continue; // 被信号中断,继续等待
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }

        for (int i = 0; i < n; i++) {
            if (events[i].data.fd == sock_fd) {
                // 处理网络套接字的传入流量
                handle_network_data(sock_fd);
            } else if (events[i].data.fd == fd_pair[0]) {
                // 清空读端缓冲区,避免重复触发
                char buf[16];
                while (read(fd_pair[0], buf, sizeof(buf)) > 0);
                // 处理窗口大小变化逻辑
                handle_window_resize();
            }
        }
    }
}

注意事项

  • 可将写端设置为非阻塞模式,避免信号处理时write阻塞:
    int flags = fcntl(fd_pair[1], F_GETFL, 0);
    fcntl(fd_pair[1], F_SETFL, flags | O_NONBLOCK);
    
  • 如果SIGWINCH频繁触发,管道中可能积累多个字节,处理时要把缓冲区读完,避免epoll重复触发可读事件。
  • 若使用select,逻辑与epoll一致,只需将两个描述符加入读集合即可。

内容的提问来源于stack exchange,提问作者Zachary Vance

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 13:52:44