管()与fork()的递归:文件描述符处理 [英] Pipe() with fork() with recursion: File Descriptors handling

查看:159
本文介绍了管()与fork()的递归:文件描述符处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个关于被问昨天现有问题的困惑:结果
C - 在Unix中递归管道试

我重新张贴问题code:

 的#include<&stdio.h中GT;
#包括LT&;&unistd.h中GT;
#包括LT&; SYS / types.h中>
#包括LT&;&stdlib.h中GT;无效管道(字符* AR [],INT POS,INT in_fd);
无效ERROR_EXIT(为c​​onst char *);
静态INT孩子= 0; / *无论是相对于main()的一个子进程* /INT主(INT ARGC,CHAR *的argv []){
    如果(的argc 2){
        的printf(用法:%s选项(选项)... \\ n,argv的[0]);
        出口(1);
    }
    管道(ARGV,1,STDIN_FILENO);
    返回0;
}无效ERROR_EXIT(为c​​onst char *柑){
    PERROR(柑);
    (子_exit:退出?)(EXIT_FAILURE);
}无效管道(字符* AR [],INT POS,诠释in_fd){
    如果(AR [POS + 1] == NULL){/ *最后一个命令* /
        如果(in_fd!= STDIN_FILENO){
            如果(dup2(in_fd,STDIN_FILENO)!= - 1)
                关闭(in_fd); / *成功重定向* /
            别的ERROR_EXIT(dup2);
        }
        execlp(AR [POS],AR [POS],NULL);
        ERROR_EXIT(execlp最后);
    }
    其他{
        INT FD [2];
        将为pid_t childpid;        如果((管(FD)== -1)||((childpid =叉())== -1)){
            ERROR_EXIT(无法安装管道);
        }
        如果(childpid == 0){/ *子执行当前命令* /
            孩子= 1;
            关闭(FD [0]);
            如果(dup2(in_fd,STDIN_FILENO)== -1)/ *从in_fd读取* /
                PERROR(无法重定向标准输入);
            如果(dup2(FD [1],STDOUT_FILENO)== - 1)/ *写于fd [1] * /
                PERROR(无法重定向标准输出);
            否则如果((接近(FD [1])== - 1)||(接近(in_fd)== - 1))
                PERROR(无法关闭额外的管道描述符);
            其他{
                execlp(AR [POS],AR [POS],NULL);
                ERROR_EXIT(无法execlp);
            }
        }
        关闭(FD [1]); / *母体执行命令的其余* /
        关闭(in_fd);
        管道(AR,POS + 1,FD [0]);
    }
}

这是存在的错误是:

 示例:
./prog LS uniq的排序头得到:
排序:Stat失败 - :错误的文件描述符

有人提出的解决方案是,因为他们已经在父进程关闭不关闭文件描述符FD子进程中的[1]和in_fd。

我的困惑:(对不起,我在Linux的新手)结果
按我的书开头的Linux程序设计,当我们fork()的一个过程,那么文件描述符也被复制。因此,家长和孩子应该有不同的文件描述符。这违背了答案。

我尝试:结果
我尝试到c自己运行此$ C $和我看到的问题就来了只有当我关闭这两个进程(父母和子女)的in_fd文件描述符。它不依赖于FD [1]。结果,
另外,有趣的是,如果我尝试 ./ PROG LS排序头它工作正常,但是当我尝试 ./ PROG LS排序uniq的头它给出了头读取错误

我的思考:
in_fd 文件描述符只是这个功能的输入int变量。似乎连叉后,只有一个文件描述符仍然正在由两个父和子共用。但我无法理解如何。


解决方案

  

当我们派生()的处理,则该文件描述符也
  复制。因此,家长和孩子应该有不同的文件
  描述


文件描述符是一个简单的整数。所以当它被复制,它具有相同的值,因此它们指向同一个文件。

所以,你可以打开父文件,并从孩子访问。只可能出现的问题是,如果该文件是从父母和孩子访问两者,在这种情况下,它不从它会访问该文件的哪个位置保证。为了避免这种情况,建议关闭FD在儿童和重新打开。

正如你指出你的企图,我也做了同样的问题说,发现这个总是在发生的第4个命令。此外, dup2()关闭它被复制的文件。在这个问题, FD [1] in_fd 被复制到孩子的标准输入标准输出。和 FD [1] in_fd 在那一刻结束。没有必要再次将其关闭。

关闭一个已经关闭的描述符会导致错误。

和你不知道父母或子女是否会先执行,如果从孩子关闭一个文件,然后再次关闭由父母,可能会引起问题,而这种行为是未predictable。

I have confusion regarding an existing question that was asked yesterday:
C - recursive piping in Unix again.

I am re-posting the problematic code:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>

void pipeline( char * ar[], int pos, int in_fd);
void error_exit(const char*);
static int child = 0; /* whether it is a child process relative to main() */

int main(int argc, char * argv[]) {
    if(argc < 2){
        printf("Usage: %s option (option) ...\n", argv[0]);
        exit(1);
    }
    pipeline(argv, 1, STDIN_FILENO);
    return 0;
}

void error_exit(const char *kom){
    perror(kom);
    (child ? _exit : exit)(EXIT_FAILURE);
}

void pipeline(char *ar[], int pos, int in_fd){
    if(ar[pos+1] == NULL){ /*last command */
        if(in_fd != STDIN_FILENO){
            if(dup2(in_fd, STDIN_FILENO) != -1)
                close(in_fd); /*successfully redirected*/
            else error_exit("dup2");
        }
        execlp(ar[pos], ar[pos], NULL);
        error_exit("execlp last");
    }
    else{
        int fd[2];
        pid_t childpid;

        if ((pipe(fd) == -1) || ((childpid = fork()) == -1)) {
            error_exit("Failed to setup pipeline");
        }
        if (childpid == 0){ /* child executes current command */
            child = 1;
            close(fd[0]);
            if (dup2(in_fd, STDIN_FILENO) == -1) /*read from in_fd */
                perror("Failed to redirect stdin");
            if (dup2(fd[1], STDOUT_FILENO) == -1)   /*write to fd[1]*/
                perror("Failed to redirect stdout");
            else if ((close(fd[1]) == -1) || (close(in_fd) == - 1))
                perror("Failed to close extra pipe descriptors");
            else {
                execlp(ar[pos], ar[pos], NULL);
                error_exit("Failed to execlp");
            }
        }
        close(fd[1]);   /* parent executes the rest of commands */
        close(in_fd);
        pipeline(ar, pos+1, fd[0]);
    }
}

The error that was occuring was:

Example: 
./prog ls uniq sort head 

gives: 
sort: stat failed: -: Bad file descriptor

The solution that was suggested was, "don't close the file descriptors fd[1] and in_fd in the child process since they are already being closed in the parent process."

My Confusion: (sorry I am a newbie in Linux)
As per my book "Beginning Linux Programming", when we fork() a process, then the file descriptors are also duplicated. Hence the parent and child should have different file descriptors. This contradicts the answer.

My Attempt:
I tried to run this code myself and I saw that the problem comes only if I close the "in_fd" file descriptor in both processes (parent and child). It does not depend on fd[1].
Also, interestingly, if I try ./prog ls sort head it works fine, but when I try ./prog ls sort head uniq it gives a read error on head.

My Thoughts: The in_fd file descriptor is just an input int variable for this function. It seems that even after fork, only one file descriptor remains that is being shared by both parent and child. But I am not able to understand how.

解决方案

when we fork() a process, then the file descriptors are also duplicated. Hence the parent and child should have different file descriptors

file descriptor is a simple integer. so when it is copied, it has same value so they point to same file.

So you can open a file in parent, and access it from child. Only problem that may occur is if the file is accessed from parent and child both, in that case it is not guaranteed from which position of the file it will access. To avoid this, it is recommended to close the fd in child and reopen.

As you have stated your attempt, I did the same for the said problem and find that this is happening for 4th command always. Also, dup2() closes the file which it is duplicating. In the problem, fd[1] and in_fd was duplicated to the child's stdin and stdout. and fd[1] and in_fd was closed in that moment. There is no need to close them again.

Closing a already closed descriptor will cause error.

And as you do not know whether parent or child is going to execute first, if you close one file from child, and again close from parent, may cause problem, and this type of behavior is unpredictable.

这篇关于管()与fork()的递归:文件描述符处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆