难以在Unix中重定向dup2和管道代码中的输出

时间:2013-07-30 03:31:19

标签: unix exec fork pipe dup2

我是unix的新手。在下面的代码中,我从命令行“〜$ foo last sort more”传递三个参数,以便复制“〜$ last | sort | more”。我正在尝试创建一个程序,它将采用三个参数(现在至少3个)。父将分叉三个进程。第一个进程将写入管道。第二个进程将读取和写入管道,第三个进程将从管道读取并写入stdout(终端)。第一个进程将执行“最后”,第二个进程将执行“排序”,第三个进程将执行“更多”,并且进程将休眠1,2和3秒以便同步。我很确定我在创建管道和重定向输入和输出时遇到了麻烦。我没有得到任何输出到终端,但我可以看到已创建进程。我将不胜感激。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>

#define FOUND       1
#define NOT_FOUND   0
#define FIRST_CHILD 1
#define LAST_CHILD  numargc
#define PATH_1 "/usr/bin/"
#define PATH_2 "/bin/"

#define DUP_READ()                             \
if (dup2(fdes[READ], fileno(stdin)) == -1)     \
            {                              \
                perror("dup error");       \
                exit(4);                   \
            }                              

#define DUP_WRITE()                            \
if (dup2(fdes[WRITE], fileno(stdout)) == -1)   \
                {                              \
                    perror("dup error");       \
                    exit(4);                   \
                }                              

#define CLOSE_FDES_READ()   \
close(fdes[READ]);

#define CLOSE_FDES_WRITE()   \
close(fdes[WRITE]);

#define EXEC(x, y)                                          \
if (execl(arraycmds[x], argv[y], (char*)NULL) == -1)        \
                {                                           \
                    perror("EXEC ERROR");                   \
                    exit(5);                                \
                }
#define PRINT                         \
printf("FD IN:%d\n", fileno(stdin));    \
printf("FD OUT:%d\n", fileno(stdout));

enum 
{
    READ, /* 0 */
    WRITE,
    MAX
};

int cmdfinder( char* cmd, char* path); /* 1 -> found, 0 -> not found */
int main (int argc, char* argv[])
{

    int numargc=argc-1;
    char arraycmds[numargc][150];
    int i=1, m=0, sleeptimes=5, numfork;
    int rc=NOT_FOUND;
    pid_t pid;
    int fdes[2];

    if(pipe(fdes) == -1)
    {
        perror("PIPE ERROR");
        exit(4);
    }

    while(i <= numargc)
    {
        memset(arraycmds[m], 0, 150);
        rc=cmdfinder(argv[i], arraycmds[m]);
        if (rc)
        {
            printf("Command found:%s\n", arraycmds[m]);
        } 
        i++;
        m++;
    }

    i=0; //array index
    numfork=1; //fork number

    while(numfork <= numargc)
    {
        if ((pid=fork()) == -1)
        {
            perror("FORK ERROR");
            exit(3);
        }
        else if (pid == 0)
        {
            /* Child */

            sleep(sleeptimes);

            if (numfork == FIRST_CHILD)
            {
                DUP_WRITE();
                EXEC(i, numfork);
            }
            else if (numfork == LAST_CHILD)
            {

                DUP_READ();
                CLOSE_FDES_WRITE();
                EXEC(i, numfork);
            }
            else 
            {

                DUP_READ();
                DUP_WRITE();
                CLOSE_FDES_READ();
                CLOSE_FDES_WRITE();

                EXEC(i, numfork);
            }
        }
        else 
        {
            /* Parent */
            printf("pid:%d\n", pid);    
            i++;
            numfork++;
            sleeptimes++;
        }
    }

    PRINT;
    printf("i:%d\n", i);
    printf("numfork:%d\n", numfork);
    printf("DONE\n");       
    return 0;
}


int cmdfinder(char* cmd, char* path)
{
    DIR* dir;
    struct dirent *direntry; 
    char *pathdir;
    int searchtimes=2; 

    while (searchtimes)
    {
        pathdir = (char*)malloc(250);
        memset(pathdir, 0, 250);

        if (searchtimes==2)
        {
            pathdir=PATH_1;
        }
        else
        {
            pathdir=PATH_2;
        }

        if ((dir  = opendir(pathdir)) == NULL)
        {
            perror("Directory not found");
            exit (1);
        }
        else
        {
            while (direntry = readdir(dir))
            {
                if (strncmp( direntry->d_name, cmd, strlen(cmd)) == 0)
                {
                    strcat(path, pathdir);
                    strcat(path, cmd);
                    //searchtimes--;
                    return FOUND;
                }
            }
        }
        closedir(dir);
        searchtimes--;
    }
    printf("%s: Not Found\n", cmd);
    return NOT_FOUND;
}

1 个答案:

答案 0 :(得分:0)

所有的宏都比你直接写它更难阅读。特别是当他们引用局部变量时。要了解EXEC发生了什么,我的眼睛必须从它的使用位置跳到它定义的位置,找出它使用的本地阵列,然后跳回去查看该访问如何适应{ {1}}。这是一个迷宫般的宏。

哇,main?你自己的cmdfinder查询,只有它是硬编码的$PATH?双哇,/usr/bin:/bin,只是为了找出一个文件是否存在,其名称已经确定?只需readdir它!或者不做任何事情,只需执行它并通过尝试下一个来处理stat。或者使用ENOENT这就是它的用途!

关于要点...你没有足够的管道,而且你没有关闭所有未使用的描述符。

execlp是由2个管道连接的3个命令的管道。你不能用一根烟斗做。第一个命令应该写入第一个管道,中间命令应该读取第一个管道并写入第二个管道,最后一个命令应该读取第二个管道。

您可以先创建两个管道,然后执行所有分支,这样可以使事情变得简单,但在每个子进程中都需要大量last | sort | more,因为它们都将继承所有管道fds。或者,您可以使用更复杂的循环,在分配将使用它的第一个进程之前创建每个管道,并在创建相关子进程后立即关闭父级中的每个描述符。我不想看到你用了多少个宏。

每个成功的dup之后都应该关闭已复制的描述符。 close是“复制”的缩写,而不是“移动”。完成后,您还有一个额外的描述符,所以不仅仅是dup - 后来也是dup2(fdes[1], fileno(stdout)。 (要非常强大,您应该检查close(fdes[1])是否已经过了,在这种情况下,请跳过fdes[1]==fileno(stdout)dup2

关注问题

您不能将一个管道用于3个进程,因为无法区分哪个数据应该到达哪个目标。当第一个进程写入管道时,当其他两个进程都尝试从中读取时,其中一个进程将获取数据,但您将无法预测哪个进程。您需要中间进程来读取第一个进程写入的内容,以及读取中间进程写入内容的最后一个进程。

你在分叉后共享文件描述符是正确的。实际管道对象是共享的。这就是整个系统运作的原因。但文件描述符 - 由小整数指定的端点,如标准输出为1,标准输入为0,等等 - 与您建议的方式不相关。相同的管道对象可以在两个进程中与相同的文件描述符号相关联,这些关联是独立的。在一个过程中关闭fd 1不会导致fd 1在任何其他过程中关闭,即使它们是相关的。

共享fd表,以便一个任务中的一个关闭在另一个任务中生效,是“pthread”功能集的一部分,而不是“fork”功能集。