我有这个程序,为孩子的stdin,stdout和&amp ;;分叉并创建三个POSIX管道。标准错误。在分叉之后,子和父关闭它们各自管道的适当端,使得子只能从stdin管道读取并且只写入stdout和stderr管道(而父对面的情况则相反)。接下来,子进程关闭并将子进程的stdin,stdout和stderr复制到其打开的管道端,然后使用execvp执行其名称作为父进程的参数传入的程序。该程序的行为与cat,ls,rm,banner,&等命令的行为相同。 ps没有任何明显的问题。
然而,当我在终端中运行程序时,这样:
程序grep blah
它需要输入但不向终端输出任何内容但是当我尝试时:
终端中的grep blah
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);
}
}
}
答案 0 :(得分:7)
许多程序在处理终端输入/输出时都是行缓冲的,但在其他情况下使用更大的块(例如管道)。 grep
就是其中之一。
如果您正在使用GNU coreutils中的grep,则可以使用grep --line-buffered
强制grep
逐行生成输出,而不是等待更多数据排队。但是,当您在stdin上收到EOF时,您真的应该修复程序以关闭输入管道的末尾:然后grep
会在输入管道的末端注意到EOF,并刷新其剩余的输出。