【问题标题】:Multiple read and write in a single pipe在单个管道中进行多次读取和写入
【发布时间】:2019-11-29 11:20:49
【问题描述】:

我正在尝试理解 Linux 中的管道。编写了一个基本代码,它将尝试两次写入文件描述符的写入端,然后执行两次读取。在第二次读取时,它被阻塞了。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>

int main()
{
    int filedes[2];
    char buffer1[] = "hello pipe1";
    char buffer2[] = "hello pipe2";
    char readbuffer[30] = {};

    if (pipe(filedes) == 0) {
        printf("Pipe successful\n");
        printf("read from %d, write to %d\n", filedes[0], filedes[1]);
        write(filedes[1], buffer1, sizeof(buffer1));
        perror("write");
        write(filedes[1], buffer2, sizeof(buffer2));
        perror("write");
        read(filedes[0], readbuffer, sizeof(readbuffer));
        printf("read:%s\n", readbuffer);
        read(filedes[0], readbuffer, sizeof(readbuffer));
        printf("read:%s\n", readbuffer);
        close(filedes[1]);
        close(filedes[0]);
    } else {
        perror("pipe failed");
    }
    return 0;
}

我得到输出为“hello pipe1”,然后在第二次读取时调用它阻塞 第二个缓冲区数据发生了什么。是不是丢了?

【问题讨论】:

    标签: c linux operating-system pipe ipc


    【解决方案1】:

    第一个read 实际上消耗了来自writes 的所有数据。对read 的第二次调用被阻塞,因为它没有找到任何可用的数据并且正在等待一些数据出现。

    为什么printf() 不显示所有数据?对write() 的每次调用都会发送一个空终止符(\0)。 printfs 说明符在第一次遇到 null 时停止打印。

    要详细了解这一点,首先要仔细查看每个缓冲区中的数据:

    char buffer1[] = "hello pipe1";
    printf("strlen(buffer1): %zu\n", strlen(buffer1));
    printf("sizeof(buffer1): %zu\n", sizeof(buffer1));
    
    strlen(buffer1): 11
    sizeof(buffer1): 12
    

    缓冲区实际上包含:

    hello pipe1\0
    

    编译器分配 12 个字节:11 个字节的文本和 1 个字节用于空终止符。

    检查write的返回值表明它实际上写入了所有12个字节:

    ssize_t n_written = write(filedes[1], buffer1, sizeof(buffer1));
    printf("Number of bytes written: %zd\n", n_written);
    
    n_written: 12
    

    它这样做是因为writecount 参数告诉它:

    write(filedes[1], buffer1, sizeof(buffer1));
    //                         ^^^^^^^^^^^^^^^ write the entire buffer, including null
    

    您可以将其更改为仅写入字符串的内容:

    write(filedes[1], buffer1, strlen(buffer1));
    //                         ^^^^^^^^^^^^^^^ write only the string's contents,
    //                                         excluding null
    

    但是你需要确定readbuffer 是空终止的。这样的事情是一个开始:

    ssize_t nread = read(filedes[0], readbuffer, sizeof(readbuffer)-1);
    if (nread > 0)
        readbuffer[nread] = '\0';
    

    请注意,readcount 参数正在为空终止符节省空间。

    【讨论】:

      【解决方案2】:

      你的 readbuffer 是 30 字节,所以这个调用:

      read(filedes[0], readbuffer, sizeof(readbuffer));
      

      最多可读取 30 个字节。

      您正在写入 24 个字节(每个字符串 11 个字符加上 nul 终止符的 1 个字节)。 read 将读取所有这些字节。什么都没有丢失。

      问题出现在这里:

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

      readbuffer 看起来像这样(\0 是 nul 终止符):

      hello pipe1\0hello pipe2\0
      

      当 printf 遇到第一个 nul 时,它会将其视为字符串的结尾,因此只会打印“hello pipe1”。

      如果您将读取缓冲区大小更改为sizeof(buffer1),这应该会按预期工作(假设两个输出字符串的大小相同)。

      【讨论】:

      • 虽然这已经正确诊断出正在发生的事情,但建议是不好的。真正的解决办法是注意系统函数的返回值,在这种情况下read()的返回值会泄露NUL后面的数据。
      猜你喜欢
      • 2016-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-01
      • 1970-01-01
      • 2020-11-04
      • 1970-01-01
      相关资源
      最近更新 更多