Windows의 익명 파이프(Anonymous Pipe)는 일반 파이프(ordinary pipe)에 해당하며, UNIX 계열 시스템의 파이프와 매우 유사하게 동작합니다. 익명 파이프는 다음과 같은 특징을 가집니다.
- 데이터가 한 방향으로만 흐르는 단방향(half-duplex) 통신 방식입니다.
- 통신하는 프로세스 간에 부모-자식 관계가 성립해야 합니다.
- 파이프의 입출력은 일반적인
ReadFile()및WriteFile()함수를 통해 수행할 수 있습니다.
CreatePipe() 함수와 매개변수
Windows API에서는 CreatePipe() 함수를 사용하여 파이프를 생성하며, 이 함수에는 네 개의 매개변수가 전달됩니다.
- 파이프에서 읽기를 위한 핸들
- 파이프에 쓰기를 위한 핸들
- 자식 프로세스가 파이프의 핸들을 상속하도록 지정하는 데 사용되는
STARTUPINFO구조체의 인스턴스 - 파이프의 버퍼 크기(바이트 단위) — 필요한 경우 지정 가능
핸들 상속과 보안 속성 설정
UNIX 시스템과 달리, Windows에서는 프로그래머가 자식 프로세스가 어떤 속성을 상속할지 명시적으로 지정해야 합니다. 이 과정은 다음과 같이 진행됩니다.
- 먼저
SECURITY_ATTRIBUTES구조체를 초기화하여 핸들이 상속될 수 있도록 허용합니다. - 그다음 자식 프로세스의 표준 입력 또는 표준 출력 핸들을 파이프의 읽기/쓰기 핸들로 리디렉션(redirection)합니다.
자식 프로세스가 파이프에서 데이터를 읽어야 하는 경우, 부모 프로세스는 자식의 표준 입력을 파이프의 읽기 핸들로 리디렉션해야 합니다. 또한 파이프는 반이중(half-duplex) 방식이므로, 자식 프로세스가 파이프의 쓰기 끝(write-end)을 상속하지 못하도록 차단해야 합니다.
예제 코드
아래 코드는 부모 프로세스가 자식 프로세스와 통신하기 위해 익명 파이프를 생성하는 예제입니다.
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#define BUFFER_SIZE 25
int main(VOID) {
HANDLE ReadHandle, WriteHandle;
STARTUPINFO si;
PROCESS_INFORMATION pi;
char message[BUFFER_SIZE] = "Greetings";
DWORD written;
/* 파이프 핸들이 상속될 수 있도록 보안 속성 설정 */
SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
/* 메모리 초기화 */
ZeroMemory(π, sizeof(pi));
/* 파이프 생성 */
if (!CreatePipe(&ReadHandle, &WriteHandle, &sa, 0)) {
fprintf(stderr, "Create Pipe Failed"); return 1;
}
/* 자식 프로세스를 위한 STARTUPINFO 구조체 설정 */
GetStartupInfo(&si);
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
/* 표준 입력을 파이프의 읽기 끝으로 리디렉션 */
si.hStdInput = ReadHandle;
si.dwFlags = STARTF_USESTDHANDLES;
/* 자식이 파이프의 쓰기 끝을 상속하지 못하도록 설정 */
SetHandleInformation(WriteHandle, HANDLE_FLAG_INHERIT, 0);
/* 자식 프로세스 생성 */
CreateProcess(NULL, "child.exe", NULL, NULL, TRUE, /* 핸들 상속 */ 0, NULL, NULL, &si, &pi);
/* 사용하지 않는 파이프의 읽기 끝 닫기 */
CloseHandle(ReadHandle);
/* 부모 프로세스가 파이프에 데이터 쓰기 */
if(!WriteFile(WriteHandle, message, BUFFER_SIZE, &written, NULL))
fprintf(stderr, "Error writing to pipe.");
/* 파이프의 쓰기 끝 닫기 */
CloseHandle(WriteHandle);
/* 자식 프로세스 종료 대기 */
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}부모 프로세스 동작 설명
위 코드에서 부모 프로세스는 파이프에 데이터를 쓰기 전에 먼저 사용하지 않는 읽기 끝(read-end)을 닫습니다. 이는 불필요한 핸들을 정리하고 리소스 누수를 방지하기 위한 좋은 습관입니다.
자식 프로세스 코드
파이프에서 데이터를 읽는 자식 프로세스의 코드는 아래와 같습니다.
#include<stdio.h>
#include<windows.h>
#define BUFFER_SIZE 25
int main(VOID){
HANDLE Readhandle;
CHAR buffer[BUFFER_SIZE];
DWORD read;
/* 파이프의 읽기 핸들 가져오기 */
Readhandle = GetStdHandle(STD_INPUT_HANDLE);
/* 자식 프로세스가 파이프에서 데이터 읽기 */
if (ReadFile(Readhandle, buffer, BUFFER_SIZE, &read, NULL))
printf("child read %s", buffer);
else
fprintf(stderr, "Error reading from pipe");
return 0;
}정리
Windows 익명 파이프는 부모-자식 관계의 프로세스 간 단방향 통신을 구현하는 간단하고 효율적인 방법입니다. CreatePipe()로 파이프를 생성하고, SECURITY_ATTRIBUTES 구조체와 STARTUPINFO 구조체를 적절히 설정하여 핸들 상속을 제어하는 것이 핵심입니다. 양방향 통신이 필요한 경우에는 두 개의 파이프를 생성하여 각각의 방향으로 데이터를 전송하도록 구현해야 합니다.