管道和叉子

时间:2011-05-05 17:13:03

标签: c linux pipe

该项目的目标是使用管道和分支来执行已经以多进程方式编写的行计数实用程序(每个参数一个进程)。我目前正致力于在扩展以处理多个args之前使单个进程正常工作。

鉴于两个可执行文件lc1lc2,我希望lc2建立到lc1的stdout文件描述符的管道,以便在execlp("lc1", argv[1], NULL)时被调用,输出将由
读入   while ((c= read(pipefd[0], readin, SIZE)) > 0)

根据我的Unix书,我应该使用open,dup2,close方法将stdout重定向到stdin,这是我的代码:

int pid, c, i;
char *readin= (char *)malloc(sizeof(SIZE));

if (pipe(pipefd)== -1)
  perror("Can't open a pipe\n");

for (i=1; i< argc; i++){
if ((pid= fork())==-1)
        perror("Can't fork\n");

  run(argv[i]);

}

//close pipe
close(1);
if (dup2(pipefd[0], 0)==-1)
  perror("Can't redirect stdin");
close(pipefd[1]);

for (i=1; i< argc; i++){
    if ((wait(NULL))== -1)
        perror("Wait error");

    while ((c= read(pipefd[0], readin, SIZE)) > 0){;
        //print buf count
        total += atoi(readin);
    }
}

运行功能

void run(char *f){
  int fp;
  if ((fp= open(f, O_RDONLY)) == -1)
      perror("Can't open the file");

  close(pipefd[0]);
  dup2(pipefd[1], 1);
  close(pipefd[1]);
  execlp("ls1", f, NULL);
} 

当我尝试执行此代码时,我收到stdin重定向错误,指出错误的文件描述符。为什么会发生这种情况,并希望得到任何修复的提示。

2 个答案:

答案 0 :(得分:2)

run(argv [i])由父和子执行,因为没有根据返回的PID分配功能,所以一个关闭后可能已关闭。 看下面的代码,他可以方便,我会在这样的情况下使用代码示例。 :

int main()
{
    int pipe_fd[2] = {0};
    int pid = -1;
    int status = -1;
    int ret_value = INVALID_CMD;
    int cmd_output_len = -1;
    status = pipe(pipe_fd);
    if(status<0)
    {
        perror("pipe create err");
    }
    else
    {
        pid = fork();
        if(pid<0)
        {
        }
        else if (pid == 0)
        {
            /*Child functionality*/
            child_func(pipe_fd, cmd);
        }
        else
        {
            /*Parent functionality*/
            cmd_output_len = parent_fun(pid, pipe_fd);
        }
    }
    return ret_value;
}

int child_func(int pipe_fd[], const char * cmd)
{
    int status = 5;
    int read_fd = pipe_fd[0];       /*read file descriptor*/
    int write_fd = pipe_fd[1];      /*write file descriptor*/

    int exit_status = 0;

    /*close read fd*/
    close(read_fd);

    /*dup2 stdout to write fd*/
    //status = dup2(1, write_fd);
    status = dup2(write_fd, 1);
    if(status<0)
    {
        exit(-1);
    }
    else
    {
        system(cmd);
        exit(0);
    }
}


int parent_fun(int child_id, int pipe_fd[])
{
    int status = -1;
    int len = 0;
    bool_e break_loop = FALSE;
    int read_fd = pipe_fd[0];       /*read file descriptor*/
    int write_fd = pipe_fd[1];      /*write file descriptor*/

    /*close write fd*/
    close(write_fd);

    while(1)
    {
        sleep(1);
        status = waitpid(child_id, &status, WNOHANG);
        switch(status)
        {
            case 0:
                    /*Child is still active*/
                    printf("No process waiting to exit..\n");
                    len = do_ur_fun(read_fd);
                    write(1, output, len);
                break;
            /*case EINTR:
            case ECHILD:
            case EINVAL:
                    perror("waitpid error");
                    break_loop = TRUE;
                break;*/
            default:
                if(status<0)
                {
                    perror("waitpid error");
                    break_loop = TRUE;
                    len = -1;
                }
                else if(child_id == status)
                {
                    /*Valid staus from child*/
                    len = read_output(read_fd, output);
                    //write(1, output, len);
                    break_loop = TRUE;
                }
                else
                {
                }
                break;
        }
        if(TRUE == break_loop)
        {
            break;
        }
    }
    return len;
}




int do_ur_fun (int read_fd)
{
        /*Do your exec*/
}

答案 1 :(得分:1)

MaheshGupta024在您的代码中发现了一个非常重要的问题;我假设你会解决这个问题。

其他问题之一是:

close(1);
if (dup2(pipefd[0], 0)==-1)
    perror("Can't redirect stdin");
close(pipefd[1]);

for (i=1; i< argc; i++){
    if ((wait(NULL))== -1)
        perror("Wait error");

    while ((c= read(pipefd[0], readin, SIZE)) > 0){;
        //print buf count
        total += atoi(readin);
    }
}

第一次关闭会关闭流程的标准输出;这很少是一个好主意。下一行将管道的读取端复制到标准输入 - 这很好。如上面的评论中所述,perror()不会退出。然后关闭管道的写入端 - 这是正确的;但是,由于你已经将它设置为来自管道,所以你应该大概关闭管道的读取端。

你的循环开始正常;您在wait()行中有多余的括号。你从pipefd[0]而不是标准输入读取 - 所以也许你不想关闭pipefd [0],但你也不需要将它复制到标准输入。然后你有一个嵌套的循环读取管道,而有更多的数据要从一个孩子读取 - 你不一定需要wait()代码与它的循环,因为内部的同时不会终止,直到所有的孩子是死的。另一方面,它没有太大的危害 - 在第一个孩子死后,你将从所有其他孩子读取数据,然后进入外循环并等待彼此的孩子,内循环立即终止没有数据可供阅读。

所以:

  • 不要关闭stdout。
  • 不要将读取的管道重复到标准输入。
  • 决定是否要清理循环 - 它会起作用,但可能更清洁。

run()功能是:

void run(char *f){
  int fp;
  if ((fp= open(f, O_RDONLY)) == -1)
      perror("Can't open the file");

  close(pipefd[0]);
  dup2(pipefd[1], 1);
  close(pipefd[1]);
  execlp("ls1", f, NULL);
}

参数应为const char *f(或使用namefile而不是f)。我也会将pipefd数组传递给函数,而不是使用全局变量 。 不要调用文件描述符fp;该名称通常表示FILE *类型的变量,而不是int。 但是,您不需要首先打开文件 - 除非您希望调用程序执行错误报告而不是调用的程序。但是,如果您确实希望调用程序执行错误报告,则应在继续之前关闭文件描述符。 (我已经评论perror()返回)。

execlp()之后打印错误消息是个好主意。函数返回的唯一时间是失败,因此无需测试其返回值。您可能也想退出 - 而不是让失败的函数在调用run()后通过主程序的其余部分。

好点:你确实关闭了管道文件描述符。

因此:

void run(const char *file, int *pipefd)
{
    close(pipefd[0]);
    dup2(pipefd[1], 1);
    close(pipefd[1]);
    execlp("ls1", f, NULL);
    perror("Failed to exec ls1");
    exit(EXIT_FAILURE);
}