c - Linux 服务器 C 代码在收到信号后停止

标签 c linux server signals signal-handling

我对这个简单的服务器代码有疑问,它按预期工作,直到收到信号。 对于调试,我在使用以下行调用 select 之前打印服务器和客户端文件描述符:

fprintf(stderr,"server_socket_fd=%d client_socket_fd=%d fd_max=%d\n", server_socket_fd, client_socket_fd, fd_max);

正常运行时一直打印

server_socket_fd=3 client_socket_fd=4 fd_max=4

但是当它接收到一个信号时它打印这一行一次

server_socket_fd=3 client_socket_fd=-1 fd_max=3

然后程序停止了。

我使用 GDB 在 signal_handler 中放置了一个断点,当它中断时我无法观察 client_socket_fd 变量,gdb 说

No symbol "client_socket_fd" in current context.

而且它没有从 signal_handler 函数正确返回。如果我观察回溯:

(gdb) bt
#0  0xb7fdccf9 in ?? ()
#1  0xb7e26af3 in __libc_start_main (main=0x8048bdd <main>, argc=1, argv=0xbfffef24, init=0x8049a00 <__libc_csu_init>, 
    fini=0x8049a70 <__libc_csu_fini>, rtld_fini=0xb7fed160 <_dl_fini>, stack_end=0xbfffef1c) at libc-start.c:287
#2  0x08048b01 in _start ()

我不知道如何进行更深入的调试。

这是主要代码:

char receive_buf[2048];
int main(int argc, char *argv[]){

    int server_socket_fd;
    int client_socket_fd = -1;
    int fd_max;

    struct sockaddr_in s_in;
    int one = 1;
    int status;

    fd_set readfds;

    int port;

    int next_option;
    const char* short_options = "hp:d:";
    const struct option long_options[] = {
        { "help",   0,  NULL,   'h'},
        { "port",   1,  NULL,   'p'},
        { "debug",   1,  NULL,   'd'},
        { NULL,     0,  NULL,   0}
    };

    program_name = argv[0];

    port = DEFAULT_PORT;
    debug = 0;

    do{
        next_option = getopt_long(argc, argv, short_options, long_options, NULL);
        switch(next_option){
            case 'h':
                print_usage(stdout, 0);
                break;
            case 'p':
                port = atoi(optarg);
                if((port < 0)||(port > 65535)){
                   fprintf(stderr, "Invalid port number (%d), using default: %d", port, DEFAULT_PORT);
                   port = DEFAULT_PORT;
                }
                break;
            case 'd':
                debug = atoi(optarg);
                if(debug < 0 || debug > 3)
                    debug = 0;
                break;
            case '?':
                print_usage(stderr, 1);
                break;
            case -1:
                break;
            default:
                abort();
        }
    }while(next_option != -1);

    /*************************  SIGNAL DEFINITIONS  ***************************/

    signal_action.sa_handler = (void *)signal_handler;
    sigemptyset(&signal_action.sa_mask);
    signal_action.sa_flags = SA_RESTART; // | SA_NOCLDSTOP;

    if(sigaction(SIGINT, &signal_action, NULL) == -1){
        fprintf(stderr, "Error setting SIGINT signal handler\n");
        exit(1);
    }

    if(sigaction(SIGTERM, &signal_action, NULL) == -1){
        fprintf(stderr, "Error setting SIGTERM signal handler\n");
        exit(1);
    }

    if(sigaction(SIGWINCH, &signal_action, NULL) == -1){
        fprintf(stderr, "Error setting SIGWINCH signal handler\n");
        exit(1);
    }

    /*  // ALSO TRIED WITH SIGNAL WITH SAME RESULT
    if(signal(SIGWINCH, signal_handler) == SIG_ERR){
        fprintf(stderr, "signal error\n");
        return 1;
    }
   */

    s_in.sin_family = PF_INET;
    s_in.sin_port = htons(port);
    s_in.sin_addr.s_addr = INADDR_ANY;

    if ((server_socket_fd = socket(s_in.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1){
        perror("Error creating socket");
        return 1;
    }


    if(setsockopt(server_socket_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1){
        perror("Error setting socket parameters");
        return 1;
    }

    ////////////////////////////////////////////
    int x;
    x=fcntl(server_socket_fd,F_GETFL,0);              // Get socket flags
    fcntl(server_socket_fd,F_SETFL,x | O_NONBLOCK);   // Add non-blocking flag
    ////////////////////////////////////////////

    if(bind(server_socket_fd, (struct sockaddr*) &s_in, sizeof(s_in)) == -1){
        perror("Error binding socket");
        return 1;
    }


    if(listen(server_socket_fd, 1) == -1){
        perror("Error creating listening socket");
        return 1;
    }else{
        printf("Server (%d) listening on port %d\n", server_socket_fd, port);
    }

    memset(receive_buf, '\0', sizeof(receive_buf));
    gettimeofday(&t_print, NULL);

    while(1){

        // SERVER
        FD_ZERO(&readfds);
        FD_SET(server_socket_fd, &readfds);
        fd_max = server_socket_fd;

        // ADD CLIENT IF CONNECTED
        if(client_socket_fd > 0){
            FD_SET(client_socket_fd, &readfds);
            if(client_socket_fd > server_socket_fd)
                fd_max = client_socket_fd;
        }

        // ADDED THIS FPRINTF TO CHECK VARIABLES  <----------------------------------
        fprintf(stderr,"server_socket_fd=%d client_socket_fd=%d fd_max=%d\n", server_socket_fd, client_socket_fd, fd_max);

        if(select(fd_max+1, &readfds, NULL, NULL, NULL) == -1){
            if(errno != EINTR){
                perror("select failed");
            }
        }

        // ACCEPT CLIENT
        if(FD_ISSET(server_socket_fd, &readfds)){

            struct sockaddr_in s_in;
            socklen_t len;

            len = sizeof(s_in);
            if((client_socket_fd = accept(server_socket_fd, (struct sockaddr*) &s_in, &len)) < 0){
                if(errno != EWOULDBLOCK){
                    perror("En accept");
                }
            }else
                printf("New client connected from %s\n", inet_ntoa(s_in.sin_addr));
        }

        // RECEIVE FROM CLIENT
        if(client_socket_fd > 0){
            if(FD_ISSET(client_socket_fd, &readfds)){
                handle_client(client_socket_fd);
            }
        }
    }

    return 0;
    }


int handle_client(int cl_fd){

    int n;

    n = recv(cl_fd, receive_buf, sizeof(receive_buf) - 1, MSG_DONTWAIT);
    if(n == 0){
        fprintf(stderr,"--------------> DEBUG: handle_client:client %d closed connection\n", cl_fd);
    }else if(n < 0){
        if(errno == EAGAIN){
            return 0;
        }else{
            fprintf(stderr,"--------------> DEBUG: handle_client: recv ERROR: client %d closed connection (errno: %d : %s)\n", cl_fd, errno, strerror(errno));
            memset(receive_buf, 0, sizeof(receive_buf));
            return -1;
        }
    }else{
        receive_buf[n] = '\0';
        fprintf(stderr, "%s\n", receive_buf);
    }
        return 0;
   }
void signal_handler(int sig){

    switch(sig){

    case SIGINT:
        exit_properly(0);
        break;
    case SIGTERM:
        exit_properly(1);
        break;
    case SIGABRT:
        fprintf(stderr, "SIGABRT signal received\n");
        break;

    case SIGWINCH:
        fprintf(stderr, "\33[2J");
        fflush(stdout);
        break;

    default:
        fprintf(stderr, "Unhandled signal %d received\n",sig);
        break;
    }
}

我不知道我还能做些什么来调试这个问题,我被困住了。任何帮助将不胜感激!

编辑:

这是失败时的 strace 输出,如您所见,它打印(并选择使用)正确的文件描述符,然后,在信号出现后,client_socket_fd 是错误的,因为接受失败并返回 EAGAIN。我已经评论了 exit_properly 调用以及 SIGTERM 和 SIGINT 的信号处理。对于 SIGWINH 信号,我什么都不做,只是返回。

STRACE 输出:

write(2, "server_socket_fd=3 client_socket"..., 47server_socket_fd=3 client_socket_fd=4 fd_max=4
) = 47
select(5, [3 4], NULL, NULL, NULL)      = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
sigreturn() (mask [])                   = -1 EINTR (Interrupted system call)
accept(3, 0xbf981e7c, [16])             = -1 EAGAIN (Resource temporarily unavailable)
write(2, "server_socket_fd=3 client_socket"..., 48server_socket_fd=3 client_socket_fd=-1 fd_max=3
) = 48
select(4, [3], NULL, NULL, NULL)        = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
sigreturn() (mask [])                   = -1 EINTR (Interrupted system call)
accept(3, 0xbf981e7c, [16])             = -1 EAGAIN (Resource temporarily unavailable)
write(2, "server_socket_fd=3 client_socket"..., 48server_socket_fd=3 client_socket_fd=-1 fd_max=3
) = 48
select(4, [3], NULL, NULL, NULL

现在的 signal_handler:

void signal_handler(int sig){

    switch(sig){
    /*
    case SIGINT:
        exit_properly(0); //sigint_flag = 1;
        break;
    case SIGTERM:
        exit_properly(1); //sigterm_flag = 1;
        break;

*/
    case SIGWINCH:
        //sigwinch_flag = 1;
/*
        fprintf(stderr, "\33[2J");
        fflush(stdout);
    */
        break;

    default:
        //fprintf(stderr, "Unhandled signal %d received\n",sig);
        break;
    }
}

也尝试过不使用 SA_RESTART 标志...同样的结果...?:/

最佳答案

select()将被信号中断,返回 -1,并将 errno 设置为 EINTR,但您的代码无法处理此问题。 即使您使用 SA_RESTART 安装信号处理程序,还有一些系统调用会被中断,返回错误条件,设置errno为EINTR .

参见《信号处理程序中断系统调用和库函数》 部分位于 http://man7.org/linux/man-pages/man7/signal.7.html/

如果选择失败,您的代码将继续检查 readfds像这样:

if(FD_ISSET(server_socket_fd, &readfds)){

但是,如果 select() 失败,fd_set您传递给它的变量处于不确定状态,您不应该依赖它们的值。

相反,如果 select 失败,您应该重新开始循环,例如像这样的 continue 语句:

  if(select(fd_max+1, &readfds, NULL, NULL, NULL) == -1){
        if(errno != EINTR){
            perror("select failed");
           //Might be severe enough to quit your program... 
        }
       continue; 
    }

关于c - Linux 服务器 C 代码在收到信号后停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43937790/

相关文章:

apache - 将内网IP映射到作为服务器的静态IP

c - Linux 唤醒阻塞串口读取

Pythonpath 仅在交互式终端中识别

linux - sqlplus 中大于 4GB 的假脱机文件

c - 从并行端口读取 4 µs 长 +5V TTL——何时使用内核中断

linux - 在远程服务器上安装 Mapserver

c - OSX/Ubuntu/Soliars 上的信号量行为不同,线程执行超出 sem_wait()

c - getc 返回值存储在一个 char 变量中

c - c编程中的*和&运算符有什么区别?

ubuntu - Virtualbox - 找不到适合运行内核的模块