JAVA/RxJava

[OS] 운영체제 기초

꾸준함. 2024. 1. 19. 22:33

Process vs Thread

 

Process

 

  • 프로그램의 실제 실행을 의미하며 프로그램 파일을 실행함으로써 프로그램 데이터들이 메모리에 올라와 CPU를 할당받고 명령을 수행하고 있는 상태
  • 프로세스는 운영체제로부터 자원을 할당받은 최소 작업 단위
  • OS는 프로세스마다 각각 독립된 메모리 영역을 동적 할당을 받는 stack과 heap 그리고 정적 할당을 받은 data와 code 형식으로 할당
  • 독립된 메모리 영역을 할당해 주기 때문에 프로세스 간 영향을 받지 않고 독립적인 작업 수행
  •  기본적으로 프로세스 간 변수나 자료에 접근할 수 없고 통신 기법을 통해 프로세스 간 통신을 해야 함

 

stack

  • 메서드 안에서 선언된 지역 변수, 매개변수, 반환 값 등이 저장
  • 함수의 호출과 함께 할당되며 함수의 호출이 완료되면 소멸

 

heap

  • 사용자에 의해 메모리 공간이 동적으로 할당되고 해제됨
  • 메모리의 낮은 주소에서 높은 주소의 방향으로 할당

 

https://lwn.net/Articles/91829/

 

data

  • 코드가 실행되면서 사용하는 전역 변수, static 변수 등
  • 프로그램의 시작과 함께 할당되며 프로그램이 종료되면 소멸

 

code

  • 사용자가 작성한 프로그램 함수들의 코드
  • CPU는 코드 영역에 저장된 명령어를 하나씩 가져가서 처리
    • CPU는 순차적 처리에 특화
    • GPU는 병렬 처리에 특화

 

Thread

 

  • 쓰레드는 프로세스가 OS로부터 할당받은 자원을 이용하는 실행 단위
    • 프로세스가 OS로부터 자원을 받는 최소의 단위라면 쓰레드는 OS의 스케줄러에 의해 관리되는 CPU의 최소 실행 단위
    • 스케줄러 알고리즘에 따라 프로세스에 속한 한 개의 쓰레드가 선점되어 CPU에 할당
    • 쓰레드 간 선점이 일어날 때 CPU의 실행 흐름이 전환되는 context switching 발생

 

  • 하나의 프로세스는 최소 하나 이상의 쓰레드를 가짐
  • 프로세스 내에서 각 필요한 stack만 할당 받고 heap, code 그리고 data 영역은 쓰레드 간 공유
  • 쓰레드 간 메모리 스택 영역에 접근할 수 없으므로 stack에 한해서만 독립적인 실행 흐름을 가질 수 있게 되어 독립적인 함수 호출 가능

 

Process vs Thread

 

안전성

  • 여러 개의 자식 프로세스 중 하나에 문제가 발생할 경우 해당 자식 프로세스만 죽고 다른 프로세스에는 영향을 끼치지 않기 때문에 프로그램 전체의 안전성 확보
  • 여러 개의 쓰레드 중 하나에 문제가 발생할 경우 전체 프로세스에 영향을 끼침
  • 쓰레드 간 공유 메모리 영역(heap)의 동시 접근으로 인한 동기화 문제 발생
  • 하나의 쓰레드에서 오류가 발생할 경우 프로세스 자체 종료 야기 가능

 

효율성

  • 프로세스 간 전환 즉 context switching 시 레지스터, 케시 메모리 초기화 등 비싼 작업 수행으로 인해 시간적인 비용 발생
  • 프로세스 생성 시 독립적으로 메모리가 할당되기 때문에 리소스 비용이 큼
  • 프로세스의 stack 영역을 제외한 메모리 영역을 공유하기 때문에 context switching 시간이 적고 리소스를 효율적으로 사용

 

통신

  • 프로세스 간 통신 기법이 어렵고 복잡해서 통신으로 인한 오버헤드 큼
  • 쓰레드 간 통신비용이 적어 통신으로 인한 오버헤드가 적음 (메모리 영역 공유)

 

동시성 vs 병렬성

 

동시성

 

  • CPU가 한 번에 많은 일을 처리하는 것에 중점을 두기 때문에 많은 작업들을 아주 빠른 시간으로 교체하면서 전체 작업을 처리 (시간적으로 동시 X)
  • 작업의 처리를 빠르게 하기 위한 목적보다는 CPU를 효율적으로 사용하는 것에 더 중점
    • 쓰레드가 작업을 처리하다 I/O 처리 요청을 받으면 context switching이 진행되어 CPU는 다른 쓰레드로 전환되어 작업을 진행

 

  • 작업 수가 CPU 코어 수보다 많을 경우 해당되며 동시성이 적용되지 않을 경우 작업을 순차적으로 진행

 

병렬성

 

  • CPU가 시간적으로 동시에 많은 일을 수행하는 것에 중점을 두기 때문에 CPU가 놀지 않고 최대한 빠르게 동작해야 함
  • 런타임에 동시에 물리적으로 작업을 실행하는 것이기 때문에 싱글 코어로는 절대 병렬성이 구현될 수 없음 (무조건 멀티 코어)
    • 작업을 여러 쓰레드로 분리하고 OS는 분리된 쓰레드들을 여러 CPU에 적절히 분배하여 동시적으로 실행되도록 하는 것

 

  • 작업 수가 CPU 코어 수보다 같거나 적을 경우 가장 효율성이 좋음

 

https://stackoverflow.com/questions/1050222/what-is-the-difference-between-concurrency-and-parallelism

 

위 사진에서 상단 사진이 동시성, 하단 사진이 병렬성을 표현합니다.

동시성이 작업 처리 방식에 대한 설계에 관한 것이라면 병렬성은 하드웨어에서 계층에서 작업 수행 방식에 관한 것입니다.

 

Context Switch

하나의 CPU는 프로세스를 순차적으로 하나씩 처리하기 때문에 여러 프로세스를 동시에 실행할 수 없는 제약이 있습니다.

따라서 CPU는 여러 프로세스를 동시성으로 처리하기 위해 한 프로세스에서 다른 프로세스로 전환하는 context switching 과정을 거쳐야 합니다.

 

Context

 

  • 프로세스 간 전환을 위해 이전 프로세스가 어디까지 명령을 수행했고 CPU 레지스터에는 어떤 값이 저장되어 있는지에 대한 정보 필요
  • 컨텍스트는 CPU가 해당 프로세스를 실행하기 위한 프로세스의 정보이며 해당 정보들은 OS에서 관리하는 PCB(Process Control Block)라고 하는 자료구조의 공간에 저장
    • context switching은 CPU가 프로세스 간 PCB 정보를 교체하고 캐시를 비우는 일련의 과정

 

  • PCB는 프로세스의 상태 정보를 저장하는 반면 TCB(Thread Control Block)는 쓰레드의 상태 정보를 저장하는 자료구조
    • TCB는 PC, CPU 정보, 그리고 PCB를 가리키는 포인터를 가짐 
    • 쓰레드가 하나 생성할 때마다 PCB 내에서 TCB가 생성되며 context switching이 일어나면 기존의 Thread 상태 정보를 TCB에 저장하고 새로운 쓰레드의 TCB를 가져와 실행

 

프로세스 상태 및 context switching이 발생하는 조건

 

프로세스는 아래와 같이 5가지 상태를 가집니다.

  • New
  • Ready
  • Running
  • Waiting
  • Terminated

 

New

프로세스를 생성하고 있는 단계로 커널 영역에 PCB가 만들어진 상태

 

Ready

프로세스가 CPU를 할당받기 위해 기다리고 있는 상태

 

Running

프로세스가 CPU를 할당 받아 명령어를 실행 중인 상태

 

Waiting

프로세스가 I/O 작업 완료 혹은 사건 발생을 기다리는 상태

 

Terminated

프로세스가 종료된 상태

 

https://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/3_Processes.html

 

context switching이 발생하는 조건은 아래와 같습니다.

  • 실행 중인 프로세스에서 I/O 작업 호출이 발생하여 I/O 작업이 끝날 때까지 프로세스 상태가 running -> waiting 상태로 전이된 경우
  • Round Robin 스케줄링과 같은 OS 스케줄링 알고리즘에 의해 현재 실행 중인 프로세스가 사용할 수 있는 시간 자원을 모두 사용했을 때 프로세스를 중지하고 (ready 상태) 다른 프로세스를 실행시켜주는 경우

 

Process context switching vs Thread context switching

 

  • 프로세스는 context switching 할 때 메모리 주소 관련 여러 가지 처리를 하기 때문에 오버헤드 큼
  • 쓰레드는 프로세스 내 메모리 영역을 고유하기 때문에 메모리 주소 관련 추가적인 작업이 필요 없어 프로세스에 비해 오버헤드가 작음 (비교적 빠름)
  • 쓰레드는 생성하는 비용이 커서 많은 수의 쓰레드 생성은 OOM을 야기하고 빈번한 context switching으로 인해 성능 저하 발생 가능

 

사용자 모드 vs 커널 모드

OS는 응용 프로그램이 하드웨어 자원에 직접 접근하는 것을 방지하여 자원을 보호하며 응용 프로그램이 하드웨어 자원에 접근하려고 할 때는 반드시 OS를 통해서만 접근해야 합니다.

OS의 여러 기능 중 핵심 기능을 담당하는 부분을 커널이라고 합니다.

 

https://ko.wikipedia.org/wiki/%EC%BB%A4%EB%84%90_%28%EC%BB%B4%ED%93%A8%ED%8C%85%29

 

 

CPU 권한 모드

 

CPU는 명령어를 실행할 때 아래와 같이 크게 두 가지 권한 모드로 구분해서 실행하며 동작하는 동안 두 가지 모드를 번갈아 가면서 수행합니다.

  • 사용자 모드
  • 커널 모드

 

커널 모드

  • 커널 영역의 코드가 실행되는 모드로써 메모리의 유저 영역, 커널 여역 모두 접근 가능
  • 하드웨어 자원에 직접 접근 가능
  • Mode Bit = 0

 

사용자 모드

  • 사용자 응용 프로그램의 코드가 실행되는 모드로써 메모리의 유저 영역만 접근 가능
  • I/O 장치들과 같은 하드웨어 자원에 직접 접근 불가
  • 대부분의 응용 프로그램은 I/O 장치나 파일로의 접근이 필요하기 때문에 이 때는 유저 모드에서 커널 모드로의 전환 필요

 

https://knowitlikepro.com/kernel-mode-and-user-mode/

 

시스템 호출 (System Call)

 

  • 응용 프로그램이 OS의 커널이 제공하는 서비스를 이용할 수 있도록 커널 모드에 접근하기 위한 인터페이스
  • 응용 프로그램이 하드웨어 혹은 파일로의 접근과 같은 기능은 커널 모드일 때 CPU가 실행하기 때문에 반드시 시스템 콜을 사용해서 커널모드로 전환 필요
    • 응용 프로그램이 작업 고자어에서 커널의 기능을 사용하기 위해 빈번하게 시스템 콜을 요청하고 이로 인해 사용자 모드와 커널 모드를 상호 전환하며 실행
    • 사용자 모드와 커널 모드를 오가는 것은 context switching과 관련이 있으며 이는 multi thread 환경에서 참고해야 할 중요한 배경 지식

 

https://www.people.vcu.edu/~jwang3/CMSC312/Linux%20system%20calls.htm

 

사용자 수준 쓰레드 vs 커널 수준 쓰레드

단어 그대로 사용자 수준 쓰레드는 사용자 응용 프로그램에서 관리하는 쓰레드이며 커널 수준 쓰레드는 OS에서 관리하는 쓰레드입니다.

 

사용자 수준 쓰레드 (User Level Thread)

 

  • 사용자 영역에서 쓰레드 라이브러리에 의해 쓰레드의 생성/종료, 쓰레드 간 메시지 전달, 쓰레드의 스케줄링 보관 등 모든 것을 관리
  • 커널은 사용자 수준 쓰레드에 대해 알지 못하며 단일 쓰레드 프로세스인 것처럼 관리

 

커널 수준 쓰레드 (Kernel Level Thread)

 

  • 커널이 쓰레드와 관련된 모든 작업 관리 (PCB/TCB 관리 및 유지)
  • 커널은 커널 쓰레드의 모든 정보를 알고 있으며 커널 쓰레드는 OS 스케줄러에 의해서 스케줄링
  • CPU는 커널에 의해 생성된 커널 쓰레드의 실행만을 담당

 

멀티 쓰레딩 모델

 

  • 앞서 언급했다시피 CPU는 OS 스케줄러가 예약하는 커널 쓰레드만 할당받아 실행시키기 때문에 사용자 수준 쓰레드의 경우 커널 수준 쓰레드와의 매핑이 필요
  • 사용자 수준 쓰레드는 아래와 같이 세 가지 모델로 커널 수준 쓰레드와 매핑 가능
    • 다대일 쓰레드 매핑
    • 일대일 쓰레드 매핑
    • 다대다 쓰레드 매핑

 

https://www.researchgate.net/figure/Three-types-of-thread-models-Popular-operating-systems-5-22-24-adopt-the_fig1_346379550

 

다대일 쓰레드 매핑

  • 다수의 사용자 수준 쓰레드가 커널 수준 쓰레드 한 개에 매핑하는 유형으로 사용자 수준의 쓰레드 모델
  • 커널 개입 없이 사용자 쓰레드끼리의 컨텍스트 스위칭이 발생하기 때문에 오버헤드가 적음
    • 스케줄링이나 동기화를 위해 커널을 호출하지 않으므로 커널 영역으로 전환하는 오버헤드 적음
    • 개별 쓰레드 단위가 아닌 단일 쓰레드의 프로세스 단위로 프로세서를 할당하기 때문에 멀티코어를 활용하여 병렬 처리 불가능 (커널 수준 쓰레드는 유저 수준 쓰레드들과 매핑만 되었을 뿐 유저 수준 쓰레드의 내용을 알지 못함)
    • 한 쓰레드가 Block I/O 발생 시 모든 쓰레드들이 Block이 발생하기 때문에 프로세스 자체를 블록할 수 있음

 

일대일 쓰레드 매핑

  • 사용자 수준 쓰레드와 커널 수준 쓰레드가 일대일 매핑하는 유형으로 커널 수준의 쓰레드 모델
  • 커널이 전체 프로세스와 쓰레드 정보를 유지해야 하기 때문에 컨텍스트 스위칭 시 오버헤드 큼
  • 자원 한정으로 인해 쓰레드를 무한정으로 생성할 수 없기 때문에 thread pool을 활용
  • 다대일 쓰레드 매핑과 달리 쓰레드 단위로 CPU를 할당하기 때문에 멀티코어를 활용한 병렬 처리 가능

 

다대다 쓰레드 매핑

  • 여러 개의 사용자 수준 쓰레드를 같은 수 또는 그 보다 작은 수의 커널 수준 쓰레드로 매핑하는 유형
    • 각 커널 수준의 쓰레드가 한 개 이상의 사용자 수준의 쓰레드와 매핑

 

  • 앞선 모델들의 단점을 어느 정도 해결하여 개발자는 필요한 만큼 많은 사용자 수준 쓰레드를 생성할 수 있고 커널 수준 쓰레드가 멀티 프로세서에서 병렬로 수행될 수 있음
    • 사용자 수준 쓰레드가 I/O 시스템 콜을 호출했을 때 커널이 다른 쓰레드의 수행을 스케줄 할 수 있음

 

참고

자바 동시성 프로그래밍 [리액티브 프로그래밍 Part.1] - 정수원 강사님

 

반응형

'JAVA > RxJava' 카테고리의 다른 글

[Java] 동기화 기법  (1) 2024.03.04
[Java] 동기화 개념  (0) 2024.02.24
[Java] Java 쓰레드 활용  (1) 2024.02.21
[Java] Java 쓰레드 기본 API  (0) 2024.02.05
[Java] 쓰레드 생성 및 실행 구조  (0) 2024.01.31