【问题标题】:pipe stuck in read (C - system calls)管道卡在读取中(C - 系统调用)
【发布时间】:2018-06-16 09:47:22
【问题描述】:

这是我第一次使用 C 语言,所以到目前为止非常困难。 我的程序读取一个file.txt。 在第一行是我必须创建的子进程的数量(var: NPROC),在剩余的行中我必须读取子进程的进程 ID、两个整数和一个字符,这是我必须计算的操作: 例如:

NPROC
(id) (n1) (op) (n2)
2
2 3 + 1
1 5 / 2
2 9 * 3

我必须使用消息队列(完美运行)将操作(逐行)发送给孩子,然后孩子应该使用管道发回结果。

这是代码:

(父亲)

//other code
for(i=0; i < NPROC; i++) {

    if(pipe(p) == -1) {
        perror("pipe");
        exit(1);
    }

    switch(currentPid = fork()){
        case 0:

            execl("./child", "./child", NULL);

            return 0;
        case -1:
            printf("Error when forking\n");
            break;
        default:

            // in the father
            childrenPids[i] = currentPid; // store current child pid
            wait(NULL);

            printf("\nParentpid:  %d, Childpid: %i \n", getpid(), currentPid);

            //close(p[1]);

            if(read(p[0], &val, sizeof(val) == -1){
                perror("read()\n");
                exit(1);
            }

            printf("Parent(%d) received value: %d\n", getpid(), val);

            break;         
    }

   //other code


(child)
 //other code
 while (1) {

        long msgtype = m->mtype;
        if(msgrcv(msgid, m, sizeof(message) - sizeof(m->mtype), msgtype, IPC_NOWAIT) == -1) {
            // if(close (p[1]) == -1){
            //     perror("close p[1] of the child\n");
            // } 
            //perror("\nmsgrcv");
            exit(1);
        }
        result[i] = calcolate_results(m);
        // printf("%i %i %c %i", m->id, m->num1, m->op, m->num2);
        printf("\nresult: %i\n", result[i]);

        val = result[i];

        if(write (p[1], &val, sizeof(val)) == -1) { 
            perror("write\n");
            exit(1);
        }
        printf("Child(%d) send val: %d\n", getpid(), val);
    }
 //other code

现在我的问题是父亲的阅读似乎没有得到消息。 当我执行程序不退出时,它会在父亲读取时循环(保持不变)。 我试图将读取放在 for 之外(因此它至少应该读取最后一条消息)但程序仍然停留在读取中。 所以如果有人可以帮助我,我将非常感激。

【问题讨论】:

  • 代码中可能有一些拼写错误,因为我将代码翻译成英文
  • 翻译是个好主意。但请确认它仍然按照描述的方式运行。 IE。确保它仍然是minimal reproducible example
  • @Yunnosch 我只翻译了变量、函数、cmets 的名称
  • 无论如何,显示的代码绝对不是minimal reproducible example,这使问题偏离主题:“寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括期望的行为、特定问题或错误以及在问题本身中重现它所需的最短代码。没有明确问题陈述的问题对其他读者没有用处。请参阅:如何创建minimal reproducible example。"
  • 我已经发布了一个答案,列出了我可以从您的代码摘录中看到的问题。 read 阻塞的常见问题是在子级中忘记close(p[0]),在父级中忘记close(p[1])

标签: c pipe system-calls


【解决方案1】:

你的代码充满了问题。

switch(currentPid = fork()){
    case 0:

这里需要关闭管道的读取端:

        close(p[0]);

通常你会在execl 之前有一个dup2。通常,如果您的子进程要加载另一个程序,您希望将其标准输出重定向到管道的写入端,如dup2(p[1], 1); close(p[1]);

        execl("./child", "./child", NULL);

        return 0;
    case -1:
        printf("Error when forking\n");
        break;
    default:

        // in the father
        childrenPids[i] = currentPid; // store current child pid
        wait(NULL);

这个wait 电话打错了地方。如果子级输出的数据多于管道一次可以容纳的数据,您就会出现死锁(子级将阻塞在write,等待父级到read 为管道提供一些数据才能退出;父级将阻塞wait,等待子进程退出后再从管道读取)。

在您给孩子做任何事情之前等待孩子退出也是没有意义的。这不应该是一种类似协程的设计吗,即孩子应该继续沿着父母运行,根据请求进行算术并将结果发送回?

        printf("\nParentpid:  %d, Childpid: %i \n", getpid(), currentPid);

        //close(p[1]);

为什么这个close 被注释掉了?父进程关闭管道的写入端至关重要(因为否则从管道的读取端读取将阻塞,因为写入端仍然在某个地方打开(在同一进程中,在这种情况下)。

        if(read(p[0], &val, sizeof(val) == -1){

这一行甚至无法编译。它缺少)

            perror("read()\n");
            exit(1);
        }

您错过了 EOF 的检查。 read 将在到达数据末尾时返回 0(并使 val 未初始化)。

您应该使用管道中的所有可用数据(直到read 返回0),然后才使用waitpid(currentPid, NULL, 0);

【讨论】:

    猜你喜欢
    • 2020-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-16
    • 2011-08-07
    • 2020-05-07
    • 1970-01-01
    相关资源
    最近更新 更多