2017-06-14 44 views
1

我已经从GNU库中拿出了这个例子。我想知道他们为什么在设置信号处理函数时第一次调用signal()函数两次,第一次调用main()函数,第二次调用函数本身。使用处理函数内部的signal()函数

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

/* This flag controls termination of the main loop. */ 
volatile sig_atomic_t keep_going = 1; 

/* The signal handler just clears the flag and re-enables itself. */ 
void 
catch_alarm (int sig) 
{ 
    keep_going = 0; 
    signal (sig, catch_alarm); 
} 

void 
do_stuff (void) 
{ 
    puts ("Doing stuff while waiting for alarm...."); 
} 

int 
main (void) 
{ 
    /* Establish a handler for SIGALRM signals. */ 
    signal (SIGALRM, catch_alarm); 

    /* Set an alarm to go off in a little while. */ 
    alarm (2); 

    /* Check the flag once in a while to see when to quit. */ 
    while (keep_going) 
    do_stuff(); 

    return EXIT_SUCCESS; 
} 

现在我的代码......

void createTimer(long freq_nanosec) 
{ 
    timer_t timerid; 
    struct sigevent sev; 
    struct itimerspec timerint; 
    struct sigaction saction; 

    /* Establish handler for timer signal */ 
    saction.sa_flags = 0; 
    saction.sa_handler = OnTimer; 
    sigemptyset(&saction.sa_mask); 
    sigaddset (&saction.sa_mask, SIGIO); 
    if (sigaction(SIGALRM, &saction, NULL) == -1) error("sigaction"); 
    else printf("OnTimer handler created\n"); 

    /* Create real time signal */ 
    sev.sigev_notify = SIGEV_SIGNAL; 
    sev.sigev_signo = SIGALRM; 
    sev.sigev_value.sival_ptr = &timerid; 
    if (timer_create(CLOCKID, &sev, &timerid) == -1) error("timer_create"); 
    else printf("timer ID is 0x%lx\n", (long) timerid); 

    /* Arm the timer */ 
    timerint.it_value.tv_sec = timerint.it_interval.tv_sec = 
     freq_nanosec/1000000000; 
    timerint.it_value.tv_nsec = timerint.it_interval.tv_nsec = 
     freq_nanosec % 1000000000; 
    if (timer_settime(timerid, 0, &timerint, NULL) == -1)      
       error("timer_settime"); 
    else printf("Timer armed\n"); 
} 

回答

0

man pagesignal,我们看到,当信号到达:

第一任一配置重置为SIG_DFL,或信号被阻塞(见下面的可移植性),然后用参数signum调用处理程序。

所以在信号到达后,其他信号将恢复到默认行为。在您的示例代码中,处理程序选择重新设置信号处理程序,以便进一步处理信号的方式与第一个相同。

这在您找到的代码中的函数catch_alarm的注释中已注明。

+0

这是否意味着每当调用信号处理函数时,该信号动作就会被释放为默认值?这很奇怪,因为当我每次使用计时器信号时都会一次又一次地调用处理程序。而且我不会在处理程序中恢复定时器信号。你能否详细说明一下? –

+0

如何设置定时器信号的处理程序? – mtrw

+0

请在上面找到我的代码 –

0

signal有两种流行版本,不同之处在于调用处理函数时信号的处置是否重置为默认值,以及处理程序执行期间信号是否被阻塞。

standard说这两个行为是实现定义的。第一个代码示例

void 
catch_alarm (int sig) 
{ 
    keep_going = 0; 
    signal (sig, catch_alarm); 
} 

假定当处理程序被调用时,实现可以将信号处置重置为默认值。这就像在处理程序的第一行中调用signal(sig, SIG_DFL)。你几乎从来不想这样做,因为下一次有一个SIGALRM信号进入时,默认动作是程序被杀死。所以处理程序调用signal(sig, catch_alarm)重新建立自己作为处理程序。

你的第二个代码示例

saction.sa_flags = 0; 
saction.sa_handler = OnTimer; 
sigemptyset(&saction.sa_mask); 
sigaddset (&saction.sa_mask, SIGIO); 
if (sigaction(SIGALRM, &saction, NULL) == -1) error("sigaction"); 

使用sigaction,这通常优于signal,因为你可以指定正是你想要的行为。标准说

新的应用程序应该使用sigaction()而不是signal()。

.sa_flagsSA_RESETHAND标志,信号的配置重置为默认的处理程序启动时,就像在signal(一版)。

但在你的情况,该标志是关闭的,因为你设置.sa_flags为0,所以你不需要编写任何代码来重新建立处理。

+0

感谢您的详细解答。第一部分我还是不明白。如果已将默认操作设置为处理程序调用重置为默认值,则已将处理程序调用保持为默认值为什么需要专门调用catch_alarm来重置已经存在的东西。或者我想念什么? 另外我认为SA_RESETHAND是一个默认的标志,不是吗? –

+0

通过“重置信号处置为默认值”,我的意思是“做同样的信号(sig,SIG_DFL)'”。对于包括SIGALRM在内的大多数信号来说,这意味着下次信号被程序接收到时,程序将被终止。 'sigaction'没有默认标志。每次都必须将'sa_flags'成员传递给它。如果您指定了一个信号处理程序,那么当“SA_RESETHAND”处于打开状态时,sigaction的行为会有所不同。 –