2017-05-15 5 views
0

私は2つのプロセス間の同期問題を解決しなければならず、信号でそれをやらなければなりません。このプロセスでSIGUSR1が処理されないのはなぜですか?

2つのプロセスで一定量の作業を行い、もう一方のプロセスに信号を送る必要があります。これは無期限に起こります。私のコードで何が起こる

は次のとおりです。

私は停止しないされ、再び親プロセスにシグナルを送り、子プロセスにシグナルを送り、親プロセスへの信号を、送信します、もう行動する。

理想的には、親と子はお互いに無期限に信号を送る必要があります。

int main() { 
    parent_pid = getpid(); 

    pid_t pid = fork(); 
    if (pid < 0) { 
     syserr_quit("fork error"); 
    } else if (pid == 0) { 
     signal(SIGUSR2, child_work); 
     while (1) {} 
    } else { 
     child_pid = pid; 

     signal(SIGUSR1, parent_work); 
     while (1) {} 
    } 
} 

void child_work (int signo) { 
    sleep(1); // fake child work 

    if (kill(parent_pid, SIGUSR1) < 0) syserr_quit("kill error"); 

    // wait for parent signal 
    pause(); 
} 

void parent_work (int signo) { 
    sleep(1); // fake parent work 

    if (kill(child_pid, SIGUSR2) < 0) syserr_quit("kill error"); 

    // wait for child signal 
    pause(); 
} 

はそれがコードの本当に少量だとすべてが私には所定の位置にあると思われるので、間違っているかを把握することはできません。

誰かが自分のマシンでスピンアップしたい場合は、コピー/ペースト後に動作するa full demoがあります。


更新:は、sigaction(3)の代わりに、信号の(2)

いくつかのコメントを1として、私は競合状態を避けるためにsigactionの使用に訴えるとデモでsleepを使用して停止

。 ハンドブックの実行中にシグナル受信がブロックされないようにするには、 SA_NODEFERフラグを設定する必要があります。

コードはまだ動作していませんが、この時間は信号の受信を停止する子のみです。

スニペットがsignalが交換されるのを除いて、上記と同様である - コンソールから確認し、シグナル伝達を開始する

struct sigaction action; 
action.sa_handler = parent/child_work; 
action.sa_flags = SA_NODEFER; 
sigaction(signo, &action, NULL); 

フルコード

と - の両方倍信号を送信します(つまり、kill -SIGUSR1 <parentid>

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

void parent_work(int signo); 
void child_work(int signo); 

void syserr_quit(char *msg); 

pid_t parent_pid; 
pid_t child_pid; 

int main() { 
    parent_pid = getpid(); 
    printf("parent pid: %d\n", parent_pid); 

    pid_t pid = fork(); 
    if (pid < 0) { 
     syserr_quit("fork error"); 
    } else if (pid == 0) { 
     struct sigaction action; 
     action.sa_handler = child_work; 
     action.sa_flags = SA_NODEFER; 
     sigaction(SIGUSR2, &action, NULL); 
     while (1) {} 
    } else { 
     child_pid = pid; 
     printf("child pid: %d\n", pid); 

     struct sigaction action; 
     action.sa_handler = parent_work; 
     action.sa_flags = SA_NODEFER; 
     sigaction(SIGUSR1, &action, NULL); 
     while (1) {} 
    } 
} 

void child_work (int signo) { 
    for (int i = 0; i < 100000000; ++i); // fake child work 

    // signal parent that a write is now possible 
    printf("send signal to %d\n", parent_pid); fflush(stdout); 
    if (kill(parent_pid, SIGUSR1) < 0) syserr_quit("kill error"); 
    printf("signal sent\n"); fflush(stdout); 

    // wait for parent signal 
    pause(); 
} 

void parent_work (int signo) { 
    for (int i = 0; i < 100000000; ++i); // fake child work 

    // signal child that a read is possible 
    printf("send signal to %d\n", child_pid); fflush(stdout); 
    if (kill(child_pid, SIGUSR2) < 0) syserr_quit("kill error"); 
    printf("signal sent\n"); fflush(stdout); 

    // wait for child signal 
    pause(); 
} 

void syserr_quit(char *msg) { 
    perror(msg); 
    exit(EXIT_FAILURE); 
} 

フィン

さてさて、私が正しくsigactionマスクを初期化していないという理由だけで更新されたコードが機能しなかったと思われる男。それはsigemptysetで完了しなければならず、あなたはなぜright hereを見つけることができます。

詳細はthe answerです。あなたのSIGUSR1シグナルハンドラparent_workが呼び出されると

+0

提示したよう、あなたのコードはコンパイルされません。シグナルハンドラは宣言されていない変数を参照し、すべての関数は宣言されていない関数 'syserr_quit()'を呼び出します。これは[mcve]の良いスタートですが、まだそこにはありません。 –

+0

@JohnBollingerそれを読ませてください。その間、私は質問の最後の行に60行の完全なデモを提供しました。 – doplumi

+1

(あなたのコードはコンパイルされないので)あなたの問題に接続されているかどうかは不明ですが、Linuxの 'signal(2)'マニュアルページから引用してください: "* の使用を避けてください:代わりにsigaction(2)を使用してください。 http://man7.org/linux/man-pages/man2/signal.2.html – cdarke

答えて

0

SIGUSR1信号がSIG_DFL(デフォルトのシグナル処理)にマッピングされたか、親プロセス(参照のために無視されますか。signal):

信号が発生し、機能にFUNC点、実装定義と同等であるかどうか:

signal(sig, SIG_DFL); 

が実行または実装は、いくつかの実装定義の集合を防止します現在の信号処理が完了するまで、信号(少なくともsigを含む)が発生するのを防止する。子プロセスは、親プロセスのために再度SIGUSR1信号を生成するとき

、その理由のために再度parent_workシグナルハンドラを呼び出すことはありません。

あなたは、後続の信号もシグナルハンドラによって処理されるようにしたいのであれば、あなたのシグナルハンドラでは、もう一度再登録信号を行う必要があります。これはまだレースのための余地を残していること

void parent_work(int signo) { 
    /* your handler code */ 
    signal(signo, parent_work); 
} 

は注意条件 - すなわち。シグナルハンドラが再登録される前に2番目のSIGUSR1が到着した場合。しかし、実際には、それはあなたのデザインに内在しています。

もちろん、より良い選択肢と一般的なアドバイスがあります。コメントの中にはすでに言及されているものもありますので、興味があればそれらを参照してください。

+1

'signal()'関数によって登録されたシグナルハンドラの振る舞いに固有の実装定義は、ハンドラを登録するために 'sigaction()'を使う代わりに解決されることに注意してください。さらに、このメカニズムにより、ハンドラが(選択されたオプションに応じて)自身を再登録する必要がなくなり、競合状態を回避することが可能になる。 –

0

sigactionマスクを正しく初期化しなかったため、更新されたコードが正しく動作しなかったようです。それはsigemptysetで完了しなければならず、あなたはなぜright hereを見つけることができます。

あなたは、ハンドラの実行中にキャッチすることができるようにしたい信号のための sigactionの正しい初期化は以下のようになり

struct sigaction sa; 
sigemptyset(&sa.sa_mask); 
action.sa = handler; 
action.sa = SA_NODEFER; 
sigaction(signo, &sa, NULL); 

最終的なコード

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

void parent_work(int signo); 
void child_work(int signo); 

void syserr_quit(char *msg); 

pid_t parent_pid; 
pid_t child_pid; 

int main() { 
    parent_pid = getpid(); 
    printf("parent pid: %d\n", parent_pid); 

    pid_t pid = fork(); 
    if (pid < 0) { 
     syserr_quit("fork error"); 
    } else if (pid == 0) { 
     struct sigaction sa; 
     sigemptyset(&sa.sa_mask); 
     sa.sa_handler = child_work; 
     sa.sa_flags = SA_NODEFER; 
     sigaction(SIGUSR2, &sa, NULL); 
     while (1) {} 
    } else { 
     child_pid = pid; 
     printf("child pid: %d\n", pid); 

     struct sigaction sa; 
     sigemptyset(&sa.sa_mask); 
     sa.sa_handler = parent_work; 
     sa.sa_flags = SA_NODEFER; 
     sigaction(SIGUSR1, &sa, NULL); 
     while (1) {} 
    } 
} 

void child_work (int signo) { 
    for (int i = 0; i < 100000000; ++i); // fake child work 

    // signal parent that a write is now possible 
    printf("send signal to %d\n", parent_pid); fflush(stdout); 
    if (kill(parent_pid, SIGUSR1) < 0) syserr_quit("kill error"); 
    printf("signal sent\n"); fflush(stdout); 

    // wait for parent signal 
    pause(); 
} 

void parent_work (int signo) { 
    for (int i = 0; i < 100000000; ++i); // fake child work 

    // signal child that a read is possible 
    printf("send signal to %d\n", child_pid); fflush(stdout); 
    if (kill(child_pid, SIGUSR2) < 0) syserr_quit("kill error"); 
    printf("signal sent\n"); fflush(stdout); 

    // wait for child signal 
    pause(); 
} 

void syserr_quit(char *msg) { 
    perror(msg); 
    exit(EXIT_FAILURE); 
} 
関連する問題