Почему был изменен обработчик SIGPIPE по умолчанию? - PullRequest
0 голосов
/ 16 декабря 2018

Я работаю над тестом, и меня спрашивают, как заставить "читать" сон или "написать" остановить процесс "

Для последнего я не понимаю, почему мой sigpipe, действительно поднято, но не останавливает процесс:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#define READING 0
#define WRITING 1
#define DESCRIPTOR_COUNT 2

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

int main(void)
{
    int tube[DESCRIPTOR_COUNT];
    pipe(tube);
//    signal(SIGPIPE, signal_handler);
    close(tube[READING]);

    if(write(tube[WRITING], "message", 8)<0)
        if(errno==EPIPE)
            printf("EPIPE returned\n");

    printf("123");

    return EXIT_SUCCESS;
}

Без сигнала () (цитируется)

С сигналом () (без кавычек)

SIGPIPE действительно получен, но в случае, если я нене справиться с этим, процесс должен остановиться , но, поскольку я могу написать «123», это означает, что процесс не был остановлен . Почему?

Также яна Fedora 28 я использую кодовые блоки 17.12.

Был ли SIGPIPE проигнорирован ...? Причина?

РЕШЕНИЕ?

struct sigaction action;
action.sa_handler = SIG_DFL;
sigaction(SIGPIPE, &action, 0);

Заменаsignal() с этим будет иметь поведение по умолчанию, как и ожидалось!

РЕДАКТИРОВАТЬ Я теперь изменил заголовок с «SIGPIPE не останавливает процесс» на «Почему был изменен обработчик SIGPIPE по умолчанию»? "

===========================================================

Ответ

После общения с ребятами из кодоблоков, кодоблоки используют wxWidgets, а в linux (fedora 28 здесь) wxWidgetsиспользует библиотеку gtk, как объяснил Марк Плотник в комментариях, gtk изменяет обработчик сигнала для SIGPIPE, поскольку кодовые блоки запускают коды с использованием fork или exec, на код, выполняемый через кодовые блоки, влияет библиотека gtk.

Ответы [ 2 ]

0 голосов
/ 16 декабря 2018

Поведение, о котором вы сообщаете, согласуется с параметром IDE Code :: Blocks, явным или неявным образом, поведением SIGPIPE для SIG_IGN.Это легко унаследовано.Это не то, что я ожидал - я ожидал, что ваша программа будет запущена с SIGPIPE (и, действительно, со всеми другими сигналами), установленным в SIG_DFL, поведение сигнала по умолчанию.Если это окажется проблемой, у вас есть основание для сообщения об ошибке разработчикам Code :: Blocks.Если окажется, что проблема не в этом, тогда у нас есть некоторые серьезные размышления, чтобы решить, что на самом деле происходит *.

Вы могли быпродемонстрируйте, происходит ли это с Code :: Blocks, обращая внимание на возвращаемое значение из signal() или используя sigaction() для опроса режима обработки сигналов без его изменения.

Например:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#define READING 0
#define WRITING 1
#define DESCRIPTOR_COUNT 2

static void signal_handler(int signum)
{
    // Lazy; normally, I'd format the signal number into the string, carefully
    (void)signum;
    write(STDOUT_FILENO, "sigpipe received\n", sizeof("sigpipe received\n")-1);
}

int main(void)
{
    void (*handler)(int) = signal(SIGPIPE, signal_handler);

    if (handler == SIG_DFL)
        printf("old handler was SIG_DFL\n");
    else if (handler == SIG_IGN)
    {
        printf("old handler was SIG_IGN\n");
        (void)signal(SIGPIPE, SIG_IGN);
    }
    else
    {
        // Standard C does not allow a cast from function pointer to object pointer
        //printf("there was a non-standard handler installed (%p)\n", (void *)handler);
        printf("there was a non-standard handler installed\n");
    }

    int tube[DESCRIPTOR_COUNT];
    pipe(tube);    
    close(tube[READING]);

    if (write(tube[WRITING], "message", 8) < 0)
    {
        if (errno == EPIPE)
            printf("EPIPE returned\n");
        else
            printf("errno = %d\n", errno);
    }

    printf("123\n");

    return EXIT_SUCCESS;
}

Обратите внимание, что стандартная идиома для настройки обработчика сигнала с помощью signal() в программе была:

if (signal(signum, SIG_IGN) != SIG_IGN)
    signal(signum, signal_handler);

Это означает, чтоесли программа была защищена от сигналов, она остается защищенной.Если он обрабатывал сигналы (по умолчанию или, возможно, явно при предварительном вызове signal()), то вы устанавливаете свой собственный обработчик сигналов.Эквивалентный код с использованием sigaction() будет иметь вид:

struct sigaction sa;
if (sigaction(signum, 0, &sa) == 0 && sa.sa_handler != SIG_IGN)
{
    sa.sa_handler = signal_handler;
    sa.sa_flag &= ~SA_SIGINFO;
    sigaction(signum, sa, 0);
}

(Одно из преимуществ этого: структура инициализируется вызовом sigaction(), поэтому нет необходимости возиться с масками.flags обеспечивает использование основного обработчика, а не расширенного обработчика.)

Когда я компилирую исходный код (pipe13.c) в программу pipe13 и запускаю его, я получаю:

$ make pipe13
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes pipe13.c -o pipe13
$ pipe13
old handler was SIG_DFL
sigpipe received
EPIPE returned
123
$ (trap '' 13; pipe13)
old handler was SIG_IGN
EPIPE returned
123
$

Этот вариант использует sigaction() для опроса обработки сигнала:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#define READING 0
#define WRITING 1
#define DESCRIPTOR_COUNT 2

int main(void)
{
    struct sigaction sa;
    if (sigaction(SIGPIPE, 0, &sa) != 0)
        fprintf(stderr, "siagaction() failed\n");
    else if (sa.sa_handler == SIG_DFL)
        printf("old handler was SIG_DFL\n");
    else if (sa.sa_handler == SIG_IGN)
        printf("old handler was SIG_IGN\n");
    else
        printf("there was a non-standard handler installed\n");

    int tube[DESCRIPTOR_COUNT];
    pipe(tube);    
    close(tube[READING]);

    if (write(tube[WRITING], "message", 8) < 0)
    {
        if (errno == EPIPE)
            printf("EPIPE returned\n");
        else
            printf("errno = %d\n", errno);
    }

    printf("123\n");

    return EXIT_SUCCESS;
}

При запуске (программа pipe83) я получаю:

$ pipe83
old handler was SIG_DFL
$ echo $?
141
$ (trap '' 13; pipe83)
old handler was SIG_IGN
EPIPE returned
123
$

Обратите внимание, что собработка сигналов по умолчанию, программа останавливается перед печатью 123.Оболочки POSIX кодируют «ребенок умер от сигнала N», сообщая о состоянии выхода как 128 + N;SIGPIPE равен 13, поэтому 141 указывает, что оболочка умерла от сигнала SIGPIPE.(Да, современные молодые люди, вероятно, написали бы (trap '' PIPE; pipe83), и это работает - такие тонкости были недоступны, когда я изучал программирование оболочки.)

Было бы не так сложно обобщить код, чтобы проверить, является ли Код:: Blocks устанавливает любые другие сигналы, отличные от обработки по умолчанию.Тем не менее, это может быть немного неудобно, если вы хотите приспособиться к тому, какие сигналы доступны на машине.


* In chat , мы установили, что программа запускается в образе VMware под управлением Fedora 28, размещенном на компьютере с Windows 10.Из-за этого существует достаточно возможных мест для возникновения проблем, так что не ясно, что проблема обязательно в Code :: Blocks - просто не ясно, откуда возникла проблема.Однако проблема действительно заключается в том, что тестовая программа запускается с обработкой SIGPIPE, установленной на SIG_IGN вместо SIG_DFL, когда она запускается из Code :: Blocks.

0 голосов
/ 16 декабря 2018

Code :: Blocks это не компилятор, а просто IDE.Он (вероятно) запускает компилятор GCC .И компилятор GCC не имеет большого значения для обработки сигналов.Прочитать signal (7) и signal-safety (7) для большего (вызов printf внутри обработчика сигнала запрещен, поскольку printf не async-signal-safe , поэтому ваш printf("sigpipe received\n"); внутри обработчика сигнала неопределенное поведение ).Обработка сигналов в основном выполняется ядром Linux (см. Его исходный код на kernel.org ), небольшая часть которого обрабатывается вашей стандартной библиотекой C, вероятно, GNU glibc.

Кажется, кодовые блоки изменяют обработчик сигнала по умолчанию для SIGPIPE

Это очень маловероятно (и почти наверняка неверно).Вы можете использовать strace (1) в своей программе, чтобы понять, какие системные вызовы она выполняет.

  printf("123");

Вы забыли \n.Помните, что stdout обычно буферизуется строкой.Или вы должны вызвать fflush (3) .

Когда вы запускаете программу из Code :: Blocks, она может работать без терминала.Я настоятельно рекомендую запускать вашу программу в эмуляторе терминала (см. tty (4) и pty (7) ).В стандартной библиотеке C разрешено использовать что-то вроде isatty (3) , чтобы вести себя иначе, когда stdout является или не является tty (в частности, буферизация выполняется по-другому, см. setvbuf (3)).Прочитайте tty demysified и termios (3) .

Вы также можете попробовать запустить вашу программу, перенаправив ее stdin, stdout, stderr в файл или канал(возможно, используя |& cat или |& less с bash или zsh), или что-либо, что не является tty.

BTW, Code :: Blocks равно бесплатное программное обеспечение .Вы можете изучить его исходный код, чтобы понять, что он делает.

Попробуйте также использовать stdbuf (1) для запуска вашей программы с различными операциями буферизации.

...