서론
프로세스는 실행 중인 프로그램이라고 생각할 수 있다. 정확히는 프로세스는 작업의 단위이다. 즉 대부분 시스템은 프로세스들의 집합으로 구성된다. 운영체제 - 프로세스는 시스템 코드를 실행시키고, 사용자 프로세스는 사용자의 코드를 실행시킨다. 이 모든 프로세스는 병행하게 처리될 수 있다.
프로세스 개념(Process Concept)
운영체제에서 모든 CPU활동을 어떻게 불러올지에 대해 많은 논의가 있었다. 초창기 컴퓨터는 작업(job)을 실행하는 일괄처리 시스템이었고, 사용자 프로그램 또는 task를 실행하는 시분할 시스템이 뒤를 이었다. 여러 측면에서 모든 활동은 유사하므로 이들을 프로세스라고 부를 수 있다.(배치 시스템 – jobs, 시분할 시스템 – user programs or tasks)
비공식적으로 프로세스는 실행 중인 프로그램이다. 1장에 언급한 도서에서는 작업(job)과 프로세스(process)라는 용어를 거의 같은 의미로 사용한다. 즉, 텍스트(text) 섹션이라고도 하는 프로그램 코드/ 전역 변수, 정적 변수가 포함된 데이터(data) 섹션/ 임시 데이터를 포함하는 스택(stack) 섹션에는 함수 매개변수, 반환 주소, 지역 변수/ 런타임 동안 동적으로 할당된 메모리를 포함하는 힙(heap) 섹션은 프로그램 카운터, 프로세서 레지스터를 포함한 현재 활동 등이 프로세스에 포함된다.
프로세스 상태(Process State)
프로세스가 실행되면 상태가 변경된다.
- New: 프로세스가 생성되고 있다.
- Running: 명령이 실행되고 있다.
- Waiting (blocked, sleep): 프로세스가 어떤 이벤트가 발생하기를 기다리고 있다.
- Ready: 프로세스가 프로세서에 할당되기를 기다리고 있다.
- Terminated: 프로세스 실행이 완료되었다.
Diagram of Process State
프로세스 제어 블록(Process Control Block, PCB)
각 프로세스는 운영체제에서 프로세스 제어 블록(혹은 태스크 제어 블록)에 의해 표현된다. 프로세스 제어 블록은 특정 프로세스와 연관된 여러 정보를 수록하며 다음과 같은 것을 포함한다.
- Process ID
- 프로세스 상태(Process state): New, ready, running, waiting, terminated
- 프로그램 카운터(Program counter): Address of the next instruction
- CPU 레지스터(CPU registers): stack pointer, general-purpose registers, …
- CPU 스케줄링 정보(CPU scheduling information): Priority, …
- 메모리 관리 정보(Memory-management information): Page table, segment table, …
- 회계 정보(Accounting information): Amount of CPU used, …
- I/O 상태 정보(I/O status information): Open files, allocated I/O devices, …
데이터 관리를 위한 메타데이터
- 프로세스용 프로세스 제어 블록, 태스크용 태스크 제어 블록 E.g. Linux의 task_struct
- 파일에 대한 파일 제어 블록 E.g. Unix 파일 시스템의 vnode
프로세스 스케줄링
다중 프로그래밍의 목적은 CPU이용을 최대화하기 위하여 항상 어떤 프로세스가 실행되도록 하는 것이다. 이 목적을 달성하기 위해 프로세스 스케줄러는 코어에서 실행 가능한 여러 프로세스 중에서 하나의 프로세스를 선택한다.
프로세스 스케줄링 큐(Process Scheduling Queues)
프로세스가 시스템에 들어가면 준비 큐(Ready queue)에 들어가서 준비 상태가 되어 메인 메모리에서 실행을 기다린다. 시스템에 다른 큐도 존재한다. 장치는 프로세서보다 상당히 느리게 실행되므로 프로세스 I/O 가 사용 가능할 때까지 기다려야 한다. I/O와 같이 특정 이벤트가 발생하기를 기다리는 프로세스는 대기 큐(Waiut queues, Device queues)에 들어간다. 프로세스는 수명주기 동안 다양한 대기열 간에 이주(migrate)되기도 한다.
스케줄(Schedulers)
CPU 스케줄러의 역할은 준비 큐에 있는 프로세스 중에서 선택된 하나의 프로세스에 CPU 코어를 할당하는 것이다. 프로세스는 다음과 같이 분류할 수 있다.
- I/O 바운드 프로세스(I/O-bound process)는 계산보다 I/O에 더 많은 시간을 소비한다. (many short CPU bursts)
- CPU 바운드 프로세스(CPU-bound process)는 계산에 더 많은 시간을 소비한다. (a few very long CPU bursts)
일부 운영체제는 스와핑으로 알려진 중간 형태의 스케줄링을 가지고 있다. 스와핑과 스케줄링은 다음에 다루도록 하겠다.
문맥 교환(Context Switch)
인터럽트는 운영체제가 CPU코어를 현재 작업에서 뺏어 커널 루틴을 실행할 수 있게 한다. 인터럽트가 발생하면 시스템은 인터럽트 처리가 끝난 후에 문맥(context)을 복구할 수 있도록 현제 실행 준인 프로세스의 현재 문맥을 저장할 필요가 있다. 즉, 프로세스를 중단했다가 재개하는 작업이다. 문맥은 프로세스의 PCB에 표현된다. 어느 모드(커널, 사용자 모드)이든 CPU의 현재 상태를 저장하는 작업(state save)을 수행하고 나중에 연산을 재개하기 위하여 상태 복구 작업(state restore)을 수행한다.
CPU 코어를 다른 프로세스로 교환하려면 이전의 프로세스의 상태를 보관하고 새로운 프로세스의 보관된 상태를 복구하는 작업이 필요하다. 이 작업을 문맥 교환(context switch)이라 한다. 문맥 교환하면, 커널은 과거 프로세스의 문맥을 PCB에 저장하고, 실행이 스케줄 된 프로세스의 저장된 문맥을 복구한 문맥 교환 시간은 순수한 오버헤드이다. 교환하는 동안 시스템이 유용한 작업을 수행하지 않는다. 문맥 교환 시간은 하드웨어에 크게 좌우된다.
프로세스 연산
프로세스 생성(Process Creation)
프로세스는 여러 개의 새로운 프로세스들을 생성할 수 있다. 앞에서 언급한 것과 같이 생성하는 프로세스를 부모 프로세스라고 부르고, 새로운 프로세스는 자식 프로세스라고 부른다. 이 새로운 프로세스들은 각각 다시 프로세스들을 생성할 수 있으며 이 프로세스들은 트리를 형성한다.
프로세스가 자식 프로세스를 생성할 때, 그 자식 프로세스는 자신의 임무를 달성하기 위하여 자원(Resource)이 필요하다. 이 자원은 운영체제로부터 직접 얻거나, 부모 프로세스가 가진 자원의 부분 집합만을 사용하도록 제한될 수 있다. 부모 프로세스는 자원을 분할하여 자식 프로세스들에게 나누어 주거나 메모리나 파일과 같은 몇몇 자원들은 자식 프로세스들이 같이 사용하게 할 수도 있다. 부모 프로세스 자원의 일부분만을 사용하도록 자식 프로세스가 쓸 수 있게 제한하며 자식 프로세스들을 많이 생성하여 시스템을 과부하 상태로 만드는 프로세스를 방지할 수 있다.
프로세스가 새로운 프로세스를 생성할 때, 두 프로세스를 실행시키는 데 두 가지 가능한 방법이 존재한다. 부모는 자식과 병행하게 실행을 계속하거나 부모는 자식이 종료될 때까지 기다린다. 새로운 프로세스들의 주소 공간 측면에서 볼 때 두 가지 가능성이 있다. 자식 프로세스는 부모 프로세스의 복사본(자식 프로세스는 부모와 똑같은 프로그램과 데이터를 가진다.)이거나 자식 프로세스가 자신에게 적재될 새로운 프로그램을 가지고 있다.
프로세스 생성 요약
- OS 커널 내에서 PCB 생성한다.
- 메모리 공간을 할당한다.
- 바이너리 프로그램을 로드한다.
- 프로그램을 초기화한다.
UNIX example
- fork 시스템 호출은 새로운 프로세스를 생성한다.
- 부모의 PCB를 복제한다.
- 메모리 공간을 할당한다.
- execve 시스템 호출은 포크 후에 사용된다.
- 디스크에서 바이너리 프로그램을 로드한다.
- 초기화
프로세스 종료(Process Termination)
프로세스는 마지막 명령문을 실행하고 운영 체제에 자신을 삭제(exit)하도록 요청한다. 이 시점에서, 자식 프로세스는 상태 값(wait)을 부모에게 반환한다. 물리 메모리와 가상 메모리, 열린 파일, 입출력 버퍼를 포함한 자식 프로세스의 모든 자원이 할당 해제되고 운영체제로 반납된다. 혹은 자식 프로세스가 할당된 리소스를 초과하거나 자식 프로세스에게 할당된 작업이 더 이상 필요하지 않은 경우 부모 프로세스는 자식 프로세스의 실행을 종료(abort)할 수 있다. 일부 운영 체제는 부모가 종료된 경우 자식의 존재를 허용하지 않는다. 프로세스가 종료되면(종료는 운영 체제에 의해 시작된다.) 모든 하위 프로세스도 종료되어야 한다. 이것을 연쇄식 종료(cascading termination)이라 한다.
프로세스가 종료하면 사용하던 자원은 운영체제가 되찾아간다. 그러나 부모 프로세스는 wait() 시스템 호출을 사용하여 자식 프로세스의 종료를 기다릴 수 있다. 호출은 상태 정보와 종료된 프로세스의 pid를 반환한다.(pid = wait(&status);) 그러나 프로세스의 종료 상태가 저장되는 프로세스 테이블의 해당 항목은 부모 프로세스가 wait()를 호출할 때까지 남아 있게 된다. 프로세스 종료 시점에 wait()을 호출을 하지 않는 즉, 기다리는 부모가 없는 자식 프로세스는 좀비(zombie) 프로세스라 부리는 상태가 된다. 모든 프로세스는 종료 시점에 아주 짧은 시간 동안 좀비 상태로 머문다. 이후 부모 프로세스가 wait()를 호출하면 좀비 프로세스의 프로세스 식별자와 프로세스 테이블이 해당 항목이 운영체제에 반환된다. 만약 부모 프로세스가 wait() 호출 없이 부모가 종료되면 해당 자식 프로세스를 고아(orphan) 프로세스라 부른다.
Process Creation & Termination
프로세스 간 통신(Inter-process communication, IPC)
협력 프로세스(Cooperating Processes)
독립 프로세스(Independent process)는 다른 프로세스의 실행에 영향을 미치거나 영향을 받을 수 없는다. 협력 프로세스(Cooperating process)는 다른 프로세스의 실행에 영향을 미치거나 영향을 받을 수 있다. 프로세스 협업의 장점으로 정보 공유(공유 파일, …), 연산 속도 향상(하위 작업을 통한 병렬 실행), 모듈성(시스템 기능을 별도의 작업으로 분할)이 있다.
Producer-Consumer Problem
협력 프로세스에는 생산자-소비자(producer-consumer) 문제가 있다. 생산자 프로세스(producer process)는 소비자 프로세스(consumer process)에서 소비되는 정보를 생성한다. 예를 들어, 컴파일러는 어셈블러가 소비하는 어셈블리 코드를 생성할 수 있다. 어셈블러는 객체 모듈(Object module)을 생성할 수 있고, 로더(loader)는 이들을 소비한다. 이 문제의 해결책은 공유 메모리를 사용하는 것이다. 즉, 두 프로세스가 병행 실행이 되도록 하는 것이다. 이때 사용되는 버퍼 두 가지가 있다. 버퍼의 크기에 실질적인 제한을 두지 않는 무제한 버퍼(unbounded-buffer)와 고정된 버퍼 크기가 있다고 가정하는 경계 버퍼(bounded-buffer)가 있다.
메시지 전달(Message passing) 프로세스는 주소 공간을 공유하지 않고 서로 통신한다. 두 가지 기본 작업 send(message)와 receive(message)가 있다. P와 Q가 통신하려면 그들 사이에 통신 링크를 설정한다. send/receive를 통해 메시지를 교환한다. 통신 링크 구현방법에도 물리적(예: 하드웨어 버스, 네트워크,…), 논리적(예: 직접/간접, 동기/비동기,…)의 두 가지 구현 방법이 있다.
직접 통신(Direct Communication)
프로세스는 서로 명시적으로 이름(식별자)을 지정(명명) 해야 한다. 만약 프로세스의 식별자가 변경이 되면 이전 식별자에 대한 모든 참조를 찾아 변경해야 한다.
- send (P, message) - 프로세스 P에게 메시지를 보낸다.
- receive(Q, message) - 프로세스 Q로부터 메시지 수신한다.
간접 통신(Indirect Communication)
메시지는 우편함(Mailbox)에서 송수신된다. 우편함(Mailbox)란 프로세스에 의해 메시지를 넣을 수 있고 메시지를 제거할 수 있는 개체로 각 사서함에는 고유한 ID가 있다. 프로세스는 우편함을 공유하는 경우에만 통신할 수 있다. 우편함을 새로 만들고 이를 통해 메시지를 보내고 받은 다음 우편함을 파괴하는 방식으로 운영이 된다. 프리미티브는 다음과 같이 정의된다.
- send(A, message) - 사서함 A로 메시지를 보낸다.
- receive(A, message) - 사서함 A에서 메시지를 받는다.
동기화(Synchronization)
메시지 전달은 봉쇄형(blocking) 또는 비봉쇄형(non-blocking) 일 수 있다. 봉쇄형(blocking)은 동기식(synchronous)으로 간주된다. 봉쇄형에서 보내기(send) 메시지가 수신될 때까지 발신자를 차단하고 수신(receive)은 메시지를 사용할 수 있을 때까지 수신자를 차단한다. 비봉쇄형(non-blocking)은 비동기식(asynchronous)으로 간주된다. 비봉쇄형에서는 보내기(send)는 발신자가 메시지를 보내고 계속하도록 하고 수신(receive)은 수신자가 유효한 메시지 또는 null을 수신하도록 한다.
핵심
- 프로세스는 실행 중인 프로그램이다. 여기에는 메모리 주소 공간(텍스트/데이터/스택/힙), 레지스터 및 기타 리소스(열린 파일, 세마포어 등)가 포함된다.
- 각 공정과 관련된 정보는 PCB(Process Control Block)에 저장된다.
- CPU 스케줄러는 다음에 실행할 프로세스를 선택하고 CPU를 할당한다.
- 부모 프로세스는 자식 프로세스를 만들고 자식 프로세스는 다른 프로세스를 만듭니다. 마지막으로 프로세스 트리가 형성된다.
- 많은 경우 프로세스는 서로 협력해야 한다.
'컴퓨터공학 > 운영체제' 카테고리의 다른 글
OS 6. 동기화 도구(Synchronization Tools) (0) | 2023.04.06 |
---|---|
OS 5. CPU 스케줄링 (0) | 2023.04.06 |
OS 4. Threads& Concurrency (0) | 2023.04.05 |
OS 2.운영 체제 구조 (0) | 2023.04.03 |
OS 1.기본개념 (0) | 2023.04.02 |
댓글