hong's android

[CS] 프로세스와 스레드 본문

Develop/Cs

[CS] 프로세스와 스레드

_hong 2025. 1. 17. 17:54

“쉽게 배우는 운영체제”책을 읽고 정리한 글입니다. 부족한 내용들은 유튜브 또는 다른 책들을 참고하였습니다.

 

프로세스

운영체제에서 프로세스는 하나의 작업 단위이다. 사용자가 마우스를 더블클릭하여 프로그램을 실행하면 그 프로그램은 프로세스가 된다. 프로그램이 프로세스로 전환될 때 운영체제는 프로그램을 메모리의 적당한 위치로 가져온다. 그와 동시에 프로세스 제어 블록을 만든다.

 

프로제스 제어 블록(pcb)

프로세스 제어 블록은 프로세스와 관련된 정보를 저장한다.

 

프로세스들은 차례대로 돌아가며 한정된 시간만큼만 cpu를 이용한다. 자신의 차례가 되면 정해진 시간만큼 cpu를 이용하고, 인터럽트 또는 시스템콜이 발생하면 다음 차례가 올 때까지 기다린다. 운영체제는 빠르게 번갈아 수행되는 프로세스의 실행 순서를 관리하고, 프로세스에 cpu를 비롯한 자원을 배분한다. 이를 위해 프로세스 제어 블록을 생성한다.

 

프로세스 제어 블록에 담기는 정보들

 

1) 프로세스 구분자 ID

2) 이전에 사용하던 레지스터 값 (중간값)

3) 프로세스 상태 (준비, 실행 등의 상태)

4) 프로세스의 메모리 저장 위치

 

즉, 프로그램이 프로세스가 된다는 것은 운영체제로부터 프로세스 제어 블록을 얻는다는 뜻이고, 프로세스가 종료된다는 것은 해당 프로세스 제어 블록이 폐기된다는 뜻이다.

 

저장장치와 메모리

 

프로세스 제어 블록은 큐에 삽입된다. 새로운 프로세스가 생성되면 프로세스 제어 블록의 구조체를 만들고 해당 큐에 삽입하며, 프로세스가 종료되면 해당 프로세스 제어블록이 차지하던 공간을 반환한다. 프로세스 제어 블록을 큐로 관리하는 이유는 PCB를 몇 개 생성할지 모르기 때문에 큰 사이즈의 배열을 만들고 관리한다면 메모리 낭비이기 때문이다.

 

PCB에 저장되는 프로세스 상태는 무엇이 있을까?

 

프로세스의 활성 상태

 

1) 생성 상태 : 프로세스가 이제 막 메모리에 적재되어 생성 PCB를 할당받은 상태입니다. 

 

2) 준비 상태 : CPU를 할당받아 실행할 수 있지만, 아직 차례가 아니기에 기다리고 있는 상태입니다. 실행될 프로세스를 CPU 스케쥴러가 선택합니다.

 

3) 실행 상태 : CPU를 할당받아 실행 중인 상태를 의미합니다. 실행 상태에 들어간 프로세스는 일정 시간(타임 슬라이스) 동안 cpu를 사용할 권리를 갖습니다.

 

4) 대기 상태 : 실행 상태에 있는 프로세스가 입출력을 요청하면 입출력이 완료될 때까지 기다리는 상태입니다. 입출력이 완료되면 인터럽트가 발생하며 해당 프로세스의 PCB는 대기 상태에 있는 프로세스가 준비 상태가 전환됩니다.

 

5) 종료 상태 : 프로세스가 종료된 상태입니다. 이때 프로세스가 사용했던 데이터를 메모리에서 삭제하고 PCB를 폐기한다.

 

프로세스 비활성 상태

 

1) 휴식 상태 : 프로세스가 작업을 일시적으로 쉬고 있는 상태이다.

 

2) 보류상태 : 프로세스가 메모리에서 잠시 쫓겨난 상태로 휴식 상태와 차이가 있다. 보류 상태에 들어간 프로세스는 메모리 밖으로 쫓겨나 스왑 영역에 보관된다. (P.144)

 

프로세스의 상태

 

문맥교환 (컨텍스트 스위칭)

 

CPU를 차지하고있던 프로세스가 나가고, 새로운 프로세스를 받아들이는 작업이다. 또한, 기존 프로세스의 컨텍스트(문맥)을 PCB에 백업하고, 새로운 프로세스를 실행하기 위해 PCB 의 정보를 복구하는 것입니다.

 


 

 

프로세스 각각은 가상의 메모리를 할당 받는다. 메모리를 다른 형태로 추상화한 것이 가상 메모리라고 하는데, 이는 하드웨어에 대한 직접적인 접근을 막는다.

또한 프로세스 내부에서 스레드들이 실행되기 때문에, 스레드의 활동 범위는 할당받은 메모리로 제한된다.

 

가상 메모리의 특징은 Physical memory인 램과 하드디스크를 합쳐 하나의 연속적인 메모리로 추상화하였다.

 

메모리는 유저 영역과 커널 영역으로 이루어져 있고 시스템 호출을 통해 커널영역에 접근이 가능해진다. 커널 영역은 커널 모드에서 실행 가능한 코드들이 존재하고, 유저 영역은 커널 모드의 코드를 직접 실행할 수 없는 코드들이다.

 

* 위에 첨부한 그림처럼 PCB는 메모리의 커널 영역에 저장된다.

 

프로세스의 구조 

메모리 유저 영역

 

프로세스의 데이터는 크기 고정 여부에 따라 두 부류로 나눌 수 있다.

코드 영역과 데이터 영역은 크기가 변하지않는 정적 할당 영역이고, 힙과 스택은 동적 할당 영역입니다. 즉, 힙과 스택은 프로세스가 실행되는 동안 만들어지는 영역이고, 그 크기가 늘어났다 눌어 들었다 하는 동적 할당 영역이다.

 

1) 정적 할당 영역

코드 영역 : 실행할 수 있는 코드, 즉 기계어로 이루어진 명령어

데이터 영역 : 프로그램이 실행되는 동안 유지되는 데이터, 전역변수

 

2) 동적 할당 영역

힙 영역 : 프로그래머가 직접 할당할 수 있는 저장공간. 객체 

스택 영역 : 데이터를 일시적으로 저장하는 공간. 지역변수 매개변수

 

프로세스의 생성과 복사

프로세스는 실행 도중 시스템 호출을 통해 다른 프로세스를 생성할 수 있다. 이때 새 프로세스를 생성한 프로세스를 부모 프로세스, 부모 프로세스에 의해 생성된 프로세스를 자식프로세스라고 합니다. 이렇게 부모 - 자식 계층 구조를 가진다.

부모 프로세스를 통해 생성된 자식 프로세스들은 fork(), exec()을 통해 실행된다. fork()는 자신의 복사본을 자식 프로세스로 생성해 내고, exec()을 통해 자신의 메모리 공간을 다른 프로그램으로 교체한다.

 

현재 프로세스를 복사하는 상황을 살펴보자. 구글의 크롬 웹 브라우저에서 어떤 페이지를 보다가 또 다른 페이지를 만들었을 때 현재의 크롬 프로세스를 복사한다. 프로세스를 복사할 때 기존의 프로세스는 부모 프로세스, 새로 생긴 프로세스는 자식 프로세스가 된다.

 

fork() 시스템 호출을 통해 프로세스를 복사할 경우 부모 프로세스 영역의 대부분이 자식 프로세스에 복사된다. 단, 프로세스 제어블록의 내용 중 일부가 변경된다. (프로세스 Id, 저장된 메모리 위치 등)

 

fork() 시스템 호출의 장점

1) 프로세스의 생성 속도가 빠르다. 왜냐하면 하드디스크로부터 프로그램을 새로 가져오지 않고 기존 메모리에서 복사하기 때문이다.

2) 부모 프로세스에서 추가 작업 없이 자식 프로세스에 상속할 수 있다. 불필요한 초기화 과정을 막을 수 있다.

3) 시스템 관리를 효율적으로 할 수 있다. 자식 프로세스를 종료하면 자식이 사용하던 자원을 부모 프로세스가 정리할 수 있다. 프로세스를 종료하면 프로세스가 사용하던 메모리, 파일을 정리해야 하는데, 이러한 정리를 부모 프로세스에 맡김으로써 시스템이 효율적으로 관리될 수 있다.

 

exec() 시스템 호출을 사용하는 목적은 프로세스의 구조체를 재활용하기 위함이다. 새로운 프로세스를 만들려면 프로세스 제어 블록을 만들고 메모리의 자리를 확보하는 과정이 필요하다. 

 

exec() 시스템 호출의 동작 과정

exec() 시스템 호출을 하면 코드 영역에 있는 기존의 내용을 지우고 새로운 코드로 바꿔버린다. 또한 데이터 영역이 새로운 변수로 채워지고 스택영역이 리셋된다. 단, 프로세스 구분자, 메모리 관련 사항 등은 변하지 않는다.

 

스레드

스레드란 프로세스를 구성하는 실행의 흐름 단위이다. 하나의 프로세스는 여러 개의 스레드를 가질 수 있다. 

CPU와 프로그래밍 기술이 발전하면서 여러 개의 코어를 가진 CPU가 생겨나 멀티 스레드를 지원하기 시작했다. 이런 상황에서 프로세스에 하나의 스레드만 있다면 여러 코어에 나누어 동시에 작업하는 것이 불가능하다. 다시 말해 멀티 스레드의 장점을 살릴 수 없다. 따라서 오늘날의 운영체제는 프로세스를 다양한 스레드로 나누어 여러 개의 코어에 배분함으로써 시스템의 효율을 높인다.

 

또한, 여러 개의 작업을 동시에 싫어 하기 위해 fork() 시스템 호출을 사용하여 부모와 똑같은 프로세스를 생성하면 낭비적인 요소가 많다. fork() 시스템 호출로 프로세스를 복사하면 코드 영역과 데이터 영역의 일부가 메모리에 중복되어 존재하며, 부모-자식 관계이지만 서로 독립적인 프로세스이므로 이러한 낭비 요소를 제거할 수 없다.

 

멀티 스레드의 장점

스레드는 하나의 프로세스 내에 여러 개의 스레드를 생성하는 멀티 스레드는 코드, 파일 등의 자원을 공유함으로써 자원의 낭비를 막고 휴율성을 향상한다. 또한 하나의 프로세스에서 여러 스레드를 사용하면 작업의 효율이 높아진다. 예를 들어, 비디오 플레이어는 재생할 파일을 저장장치에서 가져오는 입출력 부분, 가져온 데이터를 화면에 재생하는 부분으로 나눈다. 이러한 기능을 단일 스레드로 구현하면 입출력을 요청한 프로세스는 입출력이 끝날 때까지 대기 상태로 전환된다.

 

멀티 스레드의 단점

멀티스레드의 경우 모든 스레드가 프로세스 내에서 자원을 공유하기 때문에 한 스레드에 문제가 생기면 전체 프로세스에 영향을 미칠 수 있다. 반면 프로세스를 여러 개 만드는 방식의 경우 각 프로세스가 독립적이기 때문에 한 프로세스의 문제가 다른 프로세스로 전달되지 않는다.

 

스레드의 종류

 

1. 하드웨어 스레드

 

메모리에 접근하는 동안 코어는 아무 작업도 하지 않아 낭비가 발생, -> 두 개의 스레드를 실행시킨다. 이를 “하드웨어 스레드”라 합니다.

 

os 관점에서는 가상의 코어이다. 만약 싱글 코어 cpu에서 하드웨어 스레드가 두 개라면 os는 이 cpu를 듀얼 코어로 인식한다. 듀얼 코어에 맞춰서 os 레벨의 스레드들을 스케줄링한다.

 

2. OS 스레드(= 네이티브 스레드, 커널 스레드)

 

OS 커널 레벨에서 생성되고 관리되는 스레드

또한, CPU에서 실제로 실행되는 단위, CPU 스케줄링의 단위이다.

 

os스레드의 콘텍스트 스위칭은 커널이 개입합니다. 컨텍스트 스위칭이 발생할 때마다 os스레드는 유저모드에서 커널모드로 전환되고, cpu의 리소스를 사용한 다음 커널모드에서 유저모드로 전환된다. (시스템 콜에의해 유저모드에서 커널모드로 전환)

 

3. 유저 스레드

 

스레드 개념을 프로그래밍 레벨에서 추상화한 것이다.

Thread thread = new Thread();

thread.start(); -> start0 (시스템 콜 호출, os 레벨 스레드 하나 생성)

 

유저 스레드가 cpu에서 실행되려면 os 스레드와 연결되어야 한다.

 

유저 스레드와 os 스레드의 연결

1) one-to-one model

 

1 to 1 모델

 

유저 스레드와 os 스레드가 1:1 매핑된다. 스레드 관리는 os 위임되기 때문에 커널에 의해 스레드 스케줄링이 수행되기 때문에 멀티 코어의 활용을 높일 수 있다. 또한 특정 스레드가 대기 상태에 들어가도 다른 스레드는 작업을 계속할 수 있다. 하지만 컨텍스트 스위칭 시 오버헤드가 때문에 느리게 작동한다.

 

2) many-to-one model

1 to N 모델

 

여러 개의 유저스레드들과 하나의 os 스레드가 매핑된다. 컨텍스트 스위칭이 비교적 빠른데, 유저 영역에서 컨텍스트 스위칭이 가능하기 때문이다. os스레드는 하나이기 때문에 멀티 코어 활용을 하지 못한다. 한 스레드가 블락되면 모든 스레드들이 블락된다.

 

3) many-to-many model

M to N 모델

 

한 개 이상의 유저 스레드와 한 개 이상의 os 스레드들이 매핑된다. 

하나의 커널 스레드가 대기 상태에 들어가면 다른 커널 스레드가 대신 작업을 한다. 하지만 커널 레벨 스레드를 같이 사용하기 때문에 여전히 컨텍스트 스위칭 비용이 드는 단점이 있다.

 

용어 설명

* 커널 

 

운영체제 핵심이 되는 컴퓨터 프로그램, 시스템의 전반을 관리합니다. 응용 프로그램 수행에 필요한 서비스를 제공합니다. 대표적으로는 하드웨어에 직접 명령하는 로직을 수행합니다.

 

* 시스템 호출

 

운영체제는 사용자가 실행하는 응용 프로그램이 하드웨어 자원에 직접 접근하는 것을 방지하여 자원을 보호합니다. 응용 프로그램들이 하드웨어 자원에 마음대로 접근한다면 자원이 무질서하게 관리될 것이고, 응용 프로그램이 조금만 실수해도 컴퓨터 전체에 약영향을 끼칠 수 있다.

 

응용 프로그램이 실행 과정에서 하드 디스크에 접근하여 데이터를 저장하려면 운영체제에 도움을 요청해야 하고, 운영체제는 커널 영역 내의 하드 디스크에  데이터를 저장하는 코드를 실행하여 응용 프로그램의 작업을 대신 수행해 준다.

 

이러한 운영체제의 문지기 역할은 이중 모드로 구현이 된다. 이중 모드는 사용자모드 + 커널 모드를 구분하여 cpu가 명령어를 실행한다. 사용자 모드는 커널 영역의 코드를 실행할 수 없는 모드이고, 커널 모드는 운영체제 서비스를 제공받을 수 있는 실행 모드이다.

시스템 호출은 운영체제 서비스를 제공받기 위한 요청이다. 즉, 사용자모드에서 커널 모드로의 전환을 의미한다.

결국 os 스레드에서 사용자 코드와 커널 코드 모두 실행된다.

 

출처

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

2. 쉽게 배우는 운영체제

3. 쉬운코드 유튜브

'Develop > Cs' 카테고리의 다른 글

[CS] 물리 메모리 관리  (0) 2025.02.19
[CS] 프로세스 동기화  (0) 2025.02.11
[cs] HTTP의 발전 과정  (0) 2024.10.28
[Cs] 컨텍스트 스위칭  (0) 2023.01.13