信號是一中異步事件:信號處理函數和程序的主循環是兩條不同的執行路線。信號處理函數需要儘可能快地執行完畢,以確保該信 號不會被屏蔽(信號在處理期間,系統不會再觸發它)太久。一種典型的解決方案是:把信號的主要處理邏輯放到程序的朱循環中,當信號函數被觸發時,他只是簡單地通知主循環程序接收信號,並把信號值傳遞給主循環,主循環再根據接收到的信號值執行目標信號對應的邏輯代碼。 信號處理函數通常使用管道來將信號“傳遞”給主循環:信號處理函數往管道寫端寫入信號值,主循環則從管道讀端讀出該信號值。那麼主循環怎麼知道管道上何時有數據可讀呢?這很簡單,我們只需要使用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;
}