6

問題は次のとおりです。このプログラムはstdinから入力を受け取り、挿入されたバイト数をカウントする必要があります。SIGUSR1信号はメインプログラムを停止し、SIGUSR1を送信したときにコピーされたバイト数をファイル標準エラーに出力します。

それが私の先生が私にこれをすることを望んでいる方法です:1つのターミナルで実行します

cat /dev/zero | ./cpinout | cat >/dev/null

一方、2番目の端末から信号を送信します

kill -USR1 xxxx

ここで、xxxxはcpinoutのpidです。

以前のコードを更新しました:

/* cpinout.c */

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

#define BUF_SIZE 1024   

volatile sig_atomic_t countbyte = 0;
volatile sig_atomic_t sigcount = 0;

/* my_handler: gestore di signal */
static void sighandler(int signum) {
    if(sigcount != 0)
        fprintf(stderr, "Interrupted after %d byte.\n", sigcount);
    sigcount = countbyte;    
}

int main(void) {
  
    int c;
    char buffer[BUF_SIZE];
    struct sigaction action;

    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    action.sa_handler = sighandler;
    if(sigaction(SIGUSR1, &action, NULL) == -1) {
        fprintf(stderr, "sigusr: sigaction\n");
        exit(1);
    }
    while( c=getc(stdin) != EOF ) {
        countbyte++;
        fputc(c, stdout);
    }
    return(0);
}
4

2 に答える 2

4

シグナルは、C89およびPOSIX7標準に従ってのみ変数を書き込むことができ ますvolatile sig_atomic_t

シグナルハンドラが、volatile sig_atomic_tとして宣言されたオブジェクトに値を割り当てる以外の静的ストレージ期間を持つ、errno[OptionEnd]以外のオブジェクト[CX][OptionStart]を参照する場合、動作は未定義です。

多くの場合、実装はより多くの機能を提供しますが、不揮発性グローバル変数またはprintfを使用することがあなたによって提供されるものであるとは思えません。

于 2013-03-19T14:21:56.310 に答える
2


編集

コメントで、コマンドを次のように実行していると述べました。

cat /dev/zero | ./namefile | cat >/dev/null

動作は実際には問題ありません。/dev/zeroプログラムに送信されているゼロの無限のストリームです。したがって、それらを非常に迅速にカウントアップしています。割り込むと止まり、たくさん残ってしまいます。


この問題は、グローバル変数の更新中にシグナルハンドラーが呼び出される可能性があるという事実に関連している可能性があります(これに複数の命令が必要な場合)。ただし、GNUのドキュメントにはint、POSIXシステムでは常にアトミックであると想定しても安全であると記載されています。

私が考えることができる他の唯一の可能性は、ハンドラーfputcを使用してループ内で呼び出していることですprintf(ただし、プログラムによって呼び出されていない場合は、ハンドラーを呼び出しても安全printfです)。ループから削除fputcして、問題が解決するかどうかを確認してください。

編集:

これは問題を説明しているようです。これは、シグナルハンドラー内から安全に呼び出すことができる関数の種類に関連しています。

内部の簿記に静的データ構造を使用している場合、関数は再入可能ではない可能性もあります。このような関数の最も明白な例は、バッファリングされたI / Oの内部データ構造を更新するstdioライブラリのメンバー(printf()、scanf()など)です。したがって、シグナルハンドラー内からprintf()を使用する場合、printf()または別のstdioの呼び出しを実行している最中にハンドラーがメインプログラムに割り込むと、奇妙な出力が表示されたり、プログラムのクラッシュやデータの破損が発生したりすることがあります。働き。(Linuxプログラミングインターフェイス

あなたのプログラムはstdio関数を中断しています。これはこれに完全に適合しているようです。


別のアプローチは次のとおりです。

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

int countbyte = 0;  // for main program
int sigcount = 0;   // for signal handler

/* my_handler: signal handler */
static void sighandler(int signum)
{
   sigcount = countbyte;
}

int main(void)
{ 
   int c;
   struct sigaction sigact;

   sigemptyset(&sigact.sa_mask);
   sigact.sa_flags = 0;
   sigact.sa_handler = sighandler;
   sigaction(SIGUSR1, &sigact, NULL);
   while ((c = getc(stdin)) != EOF) {
      countbyte++;
      fputc(c, stdout);
   }
   if (sigcount != 0) {
      printf("Interrupted after %d bytes\n", sigcount);
   }

   return 0;
}
于 2013-03-19T14:26:44.797 に答える