管道用于多个过程

时间:2015-10-05 02:45:10

标签: c pipe fork

目前正在做一些家庭作业并且很难过。目标是生成100,000个数字并将它们全部加在一起,方法是将工作分成10个进程(每个10,000个数字)

我想我已经想出了如何分叉进程(希望如此),但是使用Pipe()来传递每个子进程的小计不起作用...下面的程序为每个子进程返回44901和449010为了跑步总数。

我努力奋斗,但我觉得这很简单,我应该能够理解。

main()
{
    int i;
    pid_t pid;
    int status = 0;
    int fd[2];
    int runningTotal = 0;
    pipe(fd);

    int t;
    int r;

    for (i = 0; i < 10; i++) {
        pid = fork();
        if (pid == 0){
            close(fd[0]);
            t = ChildProcess();
            write(fd[1], &t, sizeof(t));
            exit(0);
        }
        close(fd[1]);
        read(fd[0], &r, sizeof(r));
        runningTotal = runningTotal + r;
        wait(&status);
    }

    printf("%i\n", runningTotal);
}

int ChildProcess() {
    int i;
    int total = 0;
    int r = 0;

    for (i = 0; i < 10000; i++) {
        r = rand() % 10; // 0 to 10
        total = total + r;
    }

    printf("%i\n", total);
    return total;
}

3 个答案:

答案 0 :(得分:3)

初步诊断

如果您担心孩子们都生成相同的值,那么问题是他们都使用相同的随机序列,因为您不会在任何地方呼叫srand()。您需要将其称为once per child,并为每个孩子使用不同的种子。

它不是100%可靠,但您可能会在每个孩子中使用srand(time(0) + getpid()); - 或者甚至只是getpid(),因为这些值可以保证不同。

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

int ChildProcess(void)
{
    int total = 0;
    srand(time(0) + getpid());

    for (int i = 0; i < 10000; i++)
    {
        int r = rand() % 10; // 0 to 9 (not 10).
        total = total + r;
    }

    printf("%i\n", total);
    return total;
}

进一步审查

实际上,经过仔细研究,还有另一个问题。父进程在分支第一个子进程后关闭管道的写端,因此后续的子进程没有可用的文件描述符。读取值始终是第一个孩子的值。所以,你需要做更认真的工作。

int main(void)
{
    int fd[2];
    pipe(fd);  // Missing error check

    for (int i = 0; i < 10; i++) {
        pid_t pid = fork();
        if (pid == 0){
            close(fd[0]);
            int t = ChildProcess();
            write(fd[1], &t, sizeof(t));  // Missing error check?
            exit(0);
        }
        // Print PID here?  Error check?
    }

    close(fd[1]);

    int r;
    int runningTotal = 0;
    while (read(fd[0], &r, sizeof(r)) > 0)  // Debugging opportunities here
        runningTotal = runningTotal + r;

    while (wait(0) > 0)  // Lots of debugging opportunities here
        ;

    printf("%i\n", runningTotal);
    return 0;
}

答案 1 :(得分:3)

通常,人们会为每个孩子使用单独的管道,否则父母不可能知道它所读取的数据来自哪个过程。在这种特殊情况下,我不认为这是一个很大的问题,因为在这里,你实际上并不在意。虽然它仍然让我有点畏缩,但我认为你确实可以通过一个管道来完成这项特殊任务。

事实上,我根本不认为你的问题与管道有关。这是rand()。所有子进程计算完全相同的(伪)随机数序列,因为它们都使用相同的(默认)种子。如果要生成不同的数字序列,则需要在每个子进程中调用srand(),并在每个子进程中提供不同的种子。将生成的数字序列rand()完全由它开始的种子确定。

另请注意,如果系统的随机数生成器完全没有任何好处,那么各个进程计算的所有总和应该彼此非常接近,并且与您报告的结果非常接近。这是统计中的中心极限定理的结果,但您可以将其视为平均较小的平衡的较大结果。计算余数mod 10可能会产生轻微偏差。

答案 2 :(得分:1)

给出此代码:(摘录自发布的代码)

for (i = 0; i < 10; i++) {
    pid = fork();
    if (pid == 0){
        close(fd[0]);
        t = ChildProcess();
        write(fd[1], &t, sizeof(t));
        exit(0);
    }
    close(fd[1]);
    read(fd[0], &r, sizeof(r));
    runningTotal = runningTotal + r;
    wait(&status);
}

存在序列问题。

当父进程在循环的第一次迭代期间关闭fd [1]时,该文件描述符并不会神奇地&#39;再次打开循环的下一次迭代。

在循环中,父代码需要检查调用read()的返回值,以确保操作成功。 (在循环的第一次迭代后,它可能没有成功,因此变量&#39; r&#39;将保持不变。