我正在尝试实现一个类似于程序的示例shell,它执行命令ls | wc
使用管道来实现命令。当我执行命令时,我收到以下错误。
wc:标准输入:错误的文件描述符 0 0 0 wc: - :错误的文件描述符
请查看代码并提供输入 注意:1)parse是一个库,它接受输入的输入并将每个命令作为带有args和necesary数据的链表返回。 Parse工作正常 2)我在不同的子进程中执行每个命令,因此fork
#include <stdio.h>
#include <stdlib.h>
#include "parse.h"
int pip[3][2];
int main(int argc, char *argv[], char *envp[])
{
Pipe p;
Cmd c;
pipe(pip[0]);
pipe(pip[1]);
pid_t pid;
pid=fork();
char *host = "armadillo";
printf("%s%% ", host);
p = parse();
c=p->head;
printf("1 \n");
pid=fork();
if(pid==0)
{
close(pip[0][0]);
close(STDOUT_FILENO);
dup2(pip[0][1],STDOUT_FILENO);
execvp(c->args[0],c->args);
}
else
{
waitpid(pid,NULL,0);
}
printf("2 \n");
close(pip[0][1]);
close(pip[0][0]);
c=c->next;
printf("%s \n",c->args[0]);
pid=fork();
if(pid==0)
{
close(STDIN_FILENO);
dup2(pip[0][0],STDIN_FILENO);
close(pip[0][1]);
execvp(c->args[0],c->args);
}
else
{
waitpid(pid,NULL,0);
close(pip[0][1]);
close(pip[0][0]);
}
}
答案 0 :(得分:6)
主要问题在于:
close(pip[0][1]);
close(pip[0][0]);
...
dup2(pip[0][0],STDIN_FILENO);
close(pip[0][1]);
首先关闭文件描述符,然后在程序中再次尝试使用它们。
答案 1 :(得分:1)
您的代码中存在一些问题:
您正在成为初始流程的孙子
pid=fork();
char *host = "armadillo";
printf("%s%% ", host);
p = parse();
c=p->head;
printf("1 \n");
pid=fork(); // this fork here is wrong
你正在分叉然后再分叉,所以父母生了一个孩子,然后他们俩都生了一个孩子。 此时您已经有4个流程。
你的代码在这一部分就是这样的:
pid_t pid;
pid=fork();
char *host = "armadillo";
printf("%s%% ", host);
p = parse();
c=p->head;
printf("1 \n");
// pid=fork(); // it'll be in another part
if (pid == -1) {
// print error
exit(1);
} else if (pid == 0) {
//child
close(pip[0][0]);
close(STDOUT_FILENO);
dup2(pip[0][1],STDOUT_FILENO);
close(pip[0][1]); // I added this
execvp(c->args[0],c->args);
}
//parent
waitpid(pid,NULL,0); // it's not a good idea but I leave it here
printf("2 \n");
// now you can fork again and use the same pid variable
pid=fork();
你等着孩子完成。
if(pid==0)
{
close(pip[0][0]);
close(STDOUT_FILENO);
dup2(pip[0][1],STDOUT_FILENO);
execvp(c->args[0],c->args);
}
else
{
waitpid(pid,NULL,0); // you have more commands to execute yet, so you must do it before this
}
如果您使用父进程在管道上执行最后一个命令(wc
),那么Waitpid根本不是必需的。但是,如果您想拥有父流程,那么由您决定。如果是这样,一旦所有孩子都完成任务,你必须打电话给waitpid
。
你必须在dup2 之前关闭管道。 您发布的错误似乎是因为这个原因。
wc: standard input: Bad file descriptor 0 0 0 wc: -: Bad file descriptor
在dup2 之后,你必须关闭孩子的管道。
close(pip[0][0]); // it's ok
close(STDOUT_FILENO); // it's ok but not necessary
dup2(pip[0][1],STDOUT_FILENO);
// here you have to close(pip[0][1]) due to you have already duped it in STDOUT_FILENO
execvp(c->args[0],c->args);
如果你想要一个父母,你必须在两个孩子都复制它之后关闭它。
printf("2 \n");
close(pip[0][1]);
close(pip[0][0]); // You're closing the file descriptor which wc needs to read.
您没有检查某些功能的所有可能返回状态。
pipe
fork
execvp
dup2
还有其他需要改进的地方
int pip[3][2]; // in your case with `int pip[2]` would be enough
pipe(pip[0]);
pipe(pip[1]); // in your case you have to create just one pipe
答案 2 :(得分:1)
我采取了懒惰的方式,并写了我自己而不是修复其他代码。将其视为“C中的另一个管道示例”,但它可能有助于指出OP代码的问题。
/*
* hard-wired example program exploring how to implement
*
* system("ls | wc");
*
* using calls to pipe(2), fork(2), execvp(2) and wait(2)
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
static void
do_close(int fd)
{
if (close(fd) == -1) {
perror("close");
exit(1);
}
}
static void
do_execvp(char *const cmd[])
{
execvp(cmd[0], cmd);
/*
* if execvp returns in this text, an error occured.
*/
perror("execvp");
exit(1);
}
static void
dup_and_exec(int fd, int *pp, char *const cmd[])
{
if (dup2(pp[fd], fd) == -1) {
perror("dup2");
exit(1);
}
do_close(pp[0]);
do_close(pp[1]);
do_execvp(cmd);
}
int
main(void)
{
char *const ls_cmd[] = { "ls", 0 };
char *const wc_cmd[] = { "wc", 0 };
int fds[2];
int w_stat;
pid_t ls_pid, wc_pid, w_pid;
/* create a single pipe to connect our writer and reader processes */
if (pipe(fds) == -1) {
perror("pipe");
exit(1);
}
/* create the writer process: ls */
ls_pid = fork();
if (ls_pid == -1) {
perror("fork");
exit(1);
}
if (ls_pid == 0) {
/* this is the child - do the "ls" command */
dup_and_exec(1, fds, ls_cmd); /* no return from here */
}
/* create the reader process: wc */
wc_pid = fork();
if (wc_pid == -1) {
perror("fork");
exit(1);
}
if (wc_pid == 0) {
/* this is the child - do the "wc" command */
dup_and_exec(0, fds, wc_cmd); /* no return from here */
}
/* parent process */
/*
* It's important to close the pipe completely in the parent,
* so (in particular) there's no process that could be an
* additional writer to the "write" side of the pipe.
*
* We need to arrange things so that our reader process (the "wc"
* process in this example) will see EOF when the only writer (the
* "ls" process) closes its output and exits.
*
* If this parent process does not close the write side of the pipe,
* it remains open, since it's shared across fork(2), so the reader
* (wc) won't ever see EOF and exit, and this parent process won't
* ever see the wc exit, and everything hangs.
*
* The core problems will have started with the parent, which all
* children know to be true.
*
* The next lines also close the "read" side of the pipe, which
* is a bit cleaner, but won't affect proper operation of this
* sample program. But closing all un-needed file descriptors is
* good hygiene: for longer running applications, or for library
* code that could be called from longer running programs, avoiding
* any leaks of file descriptors is a good thing.
*/
do_close(fds[0]);
do_close(fds[1]);
while ((w_pid = wait(&w_stat)) > 0) {
printf("%s process exited", w_pid == ls_pid ? "ls" : "wc");
if (WIFEXITED(w_stat)) {
printf(" (status %d)", WEXITSTATUS(w_stat));
}
fputs("\n", stdout);
}
if (w_pid == -1 && errno != ECHILD) {
perror("wait");
exit(1);
}
return 0;
}