在单个管道中进行多次读写

Multiple read and write in a single pipe

我正在尝试理解 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" 然后在第二次读取时调用它阻塞 第二个缓冲区数据发生了什么。丢了吗?

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

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

最多读取 30 个字节。

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

问题出现在这里:

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

readbuffer 看起来像这样(其中 [=14=] 是 nul 终止符):

hello pipe1[=12=]hello pipe2[=12=]

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

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

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

为什么 printf() 不显示所有数据?每次调用 write() 都会发送一个空终止符 ([=23=])。 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[=12=]

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

检查 write 的 return 值表明它实际上写入了所有 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] = '[=17=]';

请注意 readcount 参数正在为空终止符保存 space。