博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux(服务器编程):25---epoll复用技术实现统一处理信号事件源
阅读量:3965 次
发布时间:2019-05-24

本文共 2750 字,大约阅读时间需要 9 分钟。

一、统一信号处理事件源概述

  • 信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线。显然,信号处理函数需要尽可能快地执行完毕,以确保该信号不被屏蔽(为了避免一些竞态条件,信号在处理期间,系统不会再次触发它)太久
  • 一种典型的解决办法是:
    • 把信号的主要处理逻辑放到程序的主循环中
    • 当信号处理函数被触发时,它只是简单地通过主循环程序接收到信号,并把信号值传递给主循环
    • 主循环再根据接收到的信号值执行目标信号对应的逻辑代码
  • 信号处理函数通常使用管道来将信号“传递”给主循环:
    • 信号处理函数往管道的写端写入信号值,主循环则从管道的读端读出该信号值
    • 主循环使用I/O复用系统调用来监听管道的读端文件描述符上的可读时间
  • 如此一来,信号事件就能和其他I/O事件一样被处理,即统一事件源
  • 很多优秀的I/O框架和后台服务器程序都统一处理信号和I/O事件,比如LiBEVENT I/O框架库和xinetd超级服务

二、编码实现

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LISTEM_NUM 5#define MAX_EVENT_NUM 1024int setnonblocking(int fd);void add_epoll_fd(int epoll_fd,int fd);void sig_handler(int sigalno);void add_sig(int sigalno);static int pipe_fd[2];int main(int argc,char* argv[]){ if(argc!=3){ printf("usage:./%s [server ip] [server port]\n",basename(argv[1])); exit(EXIT_FAILURE); } int ser_fd,server_port; const char* server_ip; //创建套接字 if((ser_fd=socket(AF_INET,SOCK_STREAM,0))==-1){ perror("socket"); exit(EXIT_FAILURE); } //初始化服务端地址 struct sockaddr_in server_address; server_ip=argv[1]; server_port=atoi(argv[2]); bzero(&server_address,sizeof(server_address)); server_address.sin_family=AF_INET; server_address.sin_port=htons(server_port); if(inet_pton(AF_INET,server_ip,&server_address.sin_addr.s_addr)==-1){ perror("inet_pton"); exit(EXIT_FAILURE); } //绑定服务端地址 if(bind(ser_fd,(struct sockaddr*)&server_address,sizeof(server_address))==-1){ perror("bind"); exit(EXIT_FAILURE); } //开启监听 if(listen(ser_fd,LISTEM_NUM)==-1){ perror("bind"); exit(EXIT_FAILURE); } int epoll_fd; //创建epoll事件表句柄 if((epoll_fd=epoll_create(5))==-1){ perror("epoll_create"); exit(EXIT_FAILURE); } //将服务端套接字加入到事件表中 add_epoll_fd(epoll_fd,ser_fd); //创建管道 if(socketpair(PF_UNIX,SOCK_STREAM,0,pipe_fd)==-1){ perror("socketpair"); exit(EXIT_FAILURE); } /*sockpair函数创建的管道是全双工的,不区分读写端 此处我们假设pipe_fd[1]为写端,非阻塞 pipe_fd[0]为读端 */ setnonblocking(pipe_fd[1]); add_epoll_fd(epoll_fd,pipe_fd[0]); //为一些信号绑定信号处理函数 add_sig(SIGHUP); //终端接口检测到一个连接断开,发送此信号 add_sig(SIGCHLD);//子进程终止或停止时,子进程发送此信号 add_sig(SIGTERM);//接收到kill命令 add_sig(SIGINT); //用户按下中断键(Delete或Ctrl+C) int server_running=1; int epoll_wait_ret_value; struct epoll_event events[MAX_EVENT_NUM]; while(server_running) { bzero(events,sizeof(events)); epoll_wait_ret_value=epoll_wait(epoll_fd,events,MAX_EVENT_NUM,-1); //epoll_wait函数出错 if((epoll_wait_ret_value==-1)&&(errno!=EINTR)){ close(ser_fd); perror("epoll_wait"); exit(EXIT_FAILURE); } //遍历就绪的事件 for(int i=0;i

代码解析

  • 创建一个无名管道,管道[0]端用来读取数据,[1]端用来发送数据。读写端都设置为非阻塞
  • 当信号处理函数执行时,在处理函数中向[1]端发送信号编号
  • 主函数使用epoll轮询,其中包括轮询管道[0],一旦有信息(信号编号)发来,处理信号

代码演示

  • 使用客户端工具连接程序,打印客户端连接信息

  • 使用kill命令给服务端程序发送一个编码为1的信号,可以看到服务端接收到这个信号

  • 按下Ctrl+C触发SIGINT信号,程序终止(与预期一致)

转载地址:http://ltkki.baihongyu.com/

你可能感兴趣的文章
Flutter 路由跳转fluro
查看>>
Flutter 日期插件date_format 中文 国际化 及flutter_cupertino_date_picker
查看>>
Flutter 插件笔记 | 屏幕适配 flutter_screenutil
查看>>
Flutter UI基础 - 侧拉抽屉菜单
查看>>
Flutter UI基础 - AppBar中标题文字如何居中
查看>>
Flutter UI基础 - Drawer 抽屉视图与自定义header
查看>>
Flutter UI基础 - 点击展开和关闭
查看>>
Flutter UI基础 - GridView
查看>>
Flutter UI - 打造一个圆形滑块(Slider)
查看>>
Flutter UI基础 - 分割线效果实现
查看>>
Flutter UI基础 - DecoratedBox组件
查看>>
Flutter UI基础 - 使用InkWell给任意Widget添加点击事件
查看>>
OC WKWebView的使用
查看>>
Flutter UI基础 - Image.asset 图片铺满布局
查看>>
Flutter UI基础 - Row、Column详解
查看>>
Flutter UI基础 - 添加背景图片
查看>>
Flutter UI基础 - 布局之Row/Column/Stack
查看>>
Flutter UI基础 - 层叠布局Stack的使用
查看>>
Flutter UI基础 - webview 使用和交互
查看>>
Flutter UI基础 - 时间选择器
查看>>