代码之家  ›  专栏  ›  技术社区  ›  funcs

无限循环中来自子级的read()标准输出

  •  0
  • funcs  · 技术社区  · 8 年前

    我正在分叉一个孩子,它将运行一个无限循环并做一些事情。当子项处于“就绪”状态时,文本将使用printf打印到标准输出。我正在用管道重定向孩子的stdout,以便家长可以看到它。打印此文本时,我希望父级被解除阻止。

    问题似乎是,即使在printf()调用之后,read()调用仍将保持阻塞状态。

    下面是我正在使用的一些示例代码:

    起源:

    #include <stdio.h>
    #include <unistd.h>
    
    int main()
    {
        int fd[2];
        pipe(fd);
        char buffer[256];
    
        switch(fork())
        {
            case -1:
                printf("fork failed\n");
                _exit(1);
            case 0:
                printf("Starting progtest...\n");
                close(fd[0]);
                dup2(fd[1], STDOUT_FILENO);
                close(fd[1]);
                execl("progtest", "progtest", NULL);
    
                perror("exec failed");
                _exit(1);
        }
    
        close(fd[1]);
        read(fd[0], &buffer, sizeof(buffer));
        close(fd[0]);
    
        printf("buffer: %s\n", buffer);
    
    }
    

    子项:

    #include <stdio.h>
    
    int main()
    {
        printf("ready\n");
        while(1)
        {
            // stuff happens here
        }
    
        return 0;
    }
    

    我很肯定管道安装正确。如果我删除无限循环,父循环将取消阻止并打印“ready”。

    孩子还在运行时,我有没有办法使用read()?我是不是用管道做的不对?我需要用两条不同的状态消息执行两次。

    1 回复  |  直到 8 年前
        1
  •  0
  •   Community Mohan Dere    5 年前

    您似乎正在尝试执行进程间通信(IPC)。没有与读/写进行1对1通信同步,父级或子级可以一次向文件描述符写入任意多的内容,并且由于它们是异步运行的,因此您必须确保已准备好处理此问题。

    通常,这是通过发送一个标头来完成的,该标头包含固定大小的每条消息,其中包含有效负载的长度。例如:

    协议标头

    #include <stdint.h>
    
    struct Message
    {
      uint8_t length; // assuming a maximum message length of 255 bytes, adjust as needed.
    };
    

    您可以在这里定义额外的内容,通常最好包括版本,可能是序列ID等。如果您通过网络进行通信,则此标头的大小在所有硬件实现中都是绝对的,因此需要使用stdint定义来确保架构之间的兼容性,这一点很重要。如果跨架构工作,确保endian是一致的也是一个好主意。

    发送消息

    const char data[] = "example";
    
    // create a buffer large enough to pack the header and data into it
    const uint8_t len = strlen(data) + 1;
    char buffer[sizeof(struct Message) + len];
    
    // populate the header
    struct Message *m = (struct Message *)buffer;
    m->length = len;
    
    // copy in the payload after the header
    memcpy(buffer + sizeof(struct Message), data, len);
    
    // write the packed buffer
    write(fd, buffer, sizeof(struct Message) + len);
    

    阅读消息

    struct Message m;
    if (read(fd, &m, sizeof(struct Message)) != sizeof(struct Message))
    {
      fprintf(stderr, "Failed to read the header\n");
      exit(1);
    }
    
    // If required perform validation on the header here
    
    ssize_t offset = 0;
    char data[m.length];
    while(offset < m.length)
    {
      ssize_t r = read(fd, data + offset, m.length - offset);
      if (r < 0)
      {
        fprintf(stderr, "Read of message body failed\n");
        exit(1);
      }
    
      if (r == 0)
      {
        fprintf(stderr, "The sender closed the connection\n");
        exit(1);
      }
    
      offset += r;
    }
    
    printf("Message Length: %u, Message Data: %s\n", m.length, data);
    

    需要执行while循环读取,因为如果发送方超出了写入缓冲区,并且正在阻塞等待写入剩余数据,则数据可能已被分割成块。标题也可以分为多个块,理想情况下也应该使用相同的技术读取,但为了简洁起见,我省略了这一点。