2

我目前正在实施生产者/消费者问题计划。我有一个父进程和几个子进程。一切正常,但现在我需要让我的程序每k毫秒输出我的程序正在执行的任务的进度。

起初我以为可能只是使用signal()andalarm()函数,但从一些初步测试来看,我一直在做这似乎还不够。我查看了几个日志文件,似乎onAlarm()没有被调用。我想这与父母和孩子都“忙”他们没有收到事件的事实有关?或者即使他们很忙,他们也应该能够在 onAlarm() 上接听电话?我看到的唯一解决方法是创建另一个进程,该进程具有处理此问题的单一责任。

这是我的“事件”代码:

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

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

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

5 回答 5

3

您的主要问题是alarm()需要整数秒 - 并alarm(0)用于取消任何未完成的警报。

自然的后续问题是:

如何进行亚秒级等待?

我不确定批准的方式是什么。在某些系统上,有一个 micro-sleep ( usleep()) 调用,但这不是 POSIX 2008 的一部分。usleep()在 POSIX 中的直接类似物似乎是nanosleep().

有一个sigtimewait()可能可以用来实现效果。(您也许可以使用setitimer()getitimer()代替usleep()。)

所有这些的困难在于您要么是同步的(在等待信号到达时无法继续工作),要么没有发送信号。我没有立即看到 POSIX 亚秒级警报机制,它允许您在等待超时的同时继续工作。

于 2010-12-12T02:22:54.530 回答
1

你的问题的简短回答是肯定的。即使他们附加的进程正忙于进行计算或某种 io/等待,它们也能工作。唯一的限制是该过程不会发生变化并且不会SIGALRM自行玩耍,这会导致问题。当信号到达时,进程/线程的正常执行被挂起并调用信号处理程序。这就是信号处理的全部思想以及它为什么被称为asynchronous.

你的问题更长的答案是否定的。您不希望通过信号处理程序机制实现进度报告,因为您可以在信号处理程序中使用的 API 非常有限。确切地说,您提到的示例是错误的,因为它使用了fprintf(3). 正如在其中一个答复中所述,信号是异步的。这意味着如果信号在主代码调用的中间到达malloc(3),并且你的代码malloc(3)也调用(你永远不知道,printf(3)可能会调用malloc(3)缓冲和其他需求),那么你将破坏 malloc 自己的内部数据结构并导致程序出错。您甚至可能在调用自己的非异步安全函数时遇到问题。您有一个可以在信号处理程序中调用的安全函数列表,您可以在man 7 signalAsync-signal-safe functionsalarm(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;
}
于 2014-11-10T08:19:25.313 回答
1

我不确定这是否是您的问题,但fprintf在信号处理程序内部是不安全的。例如,信号可能会在fprintf其自身内部引发,这可能会导致意外行为。或者可能fprintf正在分配一些内存,并且在malloc运行时捕获了信号。这种事情会产生看似随机的死锁和剧烈的崩溃。

在信号处理程序中进行复杂计算的安全方法是让你的信号处理程序改变已经运行的事件循环的状态,或者类似的东西。或者,对于您的特定问题,正如其他人所建议的那样,避免为此使用信号并使用更直接的睡眠呼叫。

于 2010-12-12T02:47:58.070 回答
0

对于发送信号的亚秒级计时器,您需要使用 POSIX setitimer(2) 函数。

于 2010-12-12T04:52:18.883 回答
0

如果您需要对计时器进行亚秒级分辨率,您可以使用第二个 posix 线程和usleep

于 2010-12-12T02:24:57.243 回答