본문 바로가기

Computer Science/Computer Systems

[Lecture 2] Unix File Descriptors and Pipes

#Unix File Descriptors

Unix file descritors, 유닉스 파일 디스크립터는 운영 체계에서 파일을 사용할 때 각 파일에 대한 정보를 유지하는 기억 장치의 한 영역, 또는 그 정보이다.

  • 파일 디스크립터는 사용자 프로세스가 바이트 시퀀스인 파일을 참조할 수 있도록 하는 핸들(handle)이다.
  • 유닉스는 디스크, 터미널, 네트워크 소켓, IPC 채널(파이프 등)과 같은 다양한 커널 추상화를 추상 I/O 장치 파일로 표현한다.
  • 기본 개체의 종류에 관계없이 동일한 API 제공
    • read(2), write(2), close(2), lseek(2), dup2() 등등
    • 검색 가능한 경우 읽기/쓰기 위치를 유지할 수 있음
    • 참고: 모든 종류의 파일 디스크립터에서 모든 작업이 작동하는 것은 아니다
  • open(2)와 같은 시스템 호출(system call)에서 얻은 (작은) 정수를 사용하여 표현됨
  • low-level I/O로 간주됨
  • fork() 시 자식 프로세스에 의해 상속/복제됨(clone)
  • 프로세스 Exec()의 다른 프로그램일 때 보존됨
  • 프로세스가 close()하거나 종료(killed)될 때 닫힘

#Standard Streams

Standard Streams, 표준 스트림이란 특정한 프로그래밍 언어 인터페이스뿐 아니라 유닉스 및 유닉스 계열 운영 체제(어느 정도까지는 윈도우에도 해당함)에서 컴퓨터 프로그램과 그 환경(일반적으로 단말기) 사이에 미리 연결된 입출력 통로를 가리킨다. 일반적으로 유닉스에서 동작하는 프로그램은 실행 시 세 개의 스트림이 자동으로 열린다. 이를 표준 스트림이라고 부른다. 

  • 0, 1, 2는 표준 입력 (Standard input, STDIN, 0), 표준 출력(Standard Output, STDOUT, 1) 및 표준 오류 스트림 (Standard Error, STDERR, 2)에 사용됨.
  • 프로그램에서 파일을 열 필요가 없다. 파일이 미리 연결되어 있으므로 추가 정보 없이 해당 파일을 사용할 수 있게돼있다.
  • 제어 프로그램(shell) 또는 프로그램을 시작하는 프로그램이 일반 파일, 터미널 장치 또는 다른 것을 참조하도록 설정할 수 있다.
  • 사용 시 기본 커널 객체에 액세스하는 방법은 직접 여는 것과 같다.
  • 일반적으로 프로그램은 표준 스트림이 연결되는 객체의 특정 유형에 따라 동작을 변경하지 않아야 한다.
    • 예외가 존재한다. 예를 들어, C의 stdio의 플러싱 전략(flushing strategy)은 표준 출력이 터미널인지 아닌지에 따라 달라진다.
    • Python 2의 sys.stdout.encoding fiasco

#File Descriptors – The subtle parts

파일 디스크립터를 제대로 이해하려면 커널 내부의 구현을 이해해야 한다.

  • 파일 디스크립터는 참조 카운트(reference counting)를 포함하는 2개의 간접 레이어 (indirect layers)를 사용함.
    • 프로세스별 테이블의 (정수) 파일 디스크립터는 열린 파일 테이블(global open file table)의 entry을 가리킨다.
    • 프로세스별 파일 디스크립터 테이블에는 entry수에 제한이 있음.
    • 열려 있는 각 파일 테이블 entry는 파일에 대한 읽기/쓰기 오프셋(또는 위치)을 유지한다.
    • 열린 파일 테이블의 entry는 각 파일 유사 개체에 대한 특수 항목이 포함된 global "vnode" 테이블의 항목을 가리킨다.
  • 파일 디스크립터의 테이블은 (일반적으로) 프로세스별로 이루어지지만 프로세스에서 entry를 복제하고 재정렬할 수 있다.

#In-Kernel Management of File Descriptors

커널 내 파일 디스크립터 관리

#File Descriptor Manipulation

이번에는 파일 디스크립터를 조작하는 방법에 대해 알아보자.

  • close(fd):
    • 파일 디스크립터 테이블의 entry 지우기, 열린 파일 테이블의 refcount 감소시킴 (decrement/refcount--)
    • 0인 경우 열린 파일 테이블의 entry 할당해제(deallocate)하고 vnode 테이블의 refcount를 줄임.
    • 0인 경우 vnode 테이블의 entry 할당을 해제하고 기본 개체 (underlying object) 를 닫음.
    • 특정 개체(pipes, socket)의 경우 기본 개체(underlying object)를 닫으면 해당 개체를 참조하는 모든 파일 디스크립터가 닫힌 경우에만 발생하는 중요한 side effects들이 있다.
  • dup(int fd)
    • fd와 동일한 파일 디스크립터를 참조하는 새 파일 디스크립터  만들기, increment refcount
  • dup2(int fromfd, int tofd)
    • tofd가 열려 있으면 닫음. 그런 다음 fromfd와 동일한 열린 파일 entry에 fd를 할당하고, refcount 증가시킴 (increment)
  • fork()
    • fork() 시, 하위는 상위 파일 디스크립터 테이블의 복사본을 상속함(그리고 열려 있는 각 파일 테이블 entry의 refcount 증가시킴).
  • exit()
    • exit() (또는 비정상적인 종료) 시, 모든 entry들이 닫힘.

#Pipes

Unix Pipes

유닉스 파이프는 FIFO, 경계 버퍼로, 작성기에서 판독기로 흐르는 단방향 바이트 스트림의 추상화를 제공한다.

 

  • Writers:
    • 공간이 있는 한 파이프에 데이터를 저장할 수 있다.
    • Reader가 파이프를 drain할 때까지 파이프가 가득 찬 경우 파이프를 block함
  • Readers:
    • 파이프를 읽음으로써 drain함.
    • 비어 있으면 writer이 데이터를 쓸 때까지 block함.
  • 파이프는 "경계 버퍼" 추상화(abstraction)를 제공함.
    • 경계 버퍼는 안전함, 경합 조건(race condition) 없음, 공유 메모리 없음, 커널에 의해 처리됨
    • 경계 버퍼는 상대 진행률(relative progress)을 자동으로 제어하는 흐름 제어 기능(flow control)을 제공함.
  • 이름이 지정되지 않은 파일 디스크립터 테이블 항목이 자동으로 정리된다.