C中的未命名管道,chaning stdin,fork()中的stdout

时间:2016-11-23 12:38:59

标签: c unix pipe

我正在尝试编写一个实现命令的程序:

ls -al | tr 'a-z' 'A-Z' | grep X > testing

基本上我想学习如何在C中使用两次管道。有很多帖子我们只使用一个'|'。我根据只有一个'|'的例子做了一些尝试但遗憾的是它不起作用。有一个代码。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define MAX 512
int main(int argc,char** argv){
    int pdesk[2];
    int pipedesk[2];
    if(pipe(pipedesk)==-1 || pipe(pdesk)==-1){
        perror("Pipe");
        exit(1);
    }
    switch(fork()){
        case -1 :
                perror("Creating process");
                exit(1);
        case 0:
                dup2(pdesk[1],STDOUT_FILENO);
                close(pdesk[1]);
                execlp("ls","ls","-al",NULL);
                perror("ls");
                exit(1);
        default: {
                if(fork()==0){
                    if(fork()==0){
                        dup2(pdesk[0],STDIN_FILENO);
                        close(pdesk[0]);
                        dup2(pipedesk[1],STDOUT_FILENO);
                        close(pipedesk[1]);
                        execlp("tr","tr","a-z","A-Z",NULL);
                        perror("tr");
                        exit(1);
                    }
                    dup2(pipedesk[0],STDIN_FILENO);
                    close(pipedesk[0]);
                    int desk=open("testing",O_WRONLY|O_CREAT,0640);
                    if(desk==-1){
                        perror("opening file");
                        exit(1);
                    }
                    dup2(desk,STDOUT_FILENO);
                    if(close(desk)==-1){
                        perror("closing file");
                        exit(1);
                    }
                    execlp("grep","grep","X",NULL);
                    perror("grep");
                    exit(1);
                }
//              wait(NULL);

        }
        return 0;
    }
}

3 个答案:

答案 0 :(得分:2)

当管道的另一端死亡时,您的进程会收到一个SIGPIPE,默认会终止您的进程。

所以你至少应该忽略它!

signal(SIGPIPE, SIG_IGN);

为了清洁,您应该关闭当前过程中不会使用的所有管道末端。

例如:

case 0:
    close(pdesk[0]);
    close(pipedesk[0]);
    close(pipedesk[1]);

答案 1 :(得分:0)

我不认为信号处理是相关的。有关的是确保管道正确关闭。如果有一个打开写入结束的进程,进程就不会在管道的读取端获得EOF。这包括当前的过程;如果一个进程同时打开了一个管道的读取和写入端,它将永远不会在管道的读取端获得EOF,因为理论上有一个进程可以写入它。因此,此过程将永久挂在管道上的read()

此版本的代码适用于我:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
    int pdesk[2];
    int pipedesk[2];
    int pid;

    if (pipe(pipedesk) == -1 || pipe(pdesk) == -1)
    {
        perror("Pipe");
        exit(1);
    }
    //fprintf(stderr, "PID %d: controller\n", (int)getpid());

    switch (pid = fork())
    {
    case -1:
        perror("Creating process");
        exit(1);
    case 0:
        //fprintf(stderr, "PID %d: will be 'ls'\n", (int)getpid());
        dup2(pdesk[1], STDOUT_FILENO);
        close(pdesk[0]);        // JL
        close(pdesk[1]);
        close(pipedesk[0]);     // JL
        close(pipedesk[1]);     // JL
        execlp("ls", "ls", "-al", (char *)NULL);
        perror("ls");
        exit(1);
    default:
        //fprintf(stderr, "PID %d: ls process PID = %d\n", (int)getpid(), pid);
        if ((pid = fork()) == 0)
        {
            //fprintf(stderr, "PID %d: about to fork 'tr'\n", (int)getpid());
            if ((pid = fork()) == 0)
            {
                //fprintf(stderr, "PID %d: will be 'tr'\n", (int)getpid());
                dup2(pdesk[0], STDIN_FILENO);
                close(pdesk[0]);
                close(pdesk[1]);        // JL
                dup2(pipedesk[1], STDOUT_FILENO);
                close(pipedesk[0]);     // JL
                close(pipedesk[1]);
                execlp("tr", "tr", "a-z", "A-Z", (char *)NULL);
                perror("tr");
                exit(1);
            }
            //fprintf(stderr, "PID %d: about to exec 'grep'\n", (int)getpid());
            dup2(pipedesk[0], STDIN_FILENO);
            close(pdesk[0]);        // JL
            close(pdesk[1]);        // JL
            close(pipedesk[0]);
            close(pipedesk[1]);     // JL
            int desk = open("testing", O_WRONLY | O_CREAT, 0644);
            if (desk == -1)
            {
                perror("opening file");
                exit(1);
            }
            dup2(desk, STDOUT_FILENO);
            if (close(desk) == -1)
            {
                perror("closing file");
                exit(1);
            }
            execlp("grep", "grep", "X", (char *)NULL);
            perror("grep");
            exit(1);
        }

        //fprintf(stderr, "PID %d: closing pipes\n", (int)getpid());
        close(pdesk[0]);        // JL
        close(pdesk[1]);        // JL
        close(pipedesk[0]);     // JL
        close(pipedesk[1]);     // JL

        break;
    }

    int status;
    int corpse;
    while ((corpse = wait(&status)) != -1)
        fprintf(stderr, "PID %d: child %d died 0x%.4X\n", (int)getpid(), corpse, status);

    return 0;
}

注意标记为// JL的所有行都是关闭操作。虽然你可以在没有其中一些的情况下离开(例如第一个孩子的那些,可能是&#39;可选&#39;),但你应该例行地关闭所有的管道。你没有使用。请注意,特别是原始父进程的关闭(后四个) - 这些都很重要。

在关闭多个管道时,未关闭足够的管道描述符是最常见的错误之一。通常,如果使用dup2()(或dup())将管道描述符复制到标准输入或输出,则应关闭原始管道的两个端。如果你的进程根本没有使用管道描述符,你应该关闭它们。

程序的示例输出:

$ ./pp61
PID 35665: child 35666 died 0x0000
PID 35665: child 35667 died 0x0000
$

testing档案的示例内容:

DRWXR-XR-X   44 JLEFFLER  STAFF   1496 NOV 23 17:28 .
DRWXR-XR-X  137 JLEFFLER  STAFF   4658 NOV 23 17:28 ..
DRWXR-XR-X   18 JLEFFLER  STAFF    612 OCT 23 20:00 .GIT
DR-XR-XR-X    4 JLEFFLER  STAFF    136 AUG 14 23:27 SAFE
DRWXR-XR-X   35 JLEFFLER  STAFF   1190 NOV 23 09:19 UNTRACKED
-RW-R--R--    1 JLEFFLER  STAFF     81 NOV 20 20:53 ANIMALS.TXT
DRWXR-XR-X    3 JLEFFLER  STAFF    102 SEP 12 00:03 DOC
DRWXR-XR-X    8 JLEFFLER  STAFF    272 NOV 10 20:58 ETC
DRWXR-XR-X   17 JLEFFLER  STAFF    578 JUL 15 19:06 INC
-RW-R--R--    1 JLEFFLER  STAFF   1217 NOV 21 21:26 IX37.SQL
DRWXR-XR-X    5 JLEFFLER  STAFF    170 JUL  9 23:47 LIB
-RWXR-XR-X    1 JLEFFLER  STAFF   9052 NOV 19 13:01 LL73
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV  6 15:40 LL73.DSYM
-RWXR-XR-X    1 JLEFFLER  STAFF   8896 NOV  6 10:38 LL83
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV  6 10:10 LL83.DSYM
-RW-R--R--    1 JLEFFLER  STAFF    108 NOV 20 20:53 NEWANIMAL.TXT
-RWXR-XR-X    1 JLEFFLER  STAFF   9124 NOV 20 20:38 PD43
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 20 20:38 PD43.DSYM
-RWXR-XR-X    1 JLEFFLER  STAFF   9148 NOV 23 17:28 PP61
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 23 14:11 PP61.DSYM
-RWXR-XR-X    1 JLEFFLER  STAFF   9016 NOV 23 11:07 RS19
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 23 10:03 RS19.DSYM
DRWXR-XR-X  146 JLEFFLER  STAFF   4964 OCT  9 17:06 SRC
-RWXR-XR-X    1 JLEFFLER  STAFF   8760 NOV 23 13:12 STP83
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 23 13:04 STP83.DSYM
DRWXR-XR-X    6 JLEFFLER  STAFF    204 NOV  6 21:52 TMP
-RWXR-XR-X    1 JLEFFLER  STAFF   8808 NOV 23 10:38 TR37
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 23 10:38 TR37.DSYM
-RWXR-XR-X    1 JLEFFLER  STAFF  26772 NOV 21 20:18 XY73
DRWXR-XR-X    3 JLEFFLER  STAFF    102 NOV 21 20:18 XY73.DSYM
-RW-R--R--    1 JLEFFLER  STAFF    467 NOV 21 20:18 XY73.L

答案 2 :(得分:0)

这是一个易于理解的实现。

当每个过程关闭管道的另一端时,管道的一端会关闭。因此,关闭每个对当前过程无用的管道非常重要。

#include <unistd.h>
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>

void closeAll(const int * fd_to_close){
    int i = 0;
    while(1){
        int fd = fd_to_close[i];
        if(fd == -1){
            break;
        }
        close(fd);
        i++;
    }
}

pid_t forkExec(int my_stdin, int my_stdout, int my_stderr, char *const argv[], const int* fd_to_close){
    pid_t child = fork();

    if(child != 0){
        return child;
    }

    if(my_stdin != STDIN_FILENO){
        dup2(my_stdin, STDIN_FILENO);
    }
    if(my_stdout != STDOUT_FILENO){
        dup2(my_stdout, STDOUT_FILENO);
    }
    if(my_stderr != STDERR_FILENO){
        dup2(my_stderr, STDERR_FILENO);
    }

    closeAll(fd_to_close);

    execvp(argv[0], argv);

    perror("Executing ");

    exit(1);
}

void waitFor(pid_t child){
    int status;
    pid_t w = waitpid(child, &status, 0);
    if(w == -1){
        perror("child ");
    } else {
        printf("Exit status of child %d was %d, killed by signal %d %s\n", (int) child, WEXITSTATUS(status), WTERMSIG(status), WCOREDUMP(status) ? "with coredump" : "");
    }
}

void main(int argc, char**argv){
    int pipe1[2];
    int pipe2[2];
    int fd_to_close[10];

    signal(SIGPIPE, SIG_IGN);

    if(pipe(pipe1)==-1 || pipe(pipe2)==-1){
        perror("Pipe");
        exit(1);
    }

    fd_to_close[0] = pipe1[0];
    fd_to_close[1] = pipe1[1];
    fd_to_close[2] = pipe2[0];
    fd_to_close[3] = pipe2[1];
    fd_to_close[4] = -1;

    char* cmd1[] = {"ls", "-al", NULL};
    char* cmd2[] = {"tr", "a-z", "A-Z", NULL};
    char* cmd3[] = {"grep", "X", NULL};

    pid_t child1 = forkExec(STDIN_FILENO, pipe1[1],      STDERR_FILENO, cmd1, fd_to_close);
    pid_t child2 = forkExec(pipe1[0],     pipe2[1],      STDERR_FILENO, cmd2, fd_to_close);
    pid_t child3 = forkExec(pipe2[0],     STDOUT_FILENO, STDERR_FILENO, cmd3, fd_to_close);

    // Very important :
    closeAll(fd_to_close);

    printf("pid of %s is %d\n", cmd1[0], (int)child1);
    printf("pid of %s is %d\n", cmd2[0], (int)child2);
    printf("pid of %s is %d\n", cmd3[0], (int)child3);

    waitFor(child1);
    waitFor(child2);
    waitFor(child3);
}