프로세스 개요
프로세스는 실행 중인 프로그램을 의미한다. 프로그램은 실행되기 전까지는 보조기억장치에 있는 데이터 덩어리 이지만, 실행하게된다면 메모리에 적재되면서 프로세스가 된다. 이 과정을 ‘프로세스를 생성했다’라고 표현한다.
프로세스 직접 확인하기
컴퓨터에는 다양한 프로세스가 실행되고있다. 각 프로세스는 운영체제의 도움을 받아 확인할 수 있다.
실행중인 프로세스중에서 사용자가 볼 수 있는 공간에서 실행되는 프로세스와 그렇지 않은 프로세스가 있다. 사용자가 보는 앞에서 실행되는 프로세스를 포그라운드 프로세스(foreground process)라고 하고, 후자는 백그라운드 프로세스(background process)라고 한다.
백그라운드 프로세스 중에는 사용자와 직접 상호작용할 수 있는 프로세스가 있고, 그렇지 않은 프로세스도 있다. 사용자와 상호작용하지 않고 정해진 일만 하는 프로세스를 유닉스 체계 운영체제에서는 데몬(demon)이라고 부르고, 윈도우 운영체제에서는 서비스(service)라고 부른다.
프로세스 제어 블록
모든 프로세스는 실행을 위해 CPU가 필요하지만, CPU 자원은 한정적이다. 그렇기에 돌아가면서 한정된 시간만큼 CPU를 이용한다. 프로세스가 CPU를 이용하다가 이용 시간이 끝났음을 알리는 인터럽트(타이머 인터럽트)가 발생하면 자신의 차례를 양보하고 다음 차례까 올 때까지 기다린다.
타이머 인터럽트는 클럭 신호를 발생시키는 장치에 의해 주기적으로 발생하는 하드웨어 인터럽트이다. 타임아웃 인터럽트라고도 불린다.
운영체제는 프로세스의 실행 순서를 관리하고, 프로세스에 CPU를 비롯한 자원을 배분한다. 이를 위해 운영체제는 프로세스 제어 블록(PCB; Process Control Block)을 이용한다.
PCB는 프로세스와 관련된 정보와 프로세스를 식벽하기 위한 정보를 저장하는 자료 구조이다. PCB는 커널 영역에 생성되며, 프로세스 생성 시에 만들어지고 실행이 끝나면 폐기된다.
PCB에 담기는 정보들은 다음과 같다.
- 프로세스 ID (PID; Process ID)
- PID는 프로세스를 식별하기 위해 부여하는 고유한 번호이다. 같은 프로그램이라도 두 번 실행하면 서로 다른 두 개의 PID가 부여된다.
- 레지스터 값
- 프로세스는 자신의 차례가 되면, 이전까지 작업부터 다시 실행해야한다. 따라서 레지스터의 중간값들을 모두 저장하고, 복원할 필요가 있다. 레지스터 값에는 해당 프로세스가 실행하며 사용했던 레지스터 값들이 담긴다.
- 프로세스 상태
- 프로세스가 어떤 상태인지 PCB에 기록되어야 한다. 입출력 장치를 사용하기 위해 기다리고있는지, CPU를 사용하기 위해 기다리고 있는지 등의 상태가 저장된다.
- CPU 스케줄링 정보
- 프로세스가 언제, 어떤 순서로 CPU를 할당받을지에 대한 정보이다.
- 메모리 관리 정보
- 프로세스가 메모리의 어느 주소에 저장되어있는지에 대한 정보가 저장되어야 한다. 베이스 레지스터, 한계 레지스터와 같은 정보가 여기에 저장되며, 프로세스의 주소를 알기 위한 또 다른 중요 정보중 하나인 페이지 테이블 정보도 PCB에 담긴다.
- 사용한 파일과 입출력장치 목록
- 프로세스가 실행 과정에서 특정 입출력장치나 파일을 사용하면, 해당 내용이 명시된다. 즉, 어떤 입출력장치가 이 프로세스에 할당되었고, 어떤 파일들을 열었는지에 대한 정보가 기록된다.
문맥 교환
하나의 프로세스에서 다른 프로세스로 실행 순서가 넘어갈때 일어나는 일을 알아보자.
예를들어 프로세스 A가 CPU를 할당받아 사용하다가 프로세스 B로 CPU 사용을 양보하는 상황이라고 가정해보자. 프로세스 A는 프로그램 카운터를 비롯한 각종 레지스터 값, 메모리 정보, 실행을 위해 열었던 파일이나 사용한 입출력장치 등 지금까지의 중간 정보를 백업해야한다.
이러한 중간 정보, 즉 하나의 프로세스 수행을 재개하기 위해 기억해야 할 정보를 문맥(context)이라고 한다. 하나의 프로세스 문맥은 해당 PCB에 표현되어 있다. PCB에 기록된 정보를 문맥이라고 봐도 무방하다.
프로세스가 전환되는 과정은, 이전 프로세스의 문맥을 PCB에 백업해두고 다음 프로세스의 문맥을 복구하며 진행된다. 이러한 일련의 과정을 문맥 교환(context switching)이라고 한다.
문맥 교환은 여러 프로세스가 끊임없이 빠르게 번갈아 가며 실행되는 원리이다. 문맥 교환이 자주 일어나면 그만큼 프로세스가 자주 번갈아가며 수행되기 때문에 사용자의 눈에서는 프로세스들이 동시에 실행되는것처럼 보인다.
문맥 교환을 너무 자주 하면 오버헤드가 발생할 수 있기 때문에 자주 일어난다고 해서 반드시 좋은것은 아니다.
프로세스의 메모리 영역
프로세스가 생성되면 커널영역에는 PCB가 생성된다. 이때 사용자 영역에도 메모리가 할당되는데, 크게 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 나뉜다.
- 코드 영역
- 코드 영역(code segment)은 텍스트 영역(text segment)이라고도 부른다. 여기에는 실행할 수 있는 코드, 즉 기계어로 이루어진 명령어가 저장된다. 코드 영역은 데이터가 아닌 CPU가 실행할 명령어가 담겨있기 때문에 쓰기가 금지되어있다. 즉 읽기 전용(read-only) 공간이다.
- 데이터 영역
- 데이터 영역(data segment)은 잠깐 썼다가 없앨 데이터가 아닌 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간이다. 대표적으로 전역 변수가 여기에 속한다.
- 코드 영역과 데이터 영역은 그 크기가 변하지 않는다. 프로그램을 구성하는 명령어가 바뀔 수 없으며, 데이터 영역에 저장된 내용은 프로그램이 실행되는 동안 유지되기 때문이다. 따라서 이 두 영역을 정적 할당 영역이라고 부른다. 반대로 힙과 스택 영역은 크기가 변할 수 있으므로 동적 할당 영역이라고 부른다.
- 힙 영역
- 힙 영역(heap segment)은 프로그래머가 직접 할당할 수 있는 저장 공간이다. 힙 영역에 할당했다면 언젠가는 반환해야한다. 메모리 공간을 반환한다는 의미는 ‘더 이상 해당 메모리 공간을 사용하지 않겠다’고 운영체제에 말해주는것과 같다.
- 메모리 공간을 반환하지 않는다면, 할당한 공간은 메모리 내에 계속 남아 메모리 낭비를 초래한다. 이런 문제를 메모리 누수(memory leak)라고 한다.
- 스택 영역
- 스택 영역(stack segment)은 데이터를 일시적으로 저장하는 공간이다. 데이터 영역에 담기는 값과 달리 일시적으로 사용하는 공간이다. 주로 매개변수와 지역 변수가 저장된다. 일시적으로 저장할 데이터는 스택 영역에 PUSH되고, 사용이 끝나면 POP됨으로써 스택 영역에서 사라진다.
- 힙 영역과 스택 영역은 실시간으로 그 크기가 변할 수 있기 때문에 동적 할당 영역이라고 부르며, 일반적으로 힙 영역은 메모리의 낮은 주소에서 높은 주소로 할당되고, 스택 영역은 높은 주소에서 낮은 주소로 할당된다. 그래야만 힙영역과 스택 영역에 데이터가 쌓여도 새롭게 할당되는 주소가 겹칠 일이 없다.
프로세스 상태와 계층 구조
프로세스 상태
컴퓨터는 여러 프로세스들을 빠르게 번갈아가며 실행시킨다. 그 과정에서 프로세스는 여러 상태를 거치며 실행되며, 운영체제는 프로세스의 상태를 PCB를 통해 인식하고 관리한다. 프로세스의 상태를 표현하는 방식은 운영체제마다 다르나, 프로세스가 가질 수 있는 대표적인 상태는 아래와 같다.
- 생성 상태
- 생성 상태(new)는 프로세스를 생성중인 상태이다. 막 메모리에 적재되어 PCB를 할당받은 상태로, 생성 상태를 거쳐 실행할 준비가 완료된 프로세스는 준비 상태가 되어 CPU의 할당을 기다린다.
- 준비 상태
- 준비 상태(ready)는 CPU를 할당받아 실행되기 위해 기다리고 있는 상태이다.
- 준비 상태인 프로세스가 실행 상태로 전환되는 것을 디스패치(dispatch)라고 한다.
- 실행 상태
- 실행 상태(running)는 CPU를 할당받아 실행 중인 상태를 의미한다. 실행 상태인 프로세스는 정해진 시간동안 CPU를 사용할 수 있으며, 할당된 시간이 끝나면 준비상태가 된다. 만약 실행 도중 입출력장치를 사용하여 입출력장치 작업이 끝날 때 까지 기다려야 한다면 대기 상태가 된다.
- 대기 상태
- 대기 상태(blocked)는 입출력 작업을 요청한 프로세스가 입출력을 끝낼 때 까지 기다리는 상태를 의미한다. 입출력 작업이 완료되면 준비 상태가 된다.
- 종료 상태
- 종료 상태(terminated)는 프로세스가 종료된 상태이다. 프로세스가 종료되면 운영체제는 PCB와 프로세스가 사용한 메모리를 정리한다.
프로세스가 대기 상태가 되는 이유에 입출력 작업만 있는 것은 아니다. 조금 더 일반적으로 표현하자면, 특정 이벤트가 일어나길 기다릴 때 대기 상태가 된다. 대기상태가 되는 대부분의 원인이 입출력 작업이기 때문에 ‘입출력 작업을 하면 대기 상태가 된다’고 생각해도 무방하다.
프로세스 계층 구조
프로세스는 실행 도중 시스템 호출을 통해 다른 프로세스를 생성할 수 있다. 이때 새 프로세스를 생성한 프로세스를 부모 프로세스, 부모 프로세스에 의해 생성된 프로세스를 자식 프로세스라고 한다. 두 프로세스는 엄연히 다른 프로세스이기 때문에 각기 다른 PID를 가지며, 자식 프로세스는 부모 프로세스의 PID인 PPID(Parent PID)가 기록된다.
많은 운영체제는 프로세스가 프로세스를 낳는 계층적인 구조로써 프로세스들을 관리한다. 컴퓨터가 부팅될 때 실행되는 최초의 프로세스가 자식 프로세스를 생성하고, 자식 프로세스도 또다른 자식 프로세스를 생성하는 방식으로 동작한다. 이를 프로세스 계층 구조라고 한다.
프로세스 생성 기법
이하 내용은 윈도우 운영체제와는 관련이 없지만, 수많은 운영체제의 핵심 개념이니 알아두자.
부모 프로세스를 통해 생성된 자식 프로세스는 복제와 옷갈아입기를 통해 실행된다. 조금 더 정확하게, 부모 프로세스는 fork를 통해 자신의 복사본을 자식 프로세스로 생성해내고, 만들어진 복사본(자식 프로세스)은 exec를 통해 자신의 메모리 공간을 다른 프로그램으로 교체한다. fork와 exec는 시스템 호출이다.
- fork
- 자기 자신 프로세스의 복사본을 만든다. 복사본이기 때문에 부모 프로세스의 자원들(메모리 내의 내용, 열린 파일의 목록 등)이 자식 프로세스에 상속된다. 하지만 PID값이나, 저장된 메모리 위치는 다르다.
- exec
- 자신의 메모리 공간을 새로운 프로그램으로 덮어쓴다. 즉 새로운 프로그램 내용으로 전환하여 실행하는 시스템 호출이다. 이때 코드영역과 데이터 영역의 내용이 실행할 프로그램의 내용으로 바뀌고, 나머지 영역은 초기화된다.
부모가 자식 프로세스를 실행하며 프로세스 계층 구조를 이루는 과정은 fork와 exec가 반복되는 과정이다. 부모 프로세스가 자식 프로세스를 fork한 뒤에 부로 프로세스와 자식 프로세스 누구도 exec를 호출하지 않는 경우가 있는데, 이 경우 두 프로세스 모두 같은 코드를 병행하여 실행한다.
스레드
스레드(thread)란 프로세스를 구성하는 실행의 흐름 단위이다. 하나의 프로세스는 여러개의 스레드를 가질 수 있으며, 스레드를 이용하면 하나의 프로세스에서 여러 부분을 동시에 실행할 수 있다.
프로세스와 스레드
전통적인 관점에서는 하나의 프로세스는 한번에 하나의 일만 처리했다. 즉, 프로세스가 하나의 실행 흐름을 가지고 한 번에 하나의 부분만 실행하는 프로세스를 가정했다. 이런 프로세스를 단일 스레드 프로세스라고 볼 수 있다.
하지만 스레드라는 개념이 도입되면서 하나의 프로세스가 한 번에 여러 일을 동시에 처리할 수 있게 되었다. 이런 점에서 볼 때 스레드는 ‘프로세스를 구성하는 실행 단위’라고 볼 수 있다.
스레드의 구성요소는 다음과 같다.
- 스레드 ID
- 프로세스 내에서 스레드를 식별하기 위한 ID
- 레지스터 값
- 프로그램 카운터를 비롯한 레지스터 값
- 스택
- 스레드마다 각기 다른 데이터를 저장하기 위한 공간
프로세스의 스레드들은 실행에 필요한 최소한의 정보(레지스터 값, 스택)만을 유지한 채 프로세스 자원을 공유하며 실행된다. 즉, 스레드마다 코드,데이터,힙 영역이 있는것이 아니다.
멀티프로세스와 멀티스레드
- 멀티프로세스 : 여러 프로세스를 동시에 실행할 수 있다는 것
- 멀티스레드 : 여러 스레드로 프로세스를 동시에 실행하는 것
어떤 프로그램의 동작을 병렬적으로 처리하고 싶을땐, 멀티프로세스 혹은 멀티스레드를 통해서 처리할 수 있다. 이때 두 작업은 어떤 차이가 있을까?
우선 메모리의 관점에서, 프로세스가 여러개라면 코드, 데이터, 힙, 스택 영역을 포함하는 메모리가 프로세스의 개수만큼 적재된다. 반면, 하나의 프로세스를 여러개의 스레드가 관리한다면 메모리에는 하나의 프로세스만 적재되면 된다. 또 프로세스끼리는 기본적으로 자원을 공유하지 않지만, 스레드는 프로세스내의 자원을 공유한다.
스레드가 공유하는 자원으로는 코드, 데이터, 힙 영역과 열린 파일이 있다.
프로세스의 자원을 공유한다는 특성은 때론 단점이 될 수 있는데, 멀티 프로세스 환경에서는 하나의 프로세스에 문제가 발생해도 다른 프로세스는 지장이 없거나 적다. 반면, 멀티스레드 환경에서는 하나의 스레드에 문제가 생기면 프로세스 전체에 문제가 생길 수 있다.
'도서 정리 > 혼자 공부하는 컴퓨터 구조 + 운영체제' 카테고리의 다른 글
[혼자 공부하는 컴퓨터 구조 + 운영체제] 12강 프로세스 동기화 (0) | 2025.02.15 |
---|---|
[혼자 공부하는 컴퓨터 구조 + 운영체제] 11강 CPU 스케줄링 (0) | 2025.01.30 |
[혼자 공부하는 컴퓨터 구조 + 운영체제] 9강 운영체제 시작하기 (0) | 2025.01.30 |
[혼자 공부하는 컴퓨터 구조 + 운영체제] 8강 입출력장치 (0) | 2025.01.19 |
[혼자 공부하는 컴퓨터 구조 + 운영체제] 7강 보조기억장치 (0) | 2025.01.19 |