POSIX(Portable Operating System Interface)는 이식가능 운영체제 인터페이스의 약자다. 한 운영체제에서 개발한 프로그램을 다른 운영체제에서도 쉽게 돌아가도록 하고 서로 운영체제가 달라도 같은 Command line으로 명령을 내릴 수 있다. Posix는 Unix에 기반하여 만들어 졌다. Posix 인증을 받은 OS는 대표적으로 MAC OS가 있다. Linux도 인증을 받지는 못했지만 대부분의 기능이 POSIX 표준을 지키고 있다. Windows는 Posix를 따르지 않는다. Posix 표준을 지키는 OS환경을 Unix-like environments 라고 한다. 따라서 MAC OS와 Linux는 Unix-like 하다고 말할 수 있다.
표준 스트림은 Posix를 따르는 OS에 컴퓨터에 연결된 입출력 통로를 가리킨다. 표준스트림은 표준 입력, 표준 출력, 표준 오류가 있다.
표준입력(STDIN): 표준 입력 장치의 ID 는 숫자로는 0 이며 일반적으로는 키보드다.
표준출력(STDOUT): 출력을 위한 스트림으로 표준 출력 장치의 ID 는 1이며 일반적으로는 현재 쉘을 실행한 콘솔이다.
표준에러(STDERR): 에러를 위한 스트림으로 표준 에러 장치의 ID 는 2이며 일반적으로는 표준 출력과 동일하다.
파이프는 어떤 프로그램의 출력 결과를 다음 단계의 입력값으로 이어지는 형태로 연결된 구조를 뜻한다.
man cat | grep POSIX
위의 명령어는 프로세스 하나로 동작하는 것처럼 보이지만 사실 두개의 프로세스로 돌아간다. man cat
, grep POSIX
이렇게 각각의 프로세스로 작동한다. 위 두개의 프로세스는 pipe(|)로 정보를 주고 받는다. 파이프를 기준으로 왼쪽 프로세스 man cat
의 결과 값이 오른쪽 프로세스 grep POSIX
의 input 값으로 들어간다. pipe는 총 2개의 파일을 생성한다. 하나는 왼쪽 프로세스가 자신의 결과값을 write하는 파일, 다른 하나는 오른쪽에서 read하는 파일이다.
pipe를 만들면 부모 프로세스는 파이프의 read_file과 write_file을 file descriptor에 등록한다. 부모 프로세스를 fork하면 자식이 부모의 file descriptor를 상속받는다. 파이프를 만들고 fork로 프로세스를 복사했다면 다음과 같은 모습이다.
man
과 grep
중 무엇을 부모로 하고 무엇을 자식으로 정할까? 부모 프로세스와 자식 프로세스 중 무엇이 먼저 실행될지는 모른다. 우선 우리가 아는 것은 man cat
은 write를 해야하고 grep POSIX
는 read를 해야한다.
잠시 이야기를 돌려 read() 함수를 알아보자. read()는 EOF까지 파일을 읽는다. EOF는 파일의 끝을 의미한다. EOF를 만족할 중요한 조건이 하나 있는데, 그건 바로 모든 writer가 close되어야 한다는 것이다. 자식이 read기능을 실행한다면 자식의 write_file descriptor는 열려있으면 안된다. 따라서 man cat
을 부모로 하면 자식인 grep POSIX
의 write_file을 close() 해줘야 한다. 그래야 EOF를 만족시킬 수 있다.
또한 자식의 read_file도 닫는 것이 권장된다. 왜냐하면 프로세스가 불필요한 여러개 파일에 접근하고 있는 것은 좋지 않기 때문이다. file descriptor 갯수에도 제한이 있기에 필요없는 파일은 닫는 것이 좋다. 또한 쓰는 입장에서 여러명이 파일을 읽고 있으면 진짜 reader가 누군지 판단하기 힘들다.