如何在此进程间通信情况下检查流是否已完成(在C中)?

时间:2017-07-02 17:23:37

标签: c unix pipe ipc

我正在编写代码以与另一个程序进行交互。

    FILE* streamout = fdopen(fdout[0],"r");

我使用了fdopen之前显示的功能,以便能够像读取文件一样处理读取。 问题是我不知道在以下循环中尝试从streamout读取时要使用什么停止条件。

while(fgets(endbuffer,sizeof(endbuffer),streamout) != NULL)
        {
                strcat(buffer,endbuffer);
        }

即使流式传输完成,程序也永远不会退出while循环。

修改

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>

//this function returns when it receives a particular response ie: imready    
void wait_until_ready(FILE *,FILE *);

int main(){

    int fdin[2];
    int fdout[2];

    pid_t pid;

    if ((pipe(fdin)<0)||(pipe(fdout)<0))
    {
        perror("pipe error");
        exit(1);
    }

    if ((pid = fork())<0)
    {
        perror("fork error");
        exit(2);
    }

    if (pid == 0)
    {
        close(fdin[1]);
        close(fdout[0]);
        dup2(fdin[0],0);
        dup2(fdout[1],1);
        execlp("stockfish","stockfish",0);
    }
    else
    {
        close(fdin[0]);
        close(fdout[1]);
        FILE* streamin = fdopen(fdin[1],"w");
        FILE* streamout = fdopen(fdout[0],"r");
        char buffer[1024];
        wait_until_ready(streamin,streamout);
        fprintf(streamin,"position startpos\n");
        fprintf(streamin,"go\n");
        fflush(streamin);

        while (fgets(endbuffer,sizeof(endbuffer),streamout) != NULL)
        {
                strcat(buffer,endbuffer);
        }
    }

    return 0;
}

1 个答案:

答案 0 :(得分:0)

我引用了经验法则(引用自己,AFAIK):

  • 如果你使用dup()dup2() - 或fcntl()如果你是受虐狂 - 要将管道的一端复制到标准输入或标准输出,你应该关闭 原始管道的两个 两端(1×pipe(),1×dup2()⇒2×close())。

子代码

你声称你这样做。您的代码显示您没有。你有:

if (pid == 0)
{
    close(fdin[1]);
    close(fdout[0]);
    dup2(fdin[0],0);
    dup2(fdout[1],1);
    execlp("stockfish","stockfish",0);
}

你需要:

if (pid == 0)
{
    dup2(fdin[0],0);
    dup2(fdout[1],1);
    close(fdin[0]);
    close(fdin[1]);
    close(fdout[0]);
    close(fdout[1]);
    execlp("stockfish","stockfish",0);
    fprintf(stderr, "Failed to execute 'stockfish'\n");
    exit(EXIT_FAILURE);
}

如果你不这样做,你可能会遇到问题。如果你小心(原来的两个dup2()电话很好,你可以在close()电话之前完成一些关闭,但我将所有四个电话组合在一起修改后的版本中。如果您在close()次呼叫后同时执行这两项操作,则可以对操作进行相当大的重新排序,以确定哪个dup2()首先出现,哪一次出现在哪一对中。在执行任何操作之前,您可以在一个管道上执行所有操作。等

请注意,如果execlp()失败,您应确保退出 - 或采取其他适当的规避措施。它迟早会发生,通常会导致麻烦。有些人会争辩_exit()而不是exit()。如果您在fork()之前刷新了待处理的I / O,则应该没有问题。

父代码

乍一看,父代码看起来还不错。您没有将dup2()用于标准输入或标准输出,因此只关闭管道的两个未使用的端是可以的。

你给孩子写了一条信息。你......哦,你没有关闭孩子的管道(fclose(streamin);)...所以你把信息写给孩子,然后你得到一个回复​​,但孩子现在正在等待下一个命令父级,而父级正在等待来自子级的更多数据。所以他们处于僵局 - 两个进程都在等待对方做某事,而且也不会做对方需要的事情,所以他们陷入了僵局。

如果您要将单个邮件写入stockfish,则需要在进入读取循环之前调用fclose(streamin);。如果您需要发送更多消息,您必须知道如何判断stockfish已完成当前消息,以便您可以中断循环并返回发送下一个命令。这可能是imready函数所需的另一行wait_until_ready()。所以你可能需要:

buffer[0] = '\0';   // Make sure that strcat() works sanely

while (fgets(endbuffer, sizeof(endbuffer), streamout) != NULL)
{
    if (strcmp(endbuffer, "imready\n") == 0)
        break;
    // check for buffer overflow, etc before blithely appending!
    // this is not efficient; it is sufficient, though.
    if (strlen(buffer) + strlen(endbuffer) + 1 >= sizeof(buffer))
        break;
    strcat(buffer, endbuffer);
}