【问题标题】:implementing pipe and redirection together in C在 C 中一起实现管道和重定向
【发布时间】:2018-05-30 17:52:25
【问题描述】:

我正在努力用 C 语言制作一个最小的 shell。我了解管道和重定向的工作原理。我有两个程序,一个处理重定向的程序,例如ls -l > outputfile。我有另一个处理一个管道的程序,例如ls - l | wc。我遇到的主要麻烦是将它们放在一起。基本上,我希望能够在一个程序中同时实现 piperedirection。以下是管道的部分代码。

   pipe(p);
      if (fork() == 0) {
      dup2(p[1], 1);
      close(p[0]);
      execvp(upstream[0], upstream );
      exit(1);
    }
    if (fork() == 0) {
    dup2(p[0], 0);

    close(p[1]);
    execvp(downstream[0], downstream);
    exit(1);
    }
    else { /* Parent */
        /*  so Close both ends of pipe */
    close(p[0]);
    close(p[1]);

如果我能得到一些关于这个的指针,我将不胜感激。我不确定我应该在代码的哪一部分实现重定向。 提前致谢

【问题讨论】:

  • 如果我理解正确你想实现ls -l | wc > outputfile
  • 是的,@achal 我希望这样做
  • 嗨@Sulavk 我回答了你的问题。看看吧。

标签: c pipe fork


【解决方案1】:

因此,对于重定向方面,您需要在文件句柄的 fork 中使用 dup2()。要设置管道,您需要在主线程中使用 pipe() 创建句柄,然后在 fork 中使用 dup2()。

请注意,您必须非常小心 pipe() 不会为您提供低编号的文件句柄。您可以使用 fcntrl(F_DUPFD, ...) 来欺负他们。另外,使用 CLOEXEC 去除所有漂浮在周围的垃圾二级句柄。

伪代码:

fin = open("input", read)
pipeIO = pipe()
fout = open("output", write)

fin2 = fcntrl(F_DUPFD_CLOEXEC, fin, 3);   close fin
pipeI2 = fcntrl(F_DUPFD_CLOEXEC, pipeI, 3);   close pipeI
pipeO2 = fcntrl(F_DUPFD_CLOEXEC, pipeO, 3);   close pipeO
fout2 = fcntrl(F_DUPFD_CLOEXEC, fout, 3);   close fout

fork 
{
   dup(fin2, 0)
   dup(pipeO2, 1)
  exec Command1
}
close fin2
close pipeO2
fork 
{
   dup(pipeI2, 0)
   dup(out2, 1)
   exec Command2
}
close fout2
close pipeI2

wait

或者,第二个 fork 可以链式执行:

伪代码:

fin = open("input", read)
pipeIO = pipe()
fout = open("output", write)

fin2 = fcntrl(F_DUPFD_CLOEXEC, fin, 3);   close fin
pipeI2 = fcntrl(F_DUPFD_CLOEXEC, pipeI, 3);   close pipeI
pipeO2 = fcntrl(F_DUPFD_CLOEXEC, pipeO, 3);   close pipeO
fout2 = fcntrl(F_DUPFD_CLOEXEC, fout, 3);   close fout

fork 
{
   dup(fin2, 0)
   dup(pipeO2, 1)
  exec Command1
}
dup(pipeI2, 0)
dup(out2, 1)
exec Command2

您清楚地知道,当您 fork 时,子进程有效地复制了所有句柄,并且在代码实际执行时,只需要打开一个版本。父进程或子进程必须关闭它不感兴趣的每个句柄。通过使用 CLOEXEC,我们减少了这种需求,但如果您想将父进程保持为监视器,那么它需要关闭所有这些句柄的所有副本.

dup 函数的简要介绍:

0 = 管道(句柄 [2])
这将创建一对作为输入和输出相互连接的句柄。经典地,您可以通过一个进程写入 fork 的一个句柄和另一个进程从另一个句柄中读取来与 fork 通信,无论您想使用哪种方式。在这种情况下,我们使用管道在两个子进程之间进行通信,而不涉及父进程。

句柄 = dup(句柄)
这会在最低的未使用插槽处创建一个重复的句柄。如果您的程序已关闭 stdin/stdout/stderr,它将重用它们,这将是有问题的。此外,这些句柄在 exec 调用中保持活动状态。

句柄 = dup2(句柄,trghandle)
这会在您指定的索引处创建一个重复的句柄。它会默默地关闭任何现有的 trghandle 使用,这既有用又烦人!

handle = dup3(handle,trghandle, flags)
这会在您指定的索引处创建一个重复的句柄。它将默默地关闭任何现有的 trghandle 使用,这既有用又烦人!您可以指定诸如 CLOEXEC 之类的标志,它会在您调用 exec 时自动关闭句柄。 CLOEXEC 在避免显式关闭文件方面非常有用。

int fcntl(int fd, int cmd, ... /* arg */ );
F_DUPFD (int) 使用大于或等于 arg 的最小编号可用文件描述符复制文件描述符 fd。
F_DUPFD_CLOEXEC (int; since Linux 2.6.24) 与 F_DUPFD 相同,但另外为重复文件描述符设置 close-on-exec 标志。
...这使得避免标准输入/标准输出变得容易。

【讨论】:

  • 嗨,有没有办法通过使用 dup 和 dup2 使其工作。我不熟悉图书馆的工作原理
  • 我将简要介绍一下 dup 和 fnctl 方法在正文中的作用...
【解决方案2】:

你要执行

ls -l   |   wc   >   outputfile
 |           |         |
p-1        p-2      redirect the o/p of ls | wc to outputfile

要实现上述目标,您需要使用fork() 系统调用创建进程(p-1 和p-2),然后使用dup()dup2() 系统调用将o/p 重定向到预期的位置。

这里是示例代码

int main(int argc,char *argv[]) {
        int fd[2];
        pid_t cpid;
        close(1); /* so that output file should get file descriptor 1 */
        int file_dsptr = open(argv[3],O_CREAT|O_TRUNC |O_WRONLY , 0664); /* file should be exist in current directory, otherwise use O_CREAT | O_TRUNC */
        printf("[%d]\n",file_dsptr);

        if(argc == 4){
                if(pipe(fd) == -1){
                        perror("error in pipe creation");
                        exit(EXIT_FAILURE);
                }
                cpid = fork();
                if( cpid == 0 ){ /*child process */
                        close( fd[0] ); /* close read end of pipe */
                        if( -1 == dup2(fd[1] , file_dsptr)){ /* duplicate fd[1] to fd where data.txt points */
                                exit( EXIT_FAILURE );
                        }
                        if(-1 == execlp(argv[1] , argv[1] , NULL )){ /* executes the commands */
                                exit( EXIT_FAILURE );
                        }
                        close( fd[1] );
                        _exit( EXIT_SUCCESS );
                }
                else if(cpid > 0){
                        wait( NULL ); /* wait for child to completes */
                        close( fd[1] ); /* close write end of pipe */
                        if( -1 == dup2(fd[0] , STDIN_FILENO)) {
                                exit( EXIT_FAILURE );
                        }
                        if(-1 == execlp(argv[2] , argv[2], NULL )) {
                                exit( EXIT_FAILURE );
                        }
                        close( fd[0] );
                        _exit( EXIT_SUCCESS );
                }
                else{
                        fprintf(stderr, "fork failed\n");
                        exit(EXIT_FAILURE);
                }
        }
        else{
                fprintf(stderr, "Usage Msg :. ./a.out ls wc data.txt \n");
                exit(EXIT_FAILURE);
        }
        return 0;
}

正如我在 cmets 中提到的那样运行上面的代码

gcc -Wall test.c
./a.out ls  wc  outputfile

现在检查outputfile的内容,应该是ls | wc的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-11
    • 2020-10-06
    • 1970-01-01
    • 2014-03-21
    • 1970-01-01
    相关资源
    最近更新 更多