pthread_sigmask () не работает в многопоточной программе - PullRequest
0 голосов
/ 12 июля 2020

Я новичок ie в разработке c. Недавно я заметил проблему, когда изучал многопоточную разработку, когда я устанавливаю сигнал в основном потоке Action и когда я пытаюсь заблокировать действие сигнала, установленное основным потоком в дочернем потоке, я обнаружил, что это действительно так. не работает.

Вот краткое описание кода

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>

void *thread_start(void *_arg) {
  sleep(2);

  sigset_t mask;
  sigemptyset(&mask);
  sigaddset(&mask, SIGUSR2);
  pthread_sigmask(SIG_BLOCK, &mask, NULL);

  printf("child-thread executed\n");

  while (true) {
    sleep(1);
  }

  return NULL;
}

void sig_handler(int _sig) {
  printf("executed\n");
}

int main(int argc, char *argv[]) {
  pthread_t t_id;
  int s = pthread_create(&t_id, NULL, thread_start, NULL);
  if (s != 0) {
    char *msg = strerror(s);
    printf("%s\n", msg);
  }

  printf("main-thread executed, create [%lu]\n", t_id);

  signal(SIGUSR2, sig_handler);

  while (true) {
    sleep(1);
  }

  return EXIT_SUCCESS;
}

Ответы [ 2 ]

1 голос
/ 12 июля 2020

Маска сигнала - это свойство для каждого потока , поток будет наследовать все, что родитель имеет во время создания потока, но после этого он управляет своим собственным копировать.

Другими словами, блокировка сигнала в потоке только влияет на доставку сигналов для этого потока, не для любого другого.

В любом случае, даже если бы он был совместно используемым (это не так), у вас было бы потенциальное состояние гонки, поскольку вы запускаете дочерний поток перед установкой сигнала в основном потоке. Следовательно, было бы неопределенным, был ли порядок «родитель устанавливает сигнал, затем дочерние блоки» или наоборот. Но, как уже говорилось, это не имеет значения из-за специфического для потока c характера маски сигнала.

Если вы хотите, чтобы поток управлял маской сигнала другого потока, вам нужно будет использовать некоторую форму межпотоковая связь, позволяющая другому потоку делать это сам.

0 голосов
/ 12 июля 2020

Как я писал в комментарии, любой сигнал USR1, отправленный процессу, будет доставлен с использованием основного потока. Его вывод не скажет вам точно, что произошло, поэтому это не совсем хороший способ тестирования потоков и масок сигналов. Кроме того, он использует printf() в обработчике сигнала, который может работать, а может и не работать: printf() не асинхронная c -сигнальная функция , поэтому его нельзя использовать в обработчике сигнала .

Вот лучший пример:

#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

/* This function writes a message directly to standard error,
   without using the stderr stream.  This is async-signal safe.
   Returns 0 if success, errno error code if an error occurs.
   errno is kept unchanged. */
static int write_stderr(const char *msg)
{
    const char *end = msg;
    const int   saved_errno = errno;
    int         retval = 0;
    ssize_t     n;

    /* If msg is non-NULL, find the string-terminating '\0'. */
    if (msg)
        while (*end)
            end++;

    /* Write the message to standard error. */
    while (msg < end) {
        n = write(STDERR_FILENO, msg, (size_t)(end - msg));
        if (n > 0) {
            msg += n;
        } else
        if (n != 0) {
            /* Bug, should not occur */
            retval = EIO;
            break;
        } else
        if (errno != EINTR) {
            retval = errno;
            break;
        }
    }

    /* Paranoid check that exactly the message was written */
    if (!retval)
        if (msg != end)
            retval = EIO;

    errno = saved_errno;
    return retval;
}

static volatile sig_atomic_t  done = 0;
pthread_t                     main_thread;
pthread_t                     other_thread;

static void signal_handler(int signum)
{
    const pthread_t  id = pthread_self();
    const char *thread = (id == main_thread) ? "Main thread" :
                         (id == other_thread) ? "Other thread" : "Unknown thread";
    const char *event = (signum == SIGHUP) ? "HUP" :
                        (signum == SIGUSR1) ? "USR1" :
                        (signum == SIGINT) ? "INT" :
                        (signum == SIGTERM) ? "TERM" : "Unknown signal";

    if (signum == SIGTERM || signum == SIGINT)
        done = 1;

    write_stderr(thread);
    write_stderr(": ");
    write_stderr(event);
    write_stderr(".\n");
}

static int install_handler(int signum)
{
    struct sigaction  act;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = signal_handler;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return -1;
    return 0;
}

void *other(void *unused __attribute__((unused)))
{
    sigset_t  mask;

    sigemptyset(&mask);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGHUP);
    pthread_sigmask(SIG_BLOCK, &mask, NULL);

    while (!done)
        sleep(1);

    return NULL;
}

int main(void)
{
    pthread_attr_t  attrs;
    sigset_t        mask;
    int             result;

    main_thread = pthread_self();
    other_thread = pthread_self(); /* Just to initialize it to a sane value */

    /* Install HUP, USR1, INT, and TERM signal handlers. */
    if (install_handler(SIGHUP) ||
        install_handler(SIGUSR1) ||
        install_handler(SIGINT) ||
        install_handler(SIGTERM)) {
        fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    /* Create the other thread. */
    pthread_attr_init(&attrs);
    pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);
    result = pthread_create(&other_thread, &attrs, other, NULL);
    pthread_attr_destroy(&attrs);
    if (result) {
        fprintf(stderr, "Cannot create a thread: %s.\n", strerror(result));
        return EXIT_FAILURE;
    }

    /* This thread blocks SIGUSR1. */
    sigemptyset(&mask);
    sigaddset(&mask, SIGUSR1);
    pthread_sigmask(SIG_BLOCK, &mask, NULL);

    /* Ready to handle signals. */
    printf("Send a HUP, USR1, or TERM signal to process %d.\n", (int)getpid());
    fflush(stdout);

    while (!done)
        sleep(1);

    pthread_join(other_thread, NULL);

    return EXIT_SUCCESS;
}

Сохраните его как, например, example.c, скомпилируйте и запустите, используя

gcc -Wall -O2 example.c -pthread -o exprog
./exprog

Он заблокирует сигнал USR1 в основном потоке, а HUP и TERM - в другом. Он также перехватит сигнал INT ( Ctrl + C), который не заблокирован ни в одном потоке. Если вы отправите ему сигнал INT или TERM, программа завершится.

Если вы отправите программе сигнал USR1, вы увидите, что он всегда будет доставляться с использованием другого потока.

Если вы отправите программе сигнал HUP, вы увидите, что он всегда будет доставляться с использованием основного потока.

Если вы отправите программе сигнал TERM, он также будет доставлен с использованием основного потока, но это также приведет к завершению программы (красиво).

Если вы отправите программе сигнал INT, он будет доставлен с использованием одного из потоков. Это зависит от нескольких факторов, всегда ли вы будете видеть, что он доставляется с использованием одного и того же потока или нет, но, по крайней мере, теоретически, он может быть доставлен с использованием любого потока. Этот сигнал также приведет к завершению программы (красиво).

...