即使运行它们的进程很忙,signal()和alarm()也能工作吗?或者我应该在另一个专用进程上运行它?

发布于 2024-10-06 21:40:24 字数 598 浏览 6 评论 0原文

我目前正在实施一个生产者/消费者问题计划。我有一个父进程和几个子进程。一切正常,但现在我需要让我的程序每 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);
        ...
}

I am currently implementing a Producer/Consumers problem program. I have one parent and several child processes. Everything is working but now I need to make my program output each k milliseconds the progress of the task my program is doing.

At first I thought that maybe it'd be just about using the signal() and alarm() functions, but from some preliminary testing I've been making it doesn't seem enough. I have watched over several log files and it seems onAlarm() is not being called. I guess it has to do with the fact that as that both parent and children are "busy" they don't receive the events? Or even if they are busy, they should be able to receive calls on onAlarm()? The only workaround I see for this is to create yet another process, that has as single responsability dealing with this.

This is my "events" code:

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

薄情伤 2024-10-13 21:40:24

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

自然的后续问题是:

如何进行亚秒级等待?

我不确定批准的方式是什么。在某些系统上,有一个微睡眠 (usleep()) 调用,但这不是 POSIX 2008 的一部分。 POSIX 中的 usleep() 的直接类似物出现为 nanosleep()

有一个 sigtimewait() 可以大概可以用来达到这样的效果。 (您也许可以使用 setitimer()getitimer() 代替 usleep()。)

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

Your main problem is that alarm() takes an integer number of seconds - and alarm(0) is used to cancel any outstanding alarms.

The natural follow-up question is:

How can I do sub-second waiting?

I'm not sure what the approved way is. On some systems, there is a micro-sleep (usleep()) call, but that is not part of POSIX 2008. The direct analogue of usleep() in POSIX appears to be nanosleep().

There is a sigtimewait() which could probably be used to achieve the effect. (You might be able to use setitimer() and getitimer() in lieu of usleep().)

The difficulty with all of these is that you are either synchronous (you can't get on with work while waiting for a signal to arrive) or not sent a signal. I don't immediately see a POSIX sub-second alarm mechanism, which would allow you to continue work while waiting for the timeout.

倾城月光淡如水﹏ 2024-10-13 21:40:24

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

在信号处理程序内进行复杂计算的安全方法是让信号处理程序更改已运行的事件循环的状态,或类似的操作。或者,对于您的具体问题,正如其他人建议的那样,避免使用信号并使用更直接的睡眠呼叫。

I'm not sure whether or not this is your problem, but it's not safe to fprintf inside a signal handler. For example, the signal could be raised inside of fprintf itself, which might lead to unexpected behavior. Or perhaps fprintf is allocating some memory, and the signal was caught while malloc was running. This kind of thing can produce seemingly random deadlocks and violent crashes.

The safe way to do complex computations inside a signal handler is for your signal handler to alter the state of an already-running event loop, or something like this. Or, for your specific problem, as others suggest, avoid using signals for this and use a more straightforward sleep call.

迷离° 2024-10-13 21:40:24

对你的问题的简短回答是肯定的。即使它们附加的进程正忙于进行计算或某种类型的 io/等待,它们仍然可以工作。唯一的限制是该进程不会更改并使用 SIGALRM 本身,这会导致问题。当信号到达时,进程/线程的正常执行被挂起并调用信号处理程序。这就是信号处理的整体思想以及它被称为异步的原因。

你的问题的较长答案是否定的。您不想通过信号处理程序机制实现进度报告,因为您可以在信号处理程序中使用的 API 非常有限。确切地说,您提到的示例是错误的,因为它使用fprintf(3)。正如其中一个答复信号中所述,信号是异步的。这意味着,如果信号到达调用 malloc(3) 的主代码中间,并且您的代码也调用 malloc(3) (您永远无法要知道,printf(3) 可能会调用 malloc(3) 来进行缓冲和其他需求),那么您将破坏 malloc 自己的内部数据结构并导致程序出错。您甚至可能在调用自己的非异步安全函数时遇到问题。您有一个可以在信号处理程序内部调用的安全函数列表,您可以在异步信号安全函数下的man 7 signal中找到这些函数。所以,是的,从技术上讲,只要您愿意使用这个简化的 API,您就可以通过 alarm(3) 实现进度报告,这就是我不会这样做的原因,除非程序是单线程的设计,除非我真的没有办法看到进度报告代码受到未来增强的影响,这将使其很难在信号处理程序中编写。

关于您的示例的另一个问题是 alarm(2) 不接受亚秒参数,上面的示例应该完全编译失败,或者至少显示一些有关该事实的警告。

对于微秒分辨率,您可以将 setitimer(2)ITIMER_REAL 一起使用,如前所述。

对于 Linux 上的纳秒分辨率,您可以使用 timer_create(2)CLOCK_REALTIMESIGEV_SIGNALtimer_settime(2)还有更多功能。

这是一些示例代码。请注意,这使用了我自己的错误处理宏。您可以在此项目中看到它处于可编译状态 演示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;
}

The short answer to your question is yes. They work even though the process they attached to is busy doing calculations or io/waiting of some sort. The only limitation is that the process is not changing and playing with SIGALRM itself which would cause problems. When a signal arrives the normal execution of a process/thread is suspended and the signal handler is called. That is the whole idea of signal handling and why it is called asynchronous.

The longer answer to your question is no. You wouldn't want to implement progress report via the signal handler mechanism since the API that you can use inside a signal handler is very limited. To be exact the example that you referred to wrong since it uses fprintf(3). As has been stated in one of the replies signals are asynchronous. This means that if the signal arrives in the middle of the main code calling say, malloc(3), and your code calls malloc(3) as well (you can never know, printf(3) may call malloc(3) for buffering and other needs) then you will corrupts mallocs own internal data structures and cause the program to fault. You may even have problems calling your own functions which are not async safe. You have a list of safe functions that you can call inside a signal handler and you can find those in man 7 signal under Async-signal-safe functions. So yes, technically you can implement progress reporting via alarm(3) as long as you are willing to live with this reduced API and that is the reason I would not do it unless the program is single threaded by design and unless there is really no way in which I see the progress reporting code as being subject to future enhancements that will make it hard to write inside a signal handler.

Another problem which has been stated about your example is that alarm(2) does not accept sub-second arguments and the example above should have failed compilation completely or at least showed some warnings about that fact.

For microsecond resolution you can use setitimer(2) with ITIMER_REAL as has been stated.

For nanosecond resolution on Linux you can use timer_create(2), CLOCK_REALTIME, SIGEV_SIGNAL and timer_settime(2) that have many more features.

Here is some example code. Note that this uses my own error handling macros. You can see it in a compilable state in this project 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;
}
青柠芒果 2024-10-13 21:40:24

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

If you need sub-second resolution on a timer, you can use a second posix thread and a usleep

执手闯天涯 2024-10-13 21:40:24

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

For a sub-second timer sending a signal, you need to use the POSIX setitimer(2) function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文