프로세스(Process)
Process is a program in execution
프로세스란 실행 중에 있는 프로그램을 의미한다.
메모리에 올라와 실행되고 있는 프로그램의 인스턴스(독립적인 객체)이며 스케줄링의 대상이 되는 작업(task)과 같은 의미로 쓰인다.
하드디스크에 있는 프로그램을 실행하면, 실행을 위해서 메모리 할당이 이루어지고, 할당된 메모리 공간으로 바이너리 코드가 올라가게 된다. 이 순간부터 프로세스라 불린다.
특징
- 각 프로세스는 별도의 주소 공간에서 실행되며, 각각 독립된 메모리 영역(Code, Data, Stack, Heap의 구조)을 할당받는다.
- Code : 프로그램을 실행시키면 실행파일 내에 존재하는 명령어가 메모리상에 올라가야 프로그램을 동작시킬 수 있다. 이 명령어들을 위해 존재하는 공간(쉽게 말해 소스코드가 올라간다고 생각하면 된다.)
- Data : 전역 변수나 static 변수의 할당을 위해 존재하는 공간
- Stack : 지역변수 할당과 함수 호출 시 전달되는 인자값들을 저장하기 위한 공간
- Heap : C의 malloc, calloc와 C++, Java의 new를 통한 동적 할당을 위해 존재하는 공간
- 한 프로세스는 다른 프로세스의 변수나 자료구조에 접근할 수 없다.
- 다른 프로세스에 접근하기 위해선 프로세스 간의 통신(IPC, inter-process-communication)을 사용해야 한다. ex) 파이프, 파일,소켓 등을 이용한 통신 방법 이용
- 프로세스 내부에는 최소 하나의 스레드(thread)를 가지고 있는데, 실제로는 스레드(thread) 단위로 스케줄링을 한다.
이 프로세스를 구성하는 요소들을 프로세스의 문맥(process contexts)라고 한다.
프로세스의 문맥(process context)
1. CPU 수행 상태를 나타내는 하드웨어 문맥. Program Counter 등의 register들 값을 포함한다.
PC는 다음에 수행할 명령어의 위치에 대한 정보(주소값)를 담고 있다.
CPU가 할당되었을 때, PC가 가리키는 부분부터 수행해나가면 되는 것이다.
즉, 프로세스가 실행될 부분이라고 생각할 수 있다.
2. 프로세스의 주소 공간을 포함한다.
즉, code, data, stack 각각의 공간에 어떠한 값이 들어있는가를 나타내는 주소값을 포함한다.
현재 변수의 값은 얼마인가? 메모리에는 어떤 내용들이 담겨져 있는가? 스택에는 어느 내용까지 쌓여있는가? 등에 대한 정보를 모두 담고 있다.
3. 프로세스 관련 커널 자료구조를 포함한다.
PCB(Process Control Block), Kernel Stack 등을 포함한다.
프로세스가 하나 시작될 때마다 운영체제에서는 그 프로세스를 관리하기 위해 PCB를 생성한다.
커널의 주소 공간에서 data에 해당하는 부분에 PCB를 저장해 둔다.
이는 운영체제가 이 프로세스를 어떻게 관리하는지, 어떻게 다루고 있는지 파악할 때 사용된다.
시스템 콜이 발생할 때, 어떤 프로세스가 시스템 콜을 했는지 알기 위해 커널 주소 공간의 Stack에는 시스템 콜을 한 프로세스의 커널 스택 값을 저장하고 있다.
운영체제가 문맥(context)을 알고 있어야 하는 이유
현재의 컴퓨터는 time sharing, multitasking 체제이다. 즉, 하나의 CPU가 여러 개의 프로세스를 동시에 수행한다. 짧은 속도로 여러 개의 프로세스를 수행해야 하는 CPU입장에서는 이전에 수행하고 있던 프로세스가 어디까지 진행되었는지를 알아야 할 필요가 생긴다. 그 어디까지 진행되었는지에 대한 값을 문맥 즉, context에 저장하고 있기 때문에 이 문맥을 CPU가 알고 있어야 한다.
프로세스 제어 블록(Process Control Block, PCB)
프로세스 제어 블록이란 프로세스를 관리하기 위해 필요한 프로세스 요소들의 자료구조이다.
운영체제가 현재 CPU 제어권을 다른 프로세스에게 넘겨줄 때 실행 중인 프로세스의 정보를 PCB에 저장한다.
CPU 제어권을 다시 넘겨받은 경우 PCB에 저장되어 있던 정보를 불러와 추후 작업을 실행한다.
PCB의 구성 요소
- 프로세스 식별자(Identifiter)
- 프로세스 상태(State)
- 우선순위(Priority) 같은 스케줄링에 대한 정보
- 메모리 포인터(Memory pointer)
- CPU 수행에 관련된 프로그램 카운터(Program counter), 각종 register 값
- I/O 상태정보(I/O status information)
- code, data, stack에 대한 정보
프로세스 상태
- 생성(New) : 프로세스가 생성 중
- 프로세스가 생성되었지만 실행가능한 프로세스 집합에 소속되지 못한 상태(프로그램이 메모리에 적재되지 않은 상태)
- 준비(Ready) : 프로세스가 설정되어 대기 중
- CPU를 할당받기 위해 준비 중인 상태(즉, Queue에서 대기하고 있는 상태를 의미한다. 이는 물리적인 메모리에 적재된 상태를 말한다.)
- 실행(Running) : 프로세스가 실행하는 중
- 프로세스가 CPU를 할당받아 기계어 명령어를 수행 중인 상태
- 대기(Block, wait, sleep) : 프로세스가 어떤 사건이 발생하기를 기다리고 있는 상태
- 당장 CPU를 할당해 줘도 instruction을 수행할 수 없는 상태를 말한다. 디스크에서 file을 읽어와야 하는 오래 걸리는 작업을 하고 있거나 다른 프로세스의 진행을 위해 일부러 재워둔 경우에 해당한다.
- 종료(Exit) : 프로세스가 실행 종료
- 프로세스가 실행 종료된 상태(프로그램이 메모리에서 해제된 상태) 프로세스가 종료되면 정리하는 작업을 진행하게 되는데 이 상태에 해당한다.
프로세스 상태 전이
- 디스패치(Dispatch) : 준비 -> 실행
- 타임 아웃(Time out) : 실행 -> 준비
- 대기(block) 또는 사건 준비(Event Wait) : 실행 -> 대기
- 깨움(Wake up) 또는 사건 발생(Event Occurs) : 대기 -> 준비
- new -> ready : new 상태에서 프로세스가 생성되면 OS 커널에 존재하는 Ready Queue에 올라가게 된다.
- ready -> running : Ready Queue에 있는 프로세스들을 OS가 프로세스 스케쥴링 알고리즘에 의해서 Running 상태로 가야 할 프로세스에게 CPU를 할당한다. 그러면 프로세스가 Running 상태가 된다.
- running -> ready : 현재 running 상태에 있는 프로세스 A보다 Ready Queue에서 대기하고 있는 프로세스 B가 우선순위가 높으면, preemptive schedule(선점형)인 경우 프로세스 A는 Ready 상태로 오게 되고 프로세스 B가 Running 상태가 되어 CPU를 할당받게 된다.
- running -> blocked : 현재 running 상태에 있는 프로세스 A에게 입출력(I/O) 이벤트가 발생했을 때 프로세스 A가 blocked 상태로 가게 된다.
- running -> terminate : 프로세스 종료.
Context Switch(문맥 교환)
Context Switch(문맥 교환)란 CPU를 어떤 프로세스에서 다른 프로세스로 넘겨주는 과정을 말한다.
System call이나 interrupt가 발생했다고 해서 반드시 Context Switch가 발생하는 것은 아니다. 프로세스 내부에서 System call을 요청하거나 interrupt가 외부에서 들어왔어도 운영체제에서의 일을 마치고 다시 할당되었던 CPU로 넘어가게 된다.
다른 프로세스로 넘어가는 과정은 상당한 오버헤드를 발생시킨다.
cache memory에 있던 진행하던 프로세스에 대한 cache를 모두 비워줘야 하기 때문이다. 그래서 문맥교환이 일어나는 상황은 크게 두 가지 경우이다.
Interrupt 중에서도 timer interrupt가 들어왔을 때와 I/O 요청 System Call이 들어왔을 때이다.
전환하는 동안에는 어떠한 유용한 작업도 불가능하다.
IPC(Inter Process Communication)
각 프로세스들은 별도의 공간에서 실행되기 때문에 한 프로세스에서 다른 프로세스의 메모리영역에 접근할 수 없다.
만약 프로세스가 다른 프로세스에 접근하려면 IPC를 사용해야 한다.
IPC에는 크게 두 가지 모델이 있는데 하나는 Message Passing모델이고 다른 하나는 Shared Memory 모델이다.
전자의 방식이 Message Passing 모델, 후자의 방식이 Shared Memory 모델이다.
Message Passing 모델의 경우 메시지를 전달하기 위해 커널을 들린다. 이 과정에서 User Level과 Kernel Level을 넘나 들게 되고 매번 System Call이 호출되어, 이에 따른 오버헤드가 발생한다.
Shared Memory 모델의 경우, 프로세스 사이에 공유 공간이 존재하기 때문에 시스템 호출이 필요하지 않다.
이로 인해 커널 의존성도 낮고 속도도 빠르다. 하지만 공유공간에 대한 제한이 존재한다.
스레드
프로세스 하나만을 사용해서 프로그램을 실행하기에는 메모리의 낭비가 발생한다.
스레드는 프로세스와 다르게 스레드 간 메모리를 공유하며 작동한다.
즉, 프로세스가 할당받은 자원을 이용하는 실행 흐름의 단위이다.
스레드는 운영체제의 스케줄러에 의해 독립적으로 관리될 수 있는 프로그래밍된 명령어의 가장 작은 시퀀스이다.
하나의 프로세스에는 하나 이상의 스레드를 갖고 있다.
위 그림은 프로세스와 스레드의 메모리 구조의 차이점을 보여준다.
오른쪽의 멀티 스레드를 사용한 방식을 주목하면 스레드는 stack 영역과 PC register 영역을 제외한 메모리 영역은 공유하게 된다.
스레드 간에 Code영역을 공유하기 때문에 한 프로세스 내부의 스레드들은 프로세스가 가지고 있는 함수를 자연스럽게 모두 호출할 수 있다.
뿐만 아니라 스레드는 Data, Heap영역을 공유하기 때문에 IPC 없이도 스레드 간의 통신이 가능하다. 동일한 프로세스 내부에 존재하는 스레드 A, B가 통신하기 위해 Heap 영역에 메모리 공간을 할당하고, 두 스레드가 자유롭게 접근한다고 생각하면 된다.
스레드는 프로레스처럼 스케줄링의 대상이다. 이 과정에서 컨텍스트 스위칭이 발생한다. 하지만 스레드는 공유하고 있는 메모리 영역 덕분에 컨텍스트 스위칭 때문에 발생하는 오버헤드가 프로세스에 비해 작다.
멀티 태스킹 vs 멀티 스레드
멀티 태스킹(Multi-Tasking)
CPU는 한 번에 하나의 프로세스만을 실핸한다. 예전의 컴퓨터는 이러한 이유로 하나의 프로세스를 실행하고 있을 때, 다른 프로세스는 실행하지 못했다. 즉, 게임을 하면서 카톡을 할 숭벗었다. 하지만 요즘 컴퓨터는 한번에 여러 프로세스를 실행할 수 있다. 이것을 가능하게 해주는 것이 멀티 태스킹이다.
멀티 태스킹이란 하나의 CPU에서, 여러 응용 프로그램이 동시에 실행되는 것처럼 보이도록 하는 시스템이다.
멀티 태스킹은 위 사진과 같은 스케줄링 알고리즘을 가진다.
각 프로세스의 응답 시간을 최소화하기 위하여 프로세스들을 엄청나게 잘게 쪼개서(ms 단위까지 쪼갬) 계속 번갈아가며 실행시키는 알고리즘이다. 그래서 우리의 눈에는 동시에 여러 프로세스가 실행되는 것처럼 보이는 것이다.
멀티 태스킹의 한계
- 하나의 프로세스가 동시에 여러 작업을 수행하지 못함.
- 하나의 프로세스가 하나의 작업밖에 하지 못하므로 속도의 아쉬움이 있다. 물론 하나의 프로그램을 여러 프로세로 띄워서 성능을 높일 수 있겠지만 이다음과 같은 한계는 극복하지 못한다.
- 프로세스의 컨텍스트 스위칭은 무거운 작업이다.
- 컨텍스트 스위칭이 되면, 이전의 프로세스의 상태를 PCB에 보관하고, 새로운 프로세스의 상태를 PCB에서 읽어와 CPU 내에 있는 레지스터에 적재해야 한다. 그러므로 이는 시스템에 많은 부담을 준다.
- 프로세스끼리 데이터 통신이 까다로움
- 프로세스는 독립적인 메모리 공간을 가진다. 여러 프로세스 간의 통신을 해야 하는 경우 IPC를 사용할 수 있지만 이것도 굉장히 번거로운 작업이다.
- 듀얼 코어 CPU가 등장했지만 잘 활용하지 못함.
- 하드웨어적으로는 듀얼 코어가 나왔지만 기존 멀티 태스킹 방식으로는 듀얼 코어의 장점을 100% 살리지 못함.
이러한 단점들을 보완하기 위해 나온 것이 스레드의 개념이다.
멀티 스레드
멀티 스레드는 하나의 프로세스가 여러 작업을 여러 스레드를 사용해 동시에 처리하는 것을 의미한다.
장점
- 응답성 : 프로그램의 일부분(스레드)이 중단되거나 긴 작업을 수행하더라도 프로그램의 수행이 계속되어 사용자에 대한 응답성이 증가한다. ex) 멀티 스레드가 적용된 웹 브라우저 프로그램에서 하나의 스레드가 이미지 파일을 로드하고 있는 동안, 다른 스레드에서 사용자와 상호작용 가능
- 경제성 : 프로세스 내 자원들과 메모리를 공유하기 때문에 메모리 공간과 시스템 자원 소모가 줄어든다. 스레드 간 통신이 필요한 경우에도 쉽게 데이터를 주고받을 수 있으며, 프로세스의 context switching과 달리 스레드 간의 context switching은 캐시 메모리를 비울 필요가 없기 때문에 더 빠르다.
- 멀티 프로세서 활용 : 다중 CPU 구조에서는 각각의 스레드가 다른 프로세서에서 병렬로 수행될 수 있으므로 병렬성이 증가한다.
단점
- 임계 영역(Critical Section) : 둘 이상의 스레드가 동시에 실행하면 문제를 일으키는 코드 블록.
- 공유하는 자원에 동시에 접근하는 경우, 프로세스와는 달리 스레드는 데이터와 힙 영역을 공유하기 때문에 어떤 스레드가 다른 스레드에서 사용 중인 변수나 자료구조에 접근하여 엉뚱한 값을 읽어오거나 수정할 수 있다. 따라서 동기화가 필요하다.
- 동기화를 위해 스레드의 작업 처리 순서와 공유 자원에 대한 접근을 컨트롤할 수 있다. (Java에서 Synchronized 키워드) 그러나 불필요한 부분까지 동기화를 하는 경우, 과도한 lock으로 인해 병목 현상을 발생시켜 성능이 저하될 가능성이 높기 때문에 주의해야 한다. 동기화 방법에서는 뮤텍스와 세마포어가 있다.
- context switching, 동기화 등의 이유 때문에 싱글 코어 멀티 스레딩은 스레드 생성 시간이 오히려 오버헤드로 작용해 단일 스레드 보다 느리다.
참고 자료
https://woovictory.github.io/2018/12/25/OS-Process/
https://prepinsta.com/operating-systems/process-control-block/
https://mooneegee.blogspot.com/2015/01/os-thread.html
'백엔드 > 운영체제' 카테고리의 다른 글
[운영체제] 캐시(Cache) (2) | 2023.04.25 |
---|---|
[운영체제] 메모리 계층 구조 (Memory Hierarchy) (0) | 2023.04.25 |
[운영체제] 운영체제(OS), 시스템 콜(System Call) (0) | 2023.04.25 |
[운영체제] 세마포어(semaphore) 뮤텍스(mutex) 모니터(Monitor) (0) | 2023.04.24 |
[운영체제] 경쟁 조건(Race Condition)과 임계 구역(Critical Section) (0) | 2023.04.22 |