c - 使用 Fork() 和 Pipe() 将文件拆分为多个子进程

标签 c pipe fork

这是前两个问题(丑陋的代码;被我删除)的更清晰的最终版本。

我正在使用 pipe()fork() 对子进程中的文件内容求和。为此,我根据 child 的数量平均分割文件:

文件中的 1000 行 => 2 个子进程 => 第一个子进程求和 1-500 行;第二个 child 求和第 501-1000 行 => 将总计发送回父级以对他们的每个总和进行求和。这样,就找到了整个文件的总数。


这是我的代码:

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

int main(int argc, char *argv[])
{
int numchild;
int fd[2*numchild][2]; //parent+child pipe
int i, j, len, fpos=0, val, count=0, total=0;
pid_t pid;
int nums = 1000;
FILE * file;

printf("How many children to use: ");
scanf("%d", &numchild);
printf("\nWill use %d child process(es).\n", numchild);

// create all pipes
for (i=0; i<numchild; i++)
{
    pipe(fd[i]);
}

for (i=0; i<numchild; i++)
{
    if((pid = fork()) == 0) // child process
    {
        pid = getpid();

        // read from parent
        len = read(fd[i][0], &fpos, sizeof(fpos));
        if (len > 0)
        {
            file = fopen("file1.dat", "r");
            fseek (file, fpos, SEEK_SET);
            count = 0;
            total = 0;

            printf("Child(%d): Recieved position: %d\n", pid, fpos);

            // read from file starting at fpos
            // add values read to a total value
            while (count < (nums/numchild))
            {
                fscanf(file, "%i", &val);
                total += val;
                count++;
            }
            //write to parent
            write(fd[i+numchild][1], &total, sizeof(total));
            printf("Child(%d): Sent %d to parent.\n", pid, total);
        }
        else
        {
            printf("Child(%d): Error with len\n", pid);
        }

        _exit;
    }

    // parent process
    pid = getpid();

    fpos = ((i*nums*5)/numchild); // 5 is the offset of the file values

    // write to child process
    printf("Parent(%d): Sending file position to child\n", pid);
    write(fd[i][1], &fpos, sizeof(fpos));

    // wait for child responce
    len = read(fd[i+numchild][0], &total, sizeof(total));
    if (len > 0)
    {
        printf("Parent(%d): Recieved %d from child.\n", pid, total);
        total += total;
        printf("Parent(%d): Total: %d\n", pid, total);
    }
    else
    {
        printf("Parent(%d): Error with len\n", pid);
    }
}
}

输出:

Will use 1 child process(es).
Parent(5497): Sending file position to child
Child(5498): Recieved position: 0
▒▒Child(5498): Sent 454019 to parent. //**yes this weird mark shows up**
Parent(5498): Sending file position to child
//hangs

.

Will use 2 child process(es).
Parent(5898): Sending file position to child
Child(5899): Recieved position: 0
▒|Child(5899): Sent 228601 to parent.
Parent(5899): Sending file position to child
//hangs

.

Will use 4 child process(es).
Parent(5904): Sending file position to child
Child(5905): Recieved position: 0
▒Child(5905): Sent 118304 to parent.
Parent(5905): Sending file position to child
//hangs

该代码对我可以看到总和出现的一个 child 来说工作正常,但它挂起并且 parent 从不确认 child 的输入。因此,如果我使用 2 个 child ,我只会看到第 1-500 行的总和。如果我使用 4 个 child ,我只会看到第 1-250 行的总和。

在第一个 child 将它的结果发回给 parent 后,我要挂断我的程序要做什么?

我的代码允许 children 同时工作吗?


感谢您的帮助。

-汤姆

最佳答案

您并未初始化所有管道。为简单起见,假设您有四个 child 。

然后在这里,你为总共 8 个管道,16 个文件描述符分配空间:

int fd[2*numchild][2]; //parent+child pipe

到目前为止一切顺利; parent 需要一个专门的管道来写给 child ,反之亦然, child 要写给 parent 。

但是,您没有正确初始化管道:

for (i=0; i<numchild; i++)
{
    pipe(fd[i]);
}

这只初始化了 8 个文件描述符,而不是 16 个。你需要把它设为 for( i = 0; i < 2*numchild; i++ ) .上面的循环具有初始化的效果:

fd[0][0]
fd[0][1]
fd[1][0]
fd[1][1]
fd[2][0]
fd[2][1]
fd[3][0]
fd[3][1]

然后,因为您没有正确初始化您的管道,您正在读取和写入错误的管道;具体来说,如果你有幸拥有 fd,你可能正在读写标准输出。被初始化为全零。

这解释了为什么它会死锁——您没有读取到您打算写入的管道。这解释了为什么你在标准输出上得到奇怪的输出:你正在写你的 fpos到错误的文件描述符,因为 fd没有完全初始化;你在写fpos到文件描述符 0 ,又名标准输出。

关于c - 使用 Fork() 和 Pipe() 将文件拆分为多个子进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35947391/

相关文章:

c - 为什么我们必须在这个字符串函数c 中进行malloc?

c - 不询问值并使用来自未知地方的值

c# - 在 .NET 应用程序中检测重定向控制台输出中的关闭管道

bash - 为什么这个不重定向到/dev/null?

c - 管道和流程

linux - 子进程互相等待

c - 我的代码出现段错误,我做错了什么?

c - 将 char 指针分配给字符数组时,分配使指针来自整数而不进行强制转换[默认启用]

c - 如何等待子进程发送信号?

git - 使用 Github 在 Git 中提交多个 pull 请求(一般流程)