当 grep 通过 POSIX 管道接收输入并输出到 POSIX 管道时,它的行为是什么?
我有一个程序,可以为子进程的 stdin、stdout 和 & 分叉并创建三个 POSIX 管道。标准错误。分叉后,子进程和父进程关闭各自管道的相应端,这样子进程只能从 stdin 管道读取数据,并且只能写入 stdout 和 stderr 管道(父进程则相反)。接下来,子进程关闭并将子进程的 stdin、stdout 和 stderr 复制到其开放的管道末端,然后使用 execvp 执行名称作为父进程的程序参数传入的程序。程序的行为与 cat、ls、rm、banner 等命令应有的行为相同。 ps没有任何明显的问题。
但是,当我在终端中运行该程序时,如下所示:
程序 grep 等等
它接受输入但不向终端输出任何内容,但是当我尝试时:
grep 等等
grep 等待输入,如果输入包含单词“blah”,则将其打印回来。那么问题来了,这是因为我的程序有问题吗?如下所示?或者,当 grep 必须通过 POSIX 管道进行通信时,它的这种行为是否正常?感谢您的阅读。
这是有问题的程序(请原谅第一次发帖的格式):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <limits.h>
int status, cpid; int child = 1;
char mode = 'c';
sigset_t sigMask;
void childHandler(){
sigprocmask(SIG_BLOCK,&sigMask,NULL);
int i = waitpid(-1,&status,WNOHANG);
if(i){
fprintf(stderr, "The child <%d> has terminated with code <%d>\n",cpid, WEXITSTATUS(status));
child = 0;
mode = 'c';
}
sigprocmask(SIG_UNBLOCK,&sigMask,NULL);
}
int DEBUG = 0;
int SLOW = 1;
fd_set readfds,writefds,errorfds;
struct timeval timeout;
int numready;
int cin,cout,cerr,in,out,err;
int dcout = 0;
int dcerr = 0;
int p2c_in[2], c2p_out[2], c2p_err[2];
int maxfd = 2;
int tout = 0;
void doSelect(){
FD_ZERO(&writefds);
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
FD_SET(fileno(stdout), &writefds);
FD_SET(fileno(stderr), &writefds);
FD_SET(p2c_in[1], &writefds);
FD_SET(c2p_out[0], &readfds);
FD_SET(c2p_err[0], &readfds);
if(!tout) {
numready = select((maxfd + 1),&readfds,&writefds,NULL,NULL);
}
else numready = select((maxfd + 1),&readfds,&writefds,NULL,&timeout);
in = FD_ISSET(fileno(stdin), &readfds);
out = FD_ISSET(fileno(stdout), &writefds);
err = FD_ISSET(fileno(stderr), &writefds);
cin = FD_ISSET(p2c_in[1], &writefds);
if(dcout) cout = 0;
else cout = FD_ISSET(c2p_out[0], &readfds);
if(dcerr) cerr = 0;
else cerr = FD_ISSET(c2p_err[0], &readfds);
if(DEBUG){
fprintf(stderr,"\nstdin: %d\n",in);
fprintf(stderr,"stdout: %d\n",out);
fprintf(stderr,"stderr: %d\n",err);
fprintf(stderr,"p2c_in[1]: %d\n",cin);
fprintf(stderr,"c2p_out[0]: %d\n",cout);
fprintf(stderr,"c2p_err[0]: %d\n\n",cerr);
if(SLOW)sleep(2);
}
}
int main(int agrc, char* argv[]){
//Naming convention of pipes: int [intial of write-end process]2[intial of read-end process] &
//_err represents the file descriptor which the pipe is suppose to 'filter'
int val = 0, dblval = 0;
int pipe_result,signal_result,close_result;
//This pipe has the parent writing and the child reading from the parent's input this pipe is to filter the child's stdin
if((pipe_result = pipe(p2c_in)) < 0){
perror("Creation of the pipe for the child's stdin has failed");
return pipe_result;
}
if(p2c_in[1] > maxfd) maxfd = p2c_in[1];
//This pipe has the child writing and the parent reading from the child's input this pipe is to filter the child's stdout
if((pipe_result = pipe(c2p_out)) < 0){
perror("Creation of the pipe for the child's stdout has failed");
return pipe_result;
}
if(c2p_out[0] > maxfd) maxfd = c2p_out[0];
//This pipe has the child writing and the parent reading from the child's input this pipe is to filter the child's stderr
if((pipe_result = pipe(c2p_err)) < 0){
perror("Creation of the pipe for the child's stderr has failed");
return pipe_result;
}
if(c2p_err[0] > maxfd) maxfd = c2p_err[0];
if(DEBUG){
fprintf(stderr,"stdin: %d\n",fileno(stdin));
fprintf(stderr,"stdout: %d\n",fileno(stdout));
fprintf(stderr,"stderr: %d\n",fileno(stderr));
fprintf(stderr,"p2c_in[0]: %d\n",p2c_in[0]);
fprintf(stderr,"p2c_in[1]: %d\n",p2c_in[1]);
fprintf(stderr,"c2p_out[0]: %d\n",c2p_out[0]);
fprintf(stderr,"c2p_out[1]: %d\n",c2p_out[1]);
fprintf(stderr,"c2p_err[0]: %d\n",c2p_err[0]);
fprintf(stderr,"c2p_err[1]: %d\n",c2p_err[1]);
}
sigemptyset(&sigMask);
sigaddset(&sigMask,SIGCHLD);
if((signal_result = signal(SIGCHLD, childHandler)) == SIG_ERR){
perror("Signal handler binding has failed");
return signal_result;
}
//This creates the child process
if((cpid = fork()) < 0){
perror("Creation of the child process has failed");
return cpid;
}
if(!cpid){ //Child-exclusive code
//This makes it so the child can only read input from the stdin pipe
if((close_result = close(p2c_in[1])) < 0){
perror("Closing the write end of the child's stdin pipe has failed");
exit(close_result);
}
//This makes it so the child can only write input to the stdout pipe
if((close_result = close(c2p_out[0])) < 0){
perror("Closing the read end of the child's stdout pipe has failed");
exit(close_result);
}
//This makes it so the child can only write input to the stderr pipe
if((close_result = close(c2p_err[0])) < 0){
perror("Closing the read end of the child's stderr pipe has failed");
exit(close_result);
}
//This closes the child's standard input
if((close_result = close(0)) < 0){
perror("Closing the child's standard input has failed");
exit(close_result);
}
int dup_result;
if((dup_result = dup(p2c_in[0])) < 0){
perror("Duplication of the read end of the child's stdin pipe has failed");
exit(dup_result);
}
//This closes the child's standard output
if((close_result = close(1)) < 0){
perror("Closing the child's standard output has failed");
exit(close_result);
}
if((dup_result = dup(c2p_out[1])) < 0){
perror("Duplication of the write end of the child's stdout pipe has failed");
exit(dup_result);
}
//This closes the child's standard error
if((close_result = close(2)) < 0){
perror("Closing the child's standard error has failed");
exit(close_result);
}
if((dup_result = dup(c2p_err[1])) < 0){
perror("Duplication of the write end of the child's stderr pipe has failed");
exit(dup_result);
}
execvp(argv[1], &argv[1]);
perror("execvp has failed:");
if((close_result = close(p2c_in[0])) < 0){
perror("Closing the write end of the child's stdin pipe has failed");
exit(close_result);
}
if((close_result = close(c2p_out[1])) < 0){
perror("Closing the read end of the child's stdout pipe has failed");
exit(close_result);
}
if((close_result = close(c2p_err[1])) < 0){
perror("Closing the read end of the child's stderr pipe has failed");
exit(close_result);
}
exit(66); //This occurs after p2c_in's write end is closed
} else { //Parent-exclusive code
//This makes it so the parent can only write input to the stdin pipe
if((close_result = close(p2c_in[0])) < 0){
perror("Closing the read end of the child's stdin pipe has failed");
return close_result;
}
//This makes it so the parent can read only input from the stdout pipe
if((close_result = close(c2p_out[1])) < 0){
perror("Closing the write end of the child's stdout pipe has failed");
return close_result;
}
//This makes it so the parent can read only input from the stderr pipe
if((close_result = close(c2p_err[1])) < 0){
perror("Closing the write end of the child's stderr pipe has failed");
return close_result;
}
timeout.tv_sec = 1; timeout.tv_usec = 0;
char mode = 'c';
char t_mode;
int maxlines = 20;
int sig; int lcount = 0; int fill = 0;
char buf_err;
char buf_in[MAX_CANON];
char buf_out;
int read_resultin,read_resultout,read_resulterr;
int write_resultin,write_resultout,write_resulterr;
while(1){
if(DEBUG)fprintf(stderr,"<%d> While Begin \n",getpid());
doSelect();
if(in && cin){
if(((read_resultin = read(fileno(stdin), &buf_in, MAX_CANON)) > 0) && child){
//write(2, &buf_in, read_resultin);
if((write_resultin = write(p2c_in[1], &buf_in, read_resultin)) > -1){
fill = write_resultin;
if(DEBUG)fprintf(stderr,"<%d> Input \n",getpid());
doSelect();
while(cin && child && (fill < read_resultin)){
if(DEBUG)fprintf(stderr,"<%d> Input Loop \n",getpid());
write_resultin = write(p2c_in[1], &buf_in, read_resultin - fill);
fill += write_resultin;
doSelect();
}
//if(read_resultin < 0) perror("read from stdin without a / failed");
}
else perror("Reading to child's stdin has failed");
}
}
if(DEBUG)fprintf(stderr,"<%d> Input Loop End \n",getpid());
doSelect();
if (out && cout){
if(DEBUG)fprintf(stderr,"<%d> Output \n",getpid());
while(out && cout){
if(DEBUG)fprintf(stderr,"<%d> Output Loop \n",getpid());
if((read_resultout = read(c2p_out[0], &buf_out, 1)) == 1)
write(fileno(stdout), &buf_out, 1);
else if(!read_resultout && !child/**/){
if(DEBUG)fprintf(stderr,"<%d> No More Output \n",getpid());
dcout = 1;
}
else if(read_resultout < 0)
perror("Output Read");
doSelect();
}
}
if(DEBUG)fprintf(stderr,"<%d> Output End \n",getpid());
doSelect();
if (err && cerr){
/**/if(DEBUG)fprintf(stdout,"<%d> Error \n",getpid());
while(err && cerr){
/**/if(DEBUG)fprintf(stdout,"<%d> Error Loop \n",getpid());
if((read_resulterr = read(c2p_err[0], &buf_err, 1)) == 1){
write_resulterr = write(fileno(stderr), &buf_err, 1);
if(write_resulterr < 0) perror("Error Wirte");
}else if(!read_resulterr&& !child/**/){
dcerr = 1;
if(DEBUG)fprintf(stderr,"<%d> No More Error \n",getpid());
}
else if(read_resulterr < 1) perror("Error Read");
doSelect();
}
/**/if(DEBUG)fprintf(stdout,"<%d> Error Loop End \n",getpid());
if(read_resulterr < 0) perror("Reading from child's stdout has failed");
/**/if(DEBUG)fprintf(stdout,"<%d> Error End \n",getpid());
}
if(!child && dcout && dcerr) break;
}
//This closes up the write end of p2c_in that way the child will be able to terminate for it will recieve an EOF
if((close_result = close(p2c_in[1])) < 0){
perror("The last closing of the write end of the child's stdin pipe has failed");
exit(close_result);
}
//This closes up the read end of c2p_out
if((close_result = close(c2p_out[0])) < 0){
perror("The last closing of the read end of the child's stdout pipe has failed");
exit(close_result);
}
//This closes up the read end of c2p_err
if((close_result = close(c2p_err[0])) < 0){
perror("The last closing of the read end of the child's stderr pipe has failed");
exit(close_result);
}
}
}
I have this program that forks and creates three POSIX pipes for the child's stdin, stdout, & stderr. After forking, the child and parent closes the appropriate ends of their respective pipes such that the child can only read from the stdin pipe and only write to the stdout and stderr pipes (and the opposite is true of the parent). Next, the child process closes and dups the child's stdin, stdout, and stderr to its open pipe ends and then uses execvp to execute the program whose name was passed in as the parent's program arguments. The program when behaves as it should for commands such as cat,ls,rm,banner,& ps without any noticeable issue.
However, when I run the program in the terminal like this:
program grep blah
it takes input but doesn't output anything to the terminal but when I try:
grep blah
in the terminal grep waits for input and prints it back out if the input contained the word "blah". So the question is, is this because of something wrong with my program as shown below? Or is it normal for grep to behave this way when it must communicate via POSIX pipes? Thank you for reading.
Here is the program in question (please excuse the formatting first time posting):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <limits.h>
int status, cpid; int child = 1;
char mode = 'c';
sigset_t sigMask;
void childHandler(){
sigprocmask(SIG_BLOCK,&sigMask,NULL);
int i = waitpid(-1,&status,WNOHANG);
if(i){
fprintf(stderr, "The child <%d> has terminated with code <%d>\n",cpid, WEXITSTATUS(status));
child = 0;
mode = 'c';
}
sigprocmask(SIG_UNBLOCK,&sigMask,NULL);
}
int DEBUG = 0;
int SLOW = 1;
fd_set readfds,writefds,errorfds;
struct timeval timeout;
int numready;
int cin,cout,cerr,in,out,err;
int dcout = 0;
int dcerr = 0;
int p2c_in[2], c2p_out[2], c2p_err[2];
int maxfd = 2;
int tout = 0;
void doSelect(){
FD_ZERO(&writefds);
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
FD_SET(fileno(stdout), &writefds);
FD_SET(fileno(stderr), &writefds);
FD_SET(p2c_in[1], &writefds);
FD_SET(c2p_out[0], &readfds);
FD_SET(c2p_err[0], &readfds);
if(!tout) {
numready = select((maxfd + 1),&readfds,&writefds,NULL,NULL);
}
else numready = select((maxfd + 1),&readfds,&writefds,NULL,&timeout);
in = FD_ISSET(fileno(stdin), &readfds);
out = FD_ISSET(fileno(stdout), &writefds);
err = FD_ISSET(fileno(stderr), &writefds);
cin = FD_ISSET(p2c_in[1], &writefds);
if(dcout) cout = 0;
else cout = FD_ISSET(c2p_out[0], &readfds);
if(dcerr) cerr = 0;
else cerr = FD_ISSET(c2p_err[0], &readfds);
if(DEBUG){
fprintf(stderr,"\nstdin: %d\n",in);
fprintf(stderr,"stdout: %d\n",out);
fprintf(stderr,"stderr: %d\n",err);
fprintf(stderr,"p2c_in[1]: %d\n",cin);
fprintf(stderr,"c2p_out[0]: %d\n",cout);
fprintf(stderr,"c2p_err[0]: %d\n\n",cerr);
if(SLOW)sleep(2);
}
}
int main(int agrc, char* argv[]){
//Naming convention of pipes: int [intial of write-end process]2[intial of read-end process] &
//_err represents the file descriptor which the pipe is suppose to 'filter'
int val = 0, dblval = 0;
int pipe_result,signal_result,close_result;
//This pipe has the parent writing and the child reading from the parent's input this pipe is to filter the child's stdin
if((pipe_result = pipe(p2c_in)) < 0){
perror("Creation of the pipe for the child's stdin has failed");
return pipe_result;
}
if(p2c_in[1] > maxfd) maxfd = p2c_in[1];
//This pipe has the child writing and the parent reading from the child's input this pipe is to filter the child's stdout
if((pipe_result = pipe(c2p_out)) < 0){
perror("Creation of the pipe for the child's stdout has failed");
return pipe_result;
}
if(c2p_out[0] > maxfd) maxfd = c2p_out[0];
//This pipe has the child writing and the parent reading from the child's input this pipe is to filter the child's stderr
if((pipe_result = pipe(c2p_err)) < 0){
perror("Creation of the pipe for the child's stderr has failed");
return pipe_result;
}
if(c2p_err[0] > maxfd) maxfd = c2p_err[0];
if(DEBUG){
fprintf(stderr,"stdin: %d\n",fileno(stdin));
fprintf(stderr,"stdout: %d\n",fileno(stdout));
fprintf(stderr,"stderr: %d\n",fileno(stderr));
fprintf(stderr,"p2c_in[0]: %d\n",p2c_in[0]);
fprintf(stderr,"p2c_in[1]: %d\n",p2c_in[1]);
fprintf(stderr,"c2p_out[0]: %d\n",c2p_out[0]);
fprintf(stderr,"c2p_out[1]: %d\n",c2p_out[1]);
fprintf(stderr,"c2p_err[0]: %d\n",c2p_err[0]);
fprintf(stderr,"c2p_err[1]: %d\n",c2p_err[1]);
}
sigemptyset(&sigMask);
sigaddset(&sigMask,SIGCHLD);
if((signal_result = signal(SIGCHLD, childHandler)) == SIG_ERR){
perror("Signal handler binding has failed");
return signal_result;
}
//This creates the child process
if((cpid = fork()) < 0){
perror("Creation of the child process has failed");
return cpid;
}
if(!cpid){ //Child-exclusive code
//This makes it so the child can only read input from the stdin pipe
if((close_result = close(p2c_in[1])) < 0){
perror("Closing the write end of the child's stdin pipe has failed");
exit(close_result);
}
//This makes it so the child can only write input to the stdout pipe
if((close_result = close(c2p_out[0])) < 0){
perror("Closing the read end of the child's stdout pipe has failed");
exit(close_result);
}
//This makes it so the child can only write input to the stderr pipe
if((close_result = close(c2p_err[0])) < 0){
perror("Closing the read end of the child's stderr pipe has failed");
exit(close_result);
}
//This closes the child's standard input
if((close_result = close(0)) < 0){
perror("Closing the child's standard input has failed");
exit(close_result);
}
int dup_result;
if((dup_result = dup(p2c_in[0])) < 0){
perror("Duplication of the read end of the child's stdin pipe has failed");
exit(dup_result);
}
//This closes the child's standard output
if((close_result = close(1)) < 0){
perror("Closing the child's standard output has failed");
exit(close_result);
}
if((dup_result = dup(c2p_out[1])) < 0){
perror("Duplication of the write end of the child's stdout pipe has failed");
exit(dup_result);
}
//This closes the child's standard error
if((close_result = close(2)) < 0){
perror("Closing the child's standard error has failed");
exit(close_result);
}
if((dup_result = dup(c2p_err[1])) < 0){
perror("Duplication of the write end of the child's stderr pipe has failed");
exit(dup_result);
}
execvp(argv[1], &argv[1]);
perror("execvp has failed:");
if((close_result = close(p2c_in[0])) < 0){
perror("Closing the write end of the child's stdin pipe has failed");
exit(close_result);
}
if((close_result = close(c2p_out[1])) < 0){
perror("Closing the read end of the child's stdout pipe has failed");
exit(close_result);
}
if((close_result = close(c2p_err[1])) < 0){
perror("Closing the read end of the child's stderr pipe has failed");
exit(close_result);
}
exit(66); //This occurs after p2c_in's write end is closed
} else { //Parent-exclusive code
//This makes it so the parent can only write input to the stdin pipe
if((close_result = close(p2c_in[0])) < 0){
perror("Closing the read end of the child's stdin pipe has failed");
return close_result;
}
//This makes it so the parent can read only input from the stdout pipe
if((close_result = close(c2p_out[1])) < 0){
perror("Closing the write end of the child's stdout pipe has failed");
return close_result;
}
//This makes it so the parent can read only input from the stderr pipe
if((close_result = close(c2p_err[1])) < 0){
perror("Closing the write end of the child's stderr pipe has failed");
return close_result;
}
timeout.tv_sec = 1; timeout.tv_usec = 0;
char mode = 'c';
char t_mode;
int maxlines = 20;
int sig; int lcount = 0; int fill = 0;
char buf_err;
char buf_in[MAX_CANON];
char buf_out;
int read_resultin,read_resultout,read_resulterr;
int write_resultin,write_resultout,write_resulterr;
while(1){
if(DEBUG)fprintf(stderr,"<%d> While Begin \n",getpid());
doSelect();
if(in && cin){
if(((read_resultin = read(fileno(stdin), &buf_in, MAX_CANON)) > 0) && child){
//write(2, &buf_in, read_resultin);
if((write_resultin = write(p2c_in[1], &buf_in, read_resultin)) > -1){
fill = write_resultin;
if(DEBUG)fprintf(stderr,"<%d> Input \n",getpid());
doSelect();
while(cin && child && (fill < read_resultin)){
if(DEBUG)fprintf(stderr,"<%d> Input Loop \n",getpid());
write_resultin = write(p2c_in[1], &buf_in, read_resultin - fill);
fill += write_resultin;
doSelect();
}
//if(read_resultin < 0) perror("read from stdin without a / failed");
}
else perror("Reading to child's stdin has failed");
}
}
if(DEBUG)fprintf(stderr,"<%d> Input Loop End \n",getpid());
doSelect();
if (out && cout){
if(DEBUG)fprintf(stderr,"<%d> Output \n",getpid());
while(out && cout){
if(DEBUG)fprintf(stderr,"<%d> Output Loop \n",getpid());
if((read_resultout = read(c2p_out[0], &buf_out, 1)) == 1)
write(fileno(stdout), &buf_out, 1);
else if(!read_resultout && !child/**/){
if(DEBUG)fprintf(stderr,"<%d> No More Output \n",getpid());
dcout = 1;
}
else if(read_resultout < 0)
perror("Output Read");
doSelect();
}
}
if(DEBUG)fprintf(stderr,"<%d> Output End \n",getpid());
doSelect();
if (err && cerr){
/**/if(DEBUG)fprintf(stdout,"<%d> Error \n",getpid());
while(err && cerr){
/**/if(DEBUG)fprintf(stdout,"<%d> Error Loop \n",getpid());
if((read_resulterr = read(c2p_err[0], &buf_err, 1)) == 1){
write_resulterr = write(fileno(stderr), &buf_err, 1);
if(write_resulterr < 0) perror("Error Wirte");
}else if(!read_resulterr&& !child/**/){
dcerr = 1;
if(DEBUG)fprintf(stderr,"<%d> No More Error \n",getpid());
}
else if(read_resulterr < 1) perror("Error Read");
doSelect();
}
/**/if(DEBUG)fprintf(stdout,"<%d> Error Loop End \n",getpid());
if(read_resulterr < 0) perror("Reading from child's stdout has failed");
/**/if(DEBUG)fprintf(stdout,"<%d> Error End \n",getpid());
}
if(!child && dcout && dcerr) break;
}
//This closes up the write end of p2c_in that way the child will be able to terminate for it will recieve an EOF
if((close_result = close(p2c_in[1])) < 0){
perror("The last closing of the write end of the child's stdin pipe has failed");
exit(close_result);
}
//This closes up the read end of c2p_out
if((close_result = close(c2p_out[0])) < 0){
perror("The last closing of the read end of the child's stdout pipe has failed");
exit(close_result);
}
//This closes up the read end of c2p_err
if((close_result = close(c2p_err[0])) < 0){
perror("The last closing of the read end of the child's stderr pipe has failed");
exit(close_result);
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
许多程序在处理终端输入/输出时都是行缓冲的,但在其他情况下(例如管道)使用更大的块。 grep 就是其中之一。
如果您使用 GNU coreutils 中的 grep,则可以使用 grep --line-buffered 强制 grep 逐行生成输出,而不是等待更多数据排队。但是,当您在 stdin 上收到 EOF 时,您确实应该修复程序以关闭其输入管道的末端:然后 grep 会注意到输入管道末端的 EOF,并刷新其剩余的输出。
Many programs are line-buffered when dealing with terminal input/ouput but use much larger chunks in other cases (such as pipes).
grep
is one of them.If you are using grep from GNU coreutils, you can use
grep --line-buffered
to forcegrep
to produce output line-by-line instead of waiting for more data to queue up. However, you really should fix your program to close its end of the input pipe when you receive EOF on stdin: thengrep
will notice an EOF on its end of the input pipe, and flush its remaining output.