작성자 프로필
mouuaw
코드 깎는 다람쥐
2023.08.05

프로세스 개요

지금까지는 단순히 '실행 중인 프로그램'이라고 표현했지만, 이 프로그램을 프로세스라고 합니다. 프로그램은 실행되기 전까지는 그저 보조기억장치에 있는 데이터 덩어리일 뿐이지만, 보조기억장치에 저장된 프로그램을 메모리에 적재하고 실행하는 순간 그 프로그램은 프로세스가 됩니다. 그리고 이 과정을 '프로세스를 생성한다'라고 표현합니다.

프로세스 직접 확인하기

윈도우에서는 작업 관리자의 [프로세스] 탭에서 확인할 수 있고, 유닉스 제계의 운영체제에서는 ps 명령어로 확인할 수 있습니다.

각 운영체제의 방법으로 프로세스를 확인해보면 사용자가 실행한 프로세스 외에도 알 수 없는 여러 프로세스가 실행되고 있는 것을 볼 수 있습니다. 프로세스 중에는 사용자가 볼 수 있는 공간에서 실행되는것이 있는데 이를 포그라운드 프로세스라고 하고, 보이지 않는 공간에서 실행되는건 백그라운드 프로세스라고 합니다.

백그라운드 프로세스 중에는 사용자와 상호작용할 수 있는 백그라운드 프로세스도 있지만, 사용자와 상호작용하지 않고 정해진 일만 수행하는 백그라운드 프로세스도 있습니다. 이러한 백그라운드 프로세스를 유닉스 체계 운영체제에서는 데몬이라고 부르고, 윈도우 운영체제에서는 서비스라고 부릅니다.

프로세스 제어 블록

모든 프로세스는 실행을 위해 CPU를 필요로 하지만, CPU 자원은 한정되어 있습니다. 그렇기에 프로세스들은 차례대로 돌아가며 한정된 시간만큼만 CPU를 이용합니다. 운영체제는 빠르게 번갈아 수행되는 프로세스의 실행 순서를 관리하고, 프로세스에 CPU를 비롯한 자원을 배분합니다. 이를 위해 운영체제는 프로세스 제어 블록(PCB)을 이용합니다.

프로세스 제어 블록은 프로세스와 관련된 정보를 저장하는 자료 구조입니다. 프로세스 제어 블록에는 해당 프로세스를 식별하기 위해 꼭 필요한 정보들이 저장됩니다.

메모리는 커널 영역과 사용자 영역으로 나눠져 있는데 PCB는 커널 영역에 생성됩니다. 운영체제는 수많은 프로세스들 사이에서 PCB로 특정 프로세스를 식별하고 해당 프로세스를 처리하는 데 필요한 정보를 판단합니다.

PCB는 프로세스 생성 시에 만들어지고 실행이 끝나면 폐기됩니다. 다시 말해 '새로운 프로세스가 생성되었다'는 말은 '운영체제가 PCB를 생성했다'는 말과 같고, '프로세스가 종료되었다'는 말은 '운영체제가 해당 PCB를 폐기했다'는 말과 같습니다.

이런 PCB에 담기는 정보는 다음과 같습니다

프로세스 ID

프로세스 ID(PID)는 특정 프로세스를 식별하기 위해 부여하는 고유한 번호입니다.

레지스터 값

프로세스는 자신의 실행 차례가 돌아오면 이전까지 사용했던 레지스터의 중간값들을 모두 복원합니다. 그래야만 이전까지 진행했던 작업들을 그대로 이어 실행할 수 있으니까요. 그래서 PCB 안에는 해당 프로세스가 실행하며 사용했던 프로그램 카운터를 비롯한 레지스터 값들이 담깁니다.

프로세스 상태

현재 프로세스가 어떤 상태인지도 PCB에 기록되어야 합니다. 현재 프로세스가 입출력장치를 위해 대기중인지, CPU를 이용하고 있는지 등의 프로세스 상태 정보가 저장됩니다.

CPU 스케쥴링 정보

프로세스가 언제, 어떤 순서로 CPU를 할당받을지에 대한 정보도 PCB에 기록됩니다.

메모리 관리 정보

프로세스마다 메모리에 저장된 위치가 다릅니다. 그래서 PCB에는 프로세스가 어느 주소에 저장되어 있는지에 대한 정보가 있어야 합니다. PCB에는 베이스 레지스터, 한계 레지스터 값과 같은 정보들이 담깁니다. 또한 프로세스의 주소를 알기 위한 테이블 정보도 PCB에 담깁니다.

사용한 파일과 입출력장치 목록

프로세스가 실행 과정에서 특정 입출력장치나 파일을 사용하면 PCB에 해당 내용이 명시됩니다. 즉, 어떤 입출력장치가 이 프로세스에 할당되었는지, 어떤 파일들을 열었는지에 대한 정보들이 PCB에 기록됩니다.

문맥 교환

하나의 프로세스에서 다른 프로세스로 실행 순서가 넘어가면 어떤 일이 일어날까요? 프로세스의 실행 순서가 넘어가기전의 프로세스를 A라고 하고 그 다음에 CPU를 할당받는 프로세스를 B라고 합시다.

A는 실행중에 사용하던 프로그램 카운터, 각종 레지스터 값, 메모리 정보, 파일이나 입출력장치등의 정보를 백업합니다. 이렇게 다시 프로세스를 재개하기 위해 필요한 정보들을 문맥(context)이라고 합니다.

하나의 프로세스 문맥은 해당 프로세스의 PCB에 표현되어 있습니다. PCB에 기록되는 정보들을 문맥이라고 봐도 무방합니다.

이처럼 기존 프로세스의 문맥을 PCB에 백업하고, 새로운 프로세스들을 실행하기 위해 문맥을 PCB로 부터 복구하여 새로운 프로세스를 실행하는 것을 문맥 교환이라고 합니다.

프로세스의 메모리 영역

프로세스가 생성되면 커널 영역에 PCB가 생성됩니다. 하나의 프로세스는 사용자 영역에 크게 코드 영역, 데이터 영역, 힙 영역, 스택 영역으로 나뉘어 저장됩니다.

코드 영역

코드 영역은 텍스트 영역이라고도 부릅니다. 이곳에는 말 그대로 실행할 수 있는 코드, 즉 기계어로 이루어진 명령어가 저장됩니다. 코드 영역에는 데이터가 아닌 CPU가 실행할 명령어가 담겨 있기 때문에 쓰기가 금지되어 있습니다. 다시 말해 코드 영역은 읽기 전용 공간 입니다.

데이터 영역

데이터 영역은 잠깐 썻다가 없앨 데이터가 아닌 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간입니다. 이런 데이터로는 전역 변수가 대표적입니다.

코드 영역과 데이터 영역은 그 크기가 변하지 않습니다. 프로그램을 구성하는 명령어들이 갑자기 바뀔 일이 없으니 코드 영역의 크기가 변할 리 없고, 데이터 영역에 저장될 내용은 프로그램이 실행되는 동안에만 유지될 데이터니까요. 그래서 코드 영역과 데이터 영역은 '크기가 고정된 영역'이라는 점에서 정적 할당 영역이라고도 부릅니다. 반면 힙 영역과 스택 영역은 프로세스 실행 과정에서 그 크기가 변할 수 있는 영역입니다. 그래서 이 두 영역을 동적 할당 영역이라고도 부르지요

힙 영역

힙 영역은 프로그램을 만드는 사용자, 즉 프로그래머가 직접 할당할 수 있는 저장 공간입니다. 프로그래밍 과정에서 힙 영역에 메모리 공간을 할당했다면 언젠가는 해당 공간을 반환해야 합니다. 메모리 공간을 반환하지 않는다면 할당한 공간은 메모리 내에 계속 남아 메모리 낭비를 초래합니다. 이런 문제를 메모리 누수라고 합니다.

스택 영역

스택 영역은 데이터를 일시적으로 저장하는 공간입니다. 잠깐 쓰다가 없어질 값들이 저장되는 공간이지요. 이런 데이터로는 함수의 실행이 끝나면 사라지는 매개 변수, 지역 변수가 대표적입니다.

일시적으로 저장할 데이터는 스택영역에 PUSH되고, 더 이상 필요하지 않은 데이터는 POP됨으로써 스택 영역에서 사라집니다.

프로세스 상태와 계층 구조

프로세스는 모두 저마다의 상태가 있습니다. 운영체제는 이런 프로세스의 상태를 PCB에 기록하여 관리합니다. 그리고 많은 운영체제는 이처럼 동시에 실행되는 수많은 프로세스를 계층적으로 관리합니다. 이번 절에서는 프로세스들의 상태와 계층적 관리에 대해 자세히 알아보겠습니다.

프로세스 상태

생성 상태

프로세스를 생성 중인 상태를 생성 상태라고 합니다. 이제 막 메모리에 적재되어 PCB를 할당 받은 상태를 말합니다.

준비 상태

준비 상태는 당장이라도 CPU를 할당받아 실행할 수 있지만, 아직 자신의 차례가 아니기에 기다리고 있는 상태입니다.

실행 상태

CPU를 할당받아 실행 중인 상태를 의미합니다. 실행 상태인 프로세스는 할당된 일정 시간 동안만 CPU를 사용할 수 있습니다. 이때 프로세스가 할당된 시간을 모두 사용한다면 다시 준비상태가 되고, 실행 도중 입출력 장치를 사용하여 입출력 장치의 작업이 끝날 때까지 기다려야 한다면 대기 상태가 됩니다.

대기 상태

프로세스 실행 도중 입출력장치를 사용하는 경우, 입출력 작업을 요청한 프로세스는 입출력 작업이 끝날 때까지 기다려야 합니다. 이런 상태를 대기 상태라고 합니다.

종료 상태

종료 상태는 프로세스가 종료된 상태입니다. 프로세스가 종료되면 운영체제는 PCB와 프로세스가 사용한 메모리를 정리합니다.

프로세스 계층 구조

프로세스는 실행 도중 다른 프로세스를 생성할 수 있습니다. 이 때 새 프로세스를 생성한 프로세스를 부모 프로세스, 생성된 프로세스를 자식 프로세스라고 합니다.

부모, 자식 프로세스는 다른 프로세스이기에 각기 다른 PID를 가집니다. 일부 운영체제에서는 자식 프로세스의 PCB에 부모 프로세스의 PID인 PPID가 기록되기도 합니다.

많은 운영체제는 이처럼 프로세스가 프로세스를 낳는 계층적인 구조로써 프로세스들을 관리합니다. 이런 구조를 도표로 그리면 트리 구조가 나타나는데 이를 프로세스 계층 구조라고합니다.

프로세스 생성 기법

부모 프로세스가 자식 프로세스를 어떻게 만들어 내고, 자식 프로세스는 어떻게 자신만의 코드를 실행하는지 조금 더 자세히 알아봅시다.

부모 프로세스는 fork를 통해 자신의 복사본을 자식 프로세스로 생성해내고, 만들어진 복사본은 exec를 통해 자신의 메모리 공간을 다른 프로그램으로 교체합니다.

fork와 exec는 시스템 호출입니다. 부모 프로세스는 fork 시스템 호출을 통해 자신의 복사본을 자식 프로세스로 생성합니다. 즉, fork는 자기 자신 프로세스의 복사본을 만드는 시스템 호출입니다.

자식 프로세스는 부모 프로세스의 복사본이기 때문에 부모 프로세스의 자원들, 이를테면 메모리 내의 내용, 열린 파일의 목록 등이 자식 프로세스에 상속됩니다.

복사본이 만들어진 뒤에 자식 프로세스는 exec 시스템 호출을 통해 새로운 프로그램으로 전환됩니다. exec를 호출하면 코드 영역과 데이터 영역의 내용이 실행할 프로그램의 내용으로 바뀌고, 나머지 영역은 초기화됩니다.

스레드

스레드는 프로세스를 구성하는 실행 흐름의 단위입니다. 그리고 하나의 프로세스는 여러 개의 스레드를 가질 수 있습니다. 스레드를 이용하면 하나의 프로세스에서 여러 부분을 동시에 실행할 수 있습니다.

이번 절에서는 스레드와 멀티스레드가 무엇인지, 멀티스레드는 멀티프로세스와 어떤 차이가 있는지를 알아보겠습니다.

프로세스와 스레드

전통적인 관점에서 프로세스는 한 번에 하나의 일만 처리했습니다. 이렇게 '실행 흐름의 단위가 하나'인 프로세스들을 단일 스레드 프로세스라고 합니다.

하지만 스레드라는 개념이 도입되면서 하나의 프로세스가 한 번에 여러 일을 동시에 처리할 수 있게 되었습니다. 즉, 프로세스를 구성하는 여러 명령어를 동시에 실행할 수 있게 된 것이지요.

스레드는 프로세스 내에서 각기 다른 스레드 ID, 프로그램 카운터 값을 비롯한 레지스터 값, 스택으로 구성됩니다. 각자 프로그램 카운터 값을 비롯한 레지스터 값, 스택을 가지고 있기에 스레드마다 각기 다른 코드를 실행할 수 있습니다.

여기서 중요한 점은 스레드들은 실행에 필요한 최소한의 정보만을 유지한 채 프로세스 자원을 공유하며 실행된다는 점입니다.

각각의 스레드를 위해 코드/데이터/힙 영역이 따로 존재하지 않습니다.

멀티프로세스와 멀티스레드

컴퓨터는 실행 과정에서 여러 프로세스가 동시에 실행될 수 있고, 그 프로세스를 이루는 스레드는 여러 개 있을 수 있다고 했습니다. 이때 여러 프로세스를 동시에 실행하는 것을 멀티프로세스, 그리고 여러 스레드로 프로세스를 동시에 실행하는 것을 멀티스레드라고 합니다.

멀티프로세스와 멀티스레드의 차이는 무엇일까요? 프로세스끼리는 기본적으로 자원을 공유하지 않지만, 스레드끼리는 같은 프로세스 내의 자원을 공유합니다.

여러 프로세스를 병행 실행하는 것보다 여러 스레드를 활용하는 것이 메모리를 더 효율적으로 사용할 수 있겠지요.

프로세스의 자원을 공유한다는 특성은 때론 단점이 됩니다. 멀티 프로세스로 동작시킬 경우, 다른 프로세스가 문제가 발생하면 다른 프로세스에 지장이 없지만 멀티스레드 환경에선 하나의 스레드에 문제가 생기면 전체 프로세스에 문제가 발생합니다.

스터디 프로필
코드 깎는 다람쥐
의 다른 카테고리
0
👍0
👏0
🤔
댓글 작성