문제

파이프와 함께 연결할 수있는 독립적 인 스레드로 실행되는 별도의 프로그램 모듈을 작성하는 데 관심이 있습니다. 동기는 각 모듈을 완전히 독립적으로 작성하고 테스트하거나 다른 언어로 작성하거나 다른 시스템에서 다른 모듈을 실행할 수 있다는 것입니다. 여기에는 다양한 가능성이 있습니다. 나는 파이핑을 한동안 사용했지만 그 행동의 뉘앙스에 익숙하지 않습니다.

  • 수신 종료가 입력 대기 대기를 막을 것 같습니다. 그러나 내가 기대할 수있는 전송 엔드 블록이 때때로 누군가가 스트림에서 읽을 때까지 기다릴 것인가?
  • 스트림에 EOF를 쓰면 닫을 때까지 그 스트림에 계속 쓸 수 있습니까?
  • 명명되지 않은 파이프와 이름이없는 동작에 차이가 있습니까?
  • 이름이 지정된 파이프로 먼저 열린 파이프의 끝이 중요합니까?
  • 파이프의 동작이 다른 Linux 시스템간에 일관되게됩니까?
  • 파이프의 동작은 내가 사용하는 쉘 또는 구성 방식에 따라 다릅니 까?
  • 내가 물어봐야 할 다른 질문이나 이런 식으로 파이프를 사용하고 싶은지 알아야 할 문제가 있습니까?
도움이 되었습니까?

해결책

와우, 그것은 많은 질문입니다. 내가 모든 것을 다룰 수 있는지 봅시다 ...

수신 종료가 입력 대기를 막을 것 같습니다.

당신은 실제 '읽기'통화가 무언가가있을 때까지 차단 될 것으로 예상합니다. 그러나 파이프에서 기다리는 (그리고 얼마나 많은지) '엿보기'를 할 수있는 몇 가지 C 기능이 있다고 생각합니다. 불행히도,이 블록이 있는지 기억이 나지 않습니다.

전송 엔드 블록이 때때로 누군가가 스트림에서 읽을 때까지 기다리는 것입니다.

아니요, 보내는 것은 절대 차단해서는 안됩니다. 이것이 네트워크를 가로 질러 다른 컴퓨터로의 파이프라면 영향을 생각해보십시오. 다른 컴퓨터가 그것을받은 것으로 응답하기 위해 (아마도 높은 대기 시간을 통해) 기다리시겠습니까? 이제 대상의 독자 핸들이 닫힌 경우 다른 경우입니다. 이 경우이를 처리하려면 약간의 오류를 확인해야합니다.

스트림에 eof를 쓰면 닫을 때까지 그 스트림에 계속 쓸 수 있습니까?

나는 이것이 당신이 사용하는 언어와 파이프 구현에 달려 있다고 생각합니다. C에서는 아니요. 리눅스 쉘에서 예라고 말하고 싶습니다. 더 많은 경험을 가진 다른 사람이 그 대답해야 할 것입니다.

명명되지 않은 파이프와 이름이없는 동작에 차이가 있습니까? 내가 아는 한, 그렇습니다. 그러나 이름이 이름이 지정된 Vs에 대한 경험이 많지 않습니다. 차이점은 다음과 같습니다.

  • 단일 방향 대 양방향 통신
  • 스레드의 "in"및 "out"스트림을 읽고 쓰기

이름이 지정된 파이프로 먼저 열린 파이프의 끝이 중요합니까?

일반적으로 아니요, 그러나 초기화에 문제가 발생하여 스레드를 서로 생성하고 연결하려고합니다. 모든 서브 스레드를 생성하고 해당 파이프를 서로 동기화하는 하나의 기본 스레드가 있어야합니다.

파이프의 동작이 다른 Linux 시스템간에 일관되게됩니까?

다시 말하지만, 이것은 어떤 언어에 따라 다르지만 일반적으로 그렇습니다. Posix에 대해 들어 본 적이 있습니까? 그것이 표준입니다 (적어도 Linux의 경우 Windows는 자체적으로 수행합니다).

파이프의 동작은 내가 사용하는 쉘 또는 구성 방식에 따라 다릅니 까?

이것은 조금 더 회색 영역으로 들어가고 있습니다. 대답 ~해야 한다 쉘은 본질적으로 시스템 호출을해야하기 때문에 아니에요. 그러나 그 시점까지 모든 것은 잡을 수 있습니다.

내가 물어야 할 다른 질문이 있습니까?

당신이 요청한 질문은 당신이 시스템에 대한 괜찮은 이해를 가지고 있음을 보여줍니다. 계속 연구하고 작업 할 수있는 수준 (Shell, C, ON)에 중점을 둡니다. 그래도 시도해 보면 더 많은 것을 배울 것입니다.

다른 팁

이것은 모두 유닉스와 같은 시스템을 기반으로합니다. 최근 버전의 Windows의 특정 동작에 익숙하지 않습니다.

수신 종료가 입력 대기 대기를 막을 것 같습니다. 그러나 내가 기대할 수있는 전송 엔드 블록이 때때로 누군가가 스트림에서 읽을 때까지 기다릴 것인가?

예, 현대 기계에서는 자주 발생하지 않을 수 있습니다. 파이프에는 중간 버퍼가 있습니다 ~할 수 있다 잠재적으로 채우십시오. 그렇다면 파이프의 쓰기 측면이 실제로 차단됩니다. 그러나 당신이 그것에 대해 생각한다면, 이것을 위험에 빠질만큼 큰 파일은 많지 않습니다.

스트림에 EOF를 쓰면 닫을 때까지 그 스트림에 계속 쓸 수 있습니까?

음, CTRL-D와 같은 말, 0x04? 물론, 스트림이 그런 식으로 설정되는 한. 즉.

506 # cat | od -c
abc
^D
efg
0000000    a   b   c  \n 004  \n   e   f   g  \n                        
0000012

명명되지 않은 파이프와 이름이없는 동작에 차이가 있습니까?

예,하지만 미묘하고 구현에 따라 다릅니다. 가장 큰 것은 다른 쪽 끝이 실행되기 전에 이름이 지정된 파이프에 쓸 수 있다는 것입니다. 이름이없는 파이프를 사용하면 포크/실행 프로세스 중에 파일 디스크립터가 공유되므로 프로세스가 증가하지 않고 과도 버퍼에 액세스 할 수있는 방법이 없습니다.

이름이 지정된 파이프로 먼저 열린 파이프의 끝이 중요합니까?

아니요.

파이프의 동작이 다른 Linux 시스템간에 일관되게됩니까?

이유에서, 그렇습니다. 버퍼 크기 등은 다를 수 있습니다.

파이프의 동작은 내가 사용하는 쉘 또는 구성 방식에 따라 다릅니 까?

아니요. 파이프를 만들 때, 커버 아래에서 발생하는 일은 부모 프로세스 (쉘)가 파일 디스크립터 쌍이있는 파이프를 만듭니다. 그런 다음이 의사 코드와 같이 포크 exec를 수행합니다.

부모의:

create pipe, returning two file descriptors, call them fd[0] and fd[1]
fork write-side process
fork read-side process

쓰기 측:

close fd[0]
connect fd[1] to stdout
exec writer program

읽기 측:

close fd[1]
connect fd[0] to stdin
exec reader program

내가 물어봐야 할 다른 질문이나 이런 식으로 파이프를 사용하고 싶은지 알아야 할 문제가 있습니까?

당신이 원하는 모든 것이 실제로 이런 줄로 배치하려고합니까? 그렇지 않다면 더 일반적인 건축에 대해 생각하고 싶을 수도 있습니다. 그러나 파이프의 "좁은"인터페이스를 통해 많은 별도의 프로세스가 상호 작용한다는 통찰력은 바람직하다.

업데이트 : 파일 디스크립터 지수가 처음에 반전되었습니다. 그들은 지금 맞습니다 man 2 pipe.]

Dashogun과 Charlie Martin이 지적했듯이 이것은 큰 질문입니다. 그들의 대답의 일부는 부정확하므로 대답하겠습니다.

파이프와 함께 연결할 수있는 독립적 인 스레드로 실행되는 별도의 프로그램 모듈을 작성하는 데 관심이 있습니다.

단일 프로세스의 스레드 사이의 통신 메커니즘으로 파이프를 사용하려고하는 것에주의하십시오. 단일 프로세스에서 파이프의 읽기 및 쓰기 끝을 모두 열어두기 때문에 EOF (제로 바이트) 표시를 얻지 못할 것입니다.

프로세스를 실제로 언급하고 있다면 이는 도구 구축에 대한 고전적인 유닉스 접근 방식의 기초입니다. 표준 UNIX 프로그램의 많은 부분은 표준 입력에서 읽고, 어떻게 든 변환하며 결과를 표준 출력에 기록하는 필터입니다. 예를 들어, tr, sort, grep, 그리고 cat 모든 필터, 이름은 몇 가지입니다. 이것은 당신이 조작하는 데이터가 그것을 허용 할 때 따르는 훌륭한 패러다임입니다. 모든 데이터 조작 이이 접근법에 도움이되는 것은 아니지만 많은 것이 있습니다.

동기는 각 모듈을 완전히 독립적으로 작성하고 테스트하거나 다른 언어로 작성하거나 다른 시스템에서 다른 모듈을 실행할 수 있다는 것입니다.

좋은 점. 기계간에 파이프 메커니즘이 실제로는 없지만 rsh 또는 (더 나은) ssh. 그러나 내부적으로 이러한 프로그램은 파이프에서 로컬 데이터를 읽고 해당 데이터를 원격 기계로 보낼 수 있지만 파이프를 사용하지 않고 소켓을 통해 기계간에 통신합니다.

여기에는 다양한 가능성이 있습니다. 나는 파이핑을 한동안 사용했지만 그 행동의 뉘앙스에 익숙하지 않습니다.

확인; 질문을하는 것은 배우는 하나의 좋은 방법입니다. 물론 실험은 또 다른 것입니다.

수신 종료가 입력 대기 대기를 막을 것 같습니다. 그러나 내가 기대할 수있는 전송 엔드 블록이 때때로 누군가가 스트림에서 읽을 때까지 기다릴 것인가?

예. 파이프 버퍼의 크기에는 제한이 있습니다. 고전적으로 이것은 매우 작았습니다 -4096 또는 5120은 일반적인 값이었습니다. 최신 Linux가 더 큰 가치를 사용한다는 것을 알 수 있습니다. 당신이 사용할 수있는 fpathconf() 및 파이프 버퍼의 크기를 찾기 위해 _pc_pipe_buf. POSIX는 버퍼가 512로만 필요합니다 (즉, _posix_pipe_buf는 512입니다).

스트림에 EOF를 쓰면 닫을 때까지 그 스트림에 계속 쓸 수 있습니까?

기술적으로, 스트림에 EOF를 쓸 방법은 없습니다. 파이프 디스크립터를 닫아 EOF를 나타냅니다. Control-D 또는 Control-Z를 EOF 문자로 생각하는 경우 파이프에 관한 한 일반 문자 일뿐입니다. 정식 모드에서 실행중인 터미널에서 입력 한 경우 EOF와 같은 효과 만 있습니다 (요리. , 또는 정상).

명명되지 않은 파이프와 이름이없는 동작에 차이가 있습니까?

예, 아니요. 가장 큰 차이점은 이름이없는 파이프가 하나의 프로세스에 의해 설정되어야하며 해당 프로세스와 해당 프로세스를 공통 조상으로 공유하는 어린이 만 사용할 수 있다는 것입니다. 대조적으로, 이름 지정된 파이프는 이전에 관련되지 않은 프로세스에서 사용할 수 있습니다. 다음 큰 차이는 첫 번째의 결과입니다. 이름이없는 파이프를 사용하면 단일 함수 (시스템) 호출에서 두 개의 파일 설명자를 되 찾을 수 있습니다. pipe(), 그러나 당신은 정규를 사용하여 FIFO 또는 이름이 지정된 파이프를 열었습니다. open() 기능. (누군가가 FIFO를 만들어야합니다 mkfifo() 열기 전에 전화하십시오. 이름이없는 파이프에는 그러한 사전 설정이 필요하지 않습니다.) 그러나 파일 디스크립터가 열리면 이름이 지정된 파이프와 이름이없는 파이프 사이에는 약간의 차이가 없습니다.

이름이 지정된 파이프로 먼저 열린 파이프의 끝이 중요합니까?

아니요. FIFO를 여는 첫 번째 프로세스는 다른 쪽 끝이 열린 프로세스가있을 때까지 (일반적으로) 블록을 차단합니다. 읽기 및 쓰기를 위해 열면 (Aconventional이지만 가능) 차단되지 않을 것입니다. O_NONBLOCK 플래그를 사용하면 차단되지 않습니다.

파이프의 동작이 다른 Linux 시스템간에 일관되게됩니까?

예. 나는 내가 사용한 시스템에서 파이프에 대해 들어 본 적이 없거나 경험하지 않았다.

파이프의 동작은 내가 사용하는 쉘 또는 구성 방식에 따라 다릅니 까?

아니오 : 파이프와 FIFO는 사용하는 쉘과 무관합니다.

내가 물어봐야 할 다른 질문이나 이런 식으로 파이프를 사용하고 싶은지 알아야 할 문제가 있습니까?

글을 쓸 수있는 과정에서 파이프의 판독 끝과 읽기 프로세스에서 파이프의 쓰기 끝을 닫아야한다는 것을 기억하십시오. 파이프를 통해 양방향 통신을 원한다면 두 개의 별도 파이프를 사용하십시오. 복잡한 배관 준비를하는 경우 교착 상태를 조심하십시오. 가능합니다. 선형 파이프 라인은 교착 상태가 아닙니다 (첫 번째 프로세스가 출력을 닫지 않으면 다운 스트림 프로세스가 무기한 대기 할 수 있습니다).


나는 위와 다른 답변에 대한 의견에서 파이프 버퍼가 고전적으로 작은 크기로 제한되어 있음을 관찰했습니다. @Charlie Martin은 일부 버전의 UNIX에는 다이나믹 파이프 버퍼가 있으며 이들은 상당히 클 수 있다고 반박했습니다.

그가 어떤 것을 염두에두고 있는지 잘 모르겠습니다. Solaris, AIX, HP-UX, MacOS X, Linux 및 Cygwin / Windows XP에서 다음과 같은 테스트 프로그램을 사용했습니다 (아래 결과).

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

static const char *arg0;

static void err_syserr(char *str)
{
    int errnum = errno;
    fprintf(stderr, "%s: %s - (%d) %s\n", arg0, str, errnum, strerror(errnum));
    exit(1);
}

int main(int argc, char **argv)
{
    int pd[2];
    pid_t kid;
    size_t i = 0;
    char buffer[2] = "a";
    int flags;

    arg0 = argv[0];

    if (pipe(pd) != 0)
        err_syserr("pipe() failed");
    if ((kid = fork()) < 0)
        err_syserr("fork() failed");
    else if (kid == 0)
    {
        close(pd[1]);
        pause();
    }
    /* else */
    close(pd[0]);
    if (fcntl(pd[1], F_GETFL, &flags) == -1)
        err_syserr("fcntl(F_GETFL) failed");
    flags |= O_NONBLOCK;
    if (fcntl(pd[1], F_SETFL, &flags) == -1)
        err_syserr("fcntl(F_SETFL) failed");
    while (write(pd[1], buffer, sizeof(buffer)-1) == sizeof(buffer)-1)
    {
        putchar('.');
        if (++i % 50 ==  0)
            printf("%u\n", (unsigned)i);
    }
    if (i % 50 !=  0)
        printf("%u\n", (unsigned)i);
    kill(kid, SIGINT);
    return 0;
}

다른 플랫폼에서 추가 결과를 얻는 것이 궁금합니다. 내가 찾은 크기는 다음과 같습니다. 모든 결과는 내가 예상보다 크고 고백해야하지만 찰리와 나는 버퍼 크기와 관련하여 '상당히 큰'의 의미에 대해 토론하고있을 수 있습니다.

  • 8196-IA-64의 경우 HP-UX 11.23 (FCNTL (F_SETFL) 실패)
  • 16384 -Solaris 10
  • 16384 -MACOS X 10.5 (O_NONBLOCK가 작동하지 않았지만 FCNTL (F_SETFL)이 실패하지는 않았지만)
  • 32768 -AIX 5.3
  • 65536 -Cygwin / Windows XP (O_nonBlock은 작동하지 않았지만 fcntl (f_setfl)이 실패하지는 않았지만)
  • 65536 -Suse Linux 10 (및 Centos) (fcntl (f_setfl) 실패)

이 테스트에서 명확한 한 가지 점은 O_nonBlock은 다른 플랫폼에서는 일부 플랫폼에서 파이프와 함께 작동한다는 것입니다.

이 프로그램은 파이프와 포크를 만듭니다. 아이는 파이프의 쓰기 끝을 닫은 다음 신호가 될 때까지 잠을 자게됩니다. 이것이 Pause ()가하는 일입니다. 그런 다음 부모는 파이프의 읽기 끝을 닫고 쓰기 디스크립터에 플래그를 설정하여 전체 파이프에 쓰기를 시도하지 않도록합니다. 그런 다음 한 번에 하나의 캐릭터를 작성하고 각 캐릭터에 대해 점을 인쇄하고 50 자마다 카운트와 신형으로 도트를 인쇄합니다. 쓰기 문제를 감지 할 때 (아이가 물건을 읽지 않기 때문에 버퍼가 가득 찼습니다), 루프를 멈추고, 최종 카운트를 쓰고, 아이를 죽입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top