-
- 파이프
파이프는 프로세스간 통신을 위한 메커니즘 중 하나로,
프로세스의 데이터 흐름을 다른 프로세스로 연결할 때 사용한다. (주로 한 프로세스의 출력을 다른 프로세스의 입력으로 연결)
동일한 부모 프로세스로 부터 생성된 자식 프로세스 사이(부모-자식간 통신)에서만 사용 가능하며, 익명 파이프라고도 불린다.
파이프는 프로세스 사이에 형식이 없는 데이터의 교환을 가능케 하는데,
파이프를 통해 전달되는 데이터는 단순한 바이트 스트림의 형태이다.
파이프는 4kb의 고정된 크기(용량)을 갖는데,
만약 입력되는 데이터의 크기가 파이프 버퍼(4kb)보다 크다면 interleaving, 즉 데이터를 쪼개어 분할 전송하게 된다.
만약 입력되는 데이터의 크기가 파이프 버퍼보다 작다면 atomic, 즉 확실히 데이터를 보낼 수 있다는 것을 보장한다.
파이프는 반이중(half-duplex) 통신 방식을 사용한다. 즉, 하나의 프로세스는 읽기만 가능하고 다른 프로세스는 쓰기만 가능하다.
참고로 파이프의 읽는 부분은 file descriptor 0번에 연결되어 있으며,
파이프의 쓰는 부분은 file descriptor 1번에 연결되어 있다.
따라서, 사용하지 않는 파이프의 file descriptor을 닫아주고 시작해야 한다.
- 파이프 함수
1. int pipe(int filedes[2]);
pid_t pid; int pipefd[2] = { 0, }; pipe(pipeid); pid = fork(); if (pid == 0) { close(pipefd[0]); // close read @child write(pipefd[1], 7, sizeof(int)); close(pipefd[1]); } else { int num; close(pipefd[1]); // close write @parent while(read(pipefd[0], (int *)&num, sizeof(int)) < 0) { ; } close(pipefd[0]); printf("%d\n", num); wait(&pid); }
pipe로 파이프를 생성하여
자식은 읽기를 담당하는 파이프의 파일 지시자 0번을 닫고, 부모는 쓰기를 담당하는 파이프의 파일 지시자 1번을 닫아주어
자식의 입력을 부모가 받도록 파이프를 생성했다. (자식이 숫자 7을 보내고 부모가 이를 받았다)
2. int dup2(int oldfd, int newfd);
파일 지시자를 새 파일 지시자로 복사한다.
pid_t pid; int pipefd[2] = { 0, }; pipe(pipeid); pid = fork(); if (pid == 0) { close(pipefd[0]); dup2(pipdfd[0], STDOUT_FILENO); excel("/bin/ls", "ls", "-a", "-l", NULL); } else { close(pipefd[1]); wait(&pid); dup2(pipefd[0], STDIN_FILENO); execl("/usr/bin/sort", "sort", "-r", NULL); }
pipe생성 후 자식은 파이프의 읽기 파일 지시자를 닫고, 부모는 파이프의 쓰기 파일 지시자를 닫는다.
그리고 dup2를 이용해 자식 프로세스의 표준 출력을 파이프의 쓰기 파일 지시자로 바꾸고,
부모 프로세스의 표준 입력을 파이프의 읽기 파일 지시자로 바꾼다.
위의 Fig. 02, Fig. 03의 과정 이후 dup2, execl로 인해 다음과 같이 파이프가 바뀐다.
- 지명 파이프
지명 파이프는 익명 파이프가 부모-자식 사이에서만 사용이 가능했던 단점을 극복한 파이프로,
서로 다른 프로세스에서도 사용할 수 있으며, FIFO 라고도 불린다.
단, 각 프로세스에서 read, write가 각각 오픈이 되어있어야 통신이 연결된다.
지명 파이프는 일반 파일과 유사하게 고유의 이름과 권한을 갖는데, 특수 파일로 분류되고 관리된다.
권한을 소유한 프로세스만 접근할 수 있고, 파일 접근 프리미티브를 통해 데이터를 처리할 수 있다.
단, 커널에서 처리되기 때문에 파일의 크기는 0이다.
또한, 지명 파이프는 삭제하기 전까지 계속 유지된다.
- 지명 파이프 함수
1. int mkfifo(const char *pathname, mode_t mode);
mode 권한으로 pathname이름의 지명 파이프를 만든다.
// server mkfifo("namepipe", 0666); int fd = open("namepipe", O_RDONLY); char buf[32 + 1] = { 0x00, }; while(1) { if (read(fd, (char *)buf, 32) > 0) { put(buf); memset(buf, 0x00, 32); } } // client int fd = open("namepipe", O_WRONLY); dup2(fd, STDOUT_FILENO); while (1) { printf("[%d] %d\n", getpid(), 7); fflush(stdout); sleep(1); }
위 코드는 namepipe라는 지명 파이프를 만들어서 두 프로세스 사이에서 통신하는 코드이다.
클라이언트가 printf로 자신의 pid와 7을 출력해야 하지만,
dup2를 통해 표준 출력을 지명 파이프의 쓰기로 바꿔주었다.
서버는 이 지명 파이프의 데이터를 읽어와 화면에 출력하게 된다.