【问题标题】:process synchronization between a parent and multiple child processes using signals使用信号在父进程和多个子进程之间进行进程同步
【发布时间】:2016-07-31 15:08:09
【问题描述】:

我正在学习如何在 linux 中使用信号。有4个子进程和1个父进程。我的输出应该分为以下几个阶段:

父母收到孩子 1 的信号

父母收到来自孩子 2 的信号

父母收到孩子 3 的信号

父母收到来自孩子 4 的信号

初始化结束

第一阶段开始

孩子 1 收到来自父母的信号

孩子 2 收到来自父母的信号

孩子 3 收到来自父母的信号

孩子 4 收到来自父母的信号

父母收到孩子 1 的信号

父母收到来自孩子 2 的信号

父母收到孩子 3 的信号

父母收到来自孩子 4 的信号

第一阶段结束

第二阶段开始

孩子 1 收到来自父母的信号

孩子 2 收到来自父母的信号

孩子 3 收到来自父母的信号

孩子 4 收到来自父母的信号

我目前正在尝试在第 1 阶段结束之前完成这部分工作,但我正在苦苦挣扎。这是我的代码:

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXCP 3
int ccount=0;
void p_action(int sig){
   if (sig == SIGUSR2){
      printf("\ncontroller(%d): Received signal SIGUSR2 from child   process\n", 
            (int)getpid());
    }
    --ccount;
 }

 void c_action(int sig){
    if (sig == SIGUSR1){
      printf("\ncompute(%d): Received signal SIGUSR1 from parent process %d\n", 
            (int)getpid(), (int)getppid());
    }
    exit(0);
 }

int main(){

pid_t pid[MAXCP], parent_pid;
//int nprocs = 0;
ccount = MAXCP;

static struct sigaction pact, cact[MAXCP];

pact.sa_handler = p_action;
sigaction(SIGUSR2, &pact, NULL);


int count;
for (count = 0; count < MAXCP; count++){
    switch(pid[count] = fork()){
        case -1:
            perror("Fork error");
            exit(1);
        case 0:
            //sleep(1);
            cact[count].sa_handler = c_action;
            sigaction(SIGUSR1, &cact[count], NULL);
            printf("Sending SIGUSR2 to parent");
            kill(getppid(), SIGUSR2);
            pause();
            break;
        default:
            pause();
            //printf("Parent is sleeping");

            while (ccount != MAXCP){
                sleep(60);
            }
            for (ccount = 0; ccount < MAXCP; ccount++)
                kill(pid[count], SIGUSR1);
            break;

    }   
}


return 0;
}

我的输出是这样的:

// When I use the above code
controller(3132): Received signal SIGUSR2 from child process
// When I comment out the pause in the child section
controller(3140): Received signal SIGUSR2 from child process
Sending SIGUSR2 to parent
controller(3141): Received signal SIGUSR2 from child process
Sending SIGUSR2 to parentSending SIGUSR2 to parentSending SIGUSR2 to     parentSending SIGUSR2 to parentSending SIGUSR2 to parent
controller(3142): Received signal SIGUSR2 from child process
^C

感谢您的宝贵时间。

【问题讨论】:

  • --ccount; 未定义,您只能在信号处理程序中使用 volatile sig_atomic_t 对象。在最坏的情况下,您的信号处理程序会修改 ccount != MAXCP 中间的字节或它的任何其他用途,或者同样邪恶的东西。
  • 我放入 volatile sig_atomic_t 和 II 仍然得到相同的结果。
  • 并不意味着作为解决方案,只是指出这是未定义的行为,现在或以后可能会导致整体怪异。
  • 那么我该怎么做才能同步呢?
  • afaik 你不能同步信号:在孩子和父母中顺序发送和接收信号似乎不合适。您甚至无法确定孩子和父母的执行顺序

标签: c linux process signals


【解决方案1】:

您想要使用信号的目的与它们的设计目的并非 100% 匹配。最重要的问题是信号充当硬件中断控制器。如果多个相同值的信号“同时”发生,它们将在接收器中似乎合并为一个信号。在下面的示例中,我们通过在每个孩子中使用具有不同值的 sleep 来解决此问题。

因此,当内核重新调度进程时,它只检查自上次进程处于活动状态以来是否已收到每个信号,并在需要时将它们转发到进程中。它不知道发生了多少次。

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXCP 3
volatile int ccount=0;
void p_action(int sig, siginfo_t *info, void *ptr){
   if (sig == SIGUSR2){
      printf("\ncontroller(%d): Received signal SIGUSR2 from child %d process\n",
            (int)getpid(), (int)info->si_pid);
    }
    --ccount;
 }

 void c_action(int sig){
    if (sig == SIGUSR1){
      printf("\ncompute(%d): Received signal SIGUSR1 from parent process %d\n",
            (int)getpid(), (int)getppid());
    }
 }

int main(){
    int count;
    pid_t pid[MAXCP];
    ccount = MAXCP;

    struct sigaction pact;

    pact.sa_flags = SA_SIGINFO | SA_RESTART; /* if signal comes on top of each other, we need this */
    pact.sa_sigaction = p_action;
    sigaction(SIGUSR2, &pact, NULL);

    /* spawdn children */
    for (count = 0; count < MAXCP; count++){
        switch(pid[count] = fork()){
            case -1:
                perror("Fork error");
                exit(1);
            case 0:
                {
                    struct sigaction cact;
                    cact.sa_flags = 0;
                    cact.sa_handler = c_action;
                    sigaction(SIGUSR1, &cact, NULL);
                    pause();
                    sleep(1);
                    printf("Sending SIGUSR2 to parent (%d)\n", getppid());
                    sleep(count+1);
                    kill(getppid(), SIGUSR2);
                    exit(0);
                    break;
                }
            default:
                break;
        }
    }

    sleep (1); /* let children have time to configure sigaction() */

    /* notify all children */
    for (count = 0; count < MAXCP; count++){
        printf ("Sending SIGUSR1 to child %d\n", (int)pid[count]);
        kill(pid[count], SIGUSR1);
    }

    /* wait for children to notify back */
    while (ccount)
    {
         usleep(10000); /* else CPU throttles */
    }

    for (count = 0; count < MAXCP; count++){
        int status;
        waitpid (pid[count], &status, 0);
        printf ("Child process %d reaped. status=%d\n", pid[count], status);
        kill(pid[count], SIGUSR1);
    }

    return 0;
}

内核对每个进程都有一个关于它在睡眠时收到的信号的记录。

struct Process
{
   bool hasReceivedSIGINT;
   bool hasReceivedSIGUSR1;
   bool hasReceivedSIGUSR2;
   bool hasReceivedSIGUSR3;
};

信号用于“推动”其他进程。一个很好的例子是使进程重新加载配置文件。对于 IPC 通信,pipe() 是更好的方法。

【讨论】:

  • 斯蒂安:谢谢。在思考这个问题时,你帮了我很多。
最近更新 更多