Ch 5 Interlude: Process API

이 챕터에서는 UNIX 시스템의 프로세스 생성에 대해 다룬다

5.1 The fork() system call

fork() 는 새로운 프로세스를 생성하기 위해 사용된다.

alt_text

alt_text

해당 프로그램의 실행과정은 이렇다 먼저 인삿말을 프린트하며 자신의 Processs Identifier(PID) 를 출력한다. 그 이후 fork()를 호출하여 새로운 프로세스를 생성하는데 이때 해당 프로세스는 Child Process가 된다. 이렇게 생성된 자식은 부모의 거의 동일한 복제품이다. OS의 관점으로 볼땐 동일은 두개의 프로그램이 작동 중인것 처럼 보이며 둘다 fork 함수에서 빠져나오는 것처럼 보인다. 복제품이나 자식은 인삿말을 출력하지 않고 fork()에서 부터 생성된것 처럼 나온다. 이 부분을 보면 자식은 완전한 복제품은 아닌 것으로 확인된다. 자식은 본인의 메모리 주소, 레지스터, 프로그램 카운터 등을 갖게 되지만 fork()를 호출한 곳에 리턴하는 값은 다르다. fork 호출시 부모는 자식의 PID받게 되지만 자식은 0을 받게 된다. 위의 예제에서는 부모가 먼저 프린트 했지만 CPU sheduler에 따라 다른 결과가 나오기도 한다.

alt_text

5.2 The wait() system call

가끔은 부모가 자식의 일이 끝날 때 까지 기다리는게 필요할때도 있다. 이는 wait() system call를 통해 가능하다(waitpid()).

alt_text

앞선 예시에서는 CPU scheduler에 따라 결과가 결정되어 결과를 예측할 수 없었지만 이번 예시에는 wait를 사용하여 자식의 일이 끝날때까지 부모가 기다리도록 했다.

alt_text

wait을 사용하게 되면 parent가 먼저 실행되더라도 자식이 일이 끝나고 종료된후 wait이 리턴될때 까지 기다린다.

5.3 The exec() system call

만약 프로그램을 복제하는 것이 아닌 다른 프로그램을 새롭게 실행하고 싶다면 exec()가 정답이다.

alt_text

해당 예시에는 execvp를 사용하여 외부의 프로그램을 실행한다. 정확히 하는 일은 해당 프로그램의 실행파일과 argument를 load하여 현재 코드 segment를 덮어씌워 그 위에서 작동하도록 한다. 이에 따라 heap, stack 또한 새롭게 실행된다. 이 이후에는 OS가 프로그램을 실행한다. 따라서 새롭게 프로세스를 만드는 것이 아닌 실행중인 프로그램을 다른 프로그램으로 교체하는 것 이다. 이후의 과정은 fork() 만 호출된 것처럼 보인다.

5.4 Why? Motivating the API

이와 같이 복잡한 system call들을 사용하는 이유는 UNIX shell을 사용하기 위함이다. fork()와 exec()가 분리되어 있음으로 우리는 prompt에 원하는 프로그램의 이름을 작성하여 실행할 수 있는 것이다. 1) 실행파일의 이름이 작성된 후 shell은 2) fork()를 실행하여 프로세스를 생성하고 그 후에 3) exec()를 통해 해당 프로그램을 실행한다. 그리고 해당 작업이 끝나도록 4) wait()을 통해 기다린 후 모든 작업이 끝나면 다음 command를 기다린다.

이 처럼 fork()와 exec()가 분리되어 있기 때문에 여러가지 일을 할 수 있게 된다. 예를 들어 프로그램의 결과값을 다른 파일에 저장하고 싶다고 하자. 이때 우리는 > 기호를 사용하여 command 할 수 있는데. 이 과정에서 shell은 output이 STDOUT을 통해 콘솔로 나가지 않고 원하는 파일에 저장될 수 있도록 STDOUT을 닫는다. 그리고 파일을 생성하거나 열어 그곳에 결과를 저장한다.

alt_text

위의 과정에서 Pipe 또한 사용되었다. Pipe이란 Process간의 통신을 지원하기 위한 것으로 하나의 Process의 output를 다른 프로세스에 전달하거나 해당 output을 input으로 받기 위해 사용된다.

5.5 Process Control and Users

위에서 설명한 예제 외에도 다양한 interface가 존재하는데 가장 대표적인 것은 signal() 이다. Kill(), SIGINT(), SIGTSTP() 과 같은 시그널이 존재하며 프로세스를 통제하기 위해 사용된다.