FIFO read() 函数卡在 c 中

Posted

技术标签:

【中文标题】FIFO read() 函数卡在 c 中【英文标题】:FIFO read() function gets stuck in c 【发布时间】:2020-06-12 19:58:36 【问题描述】:

我正在尝试从进程中读取文本文件的字符串,然后通过 LINUX 上的命名管道将该字符串传递给另一个进程。问题是当我在控制台输入“./reader text.txt = receiver”时,如果我输入该行,接收过程的 read() 函数会返回错误

fcntl(fd, F_SETFL, O_NONBLOCK);

如果我删除它,就会卡在 read() 函数上。

这里是读取字符串的进程(reader)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h> 

 int main(int argc,char *argv1[])

 if(argc==4 && strcmp(argv1[2],"=") == 0)

 char mesaj[99999]; //message to be delivered

    char line[150];
    FILE *fp =fopen(argv1[1],"r");  //reading from a text file

    if(fp==NULL)

        printf("text file error!");
        exit(1);
    


    while(fgets(line,sizeof(line),fp))
        strcat(mesaj,line); //append every line to message

    

    fclose(fp);

    mesaj[strlen(mesaj)-1]='\0';

    int n =strlen(mesaj)+1;


   //printf("got the text  %s\n",mesaj);


    if(mkfifo("myFifo",0777)== -1 && errno!= EEXIST) 
        printf("Pipe error");
        exit(1);
    


    printf("opening\n");
    int fd= open("myFifo",O_RDWR);
    if(fd==-1)
        printf("open error");
        exit(1);
    
    printf("opened");


    if( write(fd, mesaj,sizeof(char)*n)==-1)
        printf("write error");
        exit(1);
    


    printf("written");

    close(fd);

    printf("closed");
    fflush(stdout);


   char mesajSizeChar[n];
   sprintf(mesajSizeChar, "%d", n);


    char *args[]=mesajSizeChar,NULL; //send the message size as parameter for the other process
    char program[]="./";
    strcat(program,argv1[3]); // recieved process name as parameter
    execv(program,args); // call the other process
    perror("execv");


    return 0;
     
 

这里是接收过程(reciever)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>

int main(int argc,char *argv1[])

int mesajSize=atoi(argv1[0]); //convert message size to integer
char mesaj[99999];

printf("\ncame here\n");


int fd= open("myFifo",O_RDWR);
fcntl(fd, F_SETFL, O_NONBLOCK);
printf("\nopen \n");

if(fd==-1)
    printf("pipe error\n");


if(read(fd,mesaj,sizeof(char)*mesajSize)==-1)
printf("read error\n");


printf("read \n");
printf("\nworked: %s \n",mesaj);

close(fd);
return 0;

 

【问题讨论】:

阅读器应该在O_WRONLY模式下打开FIFO,接收端应该在O_RDONLY模式下打开它。 您需要先将mesaj初始化为空字符串,然后才能调用strcat()char mesaj[99999] = ""; OT:为了便于阅读和理解(编译器不关心) 1)请始终缩进代码。在每个左大括号“”后缩进。在每个右大括号 '' 之前取消缩进。建议每个缩进级别为 4 个空格 OT: about: execv(program,args); // call the other process perror("execv"); return 0; 通常,返回 0 表示成功,但此返回表示失败。建议:exit( EXIT_FAILURE ); 注意:exit() 和 EXIT_FAILURE 暴露于:#include &lt;stdlib.h&gt; OT:关于:printf("read error\n"); 错误消息将输出到stderr,而不是stdout。当错误指示来自 C 库函数时,还应输出系统认为发生错误的文本原因。函数:perror( "read error" ); 用于执行这两个活动。 【参考方案1】:

问题是您在第一个过程中关闭了管道。管道没有任何永久存储,它只能在至少一个进程打开时保存数据。当您关闭管道时,您写入的数据将被丢弃。

因此,当第二个进程尝试从管道中读取数据时,没有可用的数据。

在执行第二个进程时,需要保持管道 FD 处于打开状态。在阅读器程序中去掉close(fd);

【讨论】:

【参考方案2】:

要使用 FIFO 或管道,发送方和接收方必须同时运行,但您正在尝试按顺序运行它们。 FIFO 或管道没有持久存储,因此系统不允许您写入其中一个,除非至少有一个进程打开了读取端以便能够读取它。

通常,尝试打开 FIFO 进行写入会在没有读取器时阻塞,反之亦然。您的读者正在通过打开 FIFO 进行读取和写入来解决此问题,即使它只打算写入。你会发现,如果它试图向 FIFO 发送太多数据,那么它就会阻塞,因为没有任何东西在读取数据,并且管道/FIFO 的缓冲区容量有限。当它关闭 FIFO 的 fd 时,没有任何进程打开它,之前写入它的所有数据都将丢失。

您的接收器也会错误地打开 FIFO 以进行读写,而它应该只为读取而打开它。没有数据可以从中读取,我希望尝试从中读取无限期阻塞,除非您将其置于非阻塞模式。这似乎正是您所描述的。

要修复它,我建议

    从阅读器中取出启动接收器的代码。相反,请分别启动阅读器和接收器。或者,读者可以通过fork()ing开始,结果子进程execv()ing 接收者。

    读取器应使用标志O_WRONLY打开FIFO,接收器应使用模式O_RDONLY打开它。

    您应该找到一种不同的方式来将消息长度从阅读器传递到接收器,或者更好的是,根本不需要提前告诉它消息长度。例如,您可以发送一个初始的固定长度消息来传达主消息数据的长度,但更典型的情况是接收者只是继续读取数据,直到它看到 EOF。

    阅读器将通过显式关闭或终止来使接收器看到 FIFO 上的 EOF。然而,这取决于接收器是否以只读模式打开,并且没有其他写入器。

    阅读器可能不应该尝试一次将整个消息缓冲到内存中。在任何情况下,它都不应该假设write() 调用将传输请求的全部字节数——返回值将告诉您实际传输了多少字节。您需要准备好在一个循环中使用多个write() 调用来传输所有数据。

    同样,接收方不能依赖单个read() 调用来传输一次调用中请求的全部字节数,即使它有某种方式知道有多少字节。和write()一样,你需要准备好使用多个read()s来传输所有数据。

【讨论】:

以上是关于FIFO read() 函数卡在 c 中的主要内容,如果未能解决你的问题,请参考以下文章

linux C语言mkfifo()函数 mkfifoat()函数(制作一个 FIFO 特殊文件(命名管道))

命名管道FIFO

C语言read函数

UNIX网络编程之管道与FIFO

linux下c语言编程read()函数的问题

read的Linux C函数