프로세스의 정의


 

프로세스는 운영체제(os)가 사용자에게 제공하는 가장 근본적인 추상화 (fundamental abstraction)다.

프로세스의 정의는 단순하다. 실행중인 프로그램(running program)이 프로세스의 정의다.

 

프로그램은 그 자체로는 생명이 없다. 그저 디스크에 상주하는 명령어 묶음이며, 이는 실행이 되기를 기다리고 있다.

운영체제(OS)가 이들을 실행시킬 때 비로소 프로그램은 제 역할을 한다고 볼 수 있다.

 

 

 

CPU 가상화 (virtualizing CPU)


우리가 컴퓨터를 사용할 때, 마치 여러 개의 프로세스들이 동시에 실행되고 있다고 느낀다.

또한, 우린 CPU가 어떠한 프로세스를 실행시키고 있는지 고려할 필요가 없다.

 

하나의 CPU로 여러 개의 프로세스를 동시에 실행시키고 싶지만, 이는 불가능하다.

대신에, CPU time sharing을 통해서 여러 개의 프로세스를 번갈아가며 실행하여 마치 하나의 CPU가 여러 개의 프로세스를 동시에 실행하는 것처럼 보이게 할 수 있다.

 

이와 같이,  운영체제는 단일 physical CPU로 여러개의 virtualizing CPUs를 사용하는 것처럼 환상(illusion)을 만들어내며, 이를 CPU 가상화라고 한다.

 

CPU time sharing은 유저가 여러 개의 프로세스를 동시에 실행할 수 있도록 제공하지만, CPU가 공유되므로 각각의 프로세스는 더 느리게 실행될 것이며, 잠재적인 성능에 대한 trade-off가 있다. 

 

time sharing 메카니즘에서 context-switching이 쓰이며, context-switching은 주어진 CPU에서 하나의 프로그램을 종료시키고 다른 또 하나의 프로그램을 실행시킬 수 있는 운영체제의 ability이다.

 

 

 

프로세스와 Machine State


위에서 말했듯이, 프로세스는 OS가 실행중인 프로그램이다. 그리고 프로그램은 OS가 실행하기 전까진 디스크에 상주하는 instructions(명령어) 묶음이다.

 

그렇다면, 프로세스를 구성하는 것은 무엇일까? 이를 이해하기 위해서, 프로세스의 machine state에 대해 알 필요가 있다.

 

Memory

프로세스를 구성하는 핵심 machine state 구성 요소 중 하나는 메모리(memory)다.

실행하는 프로그램의 명령어들과 읽고 쓰는 데이터들 모두 메모리에 적재되어 있다.

 

 

 

Register

프로세스의 machine state 구성 요소 중 또 다른 하나는 레지스터다.

많은 명령어들이 명시적으로 레지스터를 읽고, 업데이트한다. 이는 프로세스의 실행에 있어 필수적이다.

 

프로세스의 machine state를 구성하는 특수한 레지스터들을 몇 개 소개한다.

 

Program Counter, PC (=instruction pointer, IP)

- 다음에 실행할 프로그램 명령어를 지시한다.

 

Stack Pointer 와 Frame Pointer

- 함수 파라미터(function parameters), local variables(지역 변수)가 담긴 스택을 관리하고 주소(address)를 반환(return)하기 위해 사용된다.

 

 

Persistent Storage Device

Persistent Storage Device는 hard disk drive와 같이 영구적으로 데이터가 저장되는 저장소를 뜻한다.

프로세스는 때때로 persistent storage device에 접근(access)한다. (ex. file open 및 I/O)

 

 

 

 

대표적인 Process API


 

Create : 운영체제가 새로운 프로세스를 생성하는 메소드다. 쉘(shell)에 명령어(command)를 입력하거나, 어플리케이션 아이콘을 더블 클릭할 때, 운영체제는 사용자가 지시한 프로그램을 실행하기 위한 새로운 프로세스를 생성한다.

 

Destroy : 운영체제가 프로세스를 강제적으로 종료하도록 하는 메소드다. 물론 많은 프로세스들이 실행이 완료된 후 스스로 종료되겠지만, 그렇지 않을 경우, 사용자가 프로세스를 kill(강제 종료)하고 싶을 때 사용한다.

 

Wait : 프로세스의 실행이 종료될 때까지 기다린다.

 

Miscellaneous Control : 프로세스를 kill 또는 wait하는 것과는 또 다른 control(제어)가 가능하다. Miscellaneous Control은 프로세스를 연기(suspend)한다. 즉, 잠깐동안 실행을 멈추었다가 다시 실행을 재개한다.

 

 

 

 

Process Creation Detail, 프로세스 생성에 대한 고찰


프로그램들이 프로세스로 변화(transform)하는 과정을 좀 더 살펴보자. 운영체제는 어떻게 프로그램을 가져와 실행하고, 프로세스를 생성할까?

 

첫 번째로, 운영체제는 프로그램 코드와 정적 데이터(static data)를 메모리로 로드한다. 프로그램은 처음엔 실행가능한 형식으로 디스크(disk, 현대에 와서는 flash기반 ssd)에 상주하므로 이러한 과정이 필요하다.

 

초기 운영체제는 프로세스 로딩을 eager하게 수행(프로그램이 실행되기 전에 한 번에 모든 로딩 처리)했지만,

현대의 운영체제는 lazy-loading을 채택하고 있다. 즉, 프로그램을 실행하는 동안, 필요한 코드, 데이터만을 부분적으로 가져온다. 

lazy-loading을 깊게 이해하기 위해서, paging(페이징)과 swapping에 대한 이해가 필요하다. 이 부분은 메모리 가상화에서 다룬다.

지금은, 운영체제가 프로그램을 실행하기 위해 필요한 프로그램 비트들을 디스크로부터 메모리로 가져온다고만 알아두자.

 

두 번째로, 운영체제는 프로그램이 실행될 때 메모리의 일부를 런타임 스택으로 할당하고 프로세스에게 넘겨준다. 예를 들어, C 프로그램에서는 스택을 지역 변수(local variable), 함수 파라미터(function parameters), 주소 반환(return address)을 위해 사용한다. OS는 일반적으로 arguments(ex. main() 함수의 argc, argv)를 담은 스택을 초기화할 것이다.

 

세 번째로, 운영체제는 프로그램이 실행될 때 메모리의 일부를 프로그램의 힙(heap)으로 할당하고 프로세스에게 넘겨준다. C 프로그램에서, 힙은 명시적으로 요청받은 동적 할당 데이터들을 위해 사용된다. 예를 들어 malloc()으로 할당하고, free()로 할당 해제한다. 힙은 연결 리스트(linked list), 해쉬 테이블(hash table), 트리(trees) 등의 자료 구조 사용에 필요하다. 힙은 처음엔 작을지 모르지만, 프로그램이 실행되고, malloc()을 통해 많은 요청이 발생하면 OS는 이러한 요청을 받아들여 더 많은 힙 메모리를 할당할 것이다.

 

이 뿐만 아니라, 운영체제는 다른 초기화 작업들도 수행한다. 특히, I/O(Input/Output, 입출력) 관련 초기화 작업도 포함하는데, 예를 들어, UNIX 시스템에서는 각각의 프로세스는 디폴트하게 3 개의 open 파일 디스크립터(file descriptor)를 가진다. 3개는 각각 표준 입력, 표준 출력, 그리고 오류를 의미한다. 이 디스크립터들은 프로그램이 터미널로부터 쉽게 입력을 읽고(read input), 스크린에 출력을 프린트할 수 있도록 한다(print output).

 

이처럼, 코드와 정적 데이터를 메모리에 로드하고, 스택과 힙을 생성, 초기화 및 할당하고, I/O관련 셋업과 같은 다른 작업들을 수행한 후 OS는 비로소 프로그램 실행을 위한 준비를 거의 마친다. 마지막으로, main() 시작 포인트(entry point)에서 프로그램을 실행시키기 위한 작업을 수행한다. 운영체제는 CPU에 대한 제어권을 새롭게 생성된 프로세스에 넘겨준다. 그리고 프로그램이 실행된다.

 

 

 

Process State (프로세스 상태)


이제 우린 프로세스가 무엇인지, 어떻게 생성되는 지 어느 정도 파악했다. 이제, 프로세스가 주어진 시간 동안 어떠한 상태로 존재할 수 있는지 process states에 대해 알아보자. 단순화했을 때, 프로세스는 아래 세 가지 상태 중 하나로 존재한다.

 

Running : 프로세스가 프로세스에서 실행되고 있는 상태, 즉 명령어가 실행되고 있는 상태를 의미한다.

 

Ready : ready 상태는 프로세스가 실행될 준비가 되었지만, 어떠한 이유에서인지 운영체제가 지금은 해당 프로세스를 실행하지 않는 상태를 의미한다.

 

Blocked : blocked 상태는 프로세스가, 특정 이벤트(event)가 발생하기 전까지는 실행할 수 없는 연산을 수행하고 있는 상태이다. 흔한 예시로, 프로세스가 disk I/O request를 시작했을 때 blocked 상태가 되며, 그 동안 다른 프로세스가 프로세서를 사용할 수 있다. 

 

 

 

 

Operating System의 Data Structure


운영체제도 프로그램이며, 다른 프로그램들과 마찬가지로 다양한 관련 정보를 추적하는 주요 데이터 구조를 가지고 있다.

 

예를 들어, 각각 프로세스의 상태를 추적하기 위해 운영체제는 프로세스 리스트(process list)를 가지고 있다. 프로세스 리스트는 ready상태의 모든 프로세스를 포함하며, 어떠한 프로세스가 실행되고 있는 지를 추적하기 위한 정보들을 포함한다.

운영체제는 또한 blocked 상태의 프로세스 또한 추적해야 한다. 예를 들어, I/O event가 완료되었을 때, 운영체제는 올바른 프로세스를 깨워(wake) 다시 실행하기 위해 ready상태로 전환해야 한다.

 

이처럼, 운영체제는 프로세스를 추적하기 위해 stopped process(중지된 프로세스)의 레지스터 값들을 register context에 저장한다. 프로세스가 중지되면, 중지된 프로세스의 레지스터 값들은 레지스터 컨텍스트에 저장되며, 이 레지스터들을 복구함으로써 운영체제는 다시 프로세스를 재개(resume)할 수 있다. 이와 같은 테크닉은 context switch에서 자세하게 배운다.

 

실제로, process state는 위의 running, ready, blocked가 아닌 다른 상태로도 존재할 수 있다.

 

예를 들어, 프로세스가 생성중일 때는 initial state, 프로세스가 종료(exit)되었지만 회수(cleaned up)되지 않았을 때 final state(unix 시스템에서는 이를 zombie state라고 칭한다)가 있다.

 

final state는, 프로세스를 생성한 부모 프로세스(parent)가 종료된 자식 프로세스의 반환 코드(return code)를 검사하고, 종료된 자식 프로세스가 성공적으로 실행되었는 지 확인할 수 있도록 한다.(일반적으로 작업이 성공적으로 수행되었으면 0을, 그렇지 않으면 0이 아닌 값을 반환한다).

 

프로세스가 종료될 때, 자식 프로세스가 완료되기까지 기다리기 위한 final call(최종 호출, ex. wait())을 하고, 운영체제에게 이제 사라지는 프로세스와 관련된 자료 구조들을 정리(cleaned up)해도 된다고 명시한다.

 

 

 


 

 

출처 : pages.cs.wisc.edu/~remzi/OSTEP/

 

Operating Systems: Three Easy Pieces

Blog: Why Textbooks Should Be Free Quick: Free Book Chapters - Hardcover - Softcover (Lulu) - Softcover (Amazon) - Buy PDF - EU (Lulu) - Buy in India - Buy Stuff - Donate - For Teachers - Homework - Projects - News - Acknowledgements - Other Books Welcome

pages.cs.wisc.edu

 

+ Recent posts