管道、dup2和exec()
我必须编写一个可以运行管道的shell。例如像管道、dup2和exec(),c,linux,shell,exec,dup2,C,Linux,Shell,Exec,Dup2,我必须编写一个可以运行管道的shell。例如像ls-l | wc-l”这样的命令。我成功地解析了用户给出的命令,如下所示: “ls”=firstcmd “-l”=新加坡联邦政府 “wc”=scmd “-l”=secarg 现在我必须使用两个fork,因为命令是两个和一个管道。 我为执行该命令而编写的代码块如下所示: pid_t pid; int fd[2]; pipe(fd); pid = fork(); if(pid==0) { dup2(fd[WRITE_END]
ls-l | wc-l
”这样的命令。我成功地解析了用户给出的命令,如下所示:
“ls”=firstcmd
“-l”=新加坡联邦政府
“wc”=scmd
“-l”=secarg
现在我必须使用两个fork,因为命令是两个和一个管道。
我为执行该命令而编写的代码块如下所示:
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(pid==0)
{
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[READ_END]);
execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
}
else
{
pid=fork();
if(pid==0)
{
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
execlp(scmd, scmd, secarg, (char*) NULL);
}
}
因此,当我运行shell并输入命令ls-l | wc-l
(例如)时,execs的结果不会显示,但shell会保持正常运行
奇怪的是,命令的结果只有在我用“exit”或“^C”终止shell时才会显示
该输出有什么问题?为什么在我输入命令后它不立即显示?嗯,足够近了。在fork之后,您无法处理某些文件描述符上的close 以下是一些参考资料:
#include <fcntl.h> //
#include <stdio.h> //
#include <stdlib.h> //
#include <string.h> //
#include <sys/types.h> //
#include <sys/wait.h> //
#include <sys/stat.h> //
#include <termios.h> //
#include <unistd.h> //
//
#define INPUT_END 1 // INPUT_END means where the pipe takes input
#define OUTPUT_END 0 // OUTPUT_END means where the pipe produces output
//
int main(int argc, char* argv[]) //
{ //
pid_t pid1; // [STDIN -> terminal_input, STDOUT -> terminal_output] (of the parent process)
pid_t pid2; //
int fd[2]; //
//
pipe(fd); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input, fd[1] -> pipe_output]
pid1 = fork(); //
//
if(pid1==0) //
{ // I am going to be the wc process (i.e. taking input from the pipe)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[1] -> pipe_output] (of the WC process)
dup2(fd[OUTPUT_END], STDIN_FILENO); // [STDIN -> pipe_output, STDOUT -> terminal_output, fd[1] -> pipe_output] (of the WC process)
close(fd[OUTPUT_END]); // [STDIN -> pipe_output, STDOUT -> terminal_output] (of the WC process)
execlp("wc", "wc", "-l",(char*) NULL); //
} //
else //
{ //
pid2=fork(); //
//
if(pid2==0) //
{ // I am going to be the ls process (i.e. producing output to the pipe)
close(fd[OUTPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the ls process)
dup2(fd[INPUT_END], STDOUT_FILENO); // [STDIN -> terminal_input, STDOUT -> pipe_input, fd[0] -> pipe_input] (of the ls process)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> pipe_input] (of the ls process)
execlp("ls","ls","-l",(char*) NULL); //
} //
//
close(fd[OUTPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the parent process)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output] (of the parent process)
waitpid(-1, NULL, 0); // As the parent process - we wait for a process to die (-1) means I don't care which one - it could be either ls or wc
waitpid(-1, NULL, 0); // As the parent process - we wait for the another process to die.
// At this point we can safely assume both process are completed
} //
} //
#包括//
#包括//
#包括//
#包括//
#包括//
#包括//
#包括//
#包括//
#包括//
//
#定义输入端1//INPUT\u END表示管道接收输入的位置
#定义输出端0//OUTPUT\u END表示管道产生输出的位置
//
int main(int argc,char*argv[])//
{ //
pid_t pid1;//[STDIN->terminal_input,STDOUT->terminal_output](父进程的)
pid_t pid2//
int-fd[2]//
//
管道(fd);/[STDIN->terminal_input,STDOUT->terminal_output,fd[0]->pipe_input,fd[1]->pipe_output]
pid1=fork()//
//
如果(pid1==0)//
{//我将成为wc流程(即从管道中获取输入)
关闭(fd[INPUT_END]);/[STDIN->terminal_INPUT,STDOUT->terminal_output,fd[1]->pipe_output](WC进程的)
dup2(fd[OUTPUT\U END],STDIN\U FILENO);/[STDIN->pipe\U OUTPUT,STDOUT->terminal\U OUTPUT,fd[1]->pipe\U OUTPUT](WC过程的)
关闭(fd[OUTPUT_END]);/[STDIN->pipe_OUTPUT,STDOUT->terminal_OUTPUT](WC进程的)
execlp(“wc”、“wc”、“-l”、(char*)NULL)//
} //
否则//
{ //
pid2=fork()//
//
如果(pid2==0)//
{//我将成为ls进程(即,向管道生成输出)
关闭(fd[OUTPUT_END]);/[STDIN->terminal_input,STDOUT->terminal_OUTPUT,fd[0]->pipe_input](ls进程的)
dup2(fd[INPUT_END],标准输出文件号);/[STDIN->terminal_INPUT,标准输出->pipe_INPUT,fd[0]->pipe_INPUT](ls进程的)
关闭(fd[INPUT_END]);/[STDIN->terminal_INPUT,STDOUT->pipe_INPUT](ls进程的)
execlp(“ls”、“ls”、“-l”、(char*)NULL)//
} //
//
关闭(fd[OUTPUT_END]);/[STDIN->terminal_input,STDOUT->terminal_OUTPUT,fd[0]->pipe_input](父进程的)
关闭(父进程的fd[INPUT_END]);//[STDIN->terminal_INPUT,STDOUT->terminal_output)
waitpid(-1,NULL,0);//作为父进程-我们等待进程死亡(-1)意味着我不关心哪个进程-它可以是ls或wc
waitpid(-1,NULL,0);//作为父进程-我们等待另一个进程死亡。
//此时,我们可以安全地假设两个过程都已完成
} //
} //
您需要关闭父进程和子进程中的所有管道描述符(在子进程中复制之后)。在您的代码中,主要问题是,wc
进程没有退出,因为仍然存在写入程序(因为父进程没有关闭写入端)。更改如下所示。我还在父进程中添加了waitpid
,以等待wc
进程
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(pid==0)
{
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[READ_END]);
close(fd[WRITE_END]);
execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
fprintf(stderr, "Failed to execute '%s'\n", firstcmd);
exit(1);
}
else
{
pid=fork();
if(pid==0)
{
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
close(fd[READ_END]);
execlp(scmd, scmd, secarg,(char*) NULL);
fprintf(stderr, "Failed to execute '%s'\n", scmd);
exit(1);
}
else
{
int status;
close(fd[READ_END]);
close(fd[WRITE_END]);
waitpid(pid, &status, 0);
}
}
好的,对于这样的事情,一个简单而有效的工作就是用管道制作一个脚本,然后用C代码中的某个exec命令调用该脚本 script.sh
#!/bin/sh
ls -l | wc -l
你在C程序中所做的就是这样
char *argv[] = {"script.sh", NULL};
execv(argv[0], argv);
请注意,您必须将script.sh放在C程序的同一目录中。您需要关闭父进程中的管道FD。您可以编辑我的代码以帮助我理解您的意思吗?@BarmarRule of thumb:如果您将管道的一端复制到标准输入或标准输出,您应该关闭原始管道的两端在使用
exec*()
函数(或继续)之前使用管道