【发布时间】:2016-09-14 18:57:01
【问题描述】:
我已经构建了以下程序来尝试在我自己的 shell 中进行管道传输。 StringArray 只是我构建的 char**。代码运行良好,但是当我输入cat txt.txt | grep a 时,屏幕上没有任何内容。调试时,我看到代码似乎停在 152 左右(打印输出命令所在的位置),pid==0 和 i==0。
对于上下文,我在检测到管道后在另一个函数中调用此函数。
void doPipe(StringArray sa)
{
printf("In 69\n");
int filedes[2]; // pos. 0 output, pos. 1 input of the pipe
int filedes2[2];
int num_cmds = 0;
char *command[256];
pid_t pid;
int err = -1;
int end = 0;
// Variables used for the different loops
int i = 0;
int j = 0;
int k = 0;
int l = 0;
// First we calculate the number of commands (they are separated
// by '|')
while (sa[l] != NULL){
if (strcmp(sa[l],"|") == 0){
num_cmds++;
}
l++;
}
num_cmds++;
// Main loop of this method. For each command between '|', the
// pipes will be configured and standard input and/or output will
// be replaced. Then it will be executed
while (sa[j] != NULL && end != 1){
k = 0;
// We use an auxiliary array of pointers to store the command
// that will be executed on each iteration
while (strcmp(sa[j],"|") != 0){
command[k] = sa[j];
j++;
if (sa[j] == NULL){
// 'end' variable used to keep the program from entering
// again in the loop when no more arguments are found
end = 1;
k++;
break;
}
k++;
}
// Last position of the command will be NULL to indicate that
// it is its end when we pass it to the exec function
command[k] = NULL;
j++;
printf("In 121\n");
// Depending on whether we are in an iteration or another, we
// will set different descriptors for the pipes inputs and
// output. This way, a pipe will be shared between each two
// iterations, enabling us to connect the inputs and outputs of
// the two different commands.
if (i % 2 != 0){
pipe(filedes); // for odd i
}else{
pipe(filedes2); // for even i
}
pid=fork();
if(pid==-1){
if (i != num_cmds - 1){
if (i % 2 != 0){
close(filedes[1]); // for odd i
}else{
close(filedes2[1]); // for even i
}
}
printf("Child process could not be created\n");
return;
}
if(pid==0){
printf("In 148\n");
// If we are in the first command
if (i == 0){
printf("In 152\n");
dup2(filedes2[1], STDOUT_FILENO);
}
// If we are in the last command, depending on whether it
// is placed in an odd or even position, we will replace
// the standard input for one pipe or another. The standard
// output will be untouched because we want to see the
// output in the terminal
else if (i == num_cmds - 1){
printf("In 162\n");
if (num_cmds % 2 != 0){ // for odd number of commands
dup2(filedes[0],STDIN_FILENO);
printf("In 166\n");
}else{ // for even number of commands
dup2(filedes2[0],STDIN_FILENO);
printf("In 166\n");
}
// If we are in a command that is in the middle, we will
// have to use two pipes, one for input and another for
// output. The position is also important in order to choose
// which file descriptor corresponds to each input/output
}else{ // for odd i
if (i % 2 != 0){
dup2(filedes2[0],STDIN_FILENO);
dup2(filedes[1],STDOUT_FILENO);
}else{ // for even i
dup2(filedes[0],STDIN_FILENO);
dup2(filedes2[1],STDOUT_FILENO);
}
}
if (execvp(command[0],command)==err){
kill(getpid(),SIGTERM);
}
}
// CLOSING DESCRIPTORS ON PARENT
if (i == 0){
close(filedes2[1]);
}
else if (i == num_cmds - 1){
if (num_cmds % 2 != 0){
close(filedes[0]);
}else{
close(filedes2[0]);
}
}else{
if (i % 2 != 0){
close(filedes2[0]);
close(filedes[1]);
}else{
close(filedes[0]);
close(filedes2[1]);
}
}
waitpid(pid,NULL,0);
i++;
}
}
【问题讨论】:
-
父母似乎在等待每个孩子退出,然后再分叉下一个。这在几个层面上都是错误的。它需要启动所有个孩子,然后等待所有孩子。
-
在
execvp()失败的情况下,为什么孩子给自己发SIGTERM?只需exit()会容易得多。另外,您可以根据需要测试execvp()的返回值,但您不需要这样做,因为它只有在失败时才会返回。 -
您似乎有
printf()调用进行调试,您在其中硬编码行号。出于调试目的,打印语句没有问题,但您应该考虑使用__LINE__宏而不是硬编码行号。 -
你在没有检查返回值的情况下进行了很多系统调用。你真的应该检查每一个。预处理器可以帮助您做到这一点,同时最大限度地减少代码的丑化。
-
在两个文件描述符数组之间来回翻转是很聪明的,但反复测试
i的奇偶性以确定将哪个用于任何特定目的不是。通过为文件描述符创建一个 2x2 数组并战略性地使用指针,您可以大大简化所有这些操作。