在C中,当没有东西可读时,读取功能如何"知道"?

sav*_*v0h 9 c fork pipe parent-child

在下面的程序(不是我的程序,但是我修改过的程序)中,子进程对管道进行了两次写入.当我运行该程序时,我得到以下输出:

Received string: Hello, world!
This is the child process.
Run Code Online (Sandbox Code Playgroud)

如何由父进程执行的读取从管道缓冲区中捕获这两个字符串?什么(如果有的话)阻止父进程在读取第一个字符串(或第一个字符串的第一个字符串)之后假设没有其他内容可以从缓冲区中读取并退出?

有问题的计划:

int main(void)
{
    int     fd[2], nbytes;
    pid_t   childpid;
    char    string[] = "Hello, world!\n";
    char    string2[] = "This is the child process.\n";
    char    readbuffer[80];

    pipe(fd);

    if((childpid = fork()) == -1)
    {
            perror("fork");
            return 1;
    }

    if(childpid == 0)
    {
            /* Child process closes pipe's input file descriptor */
            close(fd[0]);

            /* Send "string" through the output side of pipe */
            write(fd[1], string, (strlen(string)));
            write(fd[1], string2, (strlen(string2)+1));
            return 1;
    }
    else
    {
            /* Parent process closes pipe's output file descriptor */
            close(fd[1]);

            /* Read in a string from the pipe */
            nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
            printf("Received string: %s", readbuffer);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Spe*_*ave 0

父级最多读取 80 个字节。因此,第一个write系统调用(也是第二个)会将第一个字符串放置在相对于管道的内核缓冲区中。系统read调用最多会读取80个字节,所以它会向内核请求数据,内核会返回缓冲区中的前80个字节的数据(所以不可能只读取字符串的第一个字符整个字符串的单次写入和 80 字节的单次阻塞读取)。

正如您所说,子级和父级之间没有同步。事实上,在我的 Linux 系统上,你的程序有时只打印第一个字符串,有时则打印两个字符串。

该问题是一个经典的竞争条件问题,有时子进程只会写入第一个字符串,然后父进程被调度,它将读取第一个字符串,然后程序将结束(或者控制权将返回到子进程之后)read, child 会将第二个字符串写入管道缓冲区,但没有人会真正读取数据)。

您应该在程序中放置一些同步函数,例如在close(fd[1]);

[...]
else
{
      /* Parent process closes up output side of pipe */
      close(fd[1]);

      /* Synchronize parent and child */
      wait(NULL);

      /* Read in a string from the pipe */
      nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
      printf("Received string: %s", readbuffer);
}
[...]
Run Code Online (Sandbox Code Playgroud)

然后,只有当两个字符串正确放置在缓冲区中时,父级才会读取数据。