10 파이프와 FIFO

파이프는 프로세스사이의 통신을 위한 메커니즘이다; 한 프로세스에 의해 파이프에 쓰여진 것은 다른 프로세스에 의해 읽혀질 수 있다. 데이터는 선입선출(FIFO) 순서로 다뤄진다. 파이프는 이름을 가지고 있지 않다.

그것이 한 번 사용하기 위해 만들어지고 양끝은 그 파이프를 만든 단일 프로세스로부터 상속되어져야만 한다. 특별한 파일인 FIFO는 파이프와 유사하지만, 파이프가 이름이 없고 임시적인 연결인 것에 비해, FIFO는 이름을 갖고 있다. 프로세스는 그것을 통해 통신의 목적으로, 이름을 가진 FIFO를 개방한다.

파이프나 FIFO는 동시에 양끝을 개방해야한다. 만일 당신이 파이프나 FIFO에 쓰기 위한 프로세스를 가지지 않은 파이프나 FIFO로부터 읽으려면, 읽기는 파일의 끝을 리턴한다. 읽기 프로세스를 가지지 않은 파이프나 FIFO에 쓰기는 에러가 발생된 것으로 취급된다. 그것은 SIGPIPE 신호로 일반화되고, 만일 그 신호가 블록 되어졌다면 에러코드 EPIPE로 에러임을 나타낸다.

특별한 파일인 파이프나 FIFO도 파일 위치시키기(positioning)를 허용한다. 읽기와 쓰기 동작은 순차적으로 일어난다; 파일의 앞에서부터 읽어서 그 끝에 출력한다.


10. 1 파이프 만들기

파이프를 만드는 함수로는 pipe함수가 있다. 이것은 파이프의 양끝을 읽기와 쓰기로 만든다. 이것은 단일한 프로세스에 사용하기에 유용하지 않고, 특별히 한 프로세스가 하나 혹은 더 많은 자식 프로세스들을( 23. 4절 [Creating a Process] 참조) 생성하기 전에 단지 파이프를 만든다. 그리고 나서 그 파이프는 부모 혹은 자식 프로세스들, 또는 두 개의 형제 프로세스 사이의 통신을 위해서 사용되어진다. 파이프 함수는 헤더파일 'unistd. h'에 선언되어 있다.

함수 : int pipe (int filedes[2])

pipe 함수는 파이프를 만들고 파이프의 끝에서(각각) 읽기와 쓰기를 위한 파일 기술자인 filedes[0] 과 filedes[1]을 저장한다. 파일기술자 0은 표준 입력이고, 파일 기술자 1은 표준 출력이다.
만일 성공하면, pipe는 0을 리턴하고, 실패하면 -1을 리턴한다. 다음의 errno는 이 함수를 위해 정의된 에러 상황이다.

EMFILE : 그 프로세스가 개방된 너무 많은 파일을 갖고 있다.

ENFILE

전제 시스템 안에 개방된 파일이 너무 많다. ENFILE에 대한 상세한 것은 2. 2절 [Error Codes]를 참조하라.
이곳의 예는 파이프를 만드는 간단한 예이다. 이 프로그램은 자식 프로세스를 만들기 위해서 fork함수( 23. 4절 [Creating a Process] 참조)를 사용한다. 부모 프로세스는 파이프에 데이터를 쓰고 자식 프로세스에 의해 그 데이터가 읽혀진다.
#include <sys/types. h>
#include <unistd. h>
#include <stdio. h>
#include <stdlib. h>
/* 파이프로부터 문자들을 읽고 그들은 표준 출력에 출력하라 */
void
read_from_pipe (int file)
{
FILE *stream;
int c;
stream = fdopen (file, "r");
while ((c = fgetc (stream)) != EOF)
putchar (c);
fclose (stream);
}
/* 파이프에 어떤 텍스트를 써라 */
void
write_to_pipe (int file)
{
FILE *stream;
stream = fdopen (file, "w");
fprintf (stream, "hello, world!\n");
fprintf (stream, "goodbye, world!\n");
fclose (stream);
}
int
main (void)
{
pid_t pid;
int mypipe[2];
/* 파이프를 만들어라 */
if (pipe (mypipe)) {
fprintf (stderr, "Pipe failed. \n");
return EXIT_FAILURE;
}
/* 자식 프로세스를 만들어라 */
if ((pid = fork ()) == 0) { /* 이것은 자식 프로세스이다. */
read_from_pipe (mypipe[0]);
return EXIT_SUCCESS;
} else if (pid < 0) {
/* fork가 실패했다. */
fprintf (stderr, "Fork failed. \n");
return EXIT_FAILURE;
} else { /* 이것은 부모 프로세스이다. */
write_to_pipe (mypipe[1]);
return EXIT_SUCCESS;
}
}


10. 2 부프로세스를 위한 파이프

파이프는 부프로세스로 실행되어지고 있는 한 프로그램으로부터 데이터를 받거나 혹은 데이터를 보내는데 일반적으로 사용된다. 이것들은 pipe( 파이프를 만드는 ), fork(부프로세스를 만들기 위한 ), dup2( 표준 입력 또는 출력 채널로 파이프를 사용하기 위한 부프로세스를 위해 ), 그리고 exec( 새로운 프로그램은 실행하기 위한)을 복합적으로 사용한다. 또는 popen과 pclose를 사용할 수 있다.

popen과 pclose를 사용함으로 인해서 얻는 이득은 사용하기 쉽고 좀더 간단한 인터페이스를 갖는다는 것이다. 그러나 직접적으로 저수준 함수를 사용함으로 인해서 프로그램의 유연성은 얻을 수가 없다.

함수 : FILE * popen (const char *command, const char *mode)

popen 함수는 system함수와 매우 가까운 연 관을 갖고 있다; 23. 1절 [Running a Command] 참조. 그것은 부프로세스로써 command 쉘 명령을 실행한다. 그렇지만 그 명령이 수행되기를 기다리는 대신에, 그것은 부프로세스에 파이프를 만들고 그 파이프에 해당하는 스트림을 리턴한다.
만일 mode 인수가 "r"로 정해진다면, 당신은 부프로세스의 표준 출력 채널로부터 데이터를 받아들인 스트림으로부터 데이터를 읽을 수 있다. 부프로세스는 부모 프로세스로부터 표준 입력 채널을 상속받는다. 유사하게, 만일 당신이 mode 인수를 "w"로 정한다면, 당신은 부프로세스의 표준 입력 채널로 데이터를 보내기 위한 스트림에 데이터를 쓸 수 있다.
부프로세스는 부모 프로세스로부터 표준 출력 채널을 상속받는다. 에러가 발생하면, popen은 널 포인터를 리턴한다. 이것은 파이프나 스트림이 만들어지지 않았거나, 부프로세스가 생성되지 않았거나, 혹은 프로그램이 실행되지 않았거나 할 때 발생할 것이다.

함수 : int pclose (FILE *stream)

pclose 함수는 popen으로 만들어진 스트림을 닫을 때 사용한다. 그것은 시스템 함수로써, 자식 프로세스가 종료되고 그 상황 값을 리턴하기를 기다린다.

여기에 다른 프로그램을 통한 출력 필터로 popen과 pclose를 어떻게 사용하는지 보여주는 예가 있다.

#include <stdio. h>
#include <stdlib. h>
void
write_data (FILE * stream)
{
int i;
for (i = 0; i < 100; i++)
fprintf (stream, "%d\n", i);
if (ferror (stream)) {
fprintf (stderr, "Output to stream failed. \n");
exit (EXIT_FAILURE);
}
}
int
main (void)
{
FILE *output;
if ((output = popen("more", "w")) == NULL) {
fprintf (stderr, "Could not run more. \n");
return EXIT_FAILURE;
}
write_data (output);
pclose (output);
return EXIT_SUCCESS;
}


10. 3 FIFO 특별한 파일들

특별한 파일인 FIFO는 다른 방법으로 만들어진다는 점을 제외하고는 파이프와 유사하다. 파이프가 작자 불명의 통신 채널인 점에 비해 FIFO 특별파일은 mkfifo를 호출함으로써 파일 시스템에 삽입된다. 일단 당신이 이 방법으로 FIFO 특별파일을 만들면, 어느 프로세스도 일반적 파일을 다루는 것과 같은 방법으로 읽기나 쓰기를 위해 그것을 개방 할 수 있다. 그렇지만, 그것에 입력이나 출력 명령을 행하기 전에 양끝이 동시에 개방되도록 해야한다. 보통의 블록을 읽기 위해서 FIFO를 개방한 동안 어떤 다른 프로세스는 출력을 위해서 같은 FIFO를 개방한다. mkfifo 함수는 헤더파일 'sys/stat. h'에 선언되어 있다.

함수 : int mkfifo (const char *filename, mode_t mode)

mkfifo 함수는 filename의 이름을 가진 FIFO특별파일을 만든다. mode 인수는 파일의 허가를 설정하기 위해 사용되어진다; 9. 8. 7절 [Setting Permissions] 참조.
일반적으로, 성공하면 mkfifo는 0을 리턴한다. 에러가 발생한 경우에는 -1을 리턴한다. 보통의 파일 이름 문법에러에 더하여 ( 6. 2. 3절 [File Name Errors] 참조) 다음의 errno는 이 함수를 위해 정의된 에러상황이다.

EEXIST : 그 이름을 가진 파일이 이미 존재한다.

ENOSPC : 디렉토리나 파일 시스템이 확장되어진 수 없다.

EROFS : 그 파일이 들어갈 디렉토리가 오직 읽기 모드 파일 시스템에 존재한다.


10. 4 파이프 입/출력의 원소수

읽거나 쓰여진 파이프의 데이터에서 만일 그 데이터의 크기가 PIPE_BUF 보다 작다면 원소이다. 이것은 시스템 안의 어떤 것도 부분적으로 수행된 상황을 말해줄 수 없는, 즉각적으로 만들어진 단위의 데이터를 참조함을 의미한다. 원소단위 입/출력은 바르게 시작되지 않았을 것이다. (그것은 데이터나 버퍼 공간을 확보하기 위해서 기다릴 필요가 있었지만, 일단 시작되었고, 그것은 즉시 끝난다. ) 데이터의 많은 양을 읽거나 쓰는 것은 원소단위가 되어지지 않을 것이다. 예를 들어, 다른 프로세스가 점유하고 있는 기술자로부터의 출력 데이터는 흩어지게 될 것이다. 27. 6절 [Limits for Files] 를 참조해서 PIPE_BUF 파라미터에 대한 자세한 정보를 얻어라.