파이프의 한쪽 끝에는 fd 읽기 및 쓰기가 모두 있습니까?

파이프의 한쪽 끝에는 fd 읽기 및 쓰기가 모두 있습니까?

내가 아는 한, 파이프의 한쪽 끝은 fd를 읽고 쓸 수 있고, 다른 쪽 끝도 fd를 읽고 쓸 수 있습니다. 이것이 바로 우리가 using을 사용하여 쓸 때 파이프의 같은 쪽 읽기 끝을 fd[1]닫고 , using을 사용하여 두 번째 끝에서 읽을 때 해당 끝의 읽기 끝을 닫는 이유입니다. 내가 맞나요?fd[0]fd[0]fd[1]

답변1

예, 파이프라인()으로 생성된 파이프라인에는 두 개의 파일 설명자가 있습니다. fd[0]읽기 및 fd[1]쓰기용.

아니요, 파이프의 양쪽 끝을 닫을 필요가 없습니다. 양방향 통신에 사용할 수 있습니다.

편집: 댓글에서 이것이 어떤 것과 관련이 있는지 궁금해하셨으므로 ls | less이에 대해서도 설명하겠습니다.

셸에는 0(stdin), 1(stdout), 2(stderr)의 세 가지 열린 파일 설명자가 있습니다. 쉘이 명령을 실행할 때 다음과 같은 작업을 수행합니다(약간 단순화했습니다:

pid = fork();
if(pid == 0) {
    /* I am the child */
    execve(...); /* Whatever the user asked for */
}
else {
    waitpid(pid); /* Wait for child to complete */
}

파일 설명자 0, 1, 2는 하위 프로세스에 의해 상속되므로 입력/출력이 예상대로 작동합니다. 이렇게 하면 ls | less리디렉션 시 약간 다른 일이 발생합니다.

int pipe[2];
pipe(pipe, 0);

pid1 = fork();
if(pid1 == 0) {
    /* This is ls, we need to remap stdout to the pipe. We don't care about reading from the pipe */
    close(pipe[0]);
    close(1);
    dup2(pipe[1], 1);
    execve(...);
}
else {
    pid2 = fork();
    if(pid2 == 0) {
        /* This is less, it reads from the pipe */
        close(pipe[1]);
        close(0);
        dup2(pipe[0], 0);
        execve(...);
    }
    else {
        waitpid(pid1);
        waitpid(pid2);
    }
}

따라서 셸은 파이프를 생성하고 분기한 다음 실행 전에 파이프를 하위 프로세스의 stdin 또는 stdout에 다시 매핑하여 데이터가 프로세스 1에서 프로세스 2로 흐를 수 있도록 합니다. 쉘 파이프는 양방향이 아니기 때문에 파이프의 한쪽 끝만 사용하고 다른 쪽 끝을 닫습니다(파일 설명자를 표준 입력 또는 표준 출력에 복사한 후 실제로 다른 쪽 끝도 닫습니다).

답변2

요약입니다채팅 대화 데니스다른 초보자들도 쉽게 사용할 수 있도록 단순화했습니다. 실행할 때 ls | less:

  1. bash 쉘은 , 및 의 상위 fd[0]프로세스 fd[1]입니다 fd[2].
  2. pipe()부모에 의해 호출되면 데이터를 보관하기 위한 버퍼에 지나지 않는 파이프를 생성하고 각 끝에는 읽기용과 쓰기용 파일 설명자가 있습니다.
  3. 하위 프로세스는 열려 있는 모든 fd 0, 1, 2와 파이프용 2개를 상속합니다. 따라서 lsfds는 둘 다 자체 버퍼 복사본을 가지고 있지만 둘 다 fds의 동일한 파이프를 가리킵니다. 첫 번째 하위 프로세스 내에서 실행하면 이제 출력을 파이프( )의 쓰기 끝으로 다시 매핑한 다음 콘솔로 다시 매핑해야 합니다. 그래서 다시 매핑이 있을 것입니다. 상위 프로세스가 아닌 하위 프로세스에만 영향을 미칩니다. 이제 의 출력이 하위 파이프의 쓰기 끝으로 전달됩니다. 여전히 읽기 끝이고 데이터 자체를 읽는 것을 원하지 않으므로 로 끝을 닫아야 합니다. 다시 말하지만, 이는 .fd[0]fd[1]bashlsfd[1]close(1)dup(fd[1])close(1)lsbashlsfd[1]fd[0]lsclose(fd[0])lsbash
  4. less파이프 파일 설명자를 상속하는 하위 프로세스를 생성하기 위해 다시 분기합니다 . 입력은 less버퍼에서 읽어야 하므로 더 이상 표준 입력이 아니므로 다시 매핑해야 하므로 fd[0]여기서 입력 은 쓰기 끝 을 읽고 닫 습니다 . 쓰고 읽은 데이터가 다시 쓰여지는 것을 방지하기 위해 필요합니다.fd[0]close(0)dup(fd[0])fd[0]lslessfd[0]fd[1]less

답변3

less 명령을 호출하기 전에 close부모에서 fd[1]을 수행해야 합니다. 그렇지 않으면 명령이 입력을 무한정 기다리게 됩니다.fork()less

관련 정보