【问题标题】:Synchronization of inter process communication between three relative processes三个相关进程之间进程间通信的同步
【发布时间】:2021-03-05 14:54:28
【问题描述】:

我最近研究了信号主题以及如何使用它们来帮助同步流程。我正在尝试写一些可以像乒乓球一样工作的东西。当有 2 个进程(父进程和子进程)时,我可以做到并且我的代码工作得很好,但是当有 3 个进程(父进程、子进程和孙子进程)时,它就不起作用了,我无法弄清楚我做错了什么,请检查我的代码并帮助我。如果输入为 10,则输出应如下所示,程序按照输入 10 的方式工作,但如果输入为 3、4、5、7、8、9 等,则它不起作用。

孙子 0
儿子 1
父亲 2
儿子 3
孙子 4
儿子 5
父亲 6
儿子 7
孙子 8
儿子 9
父亲 10

编辑:
程序应该做什么:应该在程序中创建父/子和孙进程。这 3 个进程应该只使用一个管道相互通信。程序等待用户输入一个正整数。根据这个输入,这3个进程应该一个一个地打印出一个从0开始的数字。例如,如果输入是10,则进程应该从0到1打印,每个进程只打印一个数字,然后加一。孙子打印出第一个数字。他首先打印 0 并使用管道通道发送下一个数字来处理孩子。子进程打印,将数字加一并将其发送给父进程。父进程打印该数字,将其加一并发送给子进程。像这样直到其中一个达到用户输入的数字。

对于输入 1、3、5、7、9,我的程序打印到 2、4、6、8、10,包括这些数字,这是不正确的。 对于输入 4,8,我的程序打印到数字 4 和 8,但一直等待并且不返回任何内容。

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

volatile int flag = 0;

void SigHandler(int s)
{
    signal(SIGUSR1, SigHandler);
    flag = 1;
}

int main()
{
    pid_t pid;
    int fd[2];
    int count=0, countMax, queue=0;
    scanf("%d",&countMax);

    signal(SIGUSR1, SigHandler);

    if(pipe(fd) < 0)
    {
        perror("pipe\n");
        return 1;
    }

    if((pid=fork()) < 0)
    {
        perror("fork\n");
        return 2;
    }
    else if(pid == 0)
    {
        //SON
        pid = fork();
        if(pid<0)
        {
            perror("fork\n");
            return 3;
        }
        else if(pid == 0)
        {
            //GRANDSON
            while(count < countMax)
            {
                if(!flag)
                    pause();
                flag = 0;
                read(fd[0], &count, sizeof(int));
                printf("grandson %d\n", count++);
                fflush(stdout);
                write(fd[1], &count, sizeof(int));
                kill(getppid(), SIGUSR1);
            }
            close(fd[0]);
            close(fd[1]);
            return 0;
        }
        else
        {
        //SON
            flag = 0;
            kill(pid, SIGUSR1);
            while(count < countMax)
            {
                if(!flag)
                    pause();
                flag = 0;
                read(fd[0], &count, sizeof(int));
                printf("son %d\n", count++);
                fflush(stdout);
                write(fd[1], &count, sizeof(int));
                if(queue == 0)
                {
                    kill(getppid(), SIGUSR1);
                    queue = 1;
                }
                else
                {
                    kill(pid, SIGUSR1);
                    queue = 0;
                }
            }
            close(fd[0]);
            close(fd[1]);
            return 0;
        
        }
    }
    else
    {
    //FATHER
        write(fd[1], &count, sizeof(int));
        kill(pid,SIGUSR1);
        fflush(stdout);
            while(count < countMax)
            {
                if(count>=countMax)
                    raise(SIGINT);
                if(!flag)
                    pause();
                flag = 0;
                read(fd[0], &count, sizeof(int));
                printf("father %d\n", count++);
                fflush(stdout);
                write(fd[1], &count, sizeof(int));
                kill(pid, SIGUSR1);
            }
        close(fd[0]);
        close(fd[1]);
        return 0;
    }
}

【问题讨论】:

  • 您实际上并没有描述该程序应该做什么。也就是说,这个任务的“乒乓”的正式定义是什么?示例输出很好,但您还需要描述程序的整体意图。另外,请描述您得到的结果,而不是预期的行为。
  • 我审核过了,能否请您重新提出问题?
  • 进程是无性别的。从来没有“儿子”,总是“孩子”。
  • 如果您使用信号进行通信,那么您就违反了进程只能通过一个管道进行通信的条件。
  • 好吧,这里只使用信号来同步输出队列,对于这种类型的连接来说,只有一个管道似乎就足够了,其中只有一个进程同时打印,增加整数并给出标记到队列中的下一个进程,为什么你认为违反了条件?

标签: c unix process signals posix


【解决方案1】:

我在程序中添加了一些调试输出

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

volatile int flag = 0;

void SigHandler(int s)
{
    signal(SIGUSR1, SigHandler);
    flag = 1;
}

int main()
{
    pid_t pid;
    int fd[2];
    int count=0, countMax, queue=0;
    setvbuf(stderr, NULL, _IONBF, 0);
    printf("enter maximum count: ");
    scanf("%d",&countMax);

    signal(SIGUSR1, SigHandler);

    if(pipe(fd) < 0)
    {
        perror("pipe\n");
        return 1;
    }

    if((pid=fork()) < 0)
    {
        perror("fork\n");
        return 2;
    }
    else if(pid == 0)
    {
        //SON
        pid = fork();
        if(pid<0)
        {
            perror("fork\n");
            return 3;
        }
        else if(pid == 0)
        {
            //GRANDSON
            while(count < countMax)
            {
                if(!flag)
                {
                    fprintf(stderr, "@ grandchild pause\n");
                    pause();
                }
                flag = 0;
                fprintf(stderr, "@ grandchild read (after pause)\n");
                read(fd[0], &count, sizeof(int));
                printf("grandson %d\n", count++);
                fflush(stdout);
                write(fd[1], &count, sizeof(int));
                fprintf(stderr, "@ grandchild signal child\n");
                kill(getppid(), SIGUSR1);
            }
            fprintf(stderr, "@ grandchild done\n");
            close(fd[0]);
            close(fd[1]);
            return 0;
        }
        else
        {
        //SON
            flag = 0;
            kill(pid, SIGUSR1);
            while(count < countMax)
            {
                if(!flag)
                {
                    fprintf(stderr, "- child pause\n");
                    pause();
                }
                flag = 0;
                fprintf(stderr, "- child read (after pause)\n");
                read(fd[0], &count, sizeof(int));
                printf("son %d\n", count++);
                fflush(stdout);
                write(fd[1], &count, sizeof(int));
                if(queue == 0)
                {
                    fprintf(stderr, "- child signal parent\n");
                    kill(getppid(), SIGUSR1);
                    queue = 1;
                }
                else
                {
                    fprintf(stderr, "- child signal grandchild\n");
                    kill(pid, SIGUSR1);
                    queue = 0;
                }
            }
            fprintf(stderr, "- child done\n");
            close(fd[0]);
            close(fd[1]);
            return 0;
        
        }
    }
    else
    {
    //FATHER
        write(fd[1], &count, sizeof(int));
        kill(pid,SIGUSR1);
        fflush(stdout);
            while(count < countMax)
            {
                if(count>=countMax)
                {
                    fprintf(stderr, "# parent raise SIGINT\n");
                    raise(SIGINT);
                }
                if(!flag)
                {
                    fprintf(stderr, "# parent pause\n");
                    pause();
                }
                flag = 0;
                fprintf(stderr, "# parent read (after pause)\n");
                read(fd[0], &count, sizeof(int));
                printf("father %d\n", count++);
                fflush(stdout);
                write(fd[1], &count, sizeof(int));
                fprintf(stderr, "# parent signal child\n");
                kill(pid, SIGUSR1);
            }
        fprintf(stderr, "# parent done\n");
        close(fd[0]);
        close(fd[1]);
        return 0;
    }
}

输入4 我得到这个输出:

enter maximum count: 4                                                                                                                           
# parent pause                                                                                                                                   
- child pause                                                                                                                                    
@ grandchild read (after pause)                                                                                                                  
grandson 0                                                                                                                                       
@ grandchild signal child                                                                                                                        
@ grandchild pause                                                                                                                               
- child read (after pause)                                                                                                                       
son 1                                                                                                                                            
- child signal parent                                                                                                                            
- child pause                                                                                                                                    
# parent read (after pause)                                                                                                                      
father 2                                                                                                                                         
# parent signal child                                                                                                                            
# parent pause                                                                                                                                   
- child read (after pause)                                                                                                                       
son 3                                                                                                                                            
- child signal grandchild                                                                                                                        
- child done                                                                                                                                     
@ grandchild read (after pause)                                                                                                                  
grandson 4                                                                                                                                       
@ grandchild signal child                                                                                                                        
@ grandchild done                                                                                                                                

如您所见,最后孩子已经离开了循环。孙子尝试向子进程发送信号,子进程将不再反应,而父进程正在等待pause 中的信号,但没有进程会向父进程发送信号。

此行为取决于输入值。

编辑

可以在 Tevfik Oğuzhan AKSOY 的answer 中找到修复此处所示问题的原始程序的修改版本。

【讨论】:

  • 您好,感谢您的调试输出,我在程序中找到了解决问题的方法,在输入 4k(其中 k>=1 & k 是自然数)处,程序实际上正在等待,因为父进程不知道孙子进程和子进程之间发生了什么,所以它一直在等待你提到的,它为输入 2k-1 打印了一些额外的数据,在读取函数之后通过一些 if 条件解决了这个问题。非常感谢您的支持!!
【解决方案2】:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

volatile int flag = 0;

void SigHandler(int s)
{
    signal(SIGUSR1, SigHandler);
    flag = 1;
}

int main()
{
    pid_t pid;
    pid_t pidFather = getpid();
    int fd[2];
    int count=0, countMax, queue=0;
   
    scanf("%d",&countMax);

    signal(SIGUSR1, SigHandler);

    if(pipe(fd) < 0)
    {
        perror("pipe\n");
        return 1;
    }

    if((pid=fork()) < 0)
    {
        perror("fork\n");
        return 2;
    }
    else if(pid == 0)
    {
        //SON
        pid = fork();
        if(pid<0)
        {
            perror("fork\n");
            return 3;
        }
        else if(pid == 0)
        {
            //GRANDSON
            while(count < countMax)
            {
                if(!flag)
                {
                    pause();
                }
                flag = 0;
                read(fd[0], &count, sizeof(int));
        if(count<=countMax)
        {
                    printf("grandson %d\n", count++);
                    fflush(stdout);
                    write(fd[1], &count, sizeof(int));
            kill(getppid(), SIGUSR1);
        }
            }
            close(fd[0]);
            close(fd[1]);
            return 0;
        }
        else
        {
            //SON
            flag = 0;
            kill(pid, SIGUSR1);
            while(count < countMax)
            {
                if(!flag)
                {
                    pause();
                }
                flag = 0;
                read(fd[0], &count, sizeof(int));
        if(count<=countMax)
        {
                    printf("son %d\n", count++);
                    fflush(stdout);
                    write(fd[1], &count, sizeof(int));
                    if(queue == 0)
                    {
                                kill(getppid(), SIGUSR1);
                            queue = 1;
                    }
                    else if(queue != 0)
                {
                        kill(pid, SIGUSR1);
                        queue = 0;
                    }
        }
            }
        kill(getppid(), SIGUSR1);
            close(fd[0]);
            close(fd[1]);
            return 0;
        }
    }
    else
    {
        //FATHER
    write(fd[1], &count, sizeof(int));
        kill(pid,SIGUSR1);
        fflush(stdout);
        while(count < countMax)
        {
            if(!flag)
            {
                pause();
            }
            flag = 0;
            read(fd[0], &count, sizeof(int));
        if(count<=countMax)
        {
            printf("father %d\n", count++);
            fflush(stdout);
                write(fd[1], &count, sizeof(int));
                kill(pid, SIGUSR1);
        }
        }
        close(fd[0]);
    close(fd[1]);
        return 0;
    }
}

【讨论】:

  • 您应该对此答案添加一些解释。我想这是代码的工作版本,不是吗?如果是这种情况,我可以在我的答案中添加指向此答案的链接。
  • 我刚刚用不同的输入值测试了代码。我注意到输入 8 或 16 可能存在问题。8 的序列是孙子 0 儿子 1 父亲 2 儿子 3 孙子 4 儿子 5 父亲 6 儿子 7 父亲 8。值 8 不应该由孙子打印输入 9 的情况?
  • 是的,我认为这就是答案,在我的系统中一切都很顺利,但是,不知何故,父亲和孙子进入了比赛状态,我不明白为什么。孙子应该每 4k 打印一次(对于 k>=1)。实际上,儿子的可变队列决定了轮到谁打印。有时父亲会赢得比赛而不是孙子……甚至不应该有比赛条件,但是即使变量队列说轮到孙子了,信号也会以某种方式发送给父亲……
最近更新 更多