Работают ли signal () и alarm (), даже если процесс, в котором они выполняются, занят?Или я должен запустить его на другом выделенном процессе? - PullRequest
2 голосов
/ 12 декабря 2010

В настоящее время я реализую программу для продюсеров / потребителей. У меня есть один родительский и несколько дочерних процессов. Все работает, но теперь мне нужно, чтобы моя программа выводила каждые k миллисекунды выполнения задачи, выполняемой моей программой.

Сначала я подумал, что, может быть, речь идет только об использовании функций signal() и alarm(), но из некоторых предварительных испытаний, которые я проводил, этого кажется недостаточно. Я просмотрел несколько файлов журнала, и кажется, что onAlarm() не вызывается. Я предполагаю, что это связано с тем фактом, что как родители, так и дети "заняты", они не получают события? Или даже если они заняты, они должны иметь возможность принимать вызовы onAlarm ()? Единственный обходной путь, который я вижу для этого, - это создать еще один процесс, который имеет единую ответственность, связанную с этим.

Это мой код "событий":

void onAlarm() {
    signal(SIGALRM, onAlarm);
    alarm(0.01);

        fprintf(outputFile, "ALAAAAAAAAAAAAAARMMMMMMMMMMM: %d\n", numberOfBytesRead);
}

int main() {
    signal(SIGALRM, onAlarm);
    alarm(0.01);
        ...
}

Ответы [ 5 ]

3 голосов
/ 12 декабря 2010

Ваша основная проблема заключается в том, что alarm() занимает целое число секунд - и alarm(0) используется для отмены любых невыполненных аварийных сигналов.

Естественный дополнительный вопрос:

Как я могу подсекундное ожидание?

Я не уверен, что такое одобренный способ.В некоторых системах есть вызов micro-sleep (usleep()), но он не является частью POSIX 2008. Прямой аналог usleep() в POSIX выглядит как nanosleep().

Существует sigtimewait(), который может быть использован для достижения эффекта.(Вы можете использовать setitimer() и getitimer() вместо usleep().)

Сложность во всех этих случаях заключается в том, что вы либо синхронны (вы можете 'не может продолжать работу, ожидая поступления сигнала) или не отправил сигнал.Я не сразу вижу механизм тревоги POSIX менее секунды, который позволил бы вам продолжить работу в ожидании тайм-аута.

1 голос
/ 10 ноября 2014

Короткий ответ на ваш вопрос - да. Они работают, даже несмотря на то, что процесс, к которому они подключены, занят вычислениями или ожиданием / ожиданием какого-то рода. Единственным ограничением является то, что процесс не меняется и играет с SIGALRM, что может вызвать проблемы. Когда поступает сигнал, нормальное выполнение процесса / потока приостанавливается и вызывается обработчик сигнала. В этом вся идея обработки сигналов и почему она называется asynchronous.

Более длинный ответ на ваш вопрос - нет. Вы не захотите реализовывать отчет о ходе выполнения с помощью механизма обработчика сигналов, поскольку API, который вы можете использовать внутри обработчика сигналов, очень ограничен. Если быть точным, пример, который вы указали неверно, поскольку он использует fprintf(3). Как было сказано в одном из ответов, сигналы являются асинхронными. Это означает, что если сигнал приходит в середине основного кода, вызывающего, скажем, malloc(3), а ваш код также вызывает malloc(3) (вы никогда не узнаете, printf(3) может вызвать malloc(3) для буферизации и других нужд) тогда вы испортите mallocs собственные внутренние структуры данных и вызовете сбой программы. У вас могут возникнуть проблемы с вызовом собственных функций, которые не являются асинхронными. У вас есть список безопасных функций, которые вы можете вызывать в обработчике сигналов, и вы можете найти их в man 7 signal в Async-signal-safe functions. Так что да, технически вы можете реализовать отчеты о прогрессе через alarm(3), если вы готовы жить с этим сокращенным API, и поэтому я не буду этого делать, если программа не является однопоточной по дизайну и если на самом деле нет никакого способа в котором я вижу код сообщения о прогрессе как подлежащий будущим улучшениям, которые затруднят запись в обработчике сигналов.

Другая проблема, о которой говорилось в вашем примере, заключается в том, что alarm(2) не принимает аргументы длиной более секунды, и в приведенном выше примере должна была произойти ошибка компиляции или, по крайней мере, были показаны некоторые предупреждения об этом факте.

Для разрешения в микросекундах вы можете использовать setitimer(2) с ITIMER_REAL, как было указано.

Для разрешения наносекунд в Linux вы можете использовать timer_create(2), CLOCK_REALTIME, SIGEV_SIGNAL и timer_settime(2), которые имеют гораздо больше функций.

Вот пример кода. Обратите внимание, что здесь используются мои собственные макросы обработки ошибок. Вы можете увидеть его в скомпилированном состоянии в этом проекте demos-linux

#include <signal.h> // for signal(2), SIG_ERR
#include <unistd.h> // for alarm(2), write(2)
#include <stdlib.h> // for EXIT_SUCCESS
#include <err_utils.h>  // for CHECK_NOT_M1(), CHECK_NOT_SIGT()
#include <stdio.h>  // for snprintf(3), STDERR_FILENO
/*
 * This is an example of doing progress reports via the SIGALRM signal every second.
 * The main code does a tight calculation loop and the progress reporting to stderr
 * (file descriptor 2) is done via the alarm signal handler.
 */

/*
 * The variables are global to allow the signal handler to access them easily
 * You DONT need to initialize them to 0 since that is the default.
 * The 'volatile' on i is *critical* since it will be accessed asynchronously
 * and the compiler needs to know not to put it in a register since that
 * will mean that we cannot report it's value correctly from the signal
 * handler.
 */
volatile unsigned long i;
/*
 * Remember that this is a signal handler and calls to fprintf(3) or the like
 * are forbidden so we are forced to use async-safe function (see man 7 signal).
 * That is the reason for the cumbersome code. Hopefully snprintf(3) is safe enough
 * to use.
 */
static void handler(int sig) {
    // we have to reschedule the SIGALRM every time since the alarm(2)
    // is a one time deal.
    CHECK_NOT_SIGT(signal(SIGALRM, handler), SIG_ERR);
    // no error code from alarm(2)
    alarm(1);
    char buf[100];
    int len=snprintf(buf, sizeof(buf), "did [%ld] units of work...\n", i);
    CHECK_NOT_M1(write(STDERR_FILENO, buf, len));
}

int main(int argc, char** argv, char** envp) {
    CHECK_NOT_SIGT(signal(SIGALRM, handler), SIG_ERR);
    // no error code from alarm(2)
    alarm(1);
    // a very long calculation
    while(true) {
        /* Do some real work here */
        i++;
    }
    return EXIT_SUCCESS;
}
1 голос
/ 12 декабря 2010

Я не уверен, является ли это вашей проблемой, но не безопасно fprintf внутри обработчика сигнала. Например, сигнал может быть поднят внутри самого fprintf, что может привести к неожиданному поведению. Или, возможно, fprintf выделяет некоторую память, и сигнал был перехвачен во время работы malloc. Подобные вещи могут создавать на первый взгляд случайные тупики и сильные сбои.

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

0 голосов
/ 12 декабря 2010

Для отправки секундного таймера сигнала вам нужно использовать функцию POSIX setitimer (2).

0 голосов
/ 12 декабря 2010

Если вам нужно субсекундное разрешение для таймера, вы можете использовать второй поток posix и usleep

...