C:SIGALRM - 每秒显示消息的警报

时间:2014-02-04 02:45:18

标签: c alarm

所以我试图通过报警来显示每秒钟“仍在工作......”的消息。 我包括了signal.h。

在我的主要之外,我有我的功能:(我从不为int声明/定义s)

void display_message(int s);   //Function for alarm set up
void display_message(int s) {
     printf("copyit: Still working...\n" );
     alarm(1);    //for every second
     signal(SIGALRM, display_message);
}

然后,在我的主要

while(1)
{
    signal(SIGALRM, display_message);
    alarm(1);     //Alarm signal every second.

循环开始时就在那里。但该程序从未输出“仍在工作......”的消息。我做错了什么?谢谢,非常感谢。

5 个答案:

答案 0 :(得分:12)

信号处理程序不应包含“业务逻辑”或进行库调用,例如printf。见C11§7.1.4/ 4及其脚注:

  

因此,信号处理程序通常不能调用标准库函数。

所有信号处理程序应该设置一个标志,由非中断代码执行。即使添加了一些I / O其他功能,该程序也能正确运行并且不会有崩溃的风险:

#include <signal.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>

volatile sig_atomic_t print_flag = false;

void handle_alarm( int sig ) {
    print_flag = true;
}

int main() {
    signal( SIGALRM, handle_alarm ); // Install handler first,
    alarm( 1 ); // before scheduling it to be called.

    for (;;) {
        if ( print_flag ) {
            printf( "Hello\n" );
            print_flag = false;
            alarm( 1 );
        }
    }
}

但请注意,旋转循环是一种糟糕的编程方式。此示例使用100%CPU功率,因为​​它从不睡觉。此外alarm似乎没有被C标准定义,尽管POSIX标记它是这样的,我记得它是K&amp; R的一部分。因此,就可移植性而言,您可以使用另一个POSIX工具。

答案 1 :(得分:4)

将调用移至signalalarm,直到您的循环之前。高速反复呼叫alarm会使警报从此时开始重置一秒钟,因此您永远不会达到那一秒的结束!

例如:

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

void display_message(int s) {
     printf("copyit: Still working...\n" );
     alarm(1);    //for every second
     signal(SIGALRM, display_message);
}

int main(void) {
    signal(SIGALRM, display_message);
    alarm(1);
    int n = 0;
    while (1) {
        ++n;
    }
    return 0;
}

答案 2 :(得分:1)

alarm()来电是一次性信号。

要重复闹铃,每次信号发生时都必须再次拨打alarm()

但是,等待下一个SIGALRM的正确方法是使用pause()函数。其他人没有提到的东西(相反,他们有紧密的循环,丑陋!)

话虽如此,通过简单的sleep()调用,您尝试做的事情会更容易:

// print a message every second (simplified version)
for(;;)
{
    printf("My Message\n");
    sleep(1);
}

注意:sleep()函数实际上是使用与alarm()相同的计时器实现的,并且明确提到不应该在同一代码中混合使用这两个函数。

  

sleep(3)可以使用SIGALRM实现;混合调用alarm()sleep(3)是一个坏主意。

(来自Linux man alarm

void alarm_handler(int)
{
    alarm(1);    // recurring alarm
}

int main(int argc, char *argv[])
{
    signal(SIGALRM, alarm_handler);
    alarm(1);

    for(;;)
    {
        printf("My Message\n");
        // ...do other work here if needed...
        pause();
    }

    // not reached (use Ctrl-C to exit)
    return 0;
}

您可以创建变体。例如,如果您希望第一条消息在1秒后发生而不是立即发生,请在pause()之前移动printf()

&#34;其他工作&#34;评论假设您的其他工作不会超过1秒。

如果需要并行工作,可以在特定线程上获取警报信号,但是,如果需要任何其他计时器,这可能会很复杂(即,您无法轻松共享alarm()具有其他功能的计时器。)

P.S。正如其他人所提到的,在信号处理程序中执行printf()根本不是一个好主意。

还有另一个版本alarm()main()内重置,第一条消息在一秒后出现,循环运行60秒(1分钟):

void alarm_handler(int)
{
}

int main(int argc, char *argv[])
{
    signal(SIGALRM, alarm_handler);

    for(int seconds(0); seconds < 60; ++seconds)
    {
        alarm(1);
        // ...do other work here if needed...
        pause();
        printf("My Message\n");
    }

    // reached after 1 minute
    return 0;
}

请注意,使用此方法,将打印邮件的时间将会出现偏差。在重新启动闹钟之前,打印消息的时间会添加到时钟中......因此每次呼叫之间的时间总是超过1秒。另一个循环在这方面更好,但它仍然是倾斜的。对于完美(更好)的计时器,poll()函数要好得多,因为您可以指定下次唤醒的时间。 poll()只能用于计时器。我的Snap library使用该功能(在文件底部附近查找run()函数。)

美国海军学院还有几个Alarm and other signals code samples

答案 3 :(得分:1)

请勿两次调用alarm(),只需在main()中调用一次以启动回调,然后在display_message()中调用一次即可。 在Linux(Debian 7.8)上尝试以下代码:

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

void display_message(int s);   //Function for alarm set up

void display_message(int s)
{
     printf("copyit: Still working...\n" );
     alarm(1);    //for every second
     signal(SIGALRM, display_message);
}

int main()
{
    signal(SIGALRM, display_message);
    alarm(1);     // Initial timeout setting

     while (1) 
     {   
          pause();
     }   
}

结果将是以下结果:

copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...
copyit: Still working...

答案 4 :(得分:-1)

ooga是正确的,你不断重新加载警报,以便它永远不会消失。这有效。我只是在这里放了一个sleep,所以你不要在循环中继续踩自己,但是你可能想要用一些更有用的东西来取代你的目标。

void display_message(int s)
{
     printf("copyit: Still working...\n" );
    // alarm(1);    //for every second
    // signal(SIGALRM, display_message);
}

int main(int argc, char *argv[])
{
    int ret;

    while(1)
    {
        signal(SIGALRM, display_message);
        alarm(1);

        if ((ret = sleep(3)) != 0)
        {
            printf("sleep was interrupted by SIGALRM\n");
        }
    }

    return (0);
}