linux串口编程实例_Linux编程--epoll复用技术实现统一处理信号事件源(实例分析)

本文介绍了一种利用管道和Epoll机制统一处理信号和I/O事件的方法,通过信号处理函数将信号传递给主循环,使得信号处理可以与其他I/O事件一样被处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

信号是一种异步事件:

信号处理函数和程序的主循环是两条不同的执行路线。显然,信号处理函数需要尽可能快地执行完毕,以确保该信号不被屏蔽(为了避免一些竞态条件,信号在处理期间,系统不会再次触发它)太久

一种典型的解决办法是:

  • 把信号的主要处理逻辑放到程序的主循环中
  • 当信号处理函数被触发时,它只是简单地通过主循环程序接收到信号,并把信号值传递给主循环
  • 主循环再根据接收到的信号值执行目标信号对应的逻辑代码

信号处理函数通常使用管道来将信号“传递”给主循环:

  • 信号处理函数往管道的写端写入信号值,主循环则从管道的读端读出该信号值
  • 主循环使用I/O复用系统调用来监听管道的读端文件描述符上的可读时间

如此一来,信号事件就能和其他I/O事件一样被处理,即统一事件源

很多优秀的I/O框架和后台服务器程序都统一处理信号和I/O事件,比如LiBEVENT I/O框架库和xinetd超级服务

需要C/C++ Linux服务器架构师学习资料私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

4bac83173878644c354aca35680798c8.png

二、编码实现

#include #include #include #include #include #include #include #include #include #include #include #include  #define LISTEM_NUM 5#define MAX_EVENT_NUM 1024 int 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]",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],一旦有信息(信号编号)发来,处理信号

代码演示

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

f8ccaacf071c2e13247fb9dc15de08cf.png

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

d4e0e8196c829aa6bbd3272dfd1cfff9.png

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

9c395fedcc0551a9fbe524d118998a9b.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值