파이프

EEEFFEE·2023년 8월 6일

System Programming

목록 보기
13/19
post-thumbnail

23.08.06 최초 작성
23.11.15 예제 추가

  • 두 프로세스 사이에서 한 방향으로 통신할 수 있도록 지원하는 방식

1. 이름없는 파이프 (Pipe)

  • 부모-자식 프로세스 간 통신할 수 있도록 하는 파이프
  • popen : 이미 있는 파이프를 사용 (성능 및 보안문제 있음)

#include <stdio.h>

FILE *popen(const char * command, const char *mode);

///
command : 쉘 명령
mode : 'r' 또는 'w'

Return : 파일 포인터
(NULL : Fail) 

  • pclose : 이미 있는 파이프를 닫음

#include <stdio.h>

int pclose(FILE *stream);// 관련된 waitpid 함수를 수행해 자식 프로세스들이 종료되기 기다렸다가 리턴한다.

///
stream : 닫을 파이프의 주소

Return : 자식 프로세스의 exit status
(-1 : FaiL)

  • pipe : 파이프를 생성하는 함수

#include <unistd.h>

int pipe(int fildes[2]);

///
fildes : 파이프로 사용할 file descriptor 2(fildes[0] : 읽기 전용, fildes[1] : 쓰기 전용)

Return 
(0 : Success, -1 : Fail)

ex) 예제

  • simple_pipe.c : 부모 프로세스가 데이터를 파이프로 전달하고 자식 프로세스는 파이프에 있는 데이터을 읽어 모니터에 출력하는 코드
#include <sys/wait.h>
#include "tlpi_hdr.h"

#define BUF_SIZE 10

int
main(int argc, char *argv[])
{
    int pfd[2];                             /* Pipe file descriptors */
    char buf[BUF_SIZE];
    ssize_t numRead;

    if (argc != 2 || strcmp(argv[1], "--help") == 0)
        usageErr("%s string\n", argv[0]);

    if (pipe(pfd) == -1)                    /* 파이프 생성 */
        errExit("pipe");

    switch (fork()) {
    case -1:
        errExit("fork");

    case 0:	        						/* 자식 프로세스인 경우 */
        if (close(pfd[1]) == -1)			/* Write end is unused */
            errExit("close - child");

        for (;;) {              			/* 파이프(pfd[0])에서 데이터를 읽음 */
            numRead = read(pfd[0], buf, BUF_SIZE);
            if (numRead == -1)
                errExit("read");
            if (numRead == 0)
                break;                      /* EOF를 읽을 경우 종료 */
            if (write(STDOUT_FILENO, buf, numRead) != numRead)					
                fatal("child - partial/failed write");
        }

        write(STDOUT_FILENO, "\n", 1);		/*stdout(모니터)로 출력 */
        if (close(pfd[0]) == -1)
            errExit("close");
        exit(EXIT_SUCCESS);

    default:            					/* 부모 프로세스인 경우 */
        if (close(pfd[0]) == -1)            
            errExit("close - parent");
            
		/*파이프(pfd[1])에 데이터를 씀*/
        if (write(pfd[1], argv[1], strlen(argv[1])) != strlen(argv[1]))
            fatal("parent - partial/failed write");

        if (close(pfd[1]) == -1)            /* 자식 프로세스가 EOF 읽은 경우  */
            errExit("close");
        wait(NULL);                         
        exit(EXIT_SUCCESS);
    }
}

  • popen_glob : 패턴(키워드)을 입력해 이와 일치하는 이름을 가진 파일 찾음

#include <ctype.h>
#include <limits.h>
#include "print_wait_status.h"          /* For printWaitStatus() */
#include "tlpi_hdr.h"

#define POPEN_FMT "/bin/ls -d %s 2> /dev/null"
#define PAT_SIZE 50
#define PCMD_BUF_SIZE (sizeof(POPEN_FMT) + PAT_SIZE)

int
main(int argc, char *argv[])
{
    char pat[PAT_SIZE];                 /* 패턴을 저장할 공간 */
    char popenCmd[PCMD_BUF_SIZE];
    FILE *fp;                           /* popen()함수의 return 값을 저장할 공간 */
    Boolean badPattern;                 /* 입력받은 패턴을 검사한 결과를 저장 */
    int len, status, fileCnt, j;
    char pathname[PATH_MAX];

    for (;;) {                  /* Read pattern, display results of globbing */
        printf("pattern: ");
        fflush(stdout);
        if (fgets(pat, PAT_SIZE, stdin) == NULL)
            break;                      /* EOF */
        len = strlen(pat);
        if (len <= 1)                   /* 입력 없음 */
            continue;

        if (pat[len - 1] == '\n')       /* Enter 입력 시 문장의 끝으로 치환 */
            pat[len - 1] = '\0';

										/* 문자열 검사 */
        for (j = 0, badPattern = FALSE; j < len && !badPattern; j++)		
            if (!isalnum((unsigned char) pat[j]) &&
                    strchr("_*?[^-].", pat[j]) == NULL)
                badPattern = TRUE;

        if (badPattern) {
            printf("Bad pattern character: %c\n", pat[j - 1]);
            continue;
        }

        snprintf(popenCmd, PCMD_BUF_SIZE, POPEN_FMT, pat);

        fp = popen(popenCmd, "r");
        if (fp == NULL) {
            printf("popen() failed\n");
            continue;
        }

        fileCnt = 0;
        while (fgets(pathname, PATH_MAX, fp) != NULL) {
            printf("%s", pathname);
            fileCnt++;
        }
										/* 파이프 종료 */
        status = pclose(fp);
        printf("    %d matching file%s\n", fileCnt, (fileCnt != 1) ? "s" : "");
        printf("    pclose() status = %#x\n", (unsigned int) status);
        if (status != -1)
            printWaitStatus("\t", status);
    }

    exit(EXIT_SUCCESS);
}

2. 이름 있는 파이프 (FIFO)

이름이 있는 파이프(특수파일의 한 종류). 독립적인 프로세스 사이의 통신을 위해 사용하며 미리 만들워 두어야 사용 가능하다(kernel 내부적으로 데이터 전달).

  • mkfifo : FIFO 관련된 파일을 생성함

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

///
pathname : FIFO파일을 생성 할 경로
mode : 접근 권한 지정

mkfifo pipeFile

Terminal 1
ls | tee pipeFile | sort -k5n			//tee : 파이프에도 쓰고 이후 작업에도 씀

Terminal 2
wc -m < pipeFile
  • Terminal 1 : write 모드로 파이프를 연다.
  • Terminal 2 : read 모드로 파이프를 연다.

0개의 댓글