2

インターネットで以下のコードを見つけました。とにかく、Linuxタイマーがどのように機能するかを理解しようとしていますcounter1 の値、そこにロックが必要ですか?

// timertst1.c: Simple timer demo program. Use cross-gcc 
// Vers. 1.00 - 21.Oct. 2002
// k.d.walter@t-online.de

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>

// This is a timer handler.
int counter1 = 0;
void timerHandler (int signum)
{
   printf ("timerHandler: counter= %d\n", counter1++);
   fflush (stdout);
}

// This is the one and only main function.

int main (void)
{
   struct sigaction sa;
   struct itimerval timer;

   // Install the timer handler...

   memset (&sa, 0, sizeof (sa));
   sa.sa_handler= &timerHandler;
   sigaction (SIGALRM, &sa, NULL);

   // Configure the timer to expire every 100 msec...

   timer.it_value.tv_sec= 0;            // First timeout
   timer.it_value.tv_usec=  500000;
   timer.it_interval.tv_sec= 0;         // Interval
   timer.it_interval.tv_usec= 500000;

   // Start timer...

   setitimer (ITIMER_REAL, &timer, NULL);   setitimer (ITIMER_REAL, &timer, NULL);
   // Do noting...

   while (1) {
       printf("i'm here waiting to be interuppted = %d\n",counter1);
       //some work;
       counter1++;
       //some other work;
   }
}
4

2 に答える 2

2

sig_atomic_tシグナルハンドラからプログラムの残りの部分と安全に通信するために使用することも検討できます。ただし、値の読み取り、インクリメント、および保存はアトミック操作ではないため、投稿した例ではこれは機能しません。

ここに、説明している GLIBC マニュアルからの抜粋がありますsig_atomic_t

変数へのアクセスの中断に関する不確実性を回避するために、アクセスが常にアトミックである特定のデータ型を使用できます: sig_atomic_t。このデータ型の読み取りと書き込みは、1 つの命令で行われることが保証されているため、ハンドラーがアクセスの「途中」で実行されることはありません。

sig_atomic_t 型は常に整数データ型ですが、それがどれで、何ビット含まれているかは、マシンによって異なる場合があります。

— データ型: sig_atomic_t これは整数データ型です。このタイプのオブジェクトは、常にアトミックにアクセスされます。

実際には、int はアトミックであると想定できます。ポインター型はアトミックであると想定することもできます。それはとても便利です。これらの前提は両方とも、GNU C ライブラリーがサポートするすべてのマシンと、私たちが知っているすべての POSIX システムに当てはまります。

そして、ここに使用例があります:

 #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;
 }
于 2012-06-16T21:16:28.313 に答える
1

シグナルハンドラは危険です。OS は、実行中のプログラムに割り込み、ハンドラを実行します。ハンドラーが戻ると、OS はプログラムを元の状態に戻します。

counter1++ロード、インクリメント、ストアにコンパイルされているとしましょう。ロードとストアの間で割り込みが発生した場合、一連の命令はロード、ロード、インクリメント、インクリメント、ストア、ストアになります。変数は 2 ではなく 1 増加します。災害!おっしゃるとおり、これは従来のマルチスレッドの問題のように見えます。

ロックを追加するとどうなりますか? ロード、インクリメント、ストアの代わりに、ロック、ロード、インクリメント、ストア、ロック解除を取得します。しかし、ロックとロック解除の間に信号が発生すると、OS はすぐにハンドラーにジャンプします。コードを最初にロック解除することはできません。ハンドラーがロックしようとすると、main() がまだロックを保持しているため、デッドロックします。災害!

安全な方法は、シグナル ハンドラーを作成して、何を実行する必要があるかを記録し、別の場所で実際にそれを処理するコードを作成することです。しかし、それも必ずしも単純ではありません。シグナル ハンドラ自体が中断された場合はどうなるでしょうか。この特定のケースでは問題ではありませんが、うさぎの穴の深さの例を検討する価値があります。正しいシグナルハンドラを書くのは難しいです。いくつかのトリックがあります。たとえば、シグナル ハンドラをブロックできます。しかし、それはまだ難しいです。

于 2012-06-16T18:04:45.543 に答える