通过管道读取字符串时使用fgets很困难

时间:2015-09-26 18:47:34

标签: c unix

我正在编写一个由2个进程组成的程序。父级通过stdin读取数据,然后通过管道将其发送给子级。我正在使用fgets()和fputs()来编写和读取数据。

问题是在某些情况下,子进程的fgets()调用似乎被阻止。这些是:

  1. 当文本文件发送到stdin时,一切正常,直到文件的最后一行。孩子的fgets永远不会执行,并且过程会挂起。
  2. 当我向程序发送/ dev / urandom时,在看似随机的时刻发生同样的情况。
  3. 我怀疑这个问题与这些字符串末尾的EOF或/ 0有关,但我无法确定问题。任何帮助将不胜感激。

    我正在使用消息队列作为同步手段。我也知道这段代码没有清理。这是:

    #define KEY5 7626
    
    struct pidbuf
    {
        long mtype;
        int mtext;
    } msgpid;
    
    int main(){
        sleep(5);
        int mqpid;
        FILE * strumien;
        int fd[2];
        if (pipe(fd)== -1){
            perror("Create pipe error");
            exit(1);}
    
        int buf_len = 128;
    
        msgpid.mtext=0;
        int size1=sizeof(struct pidbuf)-sizeof(long);
    
        if((mqpid = msgget(KEY5, 0666 | IPC_CREAT))==-1){
            return(-1);
        }   
        char data[buf_len];
    
        if(fork()!=0){
            close(fd[0]);
            strumien=fdopen(fd[1],"w");
            while (fgets(data,buf_len,stdin) != NULL ) {
                printf("1:\n%s\n",data);
                fputs(data,strumien);
                fflush(strumien);
                msgpid.mtype=2;
                if(msgsnd(mqpid,&msgpid,size1,0)==-1){perror("msgsnd failed: 1");}
    
                msgrcv(mqpid,&msgpid,size1, 1, 0);
            }
            if(feof(stdin)!=0) printf("\nEnd of file\n");
        }
        else{   
            close(fd[1]);
            strumien=fdopen(fd[0],"r");
            msgrcv(mqpid,&msgpid,size1, 2, 0);
            while(fgets(data,buf_len,strumien)!=NULL){
                printf("2:\n%s\n\n",data);
    
                msgpid.mtype=1;
                if(msgsnd(mqpid,&msgpid,size1,0)==-1){perror("msgsnd failed: 1");}
                msgrcv(mqpid,&msgpid,size1, 2, 0); 
            }
        }
        return 0;
    }
    

1 个答案:

答案 0 :(得分:1)

不太令人惊讶 - 父进程为读取的文件的每一行发送一条类型为2的消息,并在写入每一行后等待类型1的消息。另一方面,孩子是不同的 - 当它发送类型1并在每行之后等待类型2时,它也会在读取第一行之前等待类型2。

因此父母将发送N类型2消息,而孩子将等待N行1文件的N + 1类型2消息。这意味着在父母完成并退出后,孩子将被阻止等待最后一条永远不会到来的消息。

明显的解决方法是让父母发送一条额外的消息 - 可能是在循环之后,具体取决于你想要实现的目标。

对于/dev/urandom(或任何二进制输入),当输入包含NUL字节时,您还有一个问题。然后,父级中的fgets将很高兴地读取NUL并继续将其传递给换行符(或缓冲区已满),但是下面的fputs只会写入(并且不包括)NUL 。因此,一些数据将丢失,孩子将继续阅读以查找换行符,并在fgets中阻止该孩子,并在发送下一行之前将其阻止在msgrcv

由于fgets / fputs旨在处理文本而非二进制数据,因此没有简单的方法来解决此问题。您可以改为使用freadgetlinefwrite,它们会为您提供读取/写入数据量的明确长度,因此您可以知道它何时读取了非NUL这些数据的结尾。