你的问题的简短回答是肯定的。即使他们附加的进程正忙于进行计算或某种 io/等待,它们也能工作。唯一的限制是该过程不会发生变化并且不会SIGALRM
自行玩耍,这会导致问题。当信号到达时,进程/线程的正常执行被挂起并调用信号处理程序。这就是信号处理的全部思想以及它为什么被称为asynchronous
.
你的问题更长的答案是否定的。您不希望通过信号处理程序机制实现进度报告,因为您可以在信号处理程序中使用的 API 非常有限。确切地说,您提到的示例是错误的,因为它使用了fprintf(3)
. 正如在其中一个答复中所述,信号是异步的。这意味着如果信号在主代码调用的中间到达malloc(3)
,并且你的代码malloc(3)
也调用(你永远不知道,printf(3)
可能会调用malloc(3)
缓冲和其他需求),那么你将破坏 malloc 自己的内部数据结构并导致程序出错。您甚至可能在调用自己的非异步安全函数时遇到问题。您有一个可以在信号处理程序中调用的安全函数列表,您可以在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;
}