프로세스와 컴파일 과정

​ 프로세스는 컴퓨터에서 실행되고 있는 프로그램을 말합니다. 스레드는 프로세스 내 작업의 흐름을 지칭합니다.

image-20230722151336789

프로그램이 메모리에 올라가면 프로세스가 되는 인스턴스화가 일어납니다.

프로세스와 컴파일 과정

​ 컴파일이란 프로그램이 컴퓨터가 이해할 수 있는 기계어로 번역되어 실행될 수 있는 파일이 되는 것입니다. 프로그램의 컴파일 과정은 아래와 같습니다.

여기서 말하는 프로그램이란 C 언어 기반의 프로그램을 의미하며, 이는 별도의 컴파일 과정 없이 한 번에 한 줄씩 읽어들여서 실행하는 프로그램인 인터프리터 언어(파이썬 등)로 된 프로그램과는 다릅니다.

image-20230722151501849

  • 전처리 : 소스 코드의 주석을 제거하고 # include 등 헤더 파일을 병합하여 매크로를 치환합니다.
  • 컴파일러 : 오류 처리, 코드 최적화 작업을 하며 어셈블리어로 변환합니다.
  • 어셈블러 : 어셈블리어는 목적 코드(object code)로 변환됩니다.
  • 링커 : 프로그램 내에 있는 라이브러리 함수 또는 다른 파일들과 목적 코드를 결합하여 실행 파일을 만듭니다.
  • 라이브러리 : 라이브러리는 정적 라이브러리와 동적 라이브러리로 나뉩니다.
    • 정적 라이브러리는 프로그램 빌드 시 라이브러리가 제공하는 모든 코드를 실행 파일에 넣는 방식이며, 시스템 환경 등 외부 의존도가 낮고 코드 중복 등 메모리 효율성이 떨어지는 단점이 있습니다.
    • 동적 라이브러리는 프로그램 실행 시 필요할 때만 DLL이라는 함수 정보를 통해 참조하는 방식이며, 메모리 효율성에서의 장점과 외부 의존도가 높아진다는 단점이 있습니다.

# 프로세스의 상태

​ 프로세스는 저마다의 상태가 있습니다. 예를 들어서 윈도우 작업 관리자의 [자세히] 탭을 보면 프로세스의 상태가 보이죠.

image-20230722152756990

이런 상태 프로세스는 PCB 에 기록됩니다. PCB 는 뒤에 자세히 기술하겠습니다.

​ 컴퓨터는 여러 프로세스들을 빠르게 번갈아가면서 실행합니다. 그 과정에서 하나의 프로세스는 여러 상태를 거치며 실행되게 되는데요. 운영체제는 프로세스의 상태를 PCB 를 통해 인식하고 관리합니다. 프로세스 상태를 표현하는 방식은 대표적으로 아래와 같습니다.

image-20230722155117260

생성 상태

​ 프로세스를 생성 중인 상태를 생성상태(create) 라고 합니다. fork() 또는 exec() 함수를 통해 생성합니다. 이때 PCB가 할당됩니다.

fork() 는 부모 프로세스에서 자식 프로세스를 생성하는 함수이고 exec() 는 새로운 프로세스를 생성하는 함수입니다. 해당 함수는 뒤에서 더 자세히 알아보겠습니다.

대기 상태

​ 대기 상태(ready) 는 메모리 공간이 충분하면 메모리를 할당받고 아니면 아닌 상태로 대기하고 있으며 실행 차례가 되면 CPU 스케줄러로부터 CPU 소유권이 넘어오기를 기다리는 상태입니다.

준비 상태인 프로세스가 실행상태로 전환되는 것을 디스패치(dispatch) 라고 합니다.

대기 중단 상태

​ 대기 중단 상태(ready suspended) 는 메모리 부족으로 일시 중단된 상태입니다.

실행 상태

​ 실행 상태(running) 는 CPU 소유권과 메모리를 할당받고 인스트럭션을 수행 중인 상태를 의미합니다. 이때 프로세스가 할당된 시간을 모두 사용한다면 (타이머 인터럽트가 발생하면) 다시 준비상태가 되고, 실행 도중 입출력장치를 사용하여 입출력장치의 작업이 끝날 때까지 기다려야 한다면 중단 상태(blocked) 가 됩니다.

중단 상태

​ 중단 상태(blocked)는 어떤 이벤트가 발생한 이후 기다리며 프로세스가 차단된 상태입니다. I/O 디바이스에 의한 인터럽트로 이런 현상이 많이 발생하기도 합니다.

​ 입출력 작업은 CPU 에 비해 처리 속도가 느리기 때문에, 입출력 작업을 요청한 프로세스는 입출력장치가 입출력을 끝날 때까지(입출력 완료 인터럽트를 받을 때까지) 기다려야 합니다. 이렇게 기다리는 상태가 중단 상태입니다. 입출력 작업이 완료되면 해당 프로세스는 다시 준비 상태로 CPU 할당을 기다립니다.

일시 중단 상태

일시 중단 상태(blocked suspended)는 대기 중단과 유사합니다. 중단된 상태에서 프로세스가 실행되려고 했지만 메모리 부족으로 일시 중단된 상태입니다.

종료 상태

종료 상태(terminated)는 메모리와 CPU 소유권을 모두 놓고 가는 상태를 말합니다.

프로세스의 메모리 구조

​ 프로세스가 생성되는 커널 영역에는 PCB 가 생성되고 사용자 영역에는 프로세스가 배치됩니다.

image-20230722155805425

​ 사용자 영역의 메모리는 아래와 같이 동적영역의 스택, 힙 영역과 정적 영역인 데이터, 코드 영역으로 구분됩니다.

image-20230722155207992

스택 영역

​ 스택 영역(stack segment) 은 데이터를 일시적으로 저장하는 공간입니다. 스택에는 지역변수, 매개변수, 함수가 저장되고 컴파일 시에 크기가 결정되며 ‘동적’인 특징을 갖습니다.

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

​ 스택 영역은 함수가 함수를 재귀적으로 호출하면서 동적으로 크기가 늘어날 수 있는데, 이때 힙과 스택의 메모리 영역이 겹치면 안 되기 때문에 힙과 스택 사이의 공간을 비워 놓습니다. 따라서 힙 영역은 메모리가 낮은 주소에서 높은 주소로 할당되고, 스택 영역은 높은 주소에서 낮은 주소로 할당됩니다.

image-20230722161115896

힙 영역

​ 힙 영역(heap segment) 은 프로그래머가 직접 할당할 수 있는 저장 공간입니다. 힙 영역에 메모리 공간을 할당했다면 언전가는 해당 공간을 반환해야 합니다. 메모리 공간을 반환하는 의미는 ‘더 이상 해당 메모리 공간을 사용하지 않겠다’ 라고 운영체제에 말해주는 것과 같습니다.

​ 메모리 공간을 반환하지 않는다면 할당한 공간은 메모리 내에 계속 남아 메모리 낭비를 초래합니다. 이런 문제를 메모리 누수(memory leak) 라고 합니다.

데이터 영역

​ 데이터 영역(data segment) 은 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간입니다. 전역변수, 정적변수가 저장되고, 정적인 특징을 갖는 프로그램이 종료되면 사라지는 변수가 들어 있는 영역입니다.

​ 데이터 영역과 아래의 코드 영역은 그 크기가 변하지 않습니다. 그래서 해당 영역을 정적 할당 영역이라고 부릅니다.

코드 영역

​ 코드 영역(code segment) 은 프로그램에 내장되어 있는 소스 코드가 들어가는 영역입니다. 이 영역은 수정 불가능한 기계어로 저장되어 있으며 정적인 특징을 가집니다.

​ 해당 영역은 CPU 가 실행할 명령어가 담겨 있기 때문에 쓰기가 금지되어 있는 읽기 전용(read-only) 공간 입니다.

PCB

​ 모든 프로세스는 실행을 위해 CPU 를 필요로 하지만 CPU 자원은 한정되어 있습니다. 그렇기 때문에 프로세스들은 차례대로 돌아가면서 CPU 를 이용합니다.

​ 운영체제는 빠르게 번갈아 수행되는 프로세스의 실행순서를 관리하고 자원을 배분해야 하는데, 이를 위해 운영체제는 PCB(Process Control Block, 프로세스 제어 블록) 을 이용합니다. PCB 는 메모리 영역 중 커널 영역에 생성됩니다.

​ PCB 는 운영체제에서 프로세스에 대한 메타데이터를 저장한 데이터를 말하며 해당 프로세스를 식별하기 위해 꼭 필요한 정보들이 저장됩니다.

PCB 의 구조

PCB 는 프로세스 스케줄링 상태, 프로세스 ID 등의 다음과 같은 정보로 이루어져 있습니다.

  • 프로세스 스케줄링 상태 : 준비, 일시중단 등 프로세스가 어떤 상태인지 저장되어 있습니다.
  • 프로세스 ID : 프로세스 ID, 해당 프로세스의 자식 프로세스 ID 가 있습니다. 같은 프로그램이라도 두 번 실행하면 PID 가 다른 두 개의 프로세스가 생성됩니다.
  • 프로세스 권한 : 컴퓨터 자원 또는 I/O 디바이스에 대한 권한 정보입니다.
  • 프로그램 카운터 : 프로세스에서 실행해야 할 다음 명령어의 주소에 대한 포인터입니다.
  • CPU 레지스터 : 프로세스를 실행하기 위해 저장해야 할 레지스터에 대한 정보입니다. 프로세스는 자신의 실행 차례가 돌아오면 이전까지 사용했던 레지스터의 중간값들을 모두 복원합니다. 그래야만 이전까지 진행했던 작업들을 그대로 이어 실행할 수 있기 때문이빈다.
  • CPU 스케줄링 정보 : 프로세스가 언제, 어떤 순서로 CPU 를 할당받을지에 대한 정보입니다.
  • 계정 정보 : 프로세스 실행에 사용된 CPU 사용량, 실행한 유저의 정보입니다.
  • I/O 상태 정보 : 프로세스에 할당된 I/O 디바이스 목록입니다.

컨텍스트 스위칭

​ 하나의 프로세스에서 다른 프로세스로 실행 순서가 넘어가면 컨텍스트 스위칭(context switching) 이 발생합니다. 예를 들어 프로세스 A 가 실행되다고 프로세스 B 로 CPU 사용을 넘긴다고 가정해보겠습니다.

​ 이 상황에서 직전까지 실행되던 A 는 프로그램 카운터를 비롯한 각종 레지스터 값, 메모리 정보, 실행을 위해 열었던 파일이나 입출력 장치 등 중간 정보를 백업해야 합니다. 그래야 다음 차례가 왔을 떄 다시 실행을 재개할 수 있기 때문입니다.

​ 이러한 기억해야 할 중간 정보를 문맥(context) 이라고 합니다. 하나의 프로세스 문맥은 해당 프로세스의 PCB 에 표현되어있습니다.

​ 컴퓨터는 많은 프로그램을 동시에 실행하는 것처럼 보이지만 어떠한 시점에서 실행되고 있는 프로세스는 단 한 개이며, 많은 프로세스가 동시에 구동되는 것처럼 보이는 것은 다른 프로세스와의 컨텍스트 스위칭이 아주 빠른 속도로 실행되기 때문입니다. (싱글코어 기준)

image-20230722162516707

​ 앞의 그림처럼 한 개의 프로세스 A가 실행하다 멈추고, 프로세스 A의 PCB를 저장하고 다시 프로세스 B를 로드하여 실행합니다. 그리고 다시 프로세스 B의 PCB 를 저장하고 프로세스 A의 PCB를 로드합니다. 컨텍스트 스위칭이 일어날 때 앞의 그림처럼 유휴 시간(idle time) 이 발생하는 것을 볼 수 있습니다. 이뿐만 아니라 이 컨텍스트 스위칭에 드는 비용이 더 있습니다. 바로 캐시미스 입니다.

### 캐시미스

​ 컨텍스트 스위치가 일어나면, 새로운 프로세스가 실행되기 위해 해당 프로세스의 컨텍스트 정보를 메모리에서 CPU로 가져와야 합니다.

​ 이 때, 캐시 클리어 과정이 필요합니다. 캐시는 CPU가 빠르게 접근할 수 있는 작은 메모리 영역으로, 이전에 실행되던 프로세스(A 프로세스) 의 정보가 여전히 남아있을 수 있습니다. 새로운 프로세스의 컨텍스트를 불러오기 전에 이전 프로세스의 데이터를 제거하거나 무효화하는 과정이 필요한데, 이를 ‘캐시 클리어‘라고 합니다.

​ 캐시 클리어 과정에서 캐시 미스가 발생합니다. 캐시 미스는 CPU가 캐시에서 데이터를 찾으려고 할 때 해당 데이터가 캐시에 없는 상황을 의미합니다. 캐시가 클리어된 상태에서 새로운 프로세스의 데이터를 불러오려고 하면, 해당 데이터는 캐시에 존재하지 않으므로 메인 메모리에서 불러와야 하며, 이 과정에서 캐시 미스가 발생하게 됩니다.

​ 결국, 캐시 미스는 추가적인 시간이 소요되므로 컨텍스트 스위칭의 비용을 늘리는 요소 중 하나라고 할 수 있습니다.

스레드에서의 컨텍스트 스위칭

​ 컨텍스트 스위칭은 스레드에서도 일어납니다. 스레드는 스택 영역을 제외한 모든 메모리를 공유하기 때문에 스레드 컨텍스트 스위칭의 경우 비용이 더 적고 시간도 더 적게 걸립니다.

멀티프로세싱과 멀티스레딩

멀티 프로세싱

​ 멀티프로세싱은 즉 멀티프로세스를 통해 동시에 두 가지 이상의 일을 수행할 수 있는 것을 말합니다. 이를 통해 하나 이상의 일을 병렬로 처리할 수 있으며 특정 프로세스의 메모리, 프로세스 중 일부에 문제가 발생되더라도 다른 프로세스를 이용해서 처리할 수 있으므로 신뢰성이 높은 강점이 있습니다.

IPC

​ 멀티프로세스는 IPC (Inter Process Communication) 를 통해 프로세스끼리 데이터를 주고받고 공유 데이터를 관리합니다. IPC의 종류로는 공유 메모리, 파일, 소켓, 익명 파이프, 명명 파이프, 메시지 큐가 있습니다. 이들은 모두 메모리가 완전히 공유되는 스레드보다는 속도가 떨어집니다.

멀티 스레딩

​ 멀티스레딩은 프로세스 내 작업을 여러 개의 스레드, 멀티스레드로 처리하는 기법이며 스레드끼리 서로 자원을 공유하기 때문에 효율성이 높습니다.

image-20230722173924508

아래는 멀티 프로세스와 멀티 스레드에 관한 추가 포스팅입니다. [java] 멀티 프로세스와 멀티 스레드, 스레드풀

Ref.

  1. 혼자 공부하는 컴퓨터 구조+운영체제

댓글남기기