sigaction передает системный вызов SIGINT, но не сигнал - PullRequest
0 голосов
/ 08 сентября 2018

У меня есть цикл обработки вызова accept(2). Я хочу иметь возможность выполнить некоторую очистку при отправке SIGINT в программу. Моей первой мыслью было использовать функцию signal.

void signal_handler(int signal) {
    printf("Caught signal\n");
}

int main() {
    signal(SIGINT, &signal_handler);
    // ...
    int accept_fd = accept(sock, NULL, NULL);
    if (accept_fd == -1) {
        close(sock);
        perror("accept");
        return 1;
    }
    // ...
}

Однако, это просто печатает «Пойманный сигнал», и затем программа продолжается.

Если я изменю main для использования sigaction, программа будет работать как положено.

int main() {
    struct sigaction sa;
    sa.sa_handler = &signal_handler;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, NULL);
    // ...
    int accept_fd = accept(sock, NULL, NULL);
    if (accept_fd == -1) {
        close(sock);
        perror("accept");
        return 1;
    }
    // ...
}

При отправке SIGINT я получаю Caught Signal, затем accept: Interrupted system call. Со страницы руководства для accept(2)

ОШИБКА

...

EINTR Системный вызов был прерван сигналом, который был перехвачен до того, как прибыло действительное соединение; см. сигнал (7).

Я понимаю, что sigaction более современен, и что я должен использовать его вместо signal, но мне довольно любопытно, почему он обеспечивает такую ​​разницу в функциональности.

Ниже я включил полный пример программы для каждого случая.

Пример с сигналом (2)

#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 32

void signal_handler(int signal) {
    printf("Caught signal\n");
}

int main() {
    signal(SIGINT, &signal_handler);
    struct addrinfo hints;
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    struct addrinfo *addr_info;
    int info_result = getaddrinfo("localhost", "8080", &hints, &addr_info);
    if (info_result != 0) {
        printf("Getting address failed\n");
        return 1;
    }
    int sock = socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol);
    if (sock == -1) {
        printf("Socket Failed\n");
        return 1;
    }
    int bind_result = bind(sock, addr_info->ai_addr, addr_info->ai_addrlen);
    if (bind_result == -1) {
        close(sock);
        printf("Bind Failed\n");
        return 1;
    }
    int listen_result = listen(sock, 8);
    if (listen_result == -1) {
        close(sock);
        printf("Listen Failed\n");
        return 1;
    }
    printf("Waiting...\n");
    int accept_fd = accept(sock, NULL, NULL);
    if (accept_fd == -1) {
        close(sock);
        perror("accept");
        return 1;
    }
    printf("Got fd %d\n", accept_fd);
    char *buffer = malloc(BUFFER_SIZE * sizeof(char));
    int n;
    while ((n = read(accept_fd, buffer, BUFFER_SIZE)) > 0) {
        printf("%.*s\n", n, buffer);
    }
    close(sock);
}

Пример с sigaction (2)

#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFFER_SIZE 32

void signal_handler(int signal) {
    printf("Caught signal\n");
}

int main() {
    struct sigaction sa;
    sa.sa_handler = &signal_handler;
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, NULL);
    struct addrinfo hints;
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    struct addrinfo *addr_info;
    int info_result = getaddrinfo("localhost", "8080", &hints, &addr_info);
    if (info_result != 0) {
        printf("Getting address failed\n");
        return 1;
    }
    int sock = socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol);
    if (sock == -1) {
        printf("Socket Failed\n");
        return 1;
    }
    int bind_result = bind(sock, addr_info->ai_addr, addr_info->ai_addrlen);
    if (bind_result == -1) {
        close(sock);
        printf("Bind Failed\n");
        return 1;
    }
    int listen_result = listen(sock, 8);
    if (listen_result == -1) {
        close(sock);
        printf("Listen Failed\n");
        return 1;
    }
    printf("Waiting...\n");
    int accept_fd = accept(sock, NULL, NULL);
    if (accept_fd == -1) {
        close(sock);
        perror("accept");
        return 1;
    }
    printf("Got fd %d\n", accept_fd);
    char *buffer = malloc(BUFFER_SIZE * sizeof(char));
    int n;
    while ((n = read(accept_fd, buffer, BUFFER_SIZE)) > 0) {
        printf("%.*s\n", n, buffer);
    }
    close(sock);
}

1 Ответ

0 голосов
/ 08 сентября 2018

В BSD и Linux signal() эквивалентно sigaction() с sa_flags, установленным на SA_RESTART. Если вы установите этот флаг в своем коде sigaction(), он будет вести себя так же, как ваш код signal(). Если это не то, что вы хотите, тогда вы должны использовать только sigaction().

Примечания со страницы руководства Linux (которая также применима к BSD и OS X):

На BSD, когда вызывается обработчик сигнала, расположение сигнала не сброс, и дальнейшие экземпляры сигнала заблокированы от доставлено во время выполнения обработчика. Кроме того, определенные блокировка системных вызовов автоматически перезапускается, если прервана обработчик сигнала (см. сигнал (7)). Семантика BSD эквивалентна Вызов sigaction (2) со следующими флагами:

       sa.sa_flags = SA_RESTART;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...