Computer >> 컴퓨터 >  >> 프로그래밍 >> C 프로그래밍

Windows 익명 파이프(Anonymous Pipe) 개념과 활용 방법

Windows의 익명 파이프(Anonymous Pipe)는 일반 파이프(ordinary pipe)에 해당하며, UNIX 계열 시스템의 파이프와 매우 유사하게 동작합니다. 익명 파이프는 다음과 같은 특징을 가집니다.

  • 데이터가 한 방향으로만 흐르는 단방향(half-duplex) 통신 방식입니다.
  • 통신하는 프로세스 간에 부모-자식 관계가 성립해야 합니다.
  • 파이프의 입출력은 일반적인 ReadFile()WriteFile() 함수를 통해 수행할 수 있습니다.

CreatePipe() 함수와 매개변수

Windows API에서는 CreatePipe() 함수를 사용하여 파이프를 생성하며, 이 함수에는 네 개의 매개변수가 전달됩니다.

  • 파이프에서 읽기를 위한 핸들
  • 파이프에 쓰기를 위한 핸들
  • 자식 프로세스가 파이프의 핸들을 상속하도록 지정하는 데 사용되는 STARTUPINFO 구조체의 인스턴스
  • 파이프의 버퍼 크기(바이트 단위) — 필요한 경우 지정 가능

핸들 상속과 보안 속성 설정

UNIX 시스템과 달리, Windows에서는 프로그래머가 자식 프로세스가 어떤 속성을 상속할지 명시적으로 지정해야 합니다. 이 과정은 다음과 같이 진행됩니다.

  1. 먼저 SECURITY_ATTRIBUTES 구조체를 초기화하여 핸들이 상속될 수 있도록 허용합니다.
  2. 그다음 자식 프로세스의 표준 입력 또는 표준 출력 핸들을 파이프의 읽기/쓰기 핸들로 리디렉션(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(&pi;, 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 구조체를 적절히 설정하여 핸들 상속을 제어하는 것이 핵심입니다. 양방향 통신이 필요한 경우에는 두 개의 파이프를 생성하여 각각의 방향으로 데이터를 전송하도록 구현해야 합니다.