【问题标题】:How to wait till data is written on the other end of pipe如何等到数据写入管道的另一端
【发布时间】:2013-02-18 06:13:26
【问题描述】:

我正在用 C 语言开发一个应用程序。 父子进程通过管道进行通信。 在写入管道之前,父进程执行另一个语句。在示例代码中,我使用了 sleep(10) 来进行延迟。 在子进程中,它应该从管道中读取数据。 但是在子进程的管道读取端没有读取数据。

int main()
{
    int pid;
    FILE *fp;
    fp = fopen("test.txt","w");
    char *buff;
    int fd[2];
    int count = 0 ;
    pipe(fd);
    pid = fork();
    if(pid == 0)
    {
        close(fd[1]);
        ioctl(fd[0], FIONREAD, &count);
        fprintf(fp,"Value of count: %d ",count);
        buff = malloc(count);
        fprintf(fp,"\n TIME before read: %s",__TIME__);
        read(fd[0], buff, count);
        fprintf(fp,"\nbuffer: %s\n TIME after read %s", buff, __TIME__);
    }
    else{
        close(fd[0]);
        sleep(10);    //delay caused by application specific code replaced with sleep
        write(fd[1],"THIS is it",10);
    }
    fclose(fp);
    return 0;
}

如何让子进程等到另一端写入数据?

【问题讨论】:

    标签: c ipc pipe


    【解决方案1】:

    您的管道以阻塞模式打开,您没有做任何更改,这可能是您想要的。

    但是,由于您要做的第一件事是请求管道上等待的数据大小,然后盲目地跳入读取那么多字节(由于父级没有写了任何东西)你没有阻止,而是因为你什么都没要求而离开。

    有很多方法可以做到这一点,包括选择循环。如果您希望在数据可用之前阻塞读取,那么在单个字节上执行此操作,然后填写剩余的数据。

    这绝不是如何执行此操作的示例正确,但它是一个简短示例,说明如何等待单个字节,请求管道的读取大小以获取剩下的数据,读取它,然后继续这个,直到管道没有剩余数据并且父级关闭它们的末端:

    希望对您有所帮助。

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    
    int main()
    {
        int pid = 0;
    
        // create pipe pair
        int fd[2];
        pipe(fd);
    
        pid = fork();
        if (pid == 0)
        {
            // child side
            char *buff = NULL;
            char byte = 0;
            int count = 0;
    
            // close write side. don't need it.
            close(fd[1]);
    
            // read at least one byte from the pipe.
            while (read(fd[0], &byte, 1) == 1)
            {
                if (ioctl(fd[0], FIONREAD, &count) != -1)
                {
                    fprintf(stdout,"Child: count = %d\n",count);
    
                    // allocate space for the byte we just read + the rest
                    //  of whatever is on the pipe.
                    buff = malloc(count+1);
                    buff[0] = byte;
                    if (read(fd[0], buff+1, count) == count)
                        fprintf(stdout,"Child: received \"%s\"\n", buff);
                    free(buff);
                }
                else
                {   // could not read in-size
                    perror("Failed to read input size.");
                }
            }
    
            // close our side
            close(fd[0]);
            fprintf(stdout,"Child: Shutting down.\n");
        }
        else
        {   // close read size. don't need it.
            const char msg1[] = "Message From Parent";
            const char msg2[] = "Another Message From Parent";
            close(fd[0]);
            sleep(5); // simulate process wait
            fprintf(stdout, "Parent: sending \"%s\"\n", msg1);
            write(fd[1], msg1, sizeof(msg1));
            sleep(5); // simulate process wait
            fprintf(stdout, "Parent: sending \"%s\"\n", msg2);
            write(fd[1], msg2, sizeof(msg2));
            close(fd[1]);
            fprintf(stdout,"Parent: Shutting down.\n");
        }
        return 0;
    }
    

    输出

    Parent: sending "Message From Parent"
    Child: count = 19
    Child: received "Message From Parent"
    Parent: sending "Another Message From Parent"
    Parent: Shutting down.
    Child: count = 27
    Child: received "Another Message From Parent"
    Child: Shutting down.
    

    【讨论】:

      【解决方案2】:

      我认为之后

      ioctl(fd[0], FIONREAD, &count);  
      the count is 0.  
      
      read(fd[0], buff, count) will get no data.  
      
      try   
      read(fd[0], buff, 10)
      

      【讨论】:

      • 我使用 ioctl 来确定管道末端的数据大小,因为父进程发送的数据长度可变。
      • @Ajay 正确,但是在您点击它时,管道中还没有 no 数据。因此,您最终请求 个字节,这不会阻塞。
      • @WhozCraig 有什么办法可以让 ioctl(xxxx) 函数等待管道末端有可用数据,即与 read() 等待数据可用的方式完全相同。
      • @Ajay 没有使用我知道的ioctl(),但这不重要。如果您担心缓冲区的大小,您可以随时在single-byte read 上阻塞,并且在成功时,将管道中剩下的任何东西都喝光。要么这样做,要么只是制作一个庞大的缓冲区并读入它。选择最终是你的。
      【解决方案3】:

      问题在于获取写入管道的字节数。你在 fork() 之后就得到了它。如果读取过程首先执行,它将不包含任何数据(并且计数为零)。如果写入过程先执行,它将包含一些数据。

      如何让子进程等到另一端写入数据?

      由于你是以阻塞模式打开管道,所以你应该尽可能多地读取数据,而不是试图获取写入数据的大小。


      这是等待完整消息的修改示例:

      #include <stdio.h>
      #include <sys/ioctl.h>
      int main()
      {
      int pid;
      FILE *fp;
      fp = fopen("test.txt","w");
      char *buff = malloc(1024);
      int fd[2];
      int count = 0 ;
      pipe(fd);
      pid = fork();
              if(pid == 0)
              {
                      close(fd[1]);
      
                      int i = 0;
      
                      while ( i < 10 )
                      {
                          fprintf(fp,"\n TIME before read: %s \n",__TIME__);
                          read(fd[0], buff+i, 1);
                          ++ i;
                      }
      
                      fprintf(fp,"Full message received!\nbuffer: %s\n TIME after read %s\n", buff, __TIME__);
              }
              else{
                      close(fd[0]);
                      sleep(10);    //delay caused by application specific code replaced with sleep
                      write(fd[1],"THIS is it",10);
              }
      fclose(fp);
      return 0;
      }
      

      【讨论】:

      • 没错,但是父母每次都会写入可变大小的数据,这就是为什么我使用 ioctl 来获取管道上数据的大小。我可以阻止 ioctl 直到管道上收到数据吗?
      • @Ajay 不,没有办法阻止ioctl()
      • @Ajay 老实说,如果尺寸很重要,我强烈建议反对使用ioctl() 获取此信息。从字面上看,从您调用它到您跟进读取操作的时间,由于从管道的另一端写入了更多数据,大小可能已经无效。如果您想读取父级要发送的字节数,则将其作为 protocol 的一部分(前四个字节是 32 位网络字节顺序消息长度,例如示例)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-22
      • 1970-01-01
      • 2012-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多