2016-10-13 3 views
2

私は信号で遊んでいます:特にSIGSTOPSIGCONTです。 ここに書いたテストプログラムです。この考え方は、N + 1 プロセスのチェーン(メインプロセスを含む)を作成することです。それぞれが子供が止まるのを待ってから、 そのものを止めなければなりません。主なプロセスは、子供が を停止したときに目を覚まさなければなりません。SIGSTOP/SIGCONT POSIXの振る舞い

これを行うには、f関数がプロセスチェーンを再帰的に作成します。 の各プロセスは、自身を直接停止する子の の子孫を除いて、SIGCHLDシグナルでsigsuspendを使用します。その子が停止した場合、 のプロセスはSIGCHLDシグナルを受信し、そのターンで停止することができます。 メインプロセスがSIGCHLDシグナルを受信すると、すべての プロセスが停止状態にあることを意味するので、シグナルにSIGCONTシグナルを送信します。各プロセスはSIGCONTを自身の子に送信し、最後に終了した最後の子から を除いて終了します。

私はそれを明確にしようとしました:リターンコードテストを削除し、 コメントを書きました。

プログラムを実行すると、すべてが正常であるようですが、SIGCONT チェーンです。プロセスの中には目を覚ますものもあれば、すべてではないものもあります。 実行中のプログラム(例:ps)を見ると、すべてがうまくいくように見えます:no プロセスをブロックしました。この プログラムで何が間違っているのか分かりません。どんな助けやヒントも大歓迎です。

ここにサンプルトレースがあります。ご覧のとおり、「フォークチェーン」はうまくいっており、プロセスはSIGCHLDに停止しています。そして、最後の子が生まれて停止します。これは、各プロセスが停止するため、親に「SIGCHLDチェーン」を作成します。メインプロセスはSIGCHLDが通知されますと、それはあなたがこのチェーンが完全でないことに気づくことができ目覚めと順番になど、自身の子供にSIGCONTを送信しますれ、その子にSIGCONTを送信します。

$ ./bin/trycont 
n pid  log 
0 6257 "suspending on SIGCHLD" 
1 6258 "suspending on SIGCHLD" 
2 6259 "suspending on SIGCHLD" 
3 6260 "suspending on SIGCHLD" 
4 6261 "suspending on SIGCHLD" 
5 6262 "last child - stopping" 
4 6261 "got SIGCHLD" 
4 6261 "stopping" 
3 6260 "got SIGCHLD" 
3 6260 "stopping" 
2 6259 "got SIGCHLD" 
2 6259 "stopping" 
1 6258 "got SIGCHLD" 
1 6258 "stopping" 
0 6257 "got SIGCHLD" 
0 6257 "sending SIGCONT to 6258" 
1 6258 "awakened - sending SIGCONT to 6259" 
2 6259 "awakened - sending SIGCONT to 6260" 
# <- not the expected trace 

ここにありますプログラム:あなたが必要な場合はsrc/trycont.c

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

/* number of created processes with fork 
*/ 
#define N 5 

#define printHeader() printf("n\tpid\tlog\n"); 
#define printMsg(i, p, str, ...) printf("%d\t%d\t" #str "\n", i, p, ##__VA_ARGS__) 

void f(int n); 
void handler(int sig); 

sigset_t set; 
struct sigaction action; 

int main(int argc, char *argv[]) 
{ 
    /* mask SIGCHLD 
    */ 
    sigemptyset(&set); 
    sigaddset(&set, SIGCHLD); 
    sigprocmask(SIG_SETMASK, &set, NULL); 

    /* handler will be called when SIGCHLD is sent to the process 
    * during the handler, SIGCHLD will be masked (sa_mask) 
    */ 
    action.sa_mask = set; 
    action.sa_handler = handler; 
    action.sa_flags = 0; 

    /* SIGCHLD will trigger action 
    */ 
    sigaction(SIGCHLD, &action, NULL); 

    /* start 
    */ 
    printHeader(); 
    f(N); 

    exit(EXIT_SUCCESS); 
} 

void f(int n) 
{ 
    pid_t p, pc; 
    int myIndex; 

    myIndex = N - n; 
    p = getpid(); 

    if (n == 0) 
    { 
     /* last child 
     */ 
     printMsg(myIndex, p, "last child - stopping"); 
     kill(p, SIGSTOP); 
     printMsg(myIndex, p, "END REACHED"); 
     exit(EXIT_SUCCESS); 
    } 

    pc = fork(); 

    if (pc == 0) 
    { 
     /* recursion 
     */ 
     f(n - 1); 

     /* never reached 
     * because of exit 
     */ 
    } 

    /* father 
    */ 

    /* suspending on SIGCHLD 
    * need to unmask the signal 
    * and suspend 
    */ 
    printMsg(myIndex, p, "suspending on SIGCHLD"); 

    sigfillset(&set); 
    sigdelset(&set, SIGCHLD); 
    sigsuspend(&set); 

    printMsg(myIndex, p, "got SIGCHLD"); 

    if (n < N) 
    { 
     /* child process 
     * but not last 
     */ 
     printMsg(myIndex, p, "stopping"); 
     kill(p, SIGSTOP); 

     printMsg(myIndex, p, "awakened - sending SIGCONT to %d", pc); 
     kill(pc, SIGCONT); 
    } 
    else 
    { 
     /* root process 
     */ 
     printMsg(myIndex, p, "sending SIGCONT to %d", pc); 
     kill(pc, SIGCONT); 
    } 

    exit(EXIT_SUCCESS); 
} 

void handler(int sig) 
{ 
    switch (sig) 
    { 
    case SIGCHLD: 
     /* when the process received SIGCHLD 
     * we can ignore upcoming SIGCHLD 
     */ 
     action.sa_handler = SIG_IGN; 
     sigaction(SIGCHLD, &action, NULL); 
     break; 
    default: 
     break; 
    } 
} 

ここではMakefileのです:

CC=gcc 
DEFINES=-D_POSIX_C_SOURCE 
STD=-std=c11 -Wall -Werror 
OPTS=-O2 
CFLAGS=$(STD) $(DEFINES) $(OPTS) -g 
LDFLAGS= 

SRC=src 
OBJ=obj 
BIN=bin 

DIRS=$(BIN) $(OBJ) 

.PHONY: mkdirs clean distclean 

all: mkdirs $(BIN)/trycont 

$(BIN)/%: $(OBJ)/%.o 
    $(CC) $(CFLAGS) $(LDFLAGS) -o [email protected] $< 

$(OBJ)/%.o: $(SRC)/%.c 
    $(CC) $(CFLAGS) -c -o [email protected] $< 

mkdirs: 
    - mkdir $(DIRS) 

clean: 
    rm -vf -- $(OBJ)/*.o 

distclean: clean 
    rm -vfr -- $(DIRS) 

答えて

2

あなたデスの一部(全て?)第1のプロセスが終了したときに、システム生成のSIGHUPが死んでいます。

これは特定の状況下ではexpected POSIX behaviorです。

シェルからルートプロセスを開始すると、プロセスグループリーダーであり、その子孫はそのグループのメンバーです。そのリーダーが終了すると、プロセスグループは孤児になります。システムが、メンバが停止している新しく孤立したプロセスグループを検出すると、プロセスグループのすべてのメンバにSIGHUPとSIGCONTが送信されます。

リーダーが終了するとあなたの子孫プロセスの一部が停止するので、誰もがSIGHUPを受け取ってからSIGCONTを受け取ります.SIGCONTは実用的な意味でSIGHUPで死んでいます。

正確にどの子孫がまだ停止している(またはちょうど嬉しくてexit()に向かって前進している)かはタイミング競争です。私のシステムでは、リーダーはすぐに終了し、子孫の誰も何も印刷できません。

+0

興味深いことに、ありがとうございます。この場合、 'SIGHUP'を扱うだけでは不十分です。' SIGCONT'は停止した各プロセスに配信され、実行を再開します。各プロセスがその子を待つことは賢明です。ありがとう@ピクロウ。 – friedrich