관리 메뉴

너와 나의 스토리

[Unix] CH.7 Pipe, FIFO, I/O Multiplexing 본문

Unix/이론

[Unix] CH.7 Pipe, FIFO, I/O Multiplexing

노는게제일좋아! 2019. 11. 18. 22:29
반응형

Pipes

  • 가장 간단한 유닉스 interprocess communication(IPC) 메커니즘이다.
    • 프로세스들끼리 파일 공유하는 방식(단방향으로)
  • special file로 나타낸다
  • 예) $ who | wc -l

 

$ who | wc -l

-> who 명령어로 인해 나오는 출력 값을 wc -l의 입력으로 넣음

 

즉, [who]에서 출력 값을 pipe에 write하고, [wc -l]는 pipe에 있는 데이터를 read 함

 

 

 

* [who], [wc -l], [wc -L] 단독 실행한 결과

 

 

pipe() system call

  • First-in-first-out 방식
  • 이러한 순서는 'lseek'로 대체 불가능하다 -> pipe에서 작동 안 함
  • 두 가지 제약
    • 단방향(half duplex) 통신
    • pipe는 부모와 자식 사이에서 사용된다.
  • 인자
    • filedes[0]: read
    • filedes[1]: write
  • 프로세스가 파이프에 read 호출한 경우
    • 파이프가 비어있지 않다면 - 즉시 read 리턴
    • 파이프가 비어있다면 - 파이프에 무언가 적힐 때까지 read는 block 된다. (기다림)
  • 프로세스가 파이프에 write 호출한 경우
    • 파이프가 꽉 차있지 않다면 - 즉시 write 리턴
    • 파이프가 꽉 차 있다면 - 파이프에 여유 공간이 생길 때까지 write는 block 됨 
  • 파이프의 한 쪽이 닫히는 경우
    • write쪽이 닫히는 경우
      • read는 모든 데이터를 읽은 후 EOF를 나타내기 위해 0을 리턴한다.
      • read가 죽으면 write도 따라 죽음
    • read쪽이 닫히는 경우
      • SIGPIPE를 발생시킨다. 
      • 만약 시그널을 무시하거나 catch한 후 시그널 핸들러로부터 리턴하면, write()는 errno를 EPIPE로 설정하고 -1을 반환한다.
      • write이 죽는닥고 read가 죽지는 않음 (read는 서버같은 느낌)

 

example1

  • parent, child 둘 다 pipe에 대한 read와 write이 다 열려있다.
  • 만약 부모, 자식 프로세스가 동시에 write과 read를 진행한다면 충돌이 발생할 수 있다.
  • 이를 피하기 위해서, 필요 없는 파일 디스크립터를 close 한다.

 

  • 다음 예제의 경우, 자식이 종료되어도 부모의 write[4]이 남아 있기 때문에, read하기 위해서 파이프가 채워지기를 기다리며 영원히 block된다. (자식은 이미 죽어서 write을 못해주기 때문)

 

 

example2

 

  • 자식의 read[3]와 부모의 write[4]을 닫음

 

  • 자식이 죽으면 더 이상 write 해줄 프로세스가 없으므로 부모의 read가 블락되지 않는다.

 

 

 

 

 

 

 

Blocking & Non-blocking

  • 시스템 콜은 호출자를 영원히 block할 수 있다.
    • read는 특정 파일 타입(pipes, terminal devices, network devices)에 데이터가 존재하지 않으면 block된다.
      • read할 게 없으면 blocking 됨 <- default 옵션
    • write는 이러한 파일 타입에 의해 데이터에 즉시 접근할 수 없는 경우 block된다. (pipe에 남는 공간이 없음, network flow control, 등)
      • write할 자리 없으면 blocking 됨
    • 특정 파일 타입(FIFO)에 특정 조건이 발생할 때까지 block 됨
  • 지정된 디스크립터에 대해 nonblocking I/O를 지정하는 두 가지 방법
    • 만약 해당 디스크립터를 얻기 위해 open을 호출했다면 -> O_NONBLOCK 지정
    • 이미 open된 디스크립터라면 -> O_NONBLOCK 파일 상태 플래그를 ON하기 위해 fcntl(옵션 변경 명령어)을 호출
int pfd[2];

if(pipe (pfd) == -1)
  fatal ("pipe call");

if(fcntl(pfd[0],F_SETFL,O_NONBLOCK)==-1)
  fatal ("fcntl call");

 

 

FIFO

  • FIFP는 때때로 named pipes로 불린다.
    • pipe는 이름을 지정할 수 있지만 fifo는 이름을 지정함
  • 파이프는 공통의 조상이 파이프를 만들었을 때만 관련 프로세스 간에 사용할 수 있다.
  • 그러나 FIFO를 사용하면 관련 없는 프로세스가 데이터를 교환할 수 있다.
  • FIFO에는 소유자, 크기 및 접근 관련 권한도 있다.
  • 다른 유닉스 파일과 마찬가지로 열거나 닫거나 삭제할 수 있다.
  • 읽기 전용 또는 쓰기 전용으로 열어야 한다.
    • FIFO는 반 이중이기 때문에 읽기-쓰기용으로 열려서는 안 된다.
  • S_ISFIFO 매크로 테스트 할 수 있다.

p: FIFO file

 

channel이라는 이름의 FIFO를 만듦

P: mknod가 FIFO를 생성하도록 함

 

 

 

  • FIFO는 first-in-first-out을 의미한다.
  • pipe나 FIFO에 write할 때는 이미 존재하는 데이터 뒤에 쓰여지지만, read하는 경우에는 pipe나 FIFO의 처음부분이 리턴된다.
  • PIPE나 FIFO에 lseek를 호출하면 ESPIPE 에러가 리턴된다.

 

 

mkfifo() system call

  • 주어진 파일(pathname)을 FIFO 파일로 만들어 줌
  • 인자:
    • pathname: FIFO 파일 이름
    • mode: permission mask

 

  • Nonblocking flag(O_NONBLOCK) of open()
    • O_NONBLOCK이 지정되지 않은 일반적인 경우
      • read_only로 open: 다른 프로세스가 write을 위한 FIFO를 open할 때까지 block 됨
      • write_only로 open: 다른 프로세그가 read를 위한 FIFO를 open할 때까지 block 됨
    • O_NONBLOCK이 지정된 경우
      • read_only로 open: 파일 디스크립터를 즉시 리턴
      • write_only로 open: read를 위한 FIFO를 open한 프로세스가 없다면 errno를 ENXIO 설정하고 -1 리턴

 

example 

  • 오른쪽 중간 부분에서 open할 때, read만 할거면서 O_RDWR 지정한 이유?
    • 이유1: 사소한 이유
      • read_only로 open하는 경우 block되기 때문
      • O_RDWR로 open하면 자기 자신의 write이 FIFO에 연결되어 있어 block 안 됨
    • 이유2: 치명적인 이유
      • read_only이면 for(;;)문에서 아무 일도 안하고 계속 루프를 돌게 된다
      • RDWR을 지정하면 for(;;)문에서 read할 때, blocking되도록 해서 쓸데 없이 루프 돌지 않도록 한다.

 

 

 

 

I/O Multiplexing

  • 우리가 하나의 디스크립터로부터 read하고, 다른 것으로부터 write할 때, 우리는 루프 안에서 blocking I/O를 사용할 수 있다.
while ((n = read(STDIN_FILENO, buf, BUFSIZ)) > 0) 
		if (write(STDOUT_FILENO, buf, n) != n) 
			err_sys("write error"); 
  • 만약 우리가 두 개의 디스크립터로부터 read 해야 하는 경우

  • 이런 상황을 처리할 다른 기술이 필요하다
    • Non blocking I/O Model (polling)
      • 루프 계속 돌기 때문에 -> CPU 시간 낭비
      • multitasking system에서 피해야한다
    • Multiplexing I/O Model
      • select() or poll()
    • Signal-Driven I/O Model (event)
      • SIGIO
    • Asynchronous I/O Model (event)
      • SIGIO

 

 

select() system call

  • select하기 위해 전달된 이 인자는 kernel에게 말한다.
    • nfds: 우리가 관심있는 디스크립터가 무엇인지. 최대 fd값
      • 만약 두 파일 디스크립터 3,4가 열렸다면, nfds는 반드시 5로 설정되어야한다.
    • readfds: 주어진 디스크립터로부터 read를 원하는지
      • 시스템에 의해 즉시 입력이 가능한지 확인(읽기가 block되지 않았는지 검사하기 위한 리스트, EOF 발생 검사)
    • writefds: 주어진 디스크립터로부터 write을 원하는지
      • 시스템에 의해 즉시 출력이 가능한지 확인(쓰기가 block되지 않았는지 검사하기 위한 리스트) 
    • exceptfds: 주어진 디스크립터에 대한 예외 조건에 관심이 있는지
      • 예외가 있는지 검사하기 위한 리스트
    • timeout: 우리가 얼마나 기다리고 싶은지
      • select가 반환하기 전에 blocking될 수 있는 시간의 제한 값
      • timeout==NULL이면 영원히 wait
      • timeout==0이면 wait하지 않고 즉시 리턴
    • readfds, writefds, execptfds
      • 안 궁금하면 그냥 null pointer 넣어 놓으면 됨

 

 

  • 0번 3번을 검사하겠다는 뜻
  • 0번 검사했더니 read 안되는 경우 0으로 바꿈

 

 

 

  • select()의 리턴 값
    • return value: -1
      • 에러 발생 ex) 시그널 잡힘
      • 수정된 디스크립터 셋이 없음
    • return value: 0
      • 준비된 디스크립터가 없음 ex) timeout 값이 만료될 때
      • 모든 디스크립터 셋이 0으로 설정됨
    • return value: positive
      • 디스크립터 넘버가 준비됨
        • 모든 3개의 세트에 있는 준비된 디스크립터들의 합
        • 만약 같은 디스크립터가 read, write 둘 다 준비되어 있다면, 리턴 값으로 2번 카운트된다.
      • 세 개의 디스크립터 세트에 남아 있는 비트는 준비된 디스크립터에 해당하는 비트뿐이다.

 

 

 

 

 

출처: [UNIX system programming]

 

반응형
Comments