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
相关产品推荐
相关产品推荐

