如何围绕特定进程发送信号?

How to send a signal around specific processes?

我正在尝试围绕进程循环发送信号一定次数。我的第一个参数代表我希望创建的进程数。我的第二个只是一个占位符,我目前正在启动为 0。我的第三个是我想要传递此信号的次数。我设计的流程具有这样的关系:Parent->child1, child1->child2,child2->child3....等等。我只是想弄清楚 C,我对为什么我的代码中途停止感到困惑。它运行一两次迭代,然后停止。有人可以解释为什么吗?

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int startProcess;
int N;
int numOfCycles;
sigset_t killSet;



void myHandler1 () {
  if(N >= 2 && numOfCycles > 0) {
    printf("N=%d, numOfCycles=%d, Signal caught. PID = %d\n",N,numOfCycles,getpid());
    numOfCycles--;
    kill((getpid()+1),SIGUSR1);
  }
  else if(N >= 2 && numOfCycles == 0) {
    exit(1);
  }
  else if(N == 1 && numOfCycles > 0) {
    printf("N=%d, numOfCycles=%d, Signal caught. PID = %d\n",N,numOfCycles,getpid());
    numOfCycles--;
    kill(startProcess,SIGUSR1);
  }
  else if(N == 1 && numOfCycles == 0) {
    exit(1);
  }
  else {
    printf("Cycle Complete\n");
    exit(1);
  }
}

void main(int arg, char ** argv) {

  struct sigaction temp, vitas, arctic;
  sigemptyset(&killSet);
  sigaddset(&killSet,SIGUSR1);
  N = atoi(argv[1]);
  pid_t process1;
  startProcess = atoi(argv[2]);
  numOfCycles = atoi(argv[3]);

  temp.sa_handler = myHandler1;
  temp.sa_flags = SA_RESTART;

  // vitas.sa_handler = myHandler2;
  // vitas.sa_flags = SA_NODEFER;
  //
  // arctic.sa_handler = myHandler3;
  // arctic.sa_flags = SA_RESTART;

  sigaction(SIGUSR1, &temp, NULL);

  if (N > 1 ) {

    process1 = fork();
    if(process1 == 0) {
      if(N > 2) {
        printf("I am a child with PID=%d, PPID=%d, N =%d\n",getpid(),getppid(),N);
      }
      N--;
      char narg = N+'0';
      char *pnarg = &narg;

      if(startProcess == 0) {
        char nstartProcess[6];
        startProcess=getppid();
        sprintf(nstartProcess,"%d",startProcess);
        char *pstartProcess = &nstartProcess[0];
        execl("circle",argv[0],pnarg,pstartProcess,argv[3],NULL);
      }

      else{
        if(N == 1){
          printf("I am the final child with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
          printf("\nSignal Passing start\n\n");
          kill(startProcess, SIGUSR1);

          while(1) {
             sigsuspend(&killSet);
          }

        }
        else {
          execl("circle",argv[0],pnarg,argv[2],argv[3],NULL);
        }
      }

    }

    else {
      printf("I am a parent with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
      wait(NULL);
      while(1) {
         sigsuspend(&killSet);
      }

    }
  }
}

stalls out. Can someone explain why?

final child 没有从 sigsuspend(&killSet) 中唤醒,因为你做了 sigaddset(&killSet,SIGUSR1); - 你似乎认为你必须将要等待的信号添加到集合中,但是相反,给定集合中的信号被阻止传递。因此,只需挂断 sigaddset 电话即可。

添加到@Armali 并且由于 sigset 不是唯一要考虑的点,提供具有更简单处理程序的实现似乎更简单并且避免 fork+exec ,您会在评论中找到解释。我不确定我是否已经理解你的意图,从评论中我猜你正在寻找启动 N processus 最后一个发送 cycles 时间 SIGUSR1 到 parent,每个 child 进程在收到时将 SIGUSR1 发送到他们的 parent。如果我错了请纠正我。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>

int startProcess;
int N;
int numOfCycles;
sigset_t killSet;
// Two counters for the signal handler
int numSignals=0;
int numSignalsTotal=0;



// A simple handler that do nothing else that incrementing values in the process. 
// To use a SIGINFO handler : void myHandler1 (int signal, siginfo_t *info, void *ucontext) {
void myHandler1 (int signal) {
    numSignals++;
    numSignalsTotal++;
}

void main(int arg, char ** argv) {

  struct sigaction temp;
  // As Armali pointed that, here you should not add SIGUSR1 to the sigset a you want 
  // to suspend your process and waiting for it.
  sigemptyset(&killSet);
  N = atoi(argv[1]);
  pid_t process1 = 0;
  startProcess = atoi(argv[2]);
  numOfCycles = atoi(argv[3]);

  // A more complete initialization here, you need to include SIGUSR1 here to associate it
  // to your handler. It seems you doesn't need specific flag like SA_RESTART for it here, 
  // as the handler code doesn't require that.
  temp.sa_handler = myHandler1;
  sigemptyset(&temp.sa_mask);
  sigaddset(&temp.sa_mask,SIGUSR1);
  temp.sa_flags = 0; 
  temp.sa_restorer = NULL;

  // Always check that your function calls return OK. Always.
  if (sigaction(SIGUSR1, &temp, NULL)  != 0) {
    perror("ERR: Failed to set handler ...");
    exit(1);
  }

  // Instead of a mix of fork + exec, it is more simple to keep one process and fork it for each new process.
  // Here, we fork at least once, the parent process will alaways be "PARENT" and have a 0 startProcess,
  // the child processes will fork themselves, the parent process is their and the new forked one will be
  // the next child.
  while (startProcess == 0 || N > 1 ) {
    process1 = fork();
    // To follow the forks : printf("  Fork pid=%d %s %s %s %s\n", process1, argv[0],argv[1],argv[2],argv[3]);
    if(process1 != 0 && startProcess == 0) {
      // If fork() gives us a pid and we have startProcess 0, then we are the "PARENT" process.
      // The only thing we have to do is waiting for signals from our childs, if there is childs.
      printf("  I am a parent with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
      // If there's no child, we simply quit the loop.
      if (N == 1) break;
      // For reference, "PARENT" output what is its first child.
      printf("     -> child = %d\n" , process1);
      // Here, while we have cycles, we wait for signals
      while(numOfCycles > 0) {
        // If you use a handler, you need to operate synchronously with it, so here we block SIGUSR1 as we will wait for it.
        if (sigprocmask(SIG_BLOCK, &temp.sa_mask, NULL) != 0) {
          perror("ERR: Failed to block usr1...");
          exit(1);
        } 
        // We wait for a SIGUSR1.
        sigsuspend(&killSet);
        // We have a SIGUSR1, we unblock SIGUSR1 as we will modify numSignals and don't want handler doing that at the same time.
        if (sigprocmask(SIG_UNBLOCK, &temp.sa_mask, NULL) != 0) {
          perror("ERR: Failed to unblock usr1...");
          exit(1);
        } 
        // We output on STDOUT that "PARENT" caught a SIGUSR1, we should have the number of signals received in numSignals
        printf("  ===> PARENT : N=%d, numOfCycles=%d, Signal caught. PID = %d, count = %d\n",N,numOfCycles,getpid(), numSignals);
        // We substract that from our cycles and reset numSignals
        numOfCycles-=numSignals; numSignals=0;
      }
      // If we are here, no more cycles, so we output on STDOUT a summary of "PARENT" processing
      printf("  PARENT : N=%d, PID = %d, total = %d\n",N,getpid(), numSignalsTotal);
      // Before leaving, "PARENT" should wait for its potential remaining childs to exit
      printf("  PARENT waiting for child to terminate.\n");
      if (wait(NULL) == -1) {
        perror("ERR: PARENt failed to wait for childs termination...");
        exit(1);
      }
      // We quit the loop, so we exit
      break;
    } else { 
       // If only one process (N=1) was asked, nothing more to do for the fork, we quit the loop then exit
      if (N == 1 && startProcess == 0) break;
      // We get the parent process pid in startProcess, as we are a child process
      startProcess = getppid();
      // If we are the forked process, we decrement N, it will "our" N as child
      if (N > 1 && process1 == 0) {
        N--;
      }
      if(process1 == 0) {
        // If we are the forked process, we simply state our identity on STDOUT, the final child should be the one with N=1
        if (N > 1) {
          printf("  I am a child with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
        } else {
          printf("  I am the final child with PID=%d, PPID=%d, N =%d, startProcess=%d\n",getpid(),getppid(),N,startProcess);
        }
        if (N == 1) {
          // If we are the final child, we simply have to send SIGUSR1 signals to our parent process for the number of cycles,
          // after that we simply quit the loop and exit, no more forks are required.
          while(numOfCycles > 0) {
            printf("\n  Cycle %d, first Signal start to %d\n\n", numOfCycles--, startProcess);
            if (kill(startProcess, SIGUSR1) != 0) {
              perror("ERR: LAST CHILD : Failed to send USR1...");
              exit(1);
            }
          }
          break;
        }
        // Note that if we are NOT the final child, we have to go through the loop to fork a new process for the next child.
      } else {
        if (N > 1) {
          // If we are here, we are the forked process for a child and we are not the final child.
          // So we do our payload, waiting for SIGUSR1 signals from our child and sending SIGUSR1 to our parent for the
          // required number of cycles.
          while(numOfCycles > 0) {
            // If you use a handle, you need to operate synchronously with it, so here we block SIGUSR1 as we will wait for it.
            if (sigprocmask(SIG_BLOCK, &temp.sa_mask, NULL) != 0) {
              perror("ERR: Failed to block usr1...");
              exit(1);
            } 
            // We wait for a SIGUSR1.
            sigsuspend(&killSet);
            // We have a SIGUSR1, we unblock SIGUSR1 as we will modify numSignals and don't want handler doing that at the same time.
            if (sigprocmask(SIG_UNBLOCK, &temp.sa_mask, NULL) != 0) {
              perror("ERR: Failed to unblock usr1...");
              exit(1);
            } 
            // We output on STDOUT that this child caught a SIGUSR1, we should have the number of signals received in numSignals
            printf("  ===> CHILD: N=%d, numOfCycles=%d, Signal caught. PID = %d, count = %d\n",N,numOfCycles,getpid(), numSignals);
            // For each receveid signals from our child, we send one to our parent
            while (numSignals > 0) {
              printf("\n  Signal Passing start to %d\n\n", startProcess);
              if (kill(startProcess, SIGUSR1) != 0) {
                  perror("ERR: CHILD : Failed to send USR1...");
                  exit(1);
              }
              // We decrement accordingly our number of cycles and signals
              numOfCycles--;
              numSignals--;
            }
          }
          // If we are here, no more cycles, so we output on STDOUT a summary of the child processing
          printf("  CHILD : N=%d, PID = %d, total = %d\n",N,getpid(), numSignalsTotal);
          // We quit the loop, so we exit, then terminate our child
          break;
        }
      }
    }
  }
  // We should always restore signal handler to default when we exit.
  temp.sa_handler = SIG_DFL;
  sigemptyset(&temp.sa_mask);
  sigaddset(&temp.sa_mask,SIGUSR1);
  temp.sa_flags = 0; 
  temp.sa_restorer = NULL;

  // Always check that your function calls return OK. Always.
  if (sigaction(SIGUSR1, &temp, NULL)  != 0) {
    perror("ERR: Failed to restore SIGUSR1 ...");
    exit(1);
  }
  // We output  on STDOUT the related PID at each process termination.
  printf("  End of PID %d\n",getpid());
}