티스토리 뷰

CS

프로세스와 스레드

pjm1n 2026. 5. 12. 17:09

1.2.1 프로세스와 스레드

프로세스

컴퓨터에서 실행중인 프로그램을 의미한다.

OS는 프로그램을 실행하면서 디스크에 저장된 데이터를 메모리로 로드한다.

프로그램이 실행되면 그 실체가 곧 프로세스가 된다.

프로그램 VS 프로세스

프로그램: 디스크에 저장되어 있는 정적인 실행 파일이다. 실행되기 전의 코드 덩어리, 즉 .exe, .apk 같은 파일 자체를 의미한다. 프로세스: 그 프로그램이 메모리에 적재되어 실제로 실행중인 동적인 인스턴스이다. CPU와 메모리를 할당받아 동작하고 있는 상태이다.

비유하자면, 프로그램은 요리 레시피, 프로세스는 그 레시피를 보고 실제로 요리하고 있는 행위이다.

프로그램과 프로세스는 1:N 관계가 가능하다.

예를 들어 Chrome 같은 브라우저를 두 개 이상 띄우면 프로그램은 1개이지만, 프로세스는 2개 이상이다.

실제로 Chrome 탭 마다 별도의 프로세스를 만들기 때문에 작업 관리자에서 보면 Chrome.exe가 여러 개 떠있는 것을 볼 수 있다.

프로세스는 OS로부터 독립된 메모리영역인 코드, 데이터, 스택, 을 할당 받으며, 다른 프로세스의 메모리 영역에 접근 불가하다. 이를 메모리 격리라고 부른다.

프로세스의 메모리 영역 구조를 간단히 표현하면 다음과 같다.

스택

지역 변수, 함수의 파라미터, 반환되는 주소 값 등이 저장되는 영역이다.

높은 주소값에서 낮은 주소 값으로 메모리가 할당되며, 영역 크기는 컴파일 때 결정된다.

사용자에 의해 동적 메모리 할당이 일어나는 영역이다.

낮은 주소 값에서 높은 주소 값으로 메모리가 할당되며, 영역 크기는 런타임 때 결정된다.

데이터

전역 변수, 정적 변수, 배열, 구조체 등이 저장되는 영역이다.

데이터는 BSS(Block Stated Symbol) 영역과, 데이터 영역으로 다시 나눌 수 있다. BSS 영역은 초기화하지 않는 변수를, 데이터 영역은 초기화한 변수를 저장한다.

참고로 Kotlin은 JVM 언이이기 때문에 BSS/데이터 영역의 개념이 없다. 따라서 Method Area(Metaspace)의 static 영역과 Constant Pool(상수 풀) 에 저장한다.

코드

실행할 코드가 기계어로 컴파일되어 저장되는 영역이다.

그림과 같이 스택과 힙 사이는 빈 메모리 영역이 존재한다.

이유는 런타임에 힙과 스택 모두 크기가 변하기 때문이다. 스택은 함수가 호출될 때마다 커지고, 함수가 끝나면 줄어든다. 힙은 객체를 동적 할당할 때마다 커진다.

만약 OS가 처음부터 크기를 힙과 스택의 크기를 고정해버리면 한쪽이 부족하고 한쪽이 남는 상황이 발생하기 때문에 유연하게 처리할 수 있도록 만든 것이다.

스택은 후입선출 방식으로, 높은 주소 값에서 낮은 주소 값 순서로 사용한다. 힙 영역은 선입선출 방식으로 낮은 주소 값에서 높은 주소 값 순서로 사용한다.

하지만 메모리 영역을 공유하기 때문에 서로의 영역을 침범하는 문제가 생길 수 있다. 스택이 힙 영역을 침범하는 경우를 스택 오버플로우, 힙영역이 스택 영역을 침범하는 경우를 힙 오버플로우라고 한다.

스레드

프로세스에서 실제로 실행되는 흐름의 단위를 의미한다.

스레드는 프로세스 안에 존재하므로 프로세스의 메모리 공간을 이용하고, 스택 영역을 할당 받는다.

 

1.2.2 PCB(Process Control Block)

OS는 프로세스를 제어하기 위해 프로세스 정보를 저장한다. 이를 PCB라고 한다.

PCB는 다음 목록을 저장한다.

  • 프로세스의 현재 상태
  • PID - 프로세스의 고유 ID
  • PC(Program Counter) - 다음 실행할 명령어의 주소
  • 프로세스의 우선순위
  • 메모리 제한

 

1.2.3 프로세스의 생성

새로운 프로세스는 기존 프로세스에서 fork() 함수를 호출해 생성한다. fork()함수에는 함수를 호출한 프로세스를 복사하는 기능이 있다.

이때 기존 프로세스를 부모 프로세스, 복사된 프로세스를 자식 프로세스라고한다. 부모 프로세스에서 fork()함수를 호출하면 부모 프로세스는 자식프로세스의 PID 값을, 자식 프로세스는 0을 반환한다.

fork()가 호출되는 순간 커널은 자식 프로세스를 생성하고, fork()가 반환 순간에 이미 프로세스는 2개가 되어있다. 따라서 각각 다른 값으로 반환받는 것이다.

 

1.2.4 프로세스 상태도

모든 프로세스는 CPU에 의해 생성되고 소멸하는 과정을 거친다.

상태

  • 생성: 프로세스가 PCB는 준비 됐지만 OS로부터 승인 받기 전 이다.
  • 준비: OS로부터 승인받은 후 준비 큐에서 CPU 할당을 기다린다.
  • 실행: 프로세스가 CPU를 할당받아 실행한다.
  • 대기: 프로세스가 입출력이나 이벤트 발생을 기다려야 해서 CPU 사용을 멈추고 기다린다.
  • 종료: 프로세스 실행을 종료한다.

상태 변화

  • 생성 → 준비: 생성 상태의 프로세스가 OS로 부터 승인을 받아, 프로세스가 모여있는 준비 큐에 추가된다.
  • 준비 → 실행: 준비 큐에 있는 프로세스 중 우선순위가 높은 프로세스가 디스패치되어 실행 된다.
  • 실행 → 준비: CPU 독점을 방지하기 위해 타임아웃되어 준비 상태로 변경 된다.
  • 실행 → 대기 : 입출력 또는 이벤트 때문에 대기 상태로 변경된다. ex) 사용자 입력을 기다릴 때
  • 대기 → 준비 : 입출력 또는 이벤트가 완료되어 준비 상태로 변경된다.

디스패치: 프로세스에 CPU 자원을 할당해 해당 프로세스가 준비 상태에서 실행 상태가 되는 것을 의미

 

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

동시성

동시성은 하나의 코어(싱글코어)에서 여러 작업을 번갈아 가면서 처리하는 방식이다. CPU는 한 번에 하나의 작업만 처리할 수 있어서 여러 작업을 조금씩 돌아가면서 처리한다. 이렇게 작업을 교체하는 것을 콘텍스트 스위칭이라 한다.

병렬성

CPU가 여러 개(멀티 코어) 있어서 각 CPU에서 각 작업을 동시에 처리하는 방식이다.

멀티 프로세스

응용 프로그램 하나를 여러 프로세스로 구성하는 것을 의미한다. 멀티 프로세스 환경에서는 한 프로세스가 죽어도 다른 프로세스에 영향을 주지 않으므로 응용 프로그램을 프로세스 여러 개로 구성하는 것이 안정적이다.

하지만 시간과 메모리 공간을 많이 사용한다는 단점이 있다. CPU는 하나의 작업만 처리 할 수 있기 때문에, 여러 프로세스를 처리하려면 CPU에서 처리중인 프로세스를 교체하는 콘텍스트 작업이 필요하다.

이때 CPU에서 기존에 처리하던 프로세스가 할당 받은 메모리 영역을 다른 프로세스에서 사용할 수 있게 교체하면서 시간과 메모리가 필요하기 때문이다. 이를 오버헤드라고 부른다.

또한, 프로세스는 독립적인 메모리를 할당받는다. 따라서 프로세스 간에 공유할 자원이 있다면 IPC를 통해 프로세스 간에 자원을 공유해야 한다.

IPC(Inter Process Communiction): 서로다른 프로세스끼리 데이터를 주고 받는 기술 ex) 카톡 프로세스 ↔ 카메라 프로세스

멀티 스레드

스레드를 여러 개 생성해 스레드들이 각자 다른 작업을 처리하는 것을 의미한다.

스레드는 메모리 영역을 공유한다. 그래서 콘텍스트 스위칭할 때 오버헤드가 적게 발생하고, IPC를 사용하지 않아도 된다. 따라서 프로세스를 여러 개 생성하는 것보다 스레드를 여러 개 생성하는 것이 자원을 효율적으로 사용할 수 있다.

하지만 스택영역을 다른 스레드와 공유하므로 공유자원에 대한 동기화가 필수다. 또한 특정 스레드에 문제가 생기면 프로세스 내 다른 스레드에 영향을 미칠 수 있다.

1.2.6 콘텍스트 스위칭

CPU코어에서 실행 중인 스레드가 다른 스레드로 전환되는 것을 의미한다. 콘텍스트 스위칭이 발생하는 경우 중 하나는 인터럽트가 발생했을 때이다.

인터럽트란 CPU에서 프로세스를 처리하라다가 입출력 관련 이벤트가 발생하거나 예외 상황이 발생할 때 이에 대응할 수 있게 CPU에 처리를 요청하는 것을 의미한다. 인터럽트가 발생하는 경우는 입출력이 발생할 때, CPU 사용 시간이 만료되었을 때, 자식 프로세스를 생성할 때가 있다.

 

1.2.7 프로세스 동기화

경쟁 상태

공유 자원에 동시에 접근해 경쟁하는 상태를 의미한다.

실생활에 비유하자면 다음과 같다.

  1. 엄마가 냉장고를 열어 우유가 없는 것을 확인한다.
  2. 엄마가 우유를 사러 슈퍼마켓에 간다.
  3. 엄마가 우유를 사서 집에 돌아오는 길에 아빠가 냉장고에 우유가 없는 것을 확인한다.
  4. 아빠가 우유를 사러 슈퍼마켓에 간다.
  5. 아빠가 우유를 사서 집에 돌아온다.

냉장고 - 공유 자원 | 엄마, 아빠 - 프로세스

우유는 1개가 되길 기대했지만 2개가 되는 문제가 발생한다. 이렇게 의도하지 않는 결과를 초래하게 된다. 이러한 문제를 해결하기 위해 프로세스 동기화가 이뤄져야한다.

임계 영역

공유 자원에 접근할 수 있고 접근 순서에 따라 결과가 달라지는 코드 영역이다. 냉장고에 우유 유무를 판단하고 우유를 추가하는 부분이 임계 영역에 해당한다.

따라서 임계 영역에서 경쟁 상태가 발생하는 것을 방지하려면 여러 프로세스가 공유자원에 접근해도 데이터의 일관성이 유지되도록 프로세스 동기화를 해야한다.

임계 영역에 여러 접근이 동시에 발생하는 것을 방지하기 위한 방법으로 상호배제 기법이 있다. 상호배제 기법으로는 뮤텍스와 세마포어가 있다.

뮤텍스

락을 가진 프로세스만이 공유 자원에 접근할 수 있게 하는 방법이다. 유명한 예로는 화장실 예가 있다.

  1. 식당에는 화장실 한 칸과 화장실 문을 열 수 있는 열쇠 한 개가 있다.
  2. A가 열쇠를 가지고 화장실에 간다.
  3. 화장실에 가려던 B는 열쇠가 없어서 기다린다.
  4. A가 화장실에서 나와 열쇠를 반납하면, 기다리던 B가 열쇠를 가지고 화장실에 간다.

화장실 - 공유 자원을 포함한 임계 영역 | 열쇠 - 락 | A, B - 프로세스

임계 영역에 접근한 프로세스가 임계 영역에 락을 걸면 다른 프로세스들은 해당 프로세스가 락을 해제하기 전까지 대기해야 한다.

임계영역에 접근하지 못한 프로세스는 락을 얻기 위해 기다리는 동안 락이 풀렸는지 반복문을 돌면서 확인한다. 이를 **스핀락(바쁜 대기)**이라고 한다.

세마포어

공유 자원에 접근할 수 있는 프로세스의 수를 정해 접근을 제어하는 방법이다.

  1. 식당에 화장실 3칸, 화장실을 열 수 있는 열쇠가 3개 있다.
  2. A가 화장실 열쇠 하나를 가지고 화장실에 간다. 따라서 열쇠는 2개 남는다.
  3. B, C가 열쇠를 하나씩 가지고 화장실에 간다. 남은 열쇠가 없어서 D는 화장실에 가지 못한다.
  4. C가 화장실에서 나와 열쇠를 돌려놓으면, D가 화장실에 간다.

화장실 - 공유 자원을 포함한 임계 영역 | 열쇠 - 락 | A, B - 프로세스

열쇠의 개수는 공유자원에 접근할 수 있는 프로세스의 수를 제어하기 위한 정수 변수이다.

 

1.2.8 교착 상태

2개 이상의 프로세스가 각각 자원을 가지고 있으면서 서로의 자원을 요구하며 기다리는 상태이다. 교착상태가 발생하는 4가지 필요 충분조건이 있다.

  • 상호배제: 하나의 공유 자원에 하나의 프로세스만 접근할 수 있다.
  • 점유와 대기: 프로세스가 최소 하나의 자원을 점유하고 있는 상태에서 추가로 다른 프로세스에서 사용 중인 자원을 점유하기 위해 대기한다.
  • 비선점: 다른 프로세스에 할당된 자원을 뺏을 수 없다.
  • 환형 대기: 프로세스가 자신의 자원을 점유하면서 앞이나 뒤에 있는 프로세스의 자원을 요구한다.

교착 상태를 막으려면 4가지 조건들 중에서 한 가지라도 제거하면 된다.

 

1.2.9 스레드 안전

하나의 변수, 함수, 객체에 스레드 여러 개가 동시에 접근해도 문제가 없음을 의미한다.

 

1.2.10 IPC(Inter Process Communication)

프로세스 간에 자원을 공유하는 방식이다.

공유 메모리

프로세스는 원래 메모리를 서로 직접 접근이 불가하다. 하지만 OS가 특정 메모리 영역을 여러 프로세스에 동시에 매핑해준다면 서로 같은 메모리를 사용할 수 있다. 이를 공유 메모리라고 한다. 여러 프로레스에서 접근할 수 있으므로 동기화 문제가 발생할 수 있다.

소켓

프로세스끼리 데이터를 주고 받기 위한 통신의 끝점으로 쉽게 말하면 네트워크 통신용 출입구이다. 서버와 클라이언트 구조로 자원을 주고 받는다.

세마포어

위에서 설명했으니 패스~

파이프

FIFO(First In First Out) 형태의 메모리인 파이프를 이용해 프로세스 간 자원을 공유하는 방식이다. 파이프는 단방향 통신만 지원하므로 읽기 또는 쓰기 중 하나만 할 수 있다. 따라서 양방향 통신을 하려면 읽기 파이프와 쓰기 파이프를 각각 생성해야 한다.

메시지 큐

FIFO(First In First Out)형태의 큐 자료구조를 사용해 프로세스 간 메시지를 주고 받는 방식이다.

 

1.2.11 좀비 프로세스, 고아 프로세스

좀비 프로세스

부모 프로세스가 자식 프로세스의 종료 상태를 회수하지 않았을 경우에 남겨진 자식 프로세스를 말한다.

고아 프로세스

부모 프로세스가 자식 프로세스보다 먼저 종료되는 경우를 말한다.

좀비 프로세스는 이미 종료된 프로세스지만 고아 프로세스는 아직 실행중인 프로세스이다.

'CS' 카테고리의 다른 글

스케줄링  (0) 2026.05.18
운영체제 간단 정리  (0) 2026.02.15
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함