• 《Linux高性能服务器编程》学习总结(十)——信号


    第十章      信号

      Linux中信号是由用户、系统或进程发送给目标进程的信息,用来通知进程某个状态的改变或系统异常,其产生条件如下:1)对于前台进程,用户可以通过输入特殊的终端字符来发送信号,比如Ctrl+C发送中断信号;2)系统异常;3)系统状态变化,如SIGALRM信号;4)运行kill命令或使用kill函数。服务器程序必须处理一些常见的信号,以避免异常终止。

      我们来看一下kill函数:

    1 #include<sys/types.h>
    2 #include<signal.h>
    3 int kill(pid_t pid, int sig);

      在这个函数中,如果pid大于0则给pid为这个值的进程发送信号;如果pid等于0则发送给本进程组内的其他进程;如果pid等于-1则给除init进程外的所有进程,但发送者需要由给目标进程发送信号的权限;如果pid小于-1则向组id为-pid的进程组中的所有成员发送。后面的sig参数则是发送的信号种类,Linux中的信号族中有很多信号,这里就不一一说了。

      而接受信号的函数则是signal函数,可以指定一个接收函数来处理接收到的信号,也可以使用SIG_DFL和SIG_IGN两种预定义的宏来处理,分别代表采用默认方式处理和忽略。还有一种更加健壮的信号处理系统调用sigaction,其参数中需要指定新的信号处理方式,是一个sigset_t类型的结构体,其中包括信号掩码、信号处理函数,信号集等参数。

      信号掩码用来设置进程可以接收什么信号,当进程设置了信号掩码之后,若收到了被屏蔽的信号,则操作系统将该信号设置为进程的一个被挂起信号,如果取消被挂起信号的屏蔽就可以马上接收到这个信号。

      信号是一种异步事件,其处理函数和主循环是两条不同的执行路线,而信号处理函数必须尽快执行,以确保信号不被屏蔽,原因是信号在处理期间系统不会触发该信号,为了避免某些竞态条件。一个解决办法就是把信号的主要处理逻辑放在主函数内,信号处理函数只负责将信号值传递给主循环,这样我们就可以用I/O复用来监听信号事件:

      1 /*************************************************************************
      2     > File Name: 10-1.cpp
      3     > Author: Torrance_ZHANG
      4     > Mail: 597156711@qq.com
      5     > Created Time: Sat 10 Feb 2018 02:56:14 AM PST
      6  ************************************************************************/
      7 
      8 #include"head.h"
      9 using namespace std;
     10 
     11 #define MAX_EVENT_NUMBER 1024
     12 static int pipefd[2];
     13 
     14 int setnonblocking(int fd) {
     15     int old_option = fcntl(fd, F_GETFL);
     16     int new_option = old_option | O_NONBLOCK;
     17     fcntl(fd, F_SETFL, new_option);
     18     return old_option;
     19 }
     20 
     21 void addfd(int epollfd, int fd) {
     22     epoll_event event;
     23     event.data.fd = fd;
     24     event.events = EPOLLIN | EPOLLET;
     25     epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
     26     setnonblocking(fd);
     27 }
     28 
     29 void sig_handler(int sig) {
     30     int save_errno = errno;
     31     int msg = sig;
     32     send(pipefd[1], (char*)&msg, 1, 0);
     33     errno = save_errno;
     34 }
     35 
     36 void addsig(int sig) {
     37     struct sigaction sa;
     38     memset(&sa, 0, sizeof(sa));
     39     sa.sa_handler = sig_handler;
     40     sa.sa_flags |= SA_RESTART;
     41     sigfillset(&sa.sa_mask);
     42     assert(sigaction(sig, &sa, NULL) != -1);
     43 }
     44 
     45 int main(int argc, char* argv[]) {
     46     if(argc <= 2) {
     47         printf("usage: %s ip_address port_number
    ", basename(argv[0]));
     48         return 1;
     49     }
     50     const char* ip = argv[1];
     51     int port = atoi(argv[2]);
     52 
     53     int ret = 0;
     54     struct sockaddr_in address;
     55     bzero(&address, sizeof(address));
     56     address.sin_family = AF_INET;
     57     inet_pton(AF_INET, ip, &address.sin_addr);
     58     address.sin_port = htons(port);
     59 
     60     int listenfd = socket(AF_INET, SOCK_STREAM, 0);
     61     assert(listenfd >= 0);
     62 
     63     ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
     64     if(ret == -1) {
     65         printf("errno is %d
    ", errno);
     66         return 1;
     67     }
     68     ret = listen(listenfd, 5);
     69     assert(ret != -1);
     70 
     71     epoll_event events[MAX_EVENT_NUMBER];
     72     int epollfd = epoll_create(5);
     73     assert(epollfd != -1);
     74     addfd(epollfd, listenfd);
     75 
     76     ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
     77     assert(ret != -1);
     78     setnonblocking(pipefd[1]);
     79     addfd(epollfd, pipefd[0]);
     80 
     81     addsig(SIGHUP);
     82     addsig(SIGCHLD);
     83     addsig(SIGTERM);
     84     addsig(SIGINT);
     85     bool stop_server = false;
     86 
     87     while(!stop_server) {
     88         int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
     89         if((number < 0) && (errno != EINTR)) {
     90             printf("epoll failure
    ");
     91             break;
     92         }
     93         for(int i = 0; i < number; i ++) {
     94             int sockfd = events[i].data.fd;
     95             if(sockfd == listenfd) {
     96                 struct sockaddr_in client_address;
     97                 socklen_t client_addrlength = sizeof(client_address);
     98                 int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
     99                 addfd(epollfd, connfd);
    100             }
    101             else if((sockfd == pipefd[0]) && (events[i].events & EPOLLIN)) {
    102                 int sig;
    103                 char signals[1024];
    104                 ret = recv(pipefd[0], signals, sizeof(signals), 0);
    105                 if(ret == -1) continue;
    106                 else if(ret == 0) continue;
    107                 else {
    108                     for(int i = 0; i < ret; i ++) {
    109                         switch(signals[i]) {
    110                             case SIGCHLD:
    111                             case SIGHUP: continue;
    112                             case SIGTERM:
    113                             case SIGINT: stop_server = true;
    114                         }
    115                     }                
    116                 }
    117             }
    118             else {}
    119         }
    120     }
    121     printf("close fds
    ");
    122     close(listenfd);
    123     close(pipefd[1]);
    124     close(pipefd[0]);
    125     return 0;
    126 }

      上例演示了如何安全终止服务器的主循环。

      在网络编程中,有三个重要的信号。SIGHUP对于网络后台服务器而言一般用于强制服务器重读配置文件;SIGPIPE的产生一般是当往一个读端关闭的管道或socket写数据会引发,而程序一旦接收到SIGPIPE信号就会结束进程,所以我们一般都会捕获或者忽略之;最后就是SIGURG信号,它是另外一种通知应用进程有带外数据的方法,我们来通过一个程序看一下如何处理:

     1 /*************************************************************************
     2     > File Name: 10-3.cpp
     3     > Author: Torrance_ZHANG
     4     > Mail: 597156711@qq.com
     5     > Created Time: Sat 10 Feb 2018 03:45:14 AM PST
     6  ************************************************************************/
     7 
     8 #include"head.h"
     9 using namespace std;
    10 
    11 #define BUF_SIZE 1024
    12 static int connfd;
    13 
    14 void sig_urg(int sig) {
    15     int save_errno = errno;
    16     char buffer[BUF_SIZE];
    17     memset(buffer, 0, sizeof(buffer));
    18     int ret = recv(connfd, buffer, BUF_SIZE, MSG_OOB);
    19     printf("got %d bytes of oob data '%s'
    ", ret, buffer);
    20     errno = save_errno;
    21 }
    22 
    23 void addsig(int sig, void (*sig_handler)(int)) {
    24     struct sigaction sa;
    25     memset(&sa, 0, sizeof(sa));
    26     sa.sa_handler = sig_handler;
    27     sa.sa_flags |= SA_RESTART;
    28     sigfillset(&sa.sa_mask);
    29     assert(sigaction(sig, &sa, NULL) != -1);
    30 }
    31 
    32 int main(int argc, char* argv[]) {
    33     if(argc <= 2) {
    34         printf("usage: %s ip_address port_number
    ", basename(argv[0]));
    35         return 1;
    36     }
    37     const char* ip = argv[1];
    38     int port = atoi(argv[2]);
    39 
    40     struct sockaddr_in address;
    41     bzero(&address, sizeof(address));
    42     address.sin_family = AF_INET;
    43     inet_pton(AF_INET, ip, &address.sin_addr);
    44     address.sin_port = htons(port);
    45 
    46     int sock = socket(AF_INET, SOCK_STREAM, 0);
    47     assert(sock >= 0);
    48 
    49     int ret = bind(sock, (struct sockaddr*)&address, sizeof(address));
    50     assert(ret != -1);
    51 
    52     ret = listen(sock, 5);
    53     assert(ret != -1);
    54 
    55     struct sockaddr_in client_address;
    56     socklen_t client_addrlength = sizeof(client_address);
    57     connfd = accept(sock, (struct sockaddr*)&client_address, &client_addrlength);
    58 
    59     if(connfd < 0) printf("errno is: %d
    ", errno);
    60     else {
    61         addsig(SIGURG, sig_urg);
    62         fcntl(connfd, F_SETOWN, getpid());
    63 
    64         char buffer[BUF_SIZE];
    65         while(1) {
    66             memset(buffer, 0, sizeof(buffer));
    67             ret = recv(connfd, buffer, BUF_SIZE, 0);
    68             if(ret <= 0) {
    69                 break;
    70             }
    71             printf("got %d bytes of normal data '%s'
    ", ret, buffer);
    72         }
    73         close(connfd);
    74     }
    75     close(sock);
    76     return 0;
    77 }

      此程序在Ubuntu16.04下有问题,不能接收到带外数据,在网上找了其他利用SIGURG接收带外数据的程序都不能接收,具体原因尚不知,待日后解决。

  • 相关阅读:
    cocos2d-3.0 Helloworld::onTouchMoved的处理机制的推測
    一个Nodejs的简单计算測试程序
    js正則表達式语法
    奇妙的go语言(网页下载)
    java环境变量配置
    【数据结构与算法】二叉树深度遍历(非递归)
    $.each 和$(selector).each()的差别
    HDU-2844-Coins(多重背包)
    curl命令具体解释
    Arduino 数码管LED屏驱动
  • 原文地址:https://www.cnblogs.com/Torrance/p/8439714.html
Copyright © 2020-2023  润新知