如何解决为什么读取会阻塞管道,直到关闭写入端?
我正在努力增强对与fork
,exec
,dup
相关的事物的理解,并重定向stdin
/ stdout
/ {{1 }},编写以下stderr
型函数:
popen
// 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,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;
}
}
编译和执行:
// 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",__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
$
的-我不太理解为什么read()
在子进程完成之前似乎阻塞了(从而关闭了管道的末端)?
这是巧合吗?您可以看到我试图用read()
语句在子进程执行的中间“读取”主进程。
总共,子进程将50个字符转储到其(重定向的)stdout。 主进程是否有可能在孩子执行过程中执行sleep( 2 )
并且仅读取其中50个字符中的N个,因此主进程的printf()不会完整打印子进程中的所有四行?
(从功能上讲,一切都很好-我的问题是加深对read()
的理解)
解决方法
默认情况下,stdout
在未写入终端时被完全缓冲。因此,在刷新缓冲区之前,您的printf()
子级调用不会将任何内容写入管道。当缓冲区填满(可能是1K或4K字节)或进程退出时,就会发生这种情况。
您可以立即用fflush(stdout);
刷新缓冲区。在每个printf()
调用之后添加它,您将能够在父级中读取它们,而无需等待进程退出。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。