도입 문구
C언어에서 유닉스 파이프는 프로세스 간 데이터를 전달하는 중요한 기법입니다. 이 기법은 입출력 스트림을 활용하여 두 프로세스 간 데이터를 효과적으로 교환하는 방법을 제공합니다. 유닉스 파이프는 효율적인 데이터 흐름을 가능하게 하며, 복잡한 시스템에서도 데이터 통신을 간단히 구현할 수 있는 강력한 도구입니다. 이 기사에서는 C언어를 사용하여 유닉스 파이프를 구현하는 방법과 관련된 핵심 개념을 다룰 것입니다.
유닉스 파이프란 무엇인가
유닉스 파이프는 하나의 프로세스에서 출력한 데이터를 다른 프로세스가 입력받을 수 있도록 하는 메커니즘입니다. 이를 통해 두 프로세스 간의 통신을 효율적으로 수행할 수 있으며, 특별한 네트워크 설정 없이도 데이터를 실시간으로 전달할 수 있습니다. 파이프는 일반적으로 “한 방향”으로만 데이터를 흐르게 하며, 한 프로세스가 데이터를 쓰면 다른 프로세스가 이를 읽을 수 있습니다.
C언어에서는 pipe()
시스템 호출을 사용해 파이프를 생성하며, 이를 통해 데이터를 읽고 쓰는 두 개의 파일 디스크립터를 반환받습니다. 파이프는 매우 간단하지만, 강력한 기능을 제공하여 유닉스 및 리눅스 시스템에서 다양한 프로세스 간 통신을 가능하게 만듭니다.
파이프의 기본 사용법
파이프는 pipe()
시스템 호출을 사용하여 생성됩니다. 이 함수는 두 개의 파일 디스크립터를 반환하는데, 하나는 데이터를 쓰는 용도로, 다른 하나는 데이터를 읽는 용도로 사용됩니다. 파이프를 통해 데이터를 주고받는 과정은 간단하지만 매우 효율적입니다.
파이프의 기본 사용법은 다음과 같습니다:
1. 파이프 생성
pipe()
함수는 인자로 전달된 배열을 사용하여 두 개의 파일 디스크립터를 초기화합니다. 예를 들어:
#include <unistd.h>
#include <stdio.h>
int main() {
int pipefd[2]; // 파이프 디스크립터
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
// pipefd[0]은 읽기용 디스크립터, pipefd[1]은 쓰기용 디스크립터
return 0;
}
2. 부모 프로세스와 자식 프로세스 간 데이터 전달
파이프를 통해 데이터를 전달하려면, 부모 프로세스가 파이프의 쓰기 디스크립터를 사용해 데이터를 쓰고, 자식 프로세스가 읽기 디스크립터를 통해 데이터를 읽습니다. 이를 위해 fork()
시스템 호출로 자식 프로세스를 생성하고, 부모와 자식 간에 데이터를 전송할 수 있습니다.
3. 파이프 사용 예시
다음은 간단한 예시로, 부모 프로세스가 자식 프로세스에 데이터를 전송하는 코드입니다:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int pipefd[2];
char buffer[100];
pid_t pid;
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid = fork();
if (pid == 0) { // 자식 프로세스
close(pipefd[1]); // 쓰기 디스크립터 닫기
read(pipefd[0], buffer, sizeof(buffer));
printf("Received data: %s\n", buffer);
close(pipefd[0]);
} else { // 부모 프로세스
close(pipefd[0]); // 읽기 디스크립터 닫기
write(pipefd[1], "Hello from parent!", 19);
close(pipefd[1]);
}
return 0;
}
이 코드는 부모 프로세스가 “Hello from parent!”라는 문자열을 파이프에 쓰고, 자식 프로세스는 그 데이터를 읽어 출력합니다.
4. 파이프 사용 시 고려할 점
파이프는 한 방향으로만 데이터가 흐를 수 있기 때문에, 두 프로세스 간의 데이터 흐름을 명확히 정의해야 합니다. 또한, 파이프에 데이터를 쓰는 프로세스가 종료되면 읽기 프로세스는 EOF(End Of File)를 인식하게 되므로, 이를 처리할 방법도 고려해야 합니다.
C언어에서 파이프 생성 코드 예시
C언어에서 파이프를 생성하고 데이터를 전송하는 기본적인 코드 예시를 소개합니다. 이 예시를 통해 pipe()
함수를 사용하여 파이프를 생성하고, 데이터를 부모 프로세스에서 자식 프로세스로 전송하는 방법을 이해할 수 있습니다.
1. 기본적인 파이프 생성과 데이터 전송
다음 코드는 부모 프로세스가 데이터를 파이프를 통해 자식 프로세스로 전송하는 예시입니다. 부모 프로세스는 데이터를 쓴 후, 자식 프로세스는 이를 읽어 출력합니다.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int pipefd[2]; // 파이프 디스크립터 배열
char buffer[100]; // 데이터를 저장할 버퍼
pid_t pid;
// 파이프 생성
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid = fork(); // 자식 프로세스 생성
if (pid == 0) { // 자식 프로세스
close(pipefd[1]); // 쓰기 디스크립터 닫기
read(pipefd[0], buffer, sizeof(buffer)); // 파이프에서 데이터 읽기
printf("Received data: %s\n", buffer); // 읽은 데이터 출력
close(pipefd[0]);
} else { // 부모 프로세스
close(pipefd[0]); // 읽기 디스크립터 닫기
write(pipefd[1], "Hello from parent!", 19); // 파이프에 데이터 쓰기
close(pipefd[1]);
}
return 0;
}
2. 코드 설명
pipe(pipefd)
함수는 파이프 디스크립터 배열을 생성하여,pipefd[0]
은 읽기용,pipefd[1]
은 쓰기용 디스크립터로 사용됩니다.fork()
를 호출하여 자식 프로세스를 생성합니다. 부모 프로세스는 데이터를 파이프에 쓰고, 자식 프로세스는 이를 읽습니다.- 부모 프로세스는
pipefd[0]
을 닫고, 자식 프로세스는pipefd[1]
을 닫아서 각 프로세스가 읽기와 쓰기 작업을 수행하도록 합니다. read()
함수는 파이프에서 데이터를 읽고,write()
함수는 데이터를 파이프에 씁니다.
3. 출력 예시
실행하면 자식 프로세스에서 다음과 같은 출력이 나타납니다:
Received data: Hello from parent!
이 예시는 C언어에서 파이프를 사용해 프로세스 간에 데이터를 전달하는 기본적인 방법을 보여줍니다.
부모 프로세스와 자식 프로세스 간의 파이프 사용
C언어에서 부모 프로세스와 자식 프로세스 간에 데이터를 전달할 때 파이프를 활용하는 방법을 더 자세히 살펴봅니다. 부모 프로세스는 데이터를 파이프에 쓰고, 자식 프로세스는 이를 읽어 처리하는 구조입니다. 이 방식은 프로세스 간의 간단하면서도 효율적인 통신 방법을 제공합니다.
1. 부모-자식 프로세스 간 데이터 전송
다음 코드는 부모 프로세스가 자식 프로세스에 데이터를 전송하는 과정에서 발생할 수 있는 일반적인 패턴을 보여줍니다. 이 예시에서는 부모 프로세스가 데이터를 파이프에 쓰고, 자식 프로세스는 이를 읽어 처리합니다.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int pipefd[2]; // 파이프 디스크립터 배열
char buffer[100]; // 데이터를 저장할 버퍼
pid_t pid;
// 파이프 생성
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid = fork(); // 자식 프로세스 생성
if (pid == 0) { // 자식 프로세스
close(pipefd[1]); // 쓰기 디스크립터 닫기
read(pipefd[0], buffer, sizeof(buffer)); // 파이프에서 데이터 읽기
printf("Child received: %s\n", buffer); // 자식 프로세스에서 데이터 출력
close(pipefd[0]);
} else { // 부모 프로세스
close(pipefd[0]); // 읽기 디스크립터 닫기
write(pipefd[1], "Hello from parent!", 19); // 파이프에 데이터 쓰기
close(pipefd[1]);
}
return 0;
}
2. 코드 설명
pipe(pipefd)
로 파이프를 생성하여,pipefd[0]
은 읽기용,pipefd[1]
은 쓰기용 디스크립터로 사용됩니다.fork()
함수는 부모 프로세스와 자식 프로세스를 생성합니다. 자식 프로세스는 파이프의 읽기 디스크립터를 사용해 데이터를 읽고, 부모 프로세스는 쓰기 디스크립터를 사용해 데이터를 씁니다.- 부모 프로세스는
pipefd[0]
을 닫고, 자식 프로세스는pipefd[1]
을 닫습니다. 이는 각 프로세스가 필요한 디스크립터만 사용하게 하기 위함입니다. - 자식 프로세스는
read()
함수로 파이프에서 데이터를 읽고 이를 출력합니다.
3. 부모 프로세스에서 자식 프로세스로의 데이터 전송
이 예시에서는 부모 프로세스가 자식 프로세스로 데이터를 전송합니다. 부모는 write()
함수를 사용하여 파이프에 데이터를 쓰고, 자식은 이를 읽어 출력합니다. 부모가 데이터를 쓴 후, 자식은 이를 읽어들여 “Child received: Hello from parent!”라는 메시지를 출력합니다.
4. 파이프를 통한 양방향 통신
이 예시에서는 단방향 통신만 다루었지만, 양방향 통신을 구현하려면 두 개의 파이프를 사용해야 합니다. 즉, 부모와 자식 각자가 데이터를 주고받을 수 있도록 두 개의 파이프를 생성하고 각각에 데이터를 읽고 쓸 수 있게 해야 합니다.
5. 출력 예시
실행 후, 자식 프로세스에서 다음과 같은 출력이 나타납니다:
Child received: Hello from parent!
이 코드를 통해 부모와 자식 프로세스 간의 파이프 사용법을 이해할 수 있으며, 더 복잡한 프로세스 간 통신에도 응용할 수 있습니다.
파이프를 활용한 실시간 데이터 처리
유닉스 파이프는 실시간 데이터 스트리밍을 처리하는 데 매우 유용합니다. 두 프로세스 간의 데이터 전송을 실시간으로 처리할 수 있기 때문에, 파이프는 로그 처리, 실시간 데이터 분석, 스트리밍 서비스 등 다양한 응용 프로그램에서 사용됩니다. 이 섹션에서는 C언어에서 파이프를 사용하여 실시간 데이터를 처리하는 방법을 다룹니다.
1. 실시간 데이터 흐름 처리
파이프는 데이터를 실시간으로 전송할 수 있기 때문에, 하나의 프로세스가 데이터를 생성하거나 읽는 동안 다른 프로세스가 이를 동시에 처리할 수 있습니다. 예를 들어, 데이터가 지속적으로 발생하는 환경에서는 파이프를 사용하여 데이터를 실시간으로 전송하고 처리할 수 있습니다.
다음은 부모 프로세스가 생성한 실시간 데이터를 자식 프로세스가 읽어 처리하는 예시입니다. 이 예시에서는 부모 프로세스가 반복적으로 데이터를 파이프에 쓰고, 자식 프로세스는 이를 실시간으로 읽어 처리합니다.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int pipefd[2];
pid_t pid;
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid = fork(); // 자식 프로세스 생성
if (pid == 0) { // 자식 프로세스
close(pipefd[1]); // 쓰기 디스크립터 닫기
char buffer[100];
while (1) {
// 파이프에서 데이터를 실시간으로 읽기
int bytesRead = read(pipefd[0], buffer, sizeof(buffer));
if (bytesRead > 0) {
buffer[bytesRead] = '\0'; // 문자열 끝을 설정
printf("Child received: %s\n", buffer); // 자식 프로세스에서 데이터 처리
}
usleep(100000); // 0.1초 대기
}
close(pipefd[0]);
} else { // 부모 프로세스
close(pipefd[0]); // 읽기 디스크립터 닫기
for (int i = 0; i < 5; ++i) {
// 실시간으로 데이터를 파이프에 쓰기
char message[100];
sprintf(message, "Data %d", i + 1);
write(pipefd[1], message, strlen(message));
printf("Parent sent: %s\n", message); // 부모 프로세스에서 데이터 전송
usleep(500000); // 0.5초 대기
}
close(pipefd[1]);
}
return 0;
}
2. 코드 설명
pipe(pipefd)
로 파이프를 생성하고,pipefd[0]
은 읽기,pipefd[1]
은 쓰기용으로 사용됩니다.- 부모 프로세스는 반복적으로
write()
함수를 사용하여 데이터를 파이프에 씁니다. 데이터를 쓴 후, 0.5초 간격으로 반복합니다. - 자식 프로세스는
read()
함수로 데이터를 실시간으로 읽습니다. 읽은 데이터를 출력하고, 처리 후 0.1초 간격으로 다시 데이터를 읽습니다. - 부모 프로세스와 자식 프로세스는 각각 독립적으로 데이터를 처리하며, 파이프를 통해 실시간 데이터 흐름을 이어갑니다.
3. 실시간 데이터 처리 예시
실행하면, 부모 프로세스는 데이터를 전송하고, 자식 프로세스는 이를 실시간으로 읽어 출력합니다. 출력 예시는 다음과 같습니다:
Parent sent: Data 1
Parent sent: Data 2
Parent sent: Data 3
Parent sent: Data 4
Parent sent: Data 5
Child received: Data 1
Child received: Data 2
Child received: Data 3
Child received: Data 4
Child received: Data 5
이 예시에서 부모 프로세스는 0.5초마다 데이터를 전송하고, 자식 프로세스는 이를 실시간으로 처리합니다. usleep()
함수는 프로세스 간 데이터 흐름을 제어하여, 데이터를 실시간으로 처리할 수 있도록 돕습니다.
4. 실시간 데이터 흐름의 활용
파이프를 활용한 실시간 데이터 처리 방식은 다음과 같은 다양한 분야에서 유용하게 사용될 수 있습니다:
- 로그 처리 시스템: 시스템에서 발생하는 로그 데이터를 실시간으로 처리하고, 필요한 정보를 즉시 추출하여 보고서를 생성합니다.
- 스트리밍 서비스: 비디오 또는 오디오 스트리밍 서비스에서 실시간 데이터를 송수신합니다.
- 실시간 모니터링 시스템: 센서 데이터를 실시간으로 처리하거나, 다양한 이벤트를 모니터링합니다.
이처럼 파이프를 활용하면 실시간 데이터 처리에서 매우 유용하게 활용될 수 있습니다.
파이프를 활용한 프로세스 간 동기화
유닉스 파이프는 두 프로세스 간 데이터를 전달하는 데 중요한 역할을 하지만, 프로세스 간 동기화 문제를 해결하는 데에도 유용하게 사용될 수 있습니다. 파이프를 사용하면 한 프로세스가 데이터를 쓴 후, 다른 프로세스가 데이터를 읽을 때까지 기다리는 방식으로 동기화할 수 있습니다. 이는 특히 순차적인 데이터 처리나 특정 조건을 만족할 때까지 기다려야 하는 상황에서 유용합니다.
1. 프로세스 간 동기화 필요성
여러 프로세스가 데이터를 동시에 처리할 때, 특정 작업을 완료한 후에 다음 작업을 진행해야 하는 경우가 많습니다. 예를 들어, 부모 프로세스가 데이터를 준비한 후, 자식 프로세스가 그 데이터를 처리해야 할 때 동기화가 필요합니다. 파이프를 활용하면, 부모 프로세스가 데이터를 파이프에 쓴 후 자식 프로세스가 이를 읽기 전에 기다리도록 설정할 수 있습니다.
다음 코드는 파이프를 사용하여 부모 프로세스가 데이터를 준비한 후 자식 프로세스가 데이터를 읽고 처리하도록 동기화하는 방법을 설명합니다.
2. 파이프를 이용한 동기화 코드 예시
이 코드는 부모 프로세스가 데이터를 준비한 후 자식 프로세스가 이를 처리하는 방식으로 동기화를 구현합니다. 자식 프로세스는 부모 프로세스가 데이터를 파이프에 쓸 때까지 기다립니다.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int pipefd[2];
pid_t pid;
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid = fork(); // 자식 프로세스 생성
if (pid == 0) { // 자식 프로세스
close(pipefd[1]); // 쓰기 디스크립터 닫기
char buffer[100];
// 파이프에서 데이터를 읽을 때까지 대기
printf("Child waiting for data...\n");
read(pipefd[0], buffer, sizeof(buffer)); // 데이터 읽기
printf("Child received: %s\n", buffer); // 자식 프로세스에서 데이터 출력
close(pipefd[0]);
} else { // 부모 프로세스
close(pipefd[0]); // 읽기 디스크립터 닫기
sleep(2); // 데이터를 준비하는 시간 (예: 파일 읽기, 연산 등)
// 부모가 데이터를 파이프에 쓰기 전에 잠시 대기
char message[] = "Data from parent";
write(pipefd[1], message, strlen(message));
printf("Parent sent: %s\n", message);
close(pipefd[1]);
}
return 0;
}
3. 코드 설명
pipe(pipefd)
로 파이프를 생성하고,pipefd[0]
은 읽기,pipefd[1]
은 쓰기 디스크립터로 사용됩니다.fork()
로 부모와 자식 프로세스를 생성합니다. 부모는 2초 동안 기다린 후 데이터를 파이프에 씁니다.- 자식 프로세스는 데이터를 받기 전까지
read()
에서 대기 상태에 있습니다. 이로써 자식 프로세스는 부모가 데이터를 준비할 때까지 기다리게 됩니다. - 자식 프로세스가 데이터를 읽고 출력한 후, 부모 프로세스가 데이터를 썼다는 메시지가 출력됩니다.
4. 출력 예시
실행하면 다음과 같은 출력이 나타납니다:
Child waiting for data...
Parent sent: Data from parent
Child received: Data from parent
이 출력은 자식 프로세스가 부모가 데이터를 준비할 때까지 대기하고, 데이터를 받은 후 처리하는 과정을 보여줍니다.
5. 동기화 활용 예시
이와 같은 방식으로 파이프를 활용하면 프로세스 간 동기화를 처리할 수 있습니다. 예를 들어, 다음과 같은 상황에서 유용하게 사용할 수 있습니다:
- 데이터 처리 파이프라인: 데이터를 준비하는 프로세스가 완료되면, 후속 프로세스가 데이터를 처리하는 방식으로 순차적인 작업 흐름을 만듭니다.
- 작업 완료 신호: 한 프로세스가 작업을 완료하면, 다른 프로세스에게 이를 알려주는 신호를 전달할 수 있습니다.
- 리소스 공유: 여러 프로세스가 같은 자원에 접근할 때, 파이프를 사용해 자원을 공유하고 동기화할 수 있습니다.
이처럼 파이프는 프로세스 간 데이터 전송뿐만 아니라 동기화 문제를 해결하는 데에도 강력한 도구로 활용될 수 있습니다.
파이프와 표준 입력/출력 연결
C언어에서 파이프는 표준 입력(stdin)과 표준 출력(stdout)을 연결하는 데도 유용하게 사용됩니다. 이를 통해 파이프를 표준 I/O 스트림과 연결하여, 프로세스 간 데이터 전송뿐만 아니라, 외부 명령어 실행 결과를 파이프를 통해 처리할 수 있습니다. 이러한 방식은 프로세스 간의 간접적인 데이터 전달이나 필터링 작업을 쉽게 구현할 수 있도록 해줍니다.
1. 파이프와 표준 입력/출력 연결
파이프와 표준 I/O를 연결하면, 다른 프로세스의 출력을 자신의 입력으로 사용하거나, 입력을 다른 프로세스의 출력으로 전달할 수 있습니다. 이 방법은 쉘에서 |
(파이프 기호)로 여러 명령어를 연결하는 방식과 유사합니다.
다음 예시는 부모 프로세스가 외부 명령어를 실행하고, 그 결과를 파이프를 통해 자식 프로세스로 전달하는 구조입니다. 자식 프로세스는 이를 읽어 처리합니다.
2. 외부 명령어 실행 결과를 파이프를 통해 전달
이 예시는 부모 프로세스가 ls
명령을 실행하여 디렉토리 내용을 출력하고, 자식 프로세스는 이를 파이프를 통해 읽어 출력합니다.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int pipefd[2];
pid_t pid;
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid = fork(); // 자식 프로세스 생성
if (pid == 0) { // 자식 프로세스
close(pipefd[1]); // 쓰기 디스크립터 닫기
char buffer[100];
// 파이프에서 데이터를 읽고 출력
while (read(pipefd[0], buffer, sizeof(buffer)) > 0) {
printf("Child received: %s", buffer); // 읽은 데이터를 출력
}
close(pipefd[0]);
} else { // 부모 프로세스
close(pipefd[0]); // 읽기 디스크립터 닫기
// 자식 프로세스로 전달할 명령어의 결과를 파이프에 쓰기
dup2(pipefd[1], STDOUT_FILENO); // 표준 출력을 파이프에 연결
close(pipefd[1]); // 파이프 디스크립터 닫기 (dup2로 연결되었으므로)
// 외부 명령어 실행 (ls)
execlp("ls", "ls", "-l", (char *)NULL);
perror("execlp"); // 명령어 실행 실패 시
}
return 0;
}
3. 코드 설명
pipe(pipefd)
로 파이프를 생성하고,pipefd[0]
은 읽기용,pipefd[1]
은 쓰기용 디스크립터로 사용됩니다.fork()
로 부모 프로세스와 자식 프로세스를 생성합니다. 부모 프로세스는 외부 명령어인ls -l
을 실행하여 디렉토리 목록을 출력하고, 자식 프로세스는 이 출력을 파이프에서 읽습니다.dup2(pipefd[1], STDOUT_FILENO)
함수는 부모 프로세스의 표준 출력을 파이프의 쓰기 디스크립터로 리디렉션합니다. 이를 통해 부모 프로세스의 출력은ls
명령의 실행 결과를 파이프에 직접 전송합니다.- 자식 프로세스는 파이프에서 데이터를 읽고 이를 출력합니다.
4. 출력 예시
실행하면 자식 프로세스가 부모 프로세스에서 전달한 데이터를 읽고 출력하는 구조입니다. 예를 들어, 디렉토리에 파일이 있을 경우, ls -l
명령의 출력이 자식 프로세스에 의해 출력됩니다:
Child received: total 8
Child received: -rw-r--r-- 1 user user 1234 Jan 1 10:00 file1.txt
Child received: -rw-r--r-- 1 user user 5678 Jan 2 10:00 file2.txt
5. 표준 입력과 출력 연결의 활용
파이프를 표준 I/O와 연결하여 외부 명령어를 실행하는 방법은 다양한 실용적인 응용에 사용될 수 있습니다:
- 명령어 파이프라인: 쉘에서
ls | grep 'txt'
처럼 여러 명령어를 파이프라인으로 연결하여 복잡한 처리를 할 수 있습니다. C언어에서는 이를 통해 복잡한 데이터 필터링 작업을 자동화할 수 있습니다. - 로그 처리 시스템: 시스템 로그를 파이프를 통해 다른 프로세스로 전달하여 실시간으로 처리하거나 분석할 수 있습니다.
- 데이터 처리 파이프라인: 데이터가 외부 명령어를 통해 가공되어 다른 프로세스로 전달될 때 유용합니다.
이 방식은 쉘처럼 명령어를 이어서 실행하는 간단한 방법을 C언어로 구현할 수 있도록 하여, 다양한 응용 프로그램에서 유용하게 활용될 수 있습니다.
파이프와 부모-자식 프로세스 간 데이터 전송
부모 프로세스와 자식 프로세스 간에 데이터를 전송할 때, 파이프는 매우 유용한 도구입니다. 파이프를 사용하면 부모 프로세스에서 자식 프로세스로, 또는 자식 프로세스에서 부모 프로세스로 데이터를 안전하고 효율적으로 전달할 수 있습니다. 이 방법은 데이터를 실시간으로 전송하거나, 한 프로세스에서 발생한 결과를 다른 프로세스에서 처리할 때 유용합니다.
1. 부모-자식 프로세스 간 데이터 전송 구조
부모와 자식 프로세스 간에 데이터를 전송할 때, 보통 두 가지 방식이 있습니다:
- 부모 -> 자식: 부모 프로세스가 데이터를 생성하고, 이를 파이프에 써서 자식 프로세스가 이를 읽는 방식입니다.
- 자식 -> 부모: 자식 프로세스가 데이터를 생성하고, 이를 파이프에 써서 부모 프로세스가 이를 읽는 방식입니다.
두 방식 모두, 파이프를 사용하여 데이터를 안전하고 효율적으로 전달할 수 있습니다.
2. 부모에서 자식으로 데이터 전송
다음은 부모 프로세스가 데이터를 파이프에 써서 자식 프로세스가 이를 읽는 간단한 예시입니다.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int pipefd[2];
pid_t pid;
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid = fork(); // 자식 프로세스 생성
if (pid == 0) { // 자식 프로세스
close(pipefd[1]); // 쓰기 디스크립터 닫기
char buffer[100];
// 부모로부터 데이터를 읽기
read(pipefd[0], buffer, sizeof(buffer));
printf("Child received: %s\n", buffer); // 자식에서 받은 데이터 출력
close(pipefd[0]);
} else { // 부모 프로세스
close(pipefd[0]); // 읽기 디스크립터 닫기
// 자식에게 전달할 데이터 준비
char message[] = "Data from parent";
write(pipefd[1], message, strlen(message));
printf("Parent sent: %s\n", message); // 부모에서 전송한 데이터 출력
close(pipefd[1]);
}
return 0;
}
3. 코드 설명
pipe(pipefd)
로 파이프를 생성하고,pipefd[0]
은 읽기용,pipefd[1]
은 쓰기용 디스크립터로 사용됩니다.- 부모 프로세스는
write()
를 사용하여 파이프에 데이터를 씁니다. 이 데이터는 자식 프로세스가 읽을 수 있습니다. - 자식 프로세스는
read()
를 사용하여 파이프에서 데이터를 읽고, 이를 출력합니다.
4. 출력 예시
실행 시, 부모 프로세스가 데이터를 전송하고 자식 프로세스가 이를 읽는 출력 결과는 다음과 같습니다:
Parent sent: Data from parent
Child received: Data from parent
이 출력은 부모 프로세스가 데이터를 전송하고, 자식 프로세스가 이를 성공적으로 받아서 출력하는 과정입니다.
5. 자식에서 부모로 데이터 전송
이번에는 자식 프로세스가 데이터를 파이프에 써서 부모 프로세스가 이를 읽는 방식입니다. 부모가 자식에게 데이터를 보내는 방식과 거의 유사한데, 단지 데이터의 흐름이 반대 방향입니다.
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int pipefd[2];
pid_t pid;
if (pipe(pipefd) == -1) {
perror("pipe");
return 1;
}
pid = fork(); // 자식 프로세스 생성
if (pid == 0) { // 자식 프로세스
close(pipefd[0]); // 읽기 디스크립터 닫기
// 부모에게 보낼 데이터 준비
char message[] = "Data from child";
write(pipefd[1], message, strlen(message));
printf("Child sent: %s\n", message); // 자식에서 전송한 데이터 출력
close(pipefd[1]);
} else { // 부모 프로세스
close(pipefd[1]); // 쓰기 디스크립터 닫기
char buffer[100];
// 자식으로부터 데이터를 읽기
read(pipefd[0], buffer, sizeof(buffer));
printf("Parent received: %s\n", buffer); // 부모에서 받은 데이터 출력
close(pipefd[0]);
}
return 0;
}
6. 출력 예시
실행 시, 자식 프로세스가 데이터를 전송하고 부모 프로세스가 이를 읽는 출력 결과는 다음과 같습니다:
Child sent: Data from child
Parent received: Data from child
이 출력은 자식 프로세스가 데이터를 전송하고, 부모 프로세스가 이를 받아서 출력하는 과정입니다.
7. 부모-자식 간 데이터 전송의 활용
부모와 자식 프로세스 간 데이터 전송은 여러 실용적인 상황에서 유용하게 활용될 수 있습니다:
- 데이터 처리 및 분석: 부모 프로세스가 데이터를 준비하고, 자식 프로세스가 이를 분석하거나 가공하는 방식으로 작업을 분리할 수 있습니다.
- 병렬 처리: 작업을 여러 프로세스에 나누어 처리하고, 각 프로세스의 결과를 부모 프로세스에서 모아서 최종 결과를 생성할 수 있습니다.
- 실시간 시스템: 부모와 자식 프로세스 간의 실시간 데이터 전달을 통해, 예를 들어 센서 데이터 처리, 실시간 로그 처리 시스템 등에서 활용될 수 있습니다.
이와 같이 파이프를 활용한 부모-자식 프로세스 간 데이터 전송은 여러 종류의 시스템에서 중요한 역할을 하며, 다양한 데이터 흐름을 효율적으로 관리할 수 있게 해줍니다.
요약
본 기사에서는 C언어에서 입출력 스트림을 활용한 유닉스 파이프 구현에 대해 다뤘습니다. 파이프의 기본 개념, 부모-자식 프로세스 간 데이터 전송, 표준 입력/출력과 파이프 연결, 그리고 프로세스 간 동기화 방법까지 다양한 응용 예시를 통해 설명했습니다.
파이프는 두 프로세스 간 데이터를 효율적으로 전달하는 강력한 도구이며, 이를 통해 프로세스 간 동기화, 데이터 처리 파이프라인, 외부 명령어 실행 결과 전달 등의 다양한 작업을 구현할 수 있습니다. 표준 입출력과 파이프의 연결은 실용적인 시스템을 구축하는 데 필수적인 기법으로, 데이터를 실시간으로 전달하거나 처리할 수 있게 해줍니다.
적절한 파이프 활용은 시스템의 효율성을 높이고, 복잡한 데이터 흐름을 간단하게 처리할 수 있도록 도와줍니다.