Read() 调用在质筛程序中返回 -1

问题描述 投票:0回答:1

我正在尝试实施并发素数筛选程序。每个管道将消除当前数字的倍数。例如,第一个管道将消除所有 2 的倍数并将非倍数发送到下一个管道,下一个管道将消除所有 3 的倍数,依此类推。这是更详细的描述,但它非常简单:Detailed。我的代码如下:

void receiveAndSend(int wr[35][2], int pipeNr) {
    int p;
    if (close(wr[pipeNr][1]) < 0) {
        perror("close 1");
    }
    if (read(wr[pipeNr][0], &p, 4) < 0) {
        perror("Read1");
    }
    
    printf("prime %d, from file descriptor %d\n", p, wr[pipeNr][0]);
    int j;
    pipeNr++;
    pipe(wr[pipeNr]);
    int cont = 0; 
    
    // enclose all of this in new fork
    int sec = fork();
    if (sec == 0) {
        //close(wr[pipeNr][0]);
        while (read(wr[pipeNr - 1][0], &j, 4) > 0) {
            if (j % p != 0) {
                // printf("%d is not divisible by %d\n", j, p);
                cont = 1;
                if (write(wr[pipeNr][1], &j, 4) < 0) {
                    perror("Write x");
                }
                // perror("test");
            }   
        }       
                    
        if (close(wr[pipeNr - 1][0]) < 0) {
            perror("close x");
        }
        // perror("test2");
        
        if (cont) {
            // printf("arrived\n");
            receiveAndSend(wr, pipeNr);
        }
        exit(0);
    }   
    if (wait(0) < 0) {
        perror("Wait Outter");
    }       
}

我在第 6 行的 read 调用中遇到问题。第一次调用该函数时,它工作并读取返回 4,但在第一次递归调用后,我得到 -1。

第一个文件描述符 (

wr[0]
) 由数字 2...35 填充。我有 35 个文件描述符可用,但我不需要全部,因为没有 35 个质数。

写入下一个管道/fd 发生在第 23 行并且有效(每次迭代返回 4)。

我假设子进程或结束电话存在某种问题,但我不确定。我已经尝试过各种组合了。

更新:

按照@pts的建议在我自己的linux机器上调试问题(并打印

errno
)后,我取得了一些进展。我现在已经通过了第二次迭代,输出如下:

prime 2, from file descriptor 3                                                                                                                                                                           
prime 3, from file descriptor 4

然而,紧接着,它挂起,

errno
是“成功”。写作和
while
循环有效,但在
while
循环之后,它由于某种原因挂起,即使
errno
没有抱怨。

c unix pipe file-descriptor
1个回答
0
投票

你的实现有多个问题:

  • 读取

    p
    时,您只测试失败,而不是文件末尾,因此您可能会继续使用
    p
    的无效值。你应该只使用:

    if (read(wr[pipeNr][0], &p, 4) < 4)
        return;
    
  • 测试

    close()
    系统调用的失败是无用的,它与算法的目的无关,并且混淆了代码。

  • 相反,您应该测试

    fork()
    pipe()
    系统调用是否失败,这表明您的系统资源已用完。

  • 你应该检查

    pipeNr
    是否小于
    35
    以避免缓冲区溢出,如果主进程向初始管道写入太多数字(> 149),就会发生缓冲区溢出。传递数组长度会使函数更通用。

  • wait(0)
    (或更易读
    wait(NULL)
    )系统调用的目的是避免在所有素数都打印出来之前退出初始过程。删除此调用可让程序输出所有素数,但在初始进程已经退出到 shell 后,可能会打印部分或全部素数。

  • receiveAndSend
    函数只在子进程中做一些事情。当前进程只是等待子进程退出。这是不正确的。该程序似乎可以在删除
    wait()
    调用的情况下工作的唯一原因是子进程写入管道缓冲区,然后递归并让其子进程从管道中读取。这种方法受到管道缓冲区大小的限制:如果流较长,管道缓冲区会填满,
    write
    会等待管道读取器从管道中读取数据,这不会发生。

    要解决这个问题,你应该在子进程中递归并在父进程中执行过滤。

  • 在这个 fis 之后,

    wait()
    调用仍然会挂起,因为子进程正在等待管道中的下一个数字。父进程必须关闭
    wr[pipeNr][1]
    句柄,以便子进程在
    wr[pipeNr - 1][0]
    句柄上结束文件并退出。

这里是修改后的版本,带有取最大值的

main
函数:

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

void receiveAndSend(int wr[][2], int pipeNr, int pipeSize) {
    int p;

    close(wr[pipeNr][1]);
    if (read(wr[pipeNr][0], &p, 4) < 4)
        return;

    printf("prime %d, from file descriptor %d\n", p, wr[pipeNr][0]);

    pipeNr++;
    if (pipeNr >= pipeSize) {
        fprintf(stderr, "too many primes\n");
        return;
    }
    if (pipe(wr[pipeNr])) {
        perror("pipe");
        return;
    }

    // enclose all of this in new fork
    int sec = fork();
    if (sec < 0) {
        perror("fork");
        return;
    }
    if (sec == 0) {
        receiveAndSend(wr, pipeNr, pipeSize);
    } else {
        close(wr[pipeNr][0]);
        int j;
        while (read(wr[pipeNr - 1][0], &j, 4) > 0) {
            if (j % p != 0) {
                if (write(wr[pipeNr][1], &j, 4) < 0) {
                    perror("Write x");
                }
            }
        }
        close(wr[pipeNr - 1][0]);
        close(wr[pipeNr][1]);
        wait(NULL);
    }
}

int main(int argc, char *argv[]) {
    int wr[35][2];
    int maxp = 100;

    if (argc >= 2)
        maxp = atoi(argv[1]);

    if (pipe(wr[0])) {
        perror("pipe");
        return 1;
    }
    int sec = fork();
    if (sec < 0) {
        perror("fork");
        return 1;
    }
    if (sec == 0) {
        receiveAndSend(wr, 0, sizeof(wr) / sizeof(wr[0]));
    } else {
        close(wr[0][0]);
        for (int p = 2; p < maxp; p++) {
            write(wr[0][1], &p, 4);
        }
        close(wr[0][1]);
        wait(NULL);
    }
    return 0;
}

会议:

chqrlie ~/dev/stackoverflow > ./230311-prime 50
prime 2, from file descriptor 3
prime 3, from file descriptor 4
prime 5, from file descriptor 5
prime 7, from file descriptor 6
prime 11, from file descriptor 7
prime 13, from file descriptor 8
prime 17, from file descriptor 9
prime 19, from file descriptor 10
prime 23, from file descriptor 11
prime 29, from file descriptor 12
prime 31, from file descriptor 13
prime 37, from file descriptor 14
prime 41, from file descriptor 15
prime 43, from file descriptor 16
prime 47, from file descriptor 17

chqrlie ~/dev/stackoverflow > ./230311-prime 200
prime 2, from file descriptor 3
prime 3, from file descriptor 4
prime 5, from file descriptor 5
prime 7, from file descriptor 6
prime 11, from file descriptor 7
prime 13, from file descriptor 8
prime 17, from file descriptor 9
prime 19, from file descriptor 10
prime 23, from file descriptor 11
prime 29, from file descriptor 12
prime 31, from file descriptor 13
prime 37, from file descriptor 14
prime 41, from file descriptor 15
prime 43, from file descriptor 16
prime 47, from file descriptor 17
prime 53, from file descriptor 18
prime 59, from file descriptor 19
prime 61, from file descriptor 20
prime 67, from file descriptor 21
prime 71, from file descriptor 22
prime 73, from file descriptor 23
prime 79, from file descriptor 24
prime 83, from file descriptor 25
prime 89, from file descriptor 26
prime 97, from file descriptor 27
prime 101, from file descriptor 28
prime 103, from file descriptor 29
prime 107, from file descriptor 30
prime 109, from file descriptor 31
prime 113, from file descriptor 32
prime 127, from file descriptor 33
prime 131, from file descriptor 34
prime 137, from file descriptor 35
prime 139, from file descriptor 36
prime 149, from file descriptor 37
too many primes

注意这些进一步的评论:

  • read
    write
    系统调用不保证在成功时返回
    4
    ,它们会由于多种原因返回较小的值,例如:如果管道缓冲区对于
    write
    几乎已满或几乎为空
    read
    。实际上,这不会发生,因为管道缓冲区长度是
    4
    的倍数,并且由于迭代次数少,管道缓冲区永远不会填满。

  • 不需要句柄对数组:函数

    receiveAndSend
    可以只获取读取句柄作为参数,并使用 2 个句柄的本地数组进行
    pipe
    调用。

© www.soinside.com 2019 - 2024. All rights reserved.