我用叉子叉了一个孩子,我试着让他们同步,这样他们就能打印

child 0
parent 0
child 1
parent 1

不过,我必须使用sigsuspend,这是我目前的代码,我只得到parent suspend。没有孩子的踪迹。
int c=0, receivedP=0, receivedC=0;
sigset_t setParent, setChild;
void handler(int s){
    if(s==SIGUSR1){
        receivedC=1;
        printf("parent --sig1--> child\n");
        c++;
    }
    else{
        receivedP=1;
        printf("child --sig2--> parent\n");
    }
}
void child(){
    sigfillset(&setChild);
    sigdelset(&setChild,SIGUSR1);
    sigdelset(&setChild,SIGINT); //this makes me able to terminate the program at any time
    while(1){
        if(receivedC==0){
            printf("child suspend\n");
            sigsuspend(&setChild);
        }
        receivedC=0;
        printf("child %d\n",c);
        kill(getppid(),SIGUSR2);
    }
}
void parent(pid_t pf){
    sigfillset(&setParent);
    sigdelset(&setParent,SIGUSR2);
    sigdelset(&setParent,SIGINT); //this makes me able to terminate the program at any time
    kill(pf,SIGUSR1);
    while(1){
        if(receivedP==0){
            printf("parent suspend\n");
            sigsuspend(&setParent);
        }
        receivedP=0;
        printf("parent %d\n",c);
        kill(pf,SIGUSR1);
    }
}
int main(){
    signal(SIGUSR1,handler);
    signal(SIGUSR2,handler);
    pid_t p;
    p= fork();
    if(!p)child();
    else parent(p);
    return 0;
}

有人知道是什么原因吗?

最佳答案

我认为你是违反了信号的一个典型问题。

while(1){
    if(receivedP==0){
        printf("parent suspend\n");
        sigsuspend(&setParent);
    }
    receivedP=0;
    printf("parent %d\n",c);
    kill(pf,SIGUSR1);
}

想象一下,如果来自孩子的信号在if(receivedP==0)sigsuspend(&setParent)的指令之间到达,会发生什么。处理程序将执行,并将receivedP设置为1,但主循环不会再次检查它;它将进入sigsuspend并且永远不会出来。
为了安全地使用sigsuspend,您需要在程序不调用sigsuspend时始终阻止您关心的信号。你可以用sigprocmask来完成。还必须确保信号在处理程序执行期间被阻塞,这要求您使用sigaction而不是signal(但无论如何,您都应该这样做,因为signal严重未指定,系统到系统的变化将咬到您的屁股)。
一旦您确保信号只能在sigsuspend期间传递,就不再需要receivedPreceivedC变量;您知道信号已经发生,或者sigsuspend不会返回。(如果您的程序在每个进程中等待的信号不止一个,那么这是不正确的,但在这一点上,事情会变得更加复杂;在它出现之前不要担心它。)
事实上,一旦确定了这一点,就不需要在信号处理程序中做任何事情。计数器变量可以是parentchild的局部变量。在一个信号处理程序中尽可能少的操作总是最好的;C标准的字母允许您几乎什么都不做,而不会冒未定义行为的风险,POSIX只会将其打开一点。(练习:将此程序更改为使用sigwaitinfo以便它根本不需要处理程序函数。)
你程序的修改对我来说是可靠的。我还纠正了一些其他样式问题和小错误:注意parentchild中的循环以不同的顺序执行操作,注意main中的错误检查,注意我只阻塞了SIGUSR1SIGUSR2,因为应该允许其他几个信号终止进程(SIGTERMSIGHUPSIGQUITSIGSEGV,,…),而您不希望维护一个列表。足以阻止程序已为其安装处理程序的信号。
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

static void handler(int unused)
{
}

static void child(sigset_t *ss)
{
  unsigned int c = 0;
  pid_t parent_pid = getppid();

  sigdelset(ss, SIGUSR1);
  for (;;) {
    sigsuspend(ss);
    printf("child %u\n", c++);
    kill(parent_pid, SIGUSR2);
  }
}

static void parent(sigset_t *ss, pid_t child_pid)
{
  unsigned int c = 0;

  sigdelset(ss, SIGUSR2);
  for (;;) {
    printf("parent %u\n", c++);
    kill(child_pid, SIGUSR1);
    sigsuspend(ss);
  }
}

int main(void)
{
  // Ensure line-buffered stdout.
  if (setvbuf(stdout, 0, _IOLBF, 0)) {
    perror("setvbuf");
    return 1;
  }

  // This signal mask is in effect at all times _except_ when sleeping
  // in sigsuspend().  Note that _only_ the signals used for IPC are
  // blocked.  After forking, each process will modify it appropriately
  // for its own use of sigsuspend(); this does not affect the kernel-side
  // copy made by sigprocmask().
  sigset_t ss;
  sigemptyset(&ss);
  sigaddset(&ss, SIGUSR1);
  sigaddset(&ss, SIGUSR2);

  if (sigprocmask(SIG_BLOCK, &ss, 0)) {
    perror("sigprocmask");
    return 1;
  }

  // Always use sigaction(), not signal(); signal() is underspecified.
  // The mask here is the signal mask to use _while the handler is
  // executing_; it should also block both IPC signals.
  struct sigaction sa;
  sa.sa_handler = handler;
  sa.sa_mask    = ss;
  sa.sa_flags   = SA_RESTART;
  if (sigaction(SIGUSR1, &sa, 0) || sigaction(SIGUSR2, &sa, 0)) {
    perror("sigaction");
    return 1;
  }

  pid_t child_pid = fork();
  if (child_pid < 0) {
    perror("fork");
    return 1;
  }

  if (child_pid == 0)
    child(&ss);
  else
    parent(&ss, child_pid);

  // we never get here but the compiler might not know that
  return 0;
}

我建议您一直阅读GNU C Library手册中的section on signal handling;它包含了一些关于安全使用信号的其他有用建议。

09-30 12:03