wc: 'standard input': 错误的文件描述符
wc: 'standard input': Bad file descriptor
我正在使用 Exec 函数系列在 C 语言中模拟 bash 命令。
这只是更大项目的部分代码。为了简单起见,现在大部分内容都是硬编码的,但将来我需要使用管道或重定向运算符执行任何 bash 命令,这就是代码处于循环中的原因。我知道我可以使用单个文件描述符(没有 2d 数组)来实现当前示例的结果,但我需要概括多个 |, > 命令的代码。我无法删除循环。
即使在正确关闭所有描述符后,我仍收到错误的描述符错误。找不到原因。
int main()
{
int numOfCmds = 2;
int numOfPipes = numOfCmds -1;
int pipes[numOfPipes][2];
for(int i=0;i<numOfPipes;i++)
if(pipe(pipes[i])<0) return 1;
for(int i=0;i<numOfCmds;i++)
{
int child = fork();
if(child == 0)
{
if(i==0)
{
for(int i=0;i<numOfPipes;i++)
{
close(pipes[i][0]);
}
printf("hey\n");
dup2(pipes[i][1], STDOUT_FILENO);
close(pipes[i][1]);
char *cmd1_args[3] = {"ls", "-l", NULL};
execvp(cmd1_args[0], cmd1_args);
}
if(i==1)
{
for(int i=0;i<numOfPipes;i++)
{
close(pipes[i][1]);
}
printf("bye\n");
dup2(pipes[i][0], STDIN_FILENO);
close(pipes[i][0]);
char *cmd2_args[3] = {"wc", "-l", NULL};
execvp(cmd2_args[0], cmd2_args);
}
return 0;
}
}
for(int i=0;i<numOfCmds;i++)
{
for(int j=0;i<2;j++)
close(pipes[i][j]);
}
for(int i=0;i<numOfCmds;i++) wait(NULL);
return 0;
错误:
hey bye wc: 'standard input': Bad file descriptor 0 wc: -: Bad file
descriptor Segmentation fault (core dumped)
您的问题是您正在重用(嵌套)i
的定义。使用 GCC 的 -Wshadow
选项来避免这样做。 i
的值有时会超出文件描述符对数组的末尾,这会导致混乱(未定义的行为)。
你有:
for(int i=0;i<numOfCmds;i++)
{
int child = fork();
if(child == 0)
{
if(i==0)
{
for(int i=0;i<numOfPipes;i++)
{
close(pipes[i][0]);
}
printf("hey\n");
dup2(pipes[i][1], STDOUT_FILENO);
close(pipes[i][1]);
char *cmd1_args[3] = {"ls", "-l", NULL};
execvp(cmd1_args[0], cmd1_args);
}
if(i==1)
{
for(int i=0;i<numOfPipes;i++)
{
close(pipes[i][1]);
}
printf("bye\n");
dup2(pipes[i][0], STDIN_FILENO);
close(pipes[i][0]);
char *cmd2_args[3] = {"wc", "-l", NULL};
execvp(cmd2_args[0], cmd2_args);
}
return 0;
}
}
你的(外)循环控制变量是i
;然后你有一个由关闭一些管道的不同 i
控制的内部循环。我使用的 GCC 报告说您正在 dup2()
调用 wc
.
中越界访问 pipes[i][0]
您需要重新使用 i
(使用一些不同的名称 — j
和 k
(对于 'kids'?)是可用的)。并且 re-review 您正在仔细关闭哪些文件管道描述符。
您的父循环关闭文件描述符是:
for(int i=0;i<numOfCmds;i++)
{
for(int j=0;i<2;j++)
close(pipes[i][j]);
}
您需要使用 numOfPipes
而不是 numOfCmds
,并且您需要您的内部循环来测试 j
,而不是 i
。
这是一些经过大量检测的有效代码。但修复不是很普遍:
/* SO 7165-1018 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int numOfCmds = 2;
int numOfPipes = numOfCmds - 1;
int pipes[numOfPipes][2];
for(int i=0;i<numOfPipes;i++)
if(pipe(pipes[i])<0) return 1;
for (int i = 0; i < numOfPipes; i++)
printf("%d: pipe[%d][0] = %d; pipe[%d][1] = %d\n",
getpid(), i, pipes[i][0], i, pipes[i][1]);
for(int i=0;i<numOfCmds;i++)
{
int child = fork();
printf("%d: child = %d\n", getpid(), child);
if(child == 0)
{
if(i==0)
{
for(int i=0;i<numOfPipes;i++)
{
printf("%d: ls: close %d\n", getpid(), pipes[i][0]);
close(pipes[i][0]);
}
printf("%d: hey\n", getpid());
dup2(pipes[i][1], STDOUT_FILENO);
printf("%d: ls: close %d\n", getpid(), pipes[i][1]);
close(pipes[i][1]);
char *cmd1_args[3] = {"ls", "-l", NULL};
execvp(cmd1_args[0], cmd1_args);
fprintf(stderr, "failed to execute %s\n", cmd1_args[0]);
exit(EXIT_FAILURE);
}
if(i==1)
{
for(int i=0;i<numOfPipes;i++)
{
printf("%d: wc: close %d\n", getpid(), pipes[i][1]);
close(pipes[i][1]);
}
printf("%d: bye\n", getpid());
dup2(pipes[0][0], STDIN_FILENO);
printf("%d: wc: close %d\n", getpid(), pipes[0][0]);
close(pipes[0][0]);
char *cmd2_args[3] = {"wc", "-l", NULL};
execvp(cmd2_args[0], cmd2_args);
fprintf(stderr, "failed to execute %s\n", cmd2_args[0]);
exit(EXIT_FAILURE);
}
return 0;
}
}
for(int i=0;i<numOfPipes;i++)
{
for(int j=0;j<2;j++)
{
printf("parent (%d): close %d\n", getpid(), pipes[i][j]);
close(pipes[i][j]);
}
}
for(int i=0;i<numOfCmds;i++)
{
int corpse;
int status;
if ((corpse = wait(&status)) > 0)
printf("%d: child %d exited with status 0x%.4X\n", getpid(), corpse, status);
}
return 0;
}
一个示例 运行(程序 sh83
,编译自 sh83.c
)产生:
42654: pipe[0][0] = 3; pipe[0][1] = 4
42654: child = 42655
42655: child = 0
42655: ls: close 3
42654: child = 42656
parent (42654): close 3
parent (42654): close 4
42655: hey
42656: child = 0
42656: wc: close 4
42656: bye
42656: wc: close 3
128
42654: child 42655 exited with status 0x0000
42654: child 42656 exited with status 0x0000
如果您使用多个进程,我发现在大多数消息前加上 PID 会很有帮助 — 如输出所示。
我正在使用 Exec 函数系列在 C 语言中模拟 bash 命令。
这只是更大项目的部分代码。为了简单起见,现在大部分内容都是硬编码的,但将来我需要使用管道或重定向运算符执行任何 bash 命令,这就是代码处于循环中的原因。我知道我可以使用单个文件描述符(没有 2d 数组)来实现当前示例的结果,但我需要概括多个 |, > 命令的代码。我无法删除循环。
即使在正确关闭所有描述符后,我仍收到错误的描述符错误。找不到原因。
int main()
{
int numOfCmds = 2;
int numOfPipes = numOfCmds -1;
int pipes[numOfPipes][2];
for(int i=0;i<numOfPipes;i++)
if(pipe(pipes[i])<0) return 1;
for(int i=0;i<numOfCmds;i++)
{
int child = fork();
if(child == 0)
{
if(i==0)
{
for(int i=0;i<numOfPipes;i++)
{
close(pipes[i][0]);
}
printf("hey\n");
dup2(pipes[i][1], STDOUT_FILENO);
close(pipes[i][1]);
char *cmd1_args[3] = {"ls", "-l", NULL};
execvp(cmd1_args[0], cmd1_args);
}
if(i==1)
{
for(int i=0;i<numOfPipes;i++)
{
close(pipes[i][1]);
}
printf("bye\n");
dup2(pipes[i][0], STDIN_FILENO);
close(pipes[i][0]);
char *cmd2_args[3] = {"wc", "-l", NULL};
execvp(cmd2_args[0], cmd2_args);
}
return 0;
}
}
for(int i=0;i<numOfCmds;i++)
{
for(int j=0;i<2;j++)
close(pipes[i][j]);
}
for(int i=0;i<numOfCmds;i++) wait(NULL);
return 0;
错误:
hey bye wc: 'standard input': Bad file descriptor 0 wc: -: Bad file descriptor Segmentation fault (core dumped)
您的问题是您正在重用(嵌套)i
的定义。使用 GCC 的 -Wshadow
选项来避免这样做。 i
的值有时会超出文件描述符对数组的末尾,这会导致混乱(未定义的行为)。
你有:
for(int i=0;i<numOfCmds;i++)
{
int child = fork();
if(child == 0)
{
if(i==0)
{
for(int i=0;i<numOfPipes;i++)
{
close(pipes[i][0]);
}
printf("hey\n");
dup2(pipes[i][1], STDOUT_FILENO);
close(pipes[i][1]);
char *cmd1_args[3] = {"ls", "-l", NULL};
execvp(cmd1_args[0], cmd1_args);
}
if(i==1)
{
for(int i=0;i<numOfPipes;i++)
{
close(pipes[i][1]);
}
printf("bye\n");
dup2(pipes[i][0], STDIN_FILENO);
close(pipes[i][0]);
char *cmd2_args[3] = {"wc", "-l", NULL};
execvp(cmd2_args[0], cmd2_args);
}
return 0;
}
}
你的(外)循环控制变量是i
;然后你有一个由关闭一些管道的不同 i
控制的内部循环。我使用的 GCC 报告说您正在 dup2()
调用 wc
.
pipes[i][0]
您需要重新使用 i
(使用一些不同的名称 — j
和 k
(对于 'kids'?)是可用的)。并且 re-review 您正在仔细关闭哪些文件管道描述符。
您的父循环关闭文件描述符是:
for(int i=0;i<numOfCmds;i++)
{
for(int j=0;i<2;j++)
close(pipes[i][j]);
}
您需要使用 numOfPipes
而不是 numOfCmds
,并且您需要您的内部循环来测试 j
,而不是 i
。
这是一些经过大量检测的有效代码。但修复不是很普遍:
/* SO 7165-1018 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int numOfCmds = 2;
int numOfPipes = numOfCmds - 1;
int pipes[numOfPipes][2];
for(int i=0;i<numOfPipes;i++)
if(pipe(pipes[i])<0) return 1;
for (int i = 0; i < numOfPipes; i++)
printf("%d: pipe[%d][0] = %d; pipe[%d][1] = %d\n",
getpid(), i, pipes[i][0], i, pipes[i][1]);
for(int i=0;i<numOfCmds;i++)
{
int child = fork();
printf("%d: child = %d\n", getpid(), child);
if(child == 0)
{
if(i==0)
{
for(int i=0;i<numOfPipes;i++)
{
printf("%d: ls: close %d\n", getpid(), pipes[i][0]);
close(pipes[i][0]);
}
printf("%d: hey\n", getpid());
dup2(pipes[i][1], STDOUT_FILENO);
printf("%d: ls: close %d\n", getpid(), pipes[i][1]);
close(pipes[i][1]);
char *cmd1_args[3] = {"ls", "-l", NULL};
execvp(cmd1_args[0], cmd1_args);
fprintf(stderr, "failed to execute %s\n", cmd1_args[0]);
exit(EXIT_FAILURE);
}
if(i==1)
{
for(int i=0;i<numOfPipes;i++)
{
printf("%d: wc: close %d\n", getpid(), pipes[i][1]);
close(pipes[i][1]);
}
printf("%d: bye\n", getpid());
dup2(pipes[0][0], STDIN_FILENO);
printf("%d: wc: close %d\n", getpid(), pipes[0][0]);
close(pipes[0][0]);
char *cmd2_args[3] = {"wc", "-l", NULL};
execvp(cmd2_args[0], cmd2_args);
fprintf(stderr, "failed to execute %s\n", cmd2_args[0]);
exit(EXIT_FAILURE);
}
return 0;
}
}
for(int i=0;i<numOfPipes;i++)
{
for(int j=0;j<2;j++)
{
printf("parent (%d): close %d\n", getpid(), pipes[i][j]);
close(pipes[i][j]);
}
}
for(int i=0;i<numOfCmds;i++)
{
int corpse;
int status;
if ((corpse = wait(&status)) > 0)
printf("%d: child %d exited with status 0x%.4X\n", getpid(), corpse, status);
}
return 0;
}
一个示例 运行(程序 sh83
,编译自 sh83.c
)产生:
42654: pipe[0][0] = 3; pipe[0][1] = 4
42654: child = 42655
42655: child = 0
42655: ls: close 3
42654: child = 42656
parent (42654): close 3
parent (42654): close 4
42655: hey
42656: child = 0
42656: wc: close 4
42656: bye
42656: wc: close 3
128
42654: child 42655 exited with status 0x0000
42654: child 42656 exited with status 0x0000
如果您使用多个进程,我发现在大多数消息前加上 PID 会很有帮助 — 如输出所示。