web-dev-qa-db-ja.com

C:SIGALRM-毎秒メッセージを表示するアラーム

したがって、毎秒「まだ機能しています...」というメッセージを表示するためにアラームを呼び出そうとしています。 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.

ループが始まるとすぐそこにあります。しかし、プログラムは「まだ機能しています...」メッセージを出力しません。何を間違っているのですか?ありがとう、verありがたいです。

8
vivav

シグナルハンドラーは、「ビジネスロジック」を含むことや、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&Rの一部であったことを思い出します。したがって、移植性に関しては、別のPOSIX機能を使用することもできます。

15
Potatoswatter

signalおよびalarmへの呼び出しをループの直前に移動します。高速でalarmを繰り返し呼び出すと、その時点から1秒以内にアラームがリセットされ続けるため、その秒の終わりに到達することはありません。

例えば:

#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;
}
4
ooga

alarm()呼び出しは、1回限りの信号用です。

アラームを繰り返すには、信号が発生するたびに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()を移動します。

「他の仕事」のコメントは、あなたの他の仕事が1秒を超えないことを前提としています。

並列処理が必要な場合、特定のスレッドでアラーム信号を取得することは可能ですが、他のタイマーが必要な場合は複雑になる可能性があります(つまり、alarm()タイマーを他のタイマーと簡単に共有することはできません)関数。)

追伸他の人が述べたように、シグナルハンドラ内でprintf()を実行することは、まったく良い考えではありません。

alarm()main()内でリセットされ、1秒後に最初のメッセージが表示され、ループが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秒強です。他のループはその点でより優れていますが、それでも歪んでいます。 perfect(はるかに優れた)タイマーの場合、poll()関数は、次に起動するタイミングを指定できるため、はるかに優れています。 poll()は、タイマーでのみ使用できます。私の スナップライブラリ はその機能を使用します(ファイルの下部近くにあるrun()関数を探します)。

US Naval Academyからもいくつかの アラームおよびその他の信号コードサンプル があります。

1
Alexis Wilke

alarm()を2回呼び出さないでください。コールバックを開始するためにmain()で1回呼び出してから、display_message()で1回呼び出すだけです。 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...
1
mbornet