Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么在写端关闭之前读取会阻塞管道?_C_Linux_Pipe_Fork_Stdout - Fatal编程技术网

C 为什么在写端关闭之前读取会阻塞管道?

C 为什么在写端关闭之前读取会阻塞管道?,c,linux,pipe,fork,stdout,C,Linux,Pipe,Fork,Stdout,我试图通过编写以下popen类型函数来加深对fork、exec、dup和重定向stdin/stdout/stderr相关内容的理解: // main.c #include <pthread.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <

我试图通过编写以下popen类型函数来加深对fork、exec、dup和重定向stdin/stdout/stderr相关内容的理解:

// main.c
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define INVALID_FD (-1)

typedef enum PipeEnd {
  READ_END  = 0,
  WRITE_END = 1
} PipeEnd;

typedef int Pipe[2];

/** Encapsulates information about a created child process. */
typedef struct popen2_t {
  bool  success;  ///< true if the child process was spawned.
  Pipe  stdin;    ///< parent -> stdin[WRITE_END] -> child's stdin
  Pipe  stdout;   ///< child -> stdout[WRITE_END] -> parent reads stdout[READ_END]
  Pipe  stderr;   ///< child -> stderr[WRITE_END] -> parent reads stderr[READ_END]
  pid_t pid;      ///< child process' pid
} popen2_t;

/** dup2( p[pe] ) then close and invalidate both ends of p */
static void dupFd( Pipe p, const PipeEnd pe, const int fd ) {
  dup2( p[pe], fd);
  close( p[READ_END] );
  close( p[WRITE_END] );
  p[READ_END] = INVALID_FD;
  p[WRITE_END] = INVALID_FD;
}

popen2_t popen2( const char* cmd ) {
  popen2_t r = { false, { INVALID_FD, INVALID_FD } };

  if ( -1 == pipe( r.stdin ) ) { goto end; }
  if ( -1 == pipe( r.stdout ) ) { goto end; }
  if ( -1 == pipe( r.stderr ) ) { goto end; }

  switch ( (r.pid = fork()) ) {
    case -1: // Error
      goto end;

    case 0: // Child process
      dupFd( r.stdin, READ_END, STDIN_FILENO );
      dupFd( r.stdout, WRITE_END, STDOUT_FILENO );
      dupFd( r.stderr, WRITE_END, STDERR_FILENO );

      {
        char* argv[] = { "sh", "-c", (char*)cmd, NULL };

        if ( -1 == execvp( argv[0], argv ) ) { exit(0); }
      }
  }

  // Parent process
  close( r.stdin[READ_END] );
  r.stdin[READ_END] = INVALID_FD;
  close( r.stdout[WRITE_END] );
  r.stdout[WRITE_END] = INVALID_FD;
  close( r.stderr[WRITE_END] );
  r.stderr[WRITE_END] = INVALID_FD;
  r.success = true;

end:
  if ( ! r.success ) {
    if ( INVALID_FD != r.stdin[READ_END] ) { close( r.stdin[READ_END] ); }
    if ( INVALID_FD != r.stdin[WRITE_END] ) { close( r.stdin[WRITE_END] ); }
    if ( INVALID_FD != r.stdout[READ_END] ) { close( r.stdout[READ_END] ); }
    if ( INVALID_FD != r.stdout[WRITE_END] ) { close( r.stdout[WRITE_END] ); }
    if ( INVALID_FD != r.stderr[READ_END] ) { close( r.stderr[READ_END] ); }
    if ( INVALID_FD != r.stderr[WRITE_END] ) { close( r.stderr[WRITE_END] ); }

    r.stdin[READ_END] = r.stdin[WRITE_END] =
      r.stdout[READ_END] = r.stdout[WRITE_END] =
      r.stderr[READ_END] = r.stderr[WRITE_END] = INVALID_FD;
  }

  return r;
}

int main( int argc, char* argv[] ) {
  popen2_t p = popen2( "./child.out" );

  {
    int status = 0;


    sleep( 2 );

    {
      char buf[1024] = { '\0' };

      read( p.stdout[READ_END], buf, sizeof buf );
      printf( "%s", buf );
    }

    //pid_t wpid = waitpid( p.pid, &status, 0 );
    //return wpid == p.pid && WIFEXITED( status ) ? WEXITSTATUS( status ) : -1;
  }
}
我的问题是关于读取的——我不太明白为什么在子进程完成从而关闭其管道末端之前,读取看起来是阻塞的

这是巧合吗?你可以看到,我试图让主进程在子进程的中间用睡眠2语句执行它的读取。 子进程总共向其重定向的标准输出转储50个字符。主进程是否可能在孩子的执行过程中读取它,只读取其中50个字符中的n个,因此,主进程“PrtTF”不能全部打印子进程中的所有四行?

就功能而言,一切都很好-我的问题是更好地理解read

默认情况下,stdout在不向终端写入时会被完全缓冲。因此,在刷新缓冲区之前,子对象中的printf调用不会向管道写入任何内容。当缓冲区可能填满1K或4K字节或进程退出时,就会发生这种情况

您可以立即使用fflushstdout;刷新缓冲区;。在每次printf调用之后添加,您就可以在父进程中读取它们,而无需等待进程退出。

默认情况下,stdout在不向终端写入数据时会被完全缓冲。因此,在刷新缓冲区之前,子对象中的printf调用不会向管道写入任何内容。当缓冲区可能填满1K或4K字节或进程退出时,就会发生这种情况


您可以立即使用fflushstdout;刷新缓冲区;。在每次printf调用之后,您都可以在父进程中读取它们,而无需等待进程退出。

您认为它还能合理地做什么?@NateEldredge-我不能说这是合理的,但我想象管道可能像TCP套接字,如果管道中有任何东西,你可以阅读其中的一些部分,而不知道另一端是否完成了。@StoneThrow:事实就是这样。但是子级正在使用printf进行打印,当标准输出不是终端时,printf会被完全缓冲。因此,尽管printf已完成,但直到刷新缓冲区时才真正调用write,而刷新缓冲区是在子项退出时发生的。@StoneThrow:确实如此。在这种情况下,无论有多少字节可用,您都会看到read立即返回。您必须检查返回值以查看有多少字节。如果在此之后需要更多字节,请再次调用read(通常在循环中),它将阻塞,直到有一些字节可用或管道关闭。在所有正常情况下,您都希望以这种方式继续读取。就你的程序而言,家长会在孩子写东西之前关闭管道,这会导致孩子在写更多东西时被SIGPIPE杀死。你认为它还能合理地做什么?@NateEldredge-我不能说这是合理的,但我想象管道可能像TCP套接字,如果管道中有任何东西,你可以阅读其中的某个子集,而不管另一端是否完成了。@StoneThrow:这正是发生的事情。但是子级正在使用printf进行打印,当标准输出不是终端时,printf会被完全缓冲。因此,尽管printf已完成,但直到刷新缓冲区时才真正调用write,而刷新缓冲区是在子项退出时发生的。@StoneThrow:确实如此。在这种情况下,无论有多少字节可用,您都会看到read立即返回。您必须检查返回值以查看有多少字节。如果在此之后需要更多字节,请再次调用read(通常在循环中),它将阻塞,直到有一些字节可用或管道关闭。在所有正常情况下,您都希望以这种方式继续读取。按照您的程序,家长将在孩子完成写作之前关闭管道,这将导致孩子在写更多内容时被SIGPIPE杀死。同样感谢您-与NateEldredge的回答相同-我将根据您的解释进行实验。我们能够通过实验进行确认-感谢您的解释!也谢谢你-和NateEldredge的回答一样-我将根据你的解释去做实验。我们能够通过实验来确认-谢谢你的解释!
// child.c
#include <stdio.h>
#include <unistd.h>

int main( int argc, char* argv[] ) {
  printf( "%s:%d\n", __FILE__, __LINE__ );
  sleep( 1 );
  printf( "%s:%d\n", __FILE__, __LINE__ );
  sleep( 1 );
  printf( "%s:%d\n", __FILE__, __LINE__ );
  sleep( 1 );
  printf( "%s:%d\n", __FILE__, __LINE__ );
  sleep( 1 );
  return 0;
}
$ gcc --version && gcc -g ./child.c -o ./child.out && gcc -g ./main.c && ./a.out
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

./child.c:6
./child.c:8
./child.c:10
./child.c:12
$