프로세스
프로세스(Process)는 운영 체제에서 실행 중인 프로그램을 의미한다. 즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리를 할당받아 실행 중인 것을 의미한다. 프로세스는 프로그램이 실행될 때 메모리 상에 할당된 작업 공간과, 프로세스 실행에 필요한 자원들(레지스터, 파일, 네트워크 연결 등)을 포함한다.
프로세스는 프로그램 실행 과정에서 운영 체제로부터 할당받은 자원을 사용하여 작업을 수행하며, 프로세스는 하나 이상의 스레드(Thread)를 가지고 있을 수 있다. 각각의 프로세스는 독립적으로 실행되며, 다른 프로세스와는 메모리 공간을 공유하지 않는다.
1. 프로세스 메모리 구조
프로세스 메모리 구조는 운영 체제에서 프로세스를 실행하기 위해 메모리를 할당하는 방법을 의미한다.
대부분의 운영 체제는 프로세스 메모리 구조를 크게 네 가지 영역으로 나눈다.
- 코드(Code) 영역
프로그램 코드가 저장되는 영역으로 실행 파일의 명령어들이 위치하는 공간이다. 이 영역은 읽기 전용 메모리로서, 코드는 실행 중에 수정되지 않는다. - 데이터(Data) 영역
전역 변수와 정적 변수가 저장되는 영역이다. - 스택(Stack) 영역
함수의 호출과 관련된 지역 변수, 매개 변수, 복귀 주소 등이 저장되는 영역이다. 함수가 호출될 때마다 스택 프레임(Stack Frame)이 추가되고, 함수가 반환될 때 스택 프레임이 제거된다. - 힙(Heap) 영역
동적으로 할당된(malloc, new 등) 메모리가 저장되는 영역이다. 이 영역은 프로그램이 실행 중에 동적으로 메모리를 할당하거나 해제할 수 있기에 데이터 영역과 달리 프로그램 실행 중에 크기가 동적으로 변할 수 있다.
이렇게 나누어진 프로세스 메모리 구조는 운영 체제에서 메모리를 효율적으로 관리하고, 각각의 프로세스가 서로 독립적으로 실행될 수 있도록 한다.
2. 프로세스의 상태 변화와 PCB
프로세스는 다음과 같은 상태를 가질 수 있다.
- new(생성) : 프로세스가 생성되어, OS 커널의 Ready Queue에 올라가 있는 상태
- ready(준비 중) : 프로세스가 CPU로부터 메모리 공간을 할당받길 기다리는 상태
- running(실행 중) : 명령어들이 실행되는 상태 (interrupt이 발생하면 ready 상태로 전이, 실행을 끝마치면 exit 되어 terminated 상태로 전이, I/O나 event가 발생하면 waiting 상태로 전이)
- waiting(대기 중) : I/O 나 특정 event가 완료되길 기다리는 상태
- terminated(종료) : 프로세스가 실행을 마친 상태
프로세스가 상태를 변경할 때는 프로세스 상태 전이(Process State Transition)가 발생하며, 이때 운영 체제는 해당 프로세스를 추적하기 위한 정보를 Process Control Block(PCB)에 저장한다.
PCB는 운영 체제가 프로세스를 추적하고 관리하기 위해 사용하는 자료구조로서, 각각의 프로세스마다 PCB가 하나씩 존재하며 다음과 같은 정보가 저장된다.
- 프로세스 상태(Process State) - running, ready, waiting 등의 상태
- 프로그램 카운터(Program Counter) - 다음에 실행될 명령어의 주소
- 레지스터 값 - 프로세스가 현재까지 계산한 값
- 스케줄링 정보 - 프로세스의 우선순위, CPU 시간 할당량 등
- 메모리 관리 정보 - 프로세스가 사용하는 메모리 영역 정보
- 입출력 상태 정보 - 프로세스가 사용하는 입출력 장치 상태 정보
PCB는 프로세스가 상태를 변경할 때마다 업데이트되며, 프로세스가 종료될 때까지 유지된다. 이를 통해 운영 체제는 프로세스를 추적하고 관리함으로써 다른 프로세스와도 자원을 공유하며 안정적으로 운영될 수 있게 한다.
예를 들어, 프로세스가 waiting 상태에서 I/O 작업을 완료하면, 해당 프로세스의 PCB는 ready 상태로 변경되며, 운영 체제는 스케줄링 알고리즘에 따라 다음에 실행될 프로세스를 선택하여 CPU를 할당한다.
3. 프로세스 문맥 교환 (context-switching)
프로세스 문맥 교환(Context Switching)은 CPU가 실행 중인 프로세스를 일시 중지하고, 다른 프로세스를 실행하는 과정을 말한다. 이 과정에서 운영 체제가 현재 프로세스의 PCB 정보를 저장하고, 다음 실행할 프로세스의 PCB 정보를 불러온다.
프로세스 문맥 교환은 다음과 같은 경우에 일어난다.
- 현재 실행 중인 프로세스가 입출력 작업을 수행해야 할 경우 (해당 작업이 완료될 때까지 다른 프로세스를 실행시키면서 기다려야 하기 때문)
- 현재 실행 중인 프로세스보다 앞선 우선순위를 가진 프로세스가 실행되어야 할 경우
- 프로세스에 할당된 CPU 사용 시간이 끝났을 경우
프로세스 문맥 교환이 발생하면, 현재 실행 중인 프로세스는 CPU를 해제하고 PCB에 현재 상태를 저장한다. 그리고 운영 체제는 스케줄링 알고리즘에 따라 다음 실행할 프로세스를 선택하고, 해당 프로세스의 PCB 정보를 불러와 CPU를 할당한다. 이때, 불러온 PCB 정보를 기반으로 CPU가 실행을 시작한다.
프로세스 문맥 교환이 빈번하게 발생하면, 시스템 성능에 영향을 미칠 수 있다. 따라서, 운영 체제는 문맥 교환이 발생하는 경우를 최소화하고, 효율적으로 스케줄링하여 시스템 성능을 최적화하도록 설계되어 있다.
4. 프로세스 간 의사소통 (Inter-Process Communication, IPC)
프로세스 간 의사소통(Inter-Process Communication, IPC)은 서로 다른 프로세스 간에 데이터를 주고받을 수 있도록 하는 기능이다. IPC를 사용하면 프로세스 간의 동기화, 데이터 공유, 작업 분배 등을 효율적으로 수행할 수 있다.
IPC는 크게 두 가지 방식으로 나뉜다.
- 메시지 큐(Message Queue): 메시지 큐는 특정 큐에 데이터를 넣고, 다른 프로세스가 그 큐에서 데이터를 받아오는 방식이다. 메시지 큐를 사용하면, 데이터를 받아올 프로세스가 준비되지 않은 상황에서도 데이터를 안전하게 전송할 수 있다.
- 공유 메모리(Shared Memory): 공유 메모리는 여러 프로세스가 메모리 공간을 공유하여 데이터를 주고받는 방식이다. 공유 메모리를 사용하면 데이터를 공유하는 프로세스 간의 속도가 빠르며, 데이터 복사를 하지 않기 때문에 성능이 우수하다.
위의 두 가지 방법 외에 세마포어(Semaphore)와 같은 동기화 기법을 이용하여 IPC를 구현할 수 있다. 세마포어는 프로세스 간에 공유된 자원을 안전하게 사용하기 위한 동기화 기법으로, 임계 구역(Critical Section)에서 상호 배제를 보장한다.
IPC는 프로세스 간의 데이터 전송 및 동기화를 위해 사용되며, 멀티프로세싱, 멀티스레딩 등의 환경에서 중요한 역할을 한다.
스레드
스레드(thread)는 프로세스 내에서 실제로 작업을 실행하는 단위를 말한다. 하나의 프로세스는 여러 개의 스레드를 가질 수 있다.
스레드를 사용하면 동시에 여러 작업을 처리할 수 있다. 예를 들어, 웹 브라우저에서 여러 개의 탭을 열어서 각각 다른 웹 페이지를 로드할 때, 각 탭은 별도의 스레드로 실행된다. 이렇게 하면 한 탭에서 웹 페이지를 로드하는 작업이 끝나기를 기다리지 않고, 다른 탭에서도 동시에 웹 페이지를 로드할 수 있다.
1. 스레드의 구조와 공유 영역
스레드는 다음과 같은 구조를 가진다.
- 스택(Stack) : 스레드가 실행되면서 사용하는 지역 변수, 매개 변수, 함수 호출 정보 등을 저장하는 메모리 영역이다. 스택은 각 스레드마다 독립적으로 할당됩니다.
- PC(Program Counter) : 다음에 실행할 명령어의 주소를 저장하는 레지스터이다.
- 레지스터(Register) : 스레드가 실행되는 동안 사용하는 CPU 내부의 레지스터로, 데이터를 저장하거나 처리하는 데 사용된다.
- 우선순위(Priority) : 스레드가 실행되는 우선순위를 나타내는 값으로, 운영체제에 의해 관리된다.
스레드는 공유 메모리를 이용하여 데이터를 주고받는다. 공유 메모리는 여러 스레드에 의해 동시에 접근 가능한 메모리 영역으로 Code, Data, Heap으로 이루어진다. (프로세스의 Code, Data, Heap이다.)
공유 메모리를 사용할 때는 스레드 간의 데이터 동기화를 위해 동기화 기법을 사용합니다. 동기화 기법에는 뮤텍스, 세마포어, 경쟁조건 등이 있다. 이러한 동기화 기법을 사용하여 여러 스레드가 동시에 공유 메모리에 접근하더라도 데이터의 일관성과 안정성을 보장한다.
2. 싱글 스레드 vs 멀티 스레드
싱글 스레드와 멀티 스레드는 프로그램의 동작 방식에서 큰 차이점을 보인다.
먼저 싱글 스레드는 한 번에 하나의 작업만 처리할 수 있습니다. 즉, 프로그램이 실행되는 동안 한 번에 하나의 코드 블록만 실행되며, 다른 작업을 수행하려면 이전 작업이 완료될 때까지 기다려야 한다. 싱글 스레드는 간단하고 구현하기 쉬우며, 실행 순서가 일관적이므로 디버깅하기 쉽습니다. 하지만, 하나의 작업이 끝날 때까지 기다려야 하기 때문에 대용량 데이터 처리나 복잡한 작업의 경우 처리 속도가 느리다.
반면, 멀티 스레드는 여러 개의 스레드를 사용하여 한 번에 여러 작업을 처리할 수 있다. 멀티 스레드는 프로그램 실행 중에 여러 개의 코드 블록이 동시에 실행되므로, 처리 속도가 빠르고, 대용량 데이터 처리나 복잡한 작업도 빠르게 처리할 수 있다. 하지만, 스레드 간의 동기화 문제와 데드락 등의 문제가 발생할 수 있으며, 멀티 코어 CPU를 사용하지 않는 경우에는 스레드 간의 경쟁이 일어나 속도가 느려질 수 있다.
따라서 프로그램의 용도와 요구사항에 따라 싱글 스레드와 멀티 스레드 중 적절한 방식을 선택하여 사용해야 합니다.
3. 프로세스 내에서 여러 스레드를 사용하는 이유
프로세스 내에서 여러 개의 스레드를 사용하는 이유는 다음과 같다.
- 병렬성(Parallelism) : 여러 개의 스레드를 사용함으로써 동시에 여러 작업을 수행하여 처리 속도를 높일 수 있다. 따라서, 대용량 데이터 처리나 병렬 처리가 필요한 작업에 유리하다.
- 응답성(Responsiveness) : 사용자의 입력에 빠르게 반응할 수 있다. 예를 들어, 사용자가 프로그램에서 작업을 실행하는 동안 다른 스레드가 백그라운드에서 작업을 수행하면, 사용자는 하나의 작업이 완료될 때까지 기다릴 필요 없이 다른 작업을 수행할 수 있다.
- 자원 공유(Resource Sharing) : 스레드는 프로세스 내에서 메모리, 파일 등의 자원을 공유할 수 있다. 이를 통해, 데이터를 빠르게 공유하고, 메모리 사용량을 줄일 수 있다.
- 코드 간소화(Code Simplification) : 스레드를 사용하면, 복잡한 프로그램을 작은 코드 블록으로 분할하여 관리할 수 있다. 이를 통해 코드의 가독성과 유지 보수성이 증가하며, 개발 시간을 단축시킬 수 있다.
- 컴퓨터 자원 효율성(Efficient Resource Usage) : 하나의 프로세스 내에서 여러 개의 스레드를 사용하면, 프로세스 간의 문맥 교환 비용을 줄일 수 있다. 또한 여러 개의 스레드를 사용하는 것이 여러 개의 프로세스를 사용하는 것보다 적은 메모리를 사용하므로, 컴퓨터 자원을 효율적으로 사용할 수 있다.
프로세스 vs 스레드
프로세스와 스레드는 모두 프로그램 실행의 단위입니다. 하지만 프로세스와 스레드는 다음과 같은 차이점이 존재한다.
- 정의 : 프로세스는 실행 중인 프로그램의 인스턴스를 의미하며, 스레드는 프로세스 내에서 실행되는 여러 실행 단위를 의미한다.
- 자원 : 프로세스는 운영체제로부터 자원(메모리, 파일 등)을 할당받는 독립적인 단위이다. 스레드는 프로세스 내에서 생성되는 실행 단위이므로 프로세스가 할당받은 자원을 공유한다.
- 경량성 : 프로세스는 자체적으로 메모리 공간과 자원을 할당받아 실행되기 때문에, 비교적 무겁고 느린 편이다. 반면에 스레드는 프로세스 내에서 자원을 공유하고 경량화되어 있으므로, 생성과 문맥 전환 비용이 낮다.
- 독립성 : 프로세스는 운영체제로부터 독립적으로 자원을 할당받기 때문에, 다른 프로세스와 독립적으로 실행된다. 반면에 스레드는 프로세스 내에서 실행되기 때문에, 같은 프로세스 내의 다른 스레드와 상호 작용할 수 있다.
- 통신 : 프로세스는 IPC(Inter-Process Communication)와 같은 별도의 메커니즘을 사용하여 다른 프로세스와 통신해야 한다. 반면에 스레드는 프로세스 내의 공유 메모리를 통해 다른 스레드와 직접 통신할 수 있다.
프로세스 동기화 기법
프로세스 동기화 기법은 여러 개의 프로세스나 스레드가 공유 자원을 사용할 때 발생할 수 있는 문제를 해결하기 위해 사용하는 방법이다.
공유 자원에 대한 여러 프로세스나 스레드의 동시 접근은 경쟁 상태(race condition)와 같은 문제를 발생시킬 수 있다. 이러한 문제 발생 시에 공유 자원의 일관성을 유지하고 경쟁 상태를 해결하기 위한 기법들이 프로세스 동기화 기법이다.
프로세스 동기화 기법으로는 상호 배제(mutex), 세마포어(semaphore), 모니터(monitor) 등이 있다. 이러한 기법들은 공유 자원에 대한 접근을 제어하고, 경쟁 상태를 방지하여 프로세스 간의 동기화를 유지한다.
이를 통해 안정적인 프로그램 실행을 보장하고, 데드락(deadlock)과 같은 문제를 예방할 수 있다.
1. 임계 영역
임계 영역(Critical Section)은 다수의 프로세스 또는 스레드가 동시에 접근해서는 안 되는 공유 자원을 접근하는 코드 영역을 말한다. 즉, 한 순간에 오직 하나의 프로세스 또는 스레드만이 임계 영역에 진입하여 공유 자원을 사용할 수 있도록 독점을 보장한다.
임계 영역은 프로세스 동기화 기법을 사용하여 보호된다. 이를 위해 일반적으로 상호 배제(Mutual Exclusion)라는 기법을 사용한다. 상호 배제는 여러 프로세스 또는 스레드가 임계 영역에 진입하는 것을 방지하는 기법으로, 동시에 하나의 프로세스 또는 스레드만이 임계 영역에 접근할 수 있도록 보장한다.
임계 영역은 공유 자원에 대한 접근이 필요한 코드 영역이므로, 제대로 보호되지 않으면 여러 프로세스 또는 스레드가 동시에 공유 자원에 접근하여 데이터 불일치 문제나 예상치 못한 결과를 초래할 수 있다.
따라서 임계 영역에 대한 프로세스 동기화 기법을 적절히 사용하여 안전하게 공유 자원에 접근하도록 해야 한다.
2. 뮤텍스 vs 세마포어
뮤텍스(Mutex)와 세마포어(Semaphore)는 프로세스 동기화 기법 중 하나로, 임계 영역에 대한 상호 배제를 구현하기 위해 사용된다. 하지만 둘은 구현 방식과 사용 목적에서 차이가 있다.
먼저, 뮤텍스는 임계 영역에 오직 하나의 스레드만이 들어갈 수 있도록 보호한다. 뮤텍스 객체는 보호하고자 하는 공유 자원에 대응되는 하나의 이진 플래그(flag)로 구성됩니다. 임계 영역에 들어가기 전 뮤텍스를 획득한 스레드는 공유 자원에 대한 접근 권한을 얻고, 임계 영역을 벗어나기 전 뮤텍스를 반납해야 다른 스레드가 임계 영역에 접근할 수 있다.
반면, 세마포어는 뮤텍스와 달리, 임계 영역에 동시에 접근할 수 있는 스레드의 수를 제한하는 방식으로 동작한다.
세마포어 객체는 카운터와 큐(queue)로 구성된다. 카운터 값은 현재 임계 영역에 접근 가능한 스레드의 수를 나타내고, 큐에는 접근 권한을 기다리는 스레드들의 목록이 저장된다. 임계 영역에 들어가기 전 세마포어를 획득한 스레드는 카운터 값을 1 감소시키고, 임계 영역을 벗어나면 카운터 값을 1 증가시키며 다른 스레드가 접근할 수 있도록 한다.
즉, 뮤텍스는 오직 하나의 스레드만이 접근할 수 있도록 보호하고, 세마포어는 임계 영역에 접근 가능한 스레드의 수를 제한한다. 그래서 뮤텍스는 상호 배제를 구현할 때 사용되고, 세마포어는 자원 관리와 프로세스 동기화에 활용된다.
참고
'개발 > CS' 카테고리의 다른 글
(CS) ArrayList, LinkedList, Stack, Queue, Deque (0) | 2023.04.05 |
---|---|
(CS) 캐시 메모리, 메모리 관리 기법, 페이지 교체 알고리즘 (1) | 2023.03.27 |
(CS) 데드락, 기아 상태, 스케줄링 (0) | 2023.03.22 |
(CS) 프로세서, 메모리, MMU, 시스템 버스 (2) | 2023.03.11 |
URL vs URI (1) | 2023.01.27 |