[OSTEP] Virtualization) 5. Process API

0

OSTEP 운영체제

목록 보기
2/19
post-thumbnail

[OSTEP] 5. Process API

이 포스팅은 <<Operating Systems: Three Easy Pieces>>, Remzi H. Arpaci-Dusseau & Andrea C. Arpaci-Dusseau을 읽고 개인 학습용으로 정리한 글입니다.

CH5. 프로세스 API

  • UNIX 시스템의 프로세스 생성
    -> 프로세스를 생성하기 위하여 fork(), exec() 시스템 콜 사용
    -> 프로세스가 자신이 생성한 프로세스가 종료되기를 기다리기 윈할 때 wait() 시스템 콜 사용

1. fork() 시스템 콜

  1. 실행이 시작될 때 프로세스는 "hello world ..." 메세지를 출력
  • 이 메세지에는 프로세스 식별자(process identifier, PID)가 포함됨
  1. 프로세스는 fork() 시스템 콜을 호출
  • 생성된 프로세스는 호출한 프로세스의 복사본이다

  • 운영체제 입장에서 보면, fork()에서 리턴하기 직전인 프로세스가 두 개 존재한다

  • 자식 프로세스는 main()함수 첫 부분부터 시작하는 것이 아닌, fork()를 호출하면서부터 시작된다

  • 자식 프로세스는 자신의 주소 공간, 자신의 레지스터, 자신의 PC 값을 갖는다

  • 자식 프로세스는 fork() 시스템 콜의 반환 값이 다르다
    -> 부모 프로세스는 생성된 자식 프로세스의 PID를 반환 받는다
    -> 자식 프로세스는 0을 반환 받는다

결과


  • CPU 스케쥴러는 실행할 프로세스를 선택한다

  • 스케쥴러의 동적은 일반적으로 상당히 복잡하고 상황에 따라 다른 선택이 이루어진다
    -> 어느 프로세스가 먼저 실행된다고 단정하기 매우 어렵다
    -> 비결정성(nondeterminism)

2. wait() 시스템 콜

  • 부모 프로세스가 자식 프로세스의 종료를 대기해야 하는 경우 사용

  • 부모 프로세스는 wait() 시스템 콜을 호출하여 자식 프로세스 종료 시점까지 자신의 실행을 잠시 중지시킨다

  • 자식 프로세스가 종료되면 wait()은 리턴한다

결과

  • 프로그램은 항상 동일한 결과를 출력한다
    -> 항상 자식 프로세스가 먼저 출력을 수행한다

3. exec() 시스템 콜

  • 자신의 복사본이 아닌 다른 프로그램을 실행해야할 경우 사용

  • wc 프로그램은 단어의 개수를 세는 프로그램이다

  • 자신의 소스 파일인 p3.c를 인자로 하여 wc를 실행하고 소스 코드의 행 개수, 단어의 개수, 바이트의 개수를 알려주는 프로그램

  • 자식 프로세스는 wc 프로그램을 실행시키기 위해 execvp() 시스템 콜을 호출한다

exec() 시스템 콜 수행 과정

  • 실행 파일의 이름(ex. wc)과 약간의 인자(ex. p3.c)를 받는다

  • 해당 파일의 코드와 정적 데이터를 읽어 들여 현재 실행 중인 프로세스의 코드 세그멘트와 정적 데이터 부분을 덮어 쓴다

  • 힙과 스택 및 프로그램의 다른 주소 공간들도 새로운 프로그램의 실행을 위해 다시 초기화된다

  • ⚡그런 다음 운영체제는 argv와 같은 인자를 전달하여 프로그램을 실행시킨다

  • 새로운 프로세스를 생성는 것이 아니라 현재 실행중인 프로그램(p3)을 다른 실행 중인 프로그램(wc)으로 대체하는 것이다

결과

  • 자식 프로세스가 exec()를 호출한 후네는 p3.c는 전혀 실행되지 않는 것처럼 보인다

  • exec() 시스템 콜이 성공하게 되면 p3.c는 절대로 리턴하기 않는다

4. 왜 이런 API를?

  • UNIX의 쉘 구현하기 위해서는 fork()와 exec()를 분리해야 한다

  • fork()를 호출하고 exec()를 호출하기 전 코드를 실행할 수 있다
    -> 이 때 프로그램 환경 설정 & 다양한 기능 준비

  • 쉘은 단순한 사용자 프로그램이다

  • 쉘은 프롬프트를 표시하고 사용자의 입력을 기다린다
    -> 사용자는 명령어를 입력한다 (ex. 실행 프로그램의 이름과 필요한 인자)

  • 쉘은 파일 시스템에서 실행 파일의 위치를 찾는다
    -> 명령어를 실행하기 위하여 fork()를 호출하여 새로운 자식 프로세스를 만든다

  • exec()의 변형 중 하나를 호출하여 프로그램을 실행시킨다
    -> wait()을 호출하여 명령어가 끝나기를 기다린다

  • 자식 프로세스가 종료되면 쉘은 wait()으로부터 리턴한다
    -> 다시 프롬프트를 출력하고 다음 명령어를 기다린다

⚡예

  • wc 프로그램릐 출력은 newfile.txt라는 출력 파일로 방향이 재지정된다

  • 자식이 생성되고 exec()를 호출하기 전에 표준 출력(standard output) 파일을 닫고 newfile.txt 파일을 연다

  • ⚡UNIX 시스템은 미사용중인 파일 디스크립터를 0번부터 찾아 나간다
    -> 이 경우 표준 입출력 파일을 닫음
    -> STDOUT_FILENO가 첫 번째 사용 가능 파일 디스크립터로 탐색됨
    => open()이 호출될 때 STDOUT_FILENO가 할당됨

  • 자식 프로세스가 표준 출력 파일 디스크립터를 대상으로 하는 모든 쓰기(ex. printf()에 의한 쓰기)는 화면이 아닌 새로 열린 파일로 향하게 된다

결과

  • p4를 실행하면, 화면에 아무 이롣 일어나지 않는다

  • 프로그램 p4는 fork()를 호출하여 새로운 프로세스를 생성
    -> 출력을 p4.output 파일로 재지정
    -> execvp()를 호출하여 wc 프로그램을 실행

  • 출력 파일을 cat 해보면 wc를 실행시켰을 때 얻을 수 있는 모든 출력이 파일에 저장되어 있음을 볼 수 있다

⚡파이프

  • UNIX 파이프는 쉘과 유사한 방식으로 구현되지만 pipe() 시스템 콜을 통해 생성
    -> 한 프로세스의 출력과 다른 프로세스의 입력이 동일한 파이프에 연결된다
    -> 명령어 체인이 형성됨

  • ⚡파일에서 특정 단어가 몇 번 나오는지 세는 예제
    -> 파이프와 grep와 wc를 사용하면 쉽게 할 수 있다

5. 여타 API들

  • UNIX 시스템에는 fork(), exec(), wait() 외 많은 프로세스 관련 인터페이스 존재

  • kill() 시스템 콜: 프로세스에세 시그널(signal)을 보내는 데 사용
    -> 시그널은 프로세스를 중단(block)시키고, 삭제하는 등의 작업에 사용된다

  • 유용한 명령어들 제공
    -> ps 명령어: 어떤 프로세스가 실행 중인지 알아보기 위해 사용
    -> top 명령어: 시스템에 존재하는 프로세스와 그 프로세스가 CPU 및 다른 자원들을 얼마나 사용하고 있는지 보여줌

  • 다양한 CPU 측정기 제공
    -> 시스템의 부하 정도를 알 수 있음

📌참고자료

profile
Be able to be vulnerable, in search of truth

0개의 댓글