2011-08-11 10 views
4

これはちょっと長いです...私はLinux 2.6.33、gcc 4.4.4です。O_ASYNCの生成が停止するSIGIO

名前付きパイプを作成し、それを読み取って特定の文字列を見ると、FIFOを取り除き、それ自体を再実行する小さなプログラムを作成しました。コンパイルされ、バックグラウンドで実行すると

#include<unistd.h> 
#include<fcntl.h> 
#include<signal.h> 
#include<sys/types.h> 
#include<sys/stat.h> 
int fifo; 
#define put(x) write(1, x, (sizeof x)-1) 
void reader(int a) 
{ 
     char buf[26]; 
     int n; 
     while((n=read(fifo, buf, 25))>0){ 
      buf[25] = '\0'; 
      if(!strncmp(buf, "moo", 3)){ 
        put("exec()-ing\n"); 
        close(fifo); 
        unlink("lefifo"); 
        execl("/home/dave/a.out", "a.out", 0); 
      } 
      write(1, buf, n); 
     } 
} 

main() 
{ 
     signal(SIGIO, reader); 
     mknod("lefifo", 0600|S_IFIFO,0); 
     fifo = open("lefifo", O_RDONLY|O_NONBLOCK); 
     fcntl(fifo, F_SETOWN, getpid()); 
     fcntl(fifo, F_SETFL, O_ASYNC); 

     for(;;) 
      pause(); 
} 

は、私がlefifoにエコーすることができ、私は「MOO」で始まる文字列を入力するまでは、期待通りに動作します。次の例セッション:FIFOを作る支障がない、

execve("./a.out", ["./a.out"], [/* 36 vars */]) = 0 
mknod("lefifo", S_IFIFO|0600)   = 0 
open("lefifo", O_RDONLY|O_NONBLOCK)  = 3 
getpid()        = 3945 
fcntl(3, F_SETOWN, 3945)    = 0 
fcntl(3, F_SETFL, O_RDONLY|O_ASYNC)  = 0 
pause()         = ? ERESTARTNOHAND (To be restarted) 
--- SIGIO (I/O possible) @ 0 (0) --- 
read(3, "klar\n"..., 25)    = 5 
write(1, "klar\n"..., 5)    = 5 
read(3, ""..., 25)      = 0 
sigreturn()        = ? (mask now []) 
pause()         = ? ERESTARTNOHAND (To be restarted) 
--- SIGIO (I/O possible) @ 0 (0) --- 
read(3, "moo\n"..., 25)     = 4 
write(1, "exec()-ing\n"..., 13)   = 13 
close(3)        = 0 
unlink("lefifo")      = 0 
execve("/home/dave/a.out", ["a.out"], [/* 36 vars */]) = 0 
mknod("lefifo", S_IFIFO|0600)   = 0 
open("lefifo", O_RDONLY|O_NONBLOCK)  = 3 
getpid()        = 3945 
fcntl(3, F_SETOWN, 3945)    = 0 
fcntl(3, F_SETFL, O_RDONLY|O_ASYNC)  = 0 
pause()         = ? ERESTARTNOHAND (To be restarted) 
--- SIGTERM (Terminated) @ 0 (0) --- 

は、あなたが見ることができるように、最初の頃、​​およびSIGIOが生成されます。

$ gcc fifo.c 
$ ./a.out& 
$ echo klar > lefifo 
klar 
$ echo moo > lefifo 
exec()-ing 
$ echo klar2 > lefifo 
$ echo where did you go > lefifo 
$ echo moo > lefifo 
$ pkill a.out 

は、このトレースを(いくつかの脂肪をトリミング)を生成しますうまくいく。 exec()の後に新しいFIFOは信号を生成しません。古いものは正常終了を示し、正常に削除されたようです。

なぜこのように振舞うのか、私は非常に困惑しています。何か案は?

+0

一般に、 'sigaction'の使用を検討する必要があります。より複雑なAPIですが、これらのセマンティクスのすべてが明確に定義されており、ドキュメンテーションで話をしています。 – Omnifarious

答えて

5

signal()でシグナルハンドラをインストールすると、デフォルトの設定では、glibcはBSD信号の意味を与えます。シグナルハンドラの実行中にシグナルがブロックされ、シグナルハンドラが実行されている間はブロックされ、シグナルが返されるとブロック解除されます。

exec()をシグナルハンドラから呼び出すと、シグナルハンドラが返されないため、SIGIOはブロックされたままです。プロセスシグナルマスクはexec()に継承されているため、プロセスの新しいインスタンスではブロックされたままです。

を使用してSIGIOのブロックを明示的にブロック解除してからmain()にすると、その後の動作になるはずです。

3
シグナルハンドラがシグナルハンドラは、シグナルハンドラが終了するexecuted.Onceなるまで信号が遮断されると呼ばれる

、sigreturn()はシグナルのブロックを解除するために呼び出されます。あなたはあなたの仕事の場面で見ることができます。しかし、 'moo'と入力すると、シグナルハンドラからexeclを呼び出すため、execl()は戻ってこないので、シグナルハンドラは戻りません。シグナルハンドラが返されない場合、sigreturn()は呼び出されず、シグナルはブロックリストから削除されません。

あなたは猫の/ proc //状態を用いて信号の状態を見ることができます。プログラムを実行して/ proc // statusを見ると、sigioが保留中であり、ブロックされていることがわかります。

Linuxカーネルの記事については、www.rulingminds.comを参照してください。

1

私はexecl(またはその他の処理)がシグナルハンドラの外に移動する必要があることを示唆しています。あなたはシグナルハンドラにフラグを設定して、それをあなたにポーリングすることができますmain

関連する問題