Обработчик сигнала не увидит глобальную переменную

Вот проблема: эта программа должна получать ввод со стандартного ввода и подсчитывать вставленные байты; сигнал SIGUSR1 остановит основную программу и напечатает стандартную ошибку файла, сколько байтов было скопировано при отправке SIGUSR1.

Вот как мой учитель хочет, чтобы я это сделал: за один запуск терминала

cat /dev/zero | ./cpinout | cat >/dev/null

в то время как со второго терминала посылают сигналы с

kill -USR1 xxxx

где xxxx — pid cpinout.

Я обновил свой предыдущий код:

/* cpinout.c */

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

#define BUF_SIZE 1024   

volatile sig_atomic_t countbyte = 0;
volatile sig_atomic_t sigcount = 0;

/* my_handler: gestore di signal */
static void sighandler(int signum) {
    if(sigcount != 0)
        fprintf(stderr, "Interrupted after %d byte.\n", sigcount);
    sigcount = countbyte;    
}

int main(void) {
  
    int c;
    char buffer[BUF_SIZE];
    struct sigaction action;

    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    action.sa_handler = sighandler;
    if(sigaction(SIGUSR1, &action, NULL) == -1) {
        fprintf(stderr, "sigusr: sigaction\n");
        exit(1);
    }
    while( c=getc(stdin) != EOF ) {
        countbyte++;
        fputc(c, stdout);
    }
    return(0);
}

person elmazzun    schedule 19.03.2013    source источник
comment
Не работает даже с подписью ›.‹ У меня по-прежнему копируются миллионы байтов.   -  person elmazzun    schedule 19.03.2013


Ответы (2)



ИЗМЕНИТЬ

В комментариях вы упомянули, что запускаете команду как:

cat /dev/zero | ./namefile | cat >/dev/null

Поведение на самом деле нормальное. /dev/zero — это бесконечный поток нулей, которые передаются в программу. Так что он подсчитывает их очень быстро. Когда вы прерываете, он останавливается, и у вас остается большое число.


Проблема может быть связана с тем, что обработчик сигнала может быть вызван во время обновления глобальной переменной (если для этого требуется более одной инструкции). Однако в документации GNU утверждается, что можно с уверенностью предположить, что int всегда является атомарным в системе POSIX.

Единственная другая возможность, о которой я могу думать, это то, что вы вызываете fputc в цикле с printf в обработчике (однако должно быть безопасно вызывать printf в обработчике, если он не вызывается программой). Попробуйте удалить fputc из цикла, чтобы посмотреть, решит ли это проблему.

ИЗМЕНИТЬ:

Кажется, это объясняет проблему. Это относится к функциям, которые безопасно вызывать из обработчика сигнала:

Функции также могут быть нереентерабельными, если они используют статические структуры данных для своей внутренней отчетности. Наиболее очевидными примерами таких функций являются члены библиотеки stdio (printf(), scanf() и т. д.), которые обновляют внутренние структуры данных для буферизованного ввода-вывода. Таким образом, при использовании printf() из обработчика сигнала мы иногда можем видеть странный вывод — или даже сбой программы или повреждение данных — если обработчик прерывает основную программу в середине выполнения вызова printf() или другого stdio. функция. (Интерфейс программирования Linux)

Ваша программа прерывает функцию stdio, которая, кажется, идеально подходит для этого.


Вот альтернативный подход:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

int countbyte = 0;  // for main program
int sigcount = 0;   // for signal handler

/* my_handler: signal handler */
static void sighandler(int signum)
{
   sigcount = countbyte;
}

int main(void)
{ 
   int c;
   struct sigaction sigact;

   sigemptyset(&sigact.sa_mask);
   sigact.sa_flags = 0;
   sigact.sa_handler = sighandler;
   sigaction(SIGUSR1, &sigact, NULL);
   while ((c = getc(stdin)) != EOF) {
      countbyte++;
      fputc(c, stdout);
   }
   if (sigcount != 0) {
      printf("Interrupted after %d bytes\n", sigcount);
   }

   return 0;
}
person teppic    schedule 19.03.2013
comment
Не могли бы вы подсказать, почему, по вашему мнению, здесь уместны различия между сигналом и сигналом? - person AProgrammer; 19.03.2013
comment
@AProgrammer - в документации говорится: «Поведение signal() различается в разных версиях UNIX, а также исторически менялось в разных версиях Linux. Избегайте его использования: вместо этого используйте sigaction(2) ... Единственное переносимое использование signal() - установить расположение сигнала в SIG_DFL или SIG_IGN. Семантика при использовании signal() для установки обработчика сигнала различается в разных системах (и POSIX.1 явно разрешает это изменение); не используйте его для этой цели. - person teppic; 19.03.2013
comment
Да, есть различия в поведении между вариантами Unix, я не знаю ни одного, связанного с заявленной проблемой. - person AProgrammer; 19.03.2013
comment
@AndreaMazzocchi - какая у тебя операционная система? - person teppic; 19.03.2013
comment
Linux Xubuntu 12.10 3.5.0-23-общий - person elmazzun; 19.03.2013
comment
@AndreaMazzocchi - думаю, причина найдена. - person teppic; 19.03.2013
comment
@teppic, я попробую это позже, и я найду альтернативу fprintf, когда я хочу напечатать количество скопированных байтов до получения SIGUSR1 - person elmazzun; 20.03.2013
comment
@AndreaMazzocchi - вы можете установить глобальную переменную в своей функции обработчика на количество скопированных байтов, а затем получить к ней доступ при возврате из обработчика. - person teppic; 20.03.2013
comment
Извините @teppic, я не понял, что вы сказали :( - person elmazzun; 20.03.2013
comment
@AndreaMazzocchi - есть две глобальные переменные. Один обновляется в вашей основной программе в цикле, а другой вы устанавливаете из функции обработчика, например. countsignal = countbyte;, то когда вы вернетесь, если count_signal != 0, вы знаете, что это было прервано. - person teppic; 20.03.2013
comment
@teppic, настоящая проблема, которую я не могу понять, заключается в том, что упражнение просит меня запустить эту программу следующим образом: cat /dev/zero | ./имя_файла | cat ›/dev/null и из-за /dev/null ваш последний printf ничего не выведет на стандартный вывод... Я действительно не знаю, что делать :( - person elmazzun; 21.03.2013
comment
@AndreaMazzocchi - вы можете написать stderr, это все равно сработает - person teppic; 21.03.2013
comment
Верно! Но, опять же, количество прочитанных байтов не совпадает (я получаю миллионы байтов) - person elmazzun; 21.03.2013
comment
@AndreaMazzocchi - обновите свой вопрос своим текущим кодом - person teppic; 21.03.2013
comment
@AndreaMazzocchi - на самом деле, я понимаю, почему. Я обновлю ответ. - person teppic; 21.03.2013
comment
Спасибо @teppic, но почему он хочет, чтобы я использовал этот бесконечный поток нулей? Должен ли я также считать нули от cat /dev/zero? Я не понимаю XD В любом случае, программа, кажется, делает то, что должна делать. - person elmazzun; 21.03.2013
comment
@AndreaMazzocchi - да, программа будет считать количество нулей. Вы должны спросить его, почему :) Ваша программа в порядке. - person teppic; 21.03.2013

Сигналы могут записывать только volatile sig_atomic_t переменных в соответствии со стандартами C89 и POSIX 7. :

поведение не определено, если обработчик сигнала ссылается на любой объект [CX] [Option Start], отличный от errno [Option End] со статической продолжительностью хранения, отличной от присвоения значения объекту, объявленному как volatile sig_atomic_t

Реализации часто предлагают больше, но я сомневаюсь, что вы используете энергонезависимые глобальные переменные или printf.

person AProgrammer    schedule 19.03.2013