Linux 信号 --- 统一事件源

        信号是一中异步事件:信号处理函数和程序的主循环是两条不同的执行路线。信号处理函数需要尽可能快地执行完毕,以确保该信 号不会被屏蔽(信号在处理期间,系统不会再触发它)太久。一种典型的解决方案是:把信号的主要处理逻辑放到程序的朱循环中,当信号函数被触发时,他只是简单地通知主循环程序接收信号,并把信号值传递给主循环,主循环再根据接收到的信号值执行目标信号对应的逻辑代码。 信号处理函数通常使用管道来将信号“传递”给主循环:信号处理函数往管道写端写入信号值,主循环则从管道读端读出该信号值。那么主循环怎么知道管道上何时有数据可读呢?这很简单,我们只需要使用I/O复用系统调用来监听管道的读端文件描述符上的可读事件。如此一来,信号事件就和其他I/O事件一样被处理,即统一信号源

#include <syslog.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <assert.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <libgen.h>

#define MAX_EVENT_NUMBER 1024
static int pipefd[2];

int setnonblocking( int fd )
{
    int old_option = fcntl( fd, F_GETFL );
    int new_option = old_option | O_NONBLOCK;
    fcntl( fd, F_SETFL, new_option );
    return old_option;
}

void addfd( int epollfd, int fd)
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );
    setnonblocking( fd );
}

void sig_handler( int sig )
{
    printf("收到信号 %d\n",sig);
    int save_errno = errno;
    int msg = sig;
    send(pipefd[1],(char *)&msg, 1, 0);
    errno = save_errno;
}

void addsig( int sig )
{
    struct sigaction sa;
    memset(&sa, '\0', sizeof( sa ));
    sa.sa_handler = sig_handler;
    sa.sa_flags |= SA_RESTART;
    sigfillset( &sa.sa_mask ); //屏蔽所有信号
    assert( sigaction(sig, &sa, NULL) != -1 );
}

int main(int argc, char * argv[])
{
    if ( argc <= 2 )
    {
        printf("Usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }

    const char * ip = argv[1];
    int port = atoi(argv[2]);

    int ret = 0;
    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_family = AF_INET;
    address.sin_port = htons(port);

    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    assert (listenfd >= 0);

    ret = bind( listenfd, (struct sockaddr *)&address, sizeof(address) );
    if ( ret == -1 )
    {
        printf("errno id %s",strerror(errno));
        return 1;
    }

    ret = listen(listenfd, 5);
    assert( ret != -1);

    struct epoll_event events[ MAX_EVENT_NUMBER ];
    int epollfd = epoll_create(5);
    assert( epollfd != -1);
    addfd( epollfd, listenfd);

    // 使用sockpair来传递信息G
    ret = socketpair( PF_UNIX, SOCK_STREAM, 0, pipefd );
    assert( ret != -1 );
    setnonblocking( pipefd[1] );
    addfd( epollfd, pipefd[0] );
 
    addsig( SIGHUP );
    addsig( SIGCHLD );
    addsig( SIGTERM );
    addsig( SIGINT );

    bool stop_server = false;
    while( !stop_server )
    {
        printf("epoll_wait ......\n");
        int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
        //EINTR, 当系统调用被信号中断时,返回此错误
        if ( ( number < 0 ) && (errno != EINTR ))
        {
            printf("epoll failure\n");
            break;
        }
        
        int i = 0;
        for( i; i < number; i++ )
        {
            int sockfd = events[i].data.fd;
            if ( sockfd == listenfd )
            {
                int connfd = accept(listenfd, NULL, NULL);
                addfd(epollfd, connfd);
            }
            else if( ( sockfd == pipefd[0] ) && ( events[i].events & EPOLLIN ))
            {
                printf("信道可读\n");
                int sig;
                char signals[1024];
                ret = recv( pipefd[0], signals, sizeof(signals), 0);
                if ( ( ret == -1 ) || ( ret == 0))
                {
                    continue;
                }
                else 
                {
                    int i = 0;
                    for (i; i < ret; ++i)
                    {
                        switch( signals[i])
                        {
                            case SIGCHLD:
                            case SIGHUP:
                            {
                                printf("终端挂起信号\n");
                                continue;
                            }
                            case SIGTERM:
                            case SIGINT:
                            {
                                printf("停止进程信号\n");
                                stop_server = true;
                            }
                        }
                    }
                }
            }
            else{}
        }

    }

    printf("close fds\n");
    close(listenfd);
    close(pipefd[0]);
    close(pipefd[1]);
    return 1;
}

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值