GithubHelp home page GithubHelp logo

operatingsystem-summary's Introduction

Process vs. Thread

Process(프로세스)

프로세스(Process)란 실행 중인 프로그램(Program)을 의미한다. 프로그램을 실행하면 각 프로그램 실행을 위한 메모리가 할당 되며, CPU가 프로그램을 처리할 수 있도록 할당된 메모리 공간에 바이너리 코드가 올라온 상태를 프로세스라고 한다. 프로세스 내부에는 최소 하나의 쓰레드(Thread)가 존재한다. 프로세스는 스케줄링(Scheduling)의 대상이 되는 작업(task)과 같은 의미로 쓰이며, 실제 스케줄링은 쓰레드 단위로 이루어진다. (쓰레드가 CPU의 실행 단위가 됨)

각 프로세스는 프로그램 실행을 위한 독립적인 메모리 주소를 할당 받는데, 그 구조는 다음과 같다.

  • Code 영역: 프로그램을 실행시키는 실행 파일 내의 명령 코드를 저장하는 영역
  • Data 영역: 전역변수, 정적변수(static), 배열 등(초기화된 데이터)을 저장하는 영역
  • Heap 영역: 동적 할당을 위한 메모리 영역(C: malloc(), Java: new() 등)
  • Stack 영역: 지역변수, 매개변수, 리턴 값 등을 저장하는 영역(임시 메모리 영역)

같은 프로세스 내에 존재하는 쓰레드 간에는 Code 영역, Data 영역, Heap 영역을 공유하여 사용한다. 반면, 프로세스 간 데이터를 공유하기 위해서는 Pipe, 공유 메모리 등을 사용해야 한다.

Thread(쓰레드)

쓰레드(Thread)란 프로세스 내부에 존재하는 프로세스의 실행(흐름) 단위를 의미하며, 프로세스 마다 하나 이상 존재한다. 쓰레드는 같은 프로세스에 소속된 다른 쓰레드들과 프로세스의 메모리 공간이나 자원을 공유할 수 있다. 쓰레드는 쓰레드 ID(Thread ID), 프로그램 카운터(PC: Program Counter), 레지스터(Register) 집합, 스택(Stack)으로 구성된다. 여기서 말하는 스택이 프로세스 메모리 구조의 그 Stack 영역이다. 스택 영역은 각 쓰레드마다 독립적으로 할당되기 때문에 여러 개의 쓰레드가 하나의 프로세스에 속하는 경우라도 스택 영역은 쓰레드 간에 공유하지 않는다. 이처럼 하나의 프로세스에 여러 쓰레드가 존재하는 것을 멀티 쓰레드(Multi Thread)라고 부른다.

쓰레드마다 스택을 독립적으로 할당하는 이유

각 쓰레드는 작업 실행 후 되돌아올 주소 등을 스택에 저장하여 독립적인 실행 흐름을 유지하는데, 스택을 공유하게 되면 쓰레드로서의 역할은 불가능하게 된다. 따라서 각 쓰레드가 독립적인 실행 흐름을 갖기 위해서는 스택 영역을 쓰레드마다 독립적으로 할당해야 한다.

쓰레드마다 PC Register를 독립적으로 할당하는 이유

쓰레드는 주어진 실행 명령을 수행하면서 작업 위치를 PC에 저장한다. 이렇게 작업 위치를 저장하는 이유는 하나의 쓰레드가 작업이 끝날 때까지 CPU를 지속적으로 점유할 수 없기 때문(Multi Tasking을 위한 Context Switching 발생)이다. 즉, 각 쓰레드는 명령어를 연속적으로 수행하지 못하고 중간에 중단했다가 다시 실행해야 하는데 이를 위해서는 쓰레드마다 각각의 작업 위치를 기억할 필요가 있다. 따라서 PC Register는 쓰레드마다 독립적으로 할당해야 한다.


멀티 쓰레드

멀티 쓰레드의 장점

  • 자원을 공유하기 때문에 쓰레드 간의 통신이 간단하다.
  • Context Switching에 대한 오버헤드가 프로세스의 Context Switching에 비해 적다.

프로세스를 이용해 동시에 처리하던 일을 쓰레드로 구현할 경우 메모리 공간과 시스템 자원 소모가 줄어든다. 쓰레드 간의 통신이 필요한 경우에도 별도의 자원을 이용하는 것이 아니라 프로세스에 할당된 메모리의 Data 영역이나 Heap 영역을 이용하여 데이터를 주고 받을 수 있다. 즉, 프로세스 간 통신에 비해 쓰레드 간 통신이 훨씬 간단하다. 심지어 쓰레드의 Context switching은 프로세스의 Context switching과는 달리 캐시 메모리를 비울 필요가 없기 때문에 더 빠르다. 따라서 시스템의 throughput이 향상되고 자원 소모가 줄어들며 프로그램의 응답 시간도 단축된다. 이러한 장점 덕분에 여러 프로세스로 할 수 있는 작업들을 하나의 프로세스에서 쓰레드로 나누어 수행하는 것이다.

멀티 쓰레드의 문제점

  • 쓰레드 간의 동기화 작업이 필요하다.
  • 하나의 쓰레드가 자원을 망가뜨릴 경우, 모든 쓰레드가 작동하지 못한다. (장애 발생)

멀티 프로세스 기반으로 프로그래밍할 때는 프로세스 간에 공유하는 자원이 없기 때문에 동일한 자원에 동시 접근하는 일이 없었지만, 멀티 쓰레딩을 기반으로 프로그래밍할 때에는 이 부분을 신경써야 한다. 한 프로세스 내의 서로 다른 쓰레드가 데이터 영역과 힙 영역을 공유하기 때문에 한 쓰레드에서 사용 중인 변수나 자료구조에 또 다른 쓰레드가 접근하여 작업을 시도한다면 엉뚱한 값을 읽어 오거나 잘못된 값으로 변경할 수 있다.

이러한 문제 때문에 멀티 쓰레드 환경에서는 동기화 작업이 필요하다. 동기화를 통해 작업 처리 순서를 컨트롤하고 공유 자원에 대한 접근을 컨트롤하는 것이다. 하지만 무분별한 동기화는 과도한 락으로 인해 병목현상을 발생시켜 성능 저하를 유발할 수 있으니 주의해야 한다.

멀티 쓰레드 vs 멀티 프로세스

멀티 쓰레드는 멀티 프로세스보다 적은 메모리 공간을 차지하고 Context switching이 빠르다는 장점이 있지만, 오류로 인해 하나의 쓰레드가 종료되면 전체 쓰레드가 종료될 수 있는 문제와 동기화 문제를 안고 있다. 반면 멀티 프로세스 방식은 하나의 프로세스가 죽더라도 다른 프로세스에는 영향을 끼치지 않고 정상적으로 작업을 수행할 수 있다는 장점이 있지만, 멀티 쓰레드보다 많은 메모리 공간과 CPU 시간을 차지한다는 단점을 갖고 있다. 이 두 가지 방식은 동시에 여러 가지 작업을 수행한다는 점은 같지만, 적용해야 하는 시스템에 따라 적합/부적합이 구분된다. 따라서 대상 시스템의 성격에 따라 적합한 동작 방식을 선택하고 적용해야 한다.


멀티 프로그래밍, 멀티 태스킹, 멀티 쓰레딩, 멀티 프로세싱

멀티 프로그래밍

멀티 프로그래밍(Multi Programming)이란, 메모리에 여러 프로그램을 올려놓고 하나의 CPU에서 여러 프로그램을 동시에 실행하는 시스템을 말한다. 여기서 동시에 실행한다는 것은 의미 그대로 동시에 실행한다는 것이 아니라, 하나의 CPU에서 두 개의 프로그램을 번갈아가며 실행하는 것을 의미한다. 예를 들어 프로그램 A를 실행하다가 I/O 작업을 만나면 프로그램 B를 실행하고, 프로그램 B가 I/O 작업을 만나면 프로그램 C를 실행하거나, 또는 이전에 실행하던 프로그램 A를 이어서 실행하는 방식이다. 멀티 프로그래밍 시스템의 핵심은 여러 프로그램을 동시에 실행시켜 CPU 사용율을 극대화하는 것이다.

이러한 멀티 프로그래밍 시스템은 싱글 프로세스 시스템의 단점을 보완하긴 하지만, I/O 작업이 거의 없는 CPU bound 프로그램을 실행하는 경우에 다른 프로세스는 장시간 대기하게 된다는 문제점이 있다.

멀티 태스킹

멀티 태스킹(Multi Tasking)이란, 멀티 프로그래밍의 문제점을 보완하기 위해 고안된 시스템으로 하나의 프로세스가 한 번에 사용할 수 있는 CPU 사용 시간의 최대치를 아주 짧게 설정하여 CPU를 사용하는 프로세스를 짧은 시간 간격마다 전환하는 시스템이다. 이렇게 짧게 쪼개진 cpu time은 퀀텀(Quantum) 또는 타임 슬라이스(Time slice)라고 하며, ms 단위로 매우 짧다. 멀티 태스킹 시스템의 핵심은 프로그램의 응답 시간을 최소화하고, 여러 프로그램이 즉각적으로 반응하는 것처럼 보이게 만드는 것이다. 이런 시스템의 등장으로 음악 재생과 문서 작업을 동시에 수행하는 것들이 가능해졌다.

그러나 멀티 태스킹 시스템은 하나의 프로세스에서 여러 개의 작업을 수행할 수는 없다. 예를 들면, 메신저 어플에서 파일을 전송하면서 동시에 메시지를 주고 받거나, 음악 어플로 음악을 들으면서 다른 음악을 검색하는 것 등이 불가능하다. 이런 문제를 해결하기 위해 하나의 프로그램 내에 여러 프로세스를 만들게 되면 Context Switching의 Overhead가 크기 때문에 CPU 사용량이 커지는 문제를 야기한다. 게다가 프로세스 간에 데이터를 공유하는 것 역시도 상당히 까다로운 작업이다. 쓰레드는 이러한 문제를 해결하기 위해 고안됐다.

멀티 쓰레딩

멀티 쓰레딩(Multi Threading)은 CPU가 한 번에 처리할 수 있는 작업 실행 단위가 2개 이상이 될 수 있는 시스템을 말한다. 멀티 쓰레딩은 멀티 태스킹보다 한 단계 진보된 개념으로서 멀티 태스킹이 프로그램 간 멀티 태스킹을 의미한다면, 멀티 쓰레딩은 프로그램 내에서의 멀티 태스킹을 구현한 것으로 볼 수 있다.

멀티 프로세싱

멀티 프로세싱(Multi Processing)은 두 개 이상의 프로세서나 코어를 쓰는 시스템을 말한다. 즉, 여러 개의 CPU가 하나의 주기억장치를 공유하여 각각의 작업을 동시에, 병렬적으로 처리할 수 있다.


Context Switching(컨텍스트 스위칭)

컨텍스트 스위칭(Context Switching)이란 CPU에서 실행 중이던 프로세스/쓰레드가 다른 프로세스/쓰레드로 교체되는 것을 의미한다. 여기서 컨텍스트는 실행 중인 프로세스 또는 쓰레드의 CPU와 메모리에서의 상태를 말한다. 단일 CPU에서는 한 번에 하나의 쓰레드나 프로세스만 실행이 가능하다. 하나의 CPU나 코어(Core)에서 여러 개의 프로세스나 쓰레드를 동시에 실행하기 위해서는 컨텍스트 스위칭은 필수적이다.

컨텍스트 스위칭은 프로세스나 쓰레드가 주어진 time slice(quantum)를 모두 사용했을 때, I/O 작업으로 인해 당장 CPU를 점유할 필요가 없을 때, 다른 프로세스나 쓰레드가 사용하고 있는 리소스에 접근하기 위해 대기 중일 때, 인터럽트가 발생했을 때 등 다양한 이유로 발생한다.

컨텍스트 스위칭의 전 과정은 OS의 커널(Kernel)에서 관장한다. 커널은 OS에서 가장 핵심적인 역할을 수행하는 곳으로 컴퓨터의 하드웨어에 직접적으로 접근하는 곳이다. 컨텍스트 스위칭 또한 커널에서 컨트롤하는데, CPU에서 실행되던 프로세스나 쓰레드를 다른 프로세스나 쓰레드로 교체하는 일은 CPU와 그 외 관련 하드웨어를 조작해야 하는 일이기 때문이다.


프로세스 컨텍스트 스위칭

프로세스 간의 컨텍스트 스위칭. 엄밀히 말하면 프로세스의 실행 단위는 쓰레드이기 때문에, 서로 다른 프로세스에 소속된 쓰레드 간의 컨텍스트 스위칭이 발생하면 이를 프로세스 컨텍스트 스위칭이라고 한다.

쓰레드 컨텍스트 스위칭

동일한 프로세스에 소속된 쓰레드 간의 컨텍스트 스위칭을 말한다.

프로세스 CS와 쓰레드 CS의 공통점

  • 커널모드에서 실행된다.

  • 스위칭 시 CPU의 레지스터 상태를 교체한다.

프로세스나 쓰레드가 일반적으로 실행될 때는 OS에서 유저모드로 실행되는데 어떤 이유로 컨텍스트 스위칭이 발생하면 컨텍스트 스위칭을 진행하기 위해 통제권(Control)을 커널에 넘겨 주게 된다. 이렇게 커널에 통제권을 넘겨주는 것을 유저모드에서 커널모드로 전환된다고 표현한다. 커널에서 스위칭을 실행하고 필요한 작업이 끝나면 교체되어 새롭게 들어온 프로세스나 쓰레드에 통제권을 넘겨주어 다시 유저모드로 전환된다.

컨텍스트 스위칭 시에는 이전에 점유 중이던 프로세스의 CPU에서의 상태(각종 레지스터에 저장되어 있던 데이터)를 저장하고, 교체되는 프로세스의 상태를 CPU에 페치(Fetch)한다. 만약 교체되는 프로세스가 이전에 실행된 적이 있다면 작업이 중단되었던 위치에서 이어서 실행한다.

이 두 가지 작업은 프로세스 컨텍스트 스위칭과 쓰레드 컨텍스트 스위칭에서 공통적으로 수행하는 작업이며, 당연하게도 상당히 CPU를 사용하는 일이다.

프로세스 CS와 쓰레드 CS의 차이점

프로세스 컨텍스트 스위칭의 오버헤드가 쓰레드 컨텍스트 스위칭의 오버헤드보다 크다는 점이다. 이러한 차이는 공유 자원의 유무에서 기인한다. 쓰레드 컨텍스트 스위칭 시에는 공유 자원은 교체할 필요가 없으나 프로세스 컨텍스트 스위칭의 경우 메모리의 모든 데이터를 교체해야 하기 때문이다. 따라서 스위칭 속도 또한 작업량이 더 적은 쓰레드 컨텍스트 스위칭에서 더 빠르다.

Cache Pollution(캐시 오염)

CPU에는 처리 속도 향상을 위한 캐시(Cache)라는 메모리가 존재한다. 자주 사용하는 데이터는 CPU와 가까운 저장소에 따로 저장해둠으로써 메모리에 직접 접근하는 횟수를 줄여 처리 속도를 향상하는 것이다. 이때 사용하는 저장소가 바로 캐시(Cache)이다. 이러한 캐시 덕분에 하나의 작업을 이어서 쭉 진행하는 경우에는 메모리에 접근하지 않고도 대부분의 작업을 처리할 수가 있다.

그러나 문제는 컨텍스트 스위칭 후에 발생한다. 컨텍스트 스위칭이 발생하면, 교체된 프로세스나 쓰레드에서 사용할 데이터는 교체 전 프로세스나 쓰레드에서 사용하던 데이터와 같지 않다. 그렇기 때문에 컨텍스트 스위칭 후에는 캐시에 저장된 데이터는 처리 속도 향상에 도움이 되지 않는다. 결국 컨텍스트 스위칭 직후에는 캐시에 저장된 데이터를 사용할 수 없어 CPU 처리 성능이 나빠지게 되는데 이를 캐시 오염(Cache Pollution)이라고 한다.



Scheduler(스케줄러)

프로세스를 스케줄링 하기 위해서 세 가지 종류의 Queue가 존재한다.

  • Job Queue: 현재 시스템 내에 있는 모든 프로세스의 집합
  • Ready Queue: 현재 메모리 내에 있으면서 CPU를 점유하여 실행되기를 기다리는 프로세스의 집합
  • Device Queue: Device I/O 작업을 대기하고 있는 프로세스의 집합

Long-Term Scheduler(or Job Scheduler)

한정된 메모리 상에 많은 프로세스들이 한꺼번에 올라올 경우, 대용량 메모리(일반적으로 디스크)에 임시 저장된다. 이때 이 Job pool에 저장되어 있는 프로세스를 선별하여 메모리를 할당하고 Ready Queue로 보내는 역할을 하는 스케줄러를 장기 스케줄러(Long-tern scheduler)라고 한다.

  • 메모리와 디스크 사이의 스케줄링을 담당
  • 프로세스에 memory(및 각종 리소스)를 할당(admit)
  • Degree of Multiprogramming에 몇 개의 프로그램이 올라갈 것인지를 제어
  • 프로세스의 상태: new -> ready(in memory)

Short-Term Scheduler(or CPU Scheduler)

단기 스케줄러(Short-term scheduler)는 Ready Queue에 저장된 프로세스를 선별하여 CPU에 할당하는 스케줄러를 말한다. CPU에서 실행되는 프로세스는 다양한 이유로 매우 빠르게 교체(Context Switching)되므로 이 작업은 매우 짧은 시간 간격을 두고 수행된다. 이러한 이유로 인해 단기(Short-term)이라는 이름이 붙었다. 이와 반대로 장기 스케줄러는 메모리에 올라간 프로세스가 종료될 때 작업을 수행하고, 프로세스가 너무 많이 올라가 있는 경우에는 대기하는 등 작업 수행의 텀이 길기 때문에 장기(Long-term)라는 이름을 붙였다.

  • CPU와 메모리 사이의 스케줄링을 담당
  • Ready Queue에 존재하는 프로세스 중 어떤 프로세스를 Running 시킬지 결정
  • 프로세스에 CPU를 할당(scheduler dispatch)
  • 프로세스의 상태: Ready -> Running -> Waiting -> Ready

Medium-Term Scheduler(or Swapper)

현재 CPU가 감당하기에 너무 많은 수의 프로세스가 메모리에 올라와서 작업에 지장이 생기는 경우, Ready 상태의 프로세스 중 일부를 선별하여 디스크로 내려보내는 방식으로 처리 속도를 향상시키는데 이러한 스케줄링을 담당하는 스케줄러가 중기 스케줄러(Medium-term scheduler) 이다. 이러한 작업의 특징 때문에 Swapper라고도 부른다.

  • 여유 공간 마련을 위해 프로세스를 통째로 메모리에서 디스크로 쫓아낸다. (Swapping)
  • 프로세스에게서 Memory를 Deallocate
  • Degree of Multiprogramming 제어
  • 현 시스템에서 메모리에 너무 많은 프로그램이 동시에 올라가는 것을 조절하는 스케줄러
  • 프로세스의 상태: Ready -> Suspended

Process state - Suspended

Suspended(Stopped): 외부적인 이유로 프로세스의 수행이 정지된 상태로 메모리에서 내려간 상태를 의미한다. 프로세스 전부 디스크로 Swap out 된다. Blocked 상태는 다른 I/O 작업을 기다리는 상태이기 때문에 스스로 Ready State로 복귀할 수 있지만, Suspended state에서는 외부적인 이유로 Suspending 되었기 때문에 스스로 Ready State로 복귀할 수 없다.


오늘날에는 Virtual Memory Management의 발달로 인해 메모리에 과도한 프로세스가 적재되어 문제를 유발하는 상황이 발생하지 않게 되었다. 실행될 프로세스들은 따로 분류 없이 모두 메모리에 올라가는 것이다. 따라서 Long-term scheduling은 존재하지 않고, Long-term으로 분류되는 스케줄링이 없으니 그에 대비되는 명칭인 Short-term scheduling 또한 CPU Scheduling으로 대체되어 사용된다. 마찬가지 이유로 Medium-term scheduler 역시 사용하지 않는다.


CPU 스케줄러

CPU 스케줄러는 단기 스케줄러이므로 CPU 스케줄링 대상은 Ready Queue에 있는 프로세스들이다.

FCFS(First-Come First-Served)

특징

  • 먼저 온 고객에게 먼저 서비스를 하는 방식으로 선착순, 또는 순차적 처리를 말한다.
  • 작업이 끝날 때까지 CPU를 양보하지 않는 비선점형 스케줄링(Non-Preemptive Scheduling)

문제점

  • Convoy Effect(호위 현상): 작업 소요 시간이 긴 프로세스가 먼저 CPU를 점유하는 경우, 다른 프로세스들이 처리되지 못하고 계속 쌓이게 되면서 작업의 효율을 떨어뜨리는 현상이 발생

SJF(Shortest-Job-First)

특징

  • 다른 프로세스가 먼저 도착했어도 CPU burst time이 짧은 프로세스에게 먼저 할당
  • 비선점형 스케줄링(Non-Preemptive Scheduling)

문제점

  • CPU burst time이 긴 프로세스는 거의 영원히 CPU를 할당 받을 수 없게 되는 Starvation 발생

SRT(Shortest Remaining Time First)

특징

  • 새로운 프로세스가 도착할 때마다 새로운 스케줄링이 이루어진다.
  • 현재 수행 중인 프로세스의 남은 burst time보다 더 짧은 CPU burst time을 가지는 새로운 프로세스가 도착하면 CPU 점유를 뺏기는 선점형 스케줄링(Preemptive Scheduling)

문제점

  • Starvation 발생
  • 프로세스의 Remaining time을 예측하는 별도의 알고리즘이 필요

Priority Scheduling

특징

  • 우선순위가 가장 높은 프로세스에게 CPU를 할당한다.
  • 선점형 스케줄링 방식: 더 높은 우선순위의 프로세스가 도착하면 실행 중인 프로세스를 중단하고 CPU를 점유
  • 비선점형 스케줄링 방식: 더 높은 우선순위의 프로세스가 도착하면 Ready Queue의 Head에 넣는다.

문제점

  • Starvation 발생

해결책

  • Aging: 아무리 우선순위가 낮은 프로세스라도 대기 시간이 길어지면 우선순위를 높여준다.

Round Robin(RR)

특징

  • 현대적인 CPU 스케줄링
  • 각 프로세스는 동일한 크기의 할당 시간(time slice or time quantum)을 갖게 된다.
  • 할당 시간된 시간이 지나면 프로세스는 CPU 점유를 뺏기고 Ready Queue의 제일 마지막 순번으로 줄을 선다.
  • RR은 CPU 사용 시간이 랜덤한 프로세스들이 섞여있을 경우에 효율적

장점

  • Response time이 빨라진다. n개의 프로세스가 Ready queue에 있고 할당된 시간이 q(time quantum)인 경우, 각 프로세스는 q 단위로 CPU 시간의 1/n을 얻는다. 즉, 어떤 프로세스도 (n-1)q time unit 이상 기다리지 않는다.
  • 프로세스의 대기 시간이 CPU를 사용할 만큼 증가한다. 공정한 스케줄링(fair scheduling)이라고 할 수 있다.

주의점

설정한 time quantum이 너무 크면 FCFS와 같아진다. 반면 너무 작아질 경우에는 스케줄링 알고리즘 목적에는 이상적이겠지만, 잦은 Context Switching으로 Overhead가 발생한다. 따라서 적당한 time quantum을 설정하는 것이 중요하다. 일반적으로 ms 단위로 설정한다.



프로세스 동기화

동시성 문제

멀티 쓰레드 환경에서 동기화를 하지 않아 발생하는 문제로, 두 개 이상의 쓰레드가 동시에 공통된 자원에 접근하여 값을 조작하는 경우에 원치 않는 데이터 변질이 발생하는 현상이다.

전역 변수 count(초기값 0)에 대해 쓰레드 t1과 쓰레드 t2가 동시에 1씩 증가시키는 연산을 수행하는 상황을 예로 들 수 있다. 모든 연산이 종료되면 count = 2라는 결과를 얻어야 하지만 동기화를 하지 않으면 그보다 더 작은 수의 결과를 얻을 수 있다. 코드 레벨에서는 단순히 count++; 로 표현되는 한 줄의 명령어이지만, 이 한 줄의 코드가 실제로 CPU가 이해할 수 있는 명령어로 변환되면 다음과 유사하게 여러 개의 명령어로 나뉘게 된다.

LOAD count to R1
R1 = R1 + 1
STORE R1 to count

프로그래밍 언어에서 한 줄짜리였던 명령어가 세 줄이 되었다. 각각의 동작은 다음과 같다. (1) 메모리의 count 변수의 값을 레지스터 R1으로 불러온다. (2) 레지스터 R1에 있는 값에 1을 더하고 다시 R1에 저장한다. (3) 레지스터 R1에 있는 값을 다시 메모리의 count 변수에 저장한다.

문제는 이 세 개의 작업이 하나의 작업으로 묶여서 수행되지 않기 때문에 발생한다. 쓰레드 t1이 먼저 count 변수에 접근하여 위 연산을 수행하는 상황에서 두 번째 과정(레지스터에 불러온 값(0)에 1을 더하여 저장. 즉, R1 = 0 + 1.)까지 완료한 후에 context switching이 발생하여 쓰레드 t2가 실행된다면? t2가 작업을 끝냈든 못 끝냈든 다시 t1이 실행될 때는 세 번째 과정인 count 변수에 1을 저장하는 작업만 진행하게 된다. t2가 작업을 끝냈다면 t1이 결과값에 1을 덮어 쓸 것이고, t2 또한 작업 중간에 스위칭 되었다면 t2가 최종 결과값을 1로 덮어 쓰게 된다.

Race Condition(경쟁 조건)

이처럼 여러 프로세스/쓰레드가 동시에 같은 데이터를 조작(manipulation)할 때 타이밍이나 접근 순서에 따라(경쟁에 의해) 수행 결과를 예측할 수 없게 되는 것을 Race Condition 이라고 한다.

Synchronization(동기화)

여러 프로세스/쓰레드가 동시에 같은 데이터를 조작하여도 Race Condition 문제가 발생하지 않고, 공유 데이터의 일관성을 유지하는 것을 동기화(Sysnchronization) 라고 한다.

Critical Section(임계 영역)

공유 데이터의 일관성을 보장하기 위해, 한 번에 하나의 프로세스/쓰레드만 접근 가능하도록 만들어진 영역을 임계 영역(Critical section) 이라고 한다. 그리고 프로세스들이 데이터를 협력적으로 공유할 수 있도록 동기화를 위한 프로토콜을 설계하는 것을 Critical Section Problem이라고 한다.

Critical section problem 해결을 위한 기본 조건

critical section problem을 해결하기 위해서는 아래의 세 가지 조건을 만족해야 한다.

  • Mutual Exclusion(상호 배제)
    프로세스 P_i가 자신의 critical section에서 실행 중이라면 다른 프로세스들은 각자의 critical section에서 동시에 실행될 수 없다.
  • Progress(진행)
    ciritical section에서 현재 실행 중인 프로세스가 없는 상태에서 각자의 critical section에 진입하려는 프로세스들이 있는 경우, 나머지 영역에서 실행 중이지 않은 프로세스들만 critical section 진입 후보가 될 수 있다.
  • Bounded Waiting(한정적 대기)
    critical section에 한 번 진입했던 프로세스는 다음에 다시 critical section에 진입하도록 허용하기까지 횟수에 제한을 둔다. 이는 한 프로세스가 경쟁에 밀려 critical section에 진입하지 못하고 무한히 기다리는 statvation 현상을 방지하기 위함이다.

해결책

Lock

  • 하드웨어 기반 해결책으로서 동시에 공유 자원에 접근하는 것을 막기 위해 Critical section에 진입하는 프로세스는 Lock을 획득하고, Critical section을 빠져나올 때 Lock을 반납한다.

  • Lock을 획득하지 못한 채 critical section으로의 진입을 기다리는 프로세스들은 while문을 통해 Lock을 획득할 때까지 끊임없이 획득 시도를 하게 되는 Busy Waiting 상태가 된다. 즉, lock을 취득하는 과정에서 CPU cycle을 낭비하는 단점이 있다.

  • Spinlock의 경우 critical section에 진입하기 전에 Lock을 획득하는 함수는 CPU가 지원하는 Atomic 명령어를 사용하기 때문에 다수의 프로세스가 동시에 접근하여 Lock을 획득하는 상황은 발생하지 않는다.

Atomic Operation

  • 실행 중간에 간섭 받거나 중단되지 않는다.
  • 같은 메모리 영역에 대해 동시에 실행되지 않는다.

한계

  • 싱글프로세싱 환경에서의 이점이 없다. 싱글코어에서 Lock의 권한을 넘겨주기 위해서는 Lock을 가지고 있는 쓰레드와 Lock을 획득하기 위해 기다리는 쓰레드 간의 context switching이 반복해서 이루어져야 하기 때문이다.

Mutex(뮤텍스)

  • Lock과 동일하게 단 하나의 프로세스/쓰레드만이 critical section에 진입 가능하다. (초기값: value = 1)
  • Lock을 취득하기 위해 반복 시도(Busy waiting)하지 않고, Lock이 반납되면 깨워주기를 요청하고 대기 상태(Waiting state)로 전환한다. 이렇게 Lock을 획득할 때까지 휴식하며 대기하는 방식으로 mutual exclusion을 보장하는 것을 Mutex라고 한다.

Semaphore(세마포)

하나 이상의 프로세스/쓰레드가 Critical Section에 접근 가능하도록 하는, Signal Mechanism을 가진 장치. Semaphore의 구조는 Mutex와 굉장히 유사하나 단 하나의 프로세스/쓰레드만이 critical section에 진입할 수 있는 Mutex와 달리, 진입 가능한 프로세스/쓰레드의 수가 제한되어 있지 않다. (초기값: value >= 1)

초기값이 1이 아니기 때문에 프로세스/쓰레드가 critical section에 진입할 때마다 value가 1씩 감소, critical section에서 빠져나올 때마다 1씩 증가한다.

  • Counting Semaphore (초기값: value > 1) 사용 가능한 개수를 가진 자원에 대한 접근 제어용으로 사용되며, Semaphore는 해당 자원의 개수로 초기화된다. 자원을 사용하면 Semaphore는 감소하고, 자원을 반납하면 Semaphore는 증가한다.
  • Binary Semaphore (초기값: value = 1) Mutex와 동일하게 value는 0과 1만을 가질 수 있다. 다중프로세스들 사이의 Critical section problem을 해결하기 위해 사용된다.

Deadlock(교착상태)
교착상태(Deadlock)란 프로세스 A와 프로세스 B가 서로 상대방이 쥐고 있는 자원을 요구하며 무한 대기 상태에 빠지는 현상을 의미한다.

Monitor(모니터)

  • 고급 언어의 설계 구조물로서 개발자의 코드를 Mutual Exclusion 하도록 만든 추상화된 데이터 형태.(Java의 Synchronized)
  • 공유 자원에 접근하기 위한 키 획득과 자원 사용 후 해제를 모두 처리한다. (Semaphore는 직접 키 해제와 공유자원 접근 처리가 필요)


메모리 관리 전략

Swap

메모리의 관리를 위해 사용되는 기법. 멀티프로그래밍 환경에서 프로세스의 메모리를 보조기억장치(e.g. Hard Disk, SSD etc.)로 내보내고 다른 프로세스의 메모리를 불러들일 수 있다.

이러한 과정을 Swap이라 한다. 주기억장치(RAM)로 불러오는 과정을 Swap-in, 보조기억장치로 내려보내는 과정을 Swap-out 이라고 한다. Swap은 디스크 전송 시간이 큰 작업이기 때문에 일반적으로 메모리 공간이 부족할 때 Swapping이 시작된다.

Fragmentation(단편화)

프로세스들이 메모리에 적재되고 제거되는 일이 반복되다 보면 프로세스들이 차지하는 메모리 틈 사이에 사용하지 못할 만큼의 작은 자유 공간들이 늘어나게 되는데, 이러한 현상을 단편화(短片化; Fragmentation)이라고 한다. 단편화는 두 가지 종류로 구분된다.

  • External Fragmentation(외부 단편화): 물리 메모리(RAM)에 적재된 프로세스들 사이사이에 사용하지 못하는 작은 공간들이 만들어지는 현상. 이 작은 저장 공간들을 합치면 활용하기에 충분한 공간이 만들어질 때 외부 단편화(External Fragmentation) 가 발생했다고 한다.

  • Internal Fragmentation(내부 단편화): 내부 단편화(Internal Fragmentation)프로세스에 할당된 메모리 공간 내에 사용하지 못하고 남는 공간이 발생하는 현상. 분할된 영역이 할당된 영역보다 더 큰 경우, 할당되고 남는 영역이 생기는 것을 말한다.

  • Compaction(압축): 압축(Compaction)은 외부 단편화를 해소하기 위해 프로세스가 사용하는 공간들을 한쪽으로 몰아 자유 공간을 확보하는 방법을 말하며, 작업 효율은 좋지 않다. (과거 window의 디스크 조각 모음)

Paging(페이징)

페이징(Paging)은 하나의 프로세스가 사용하는 메모리 공간이 연속적이어야 한다는 제약을 없앤 메모리 관리 방법이다. 외부 단편화와 압축 작업을 해소하기 위해 고안된 방법론으로 물리 메모리는 Frame이라는 고정크기로 분리되어 있고, 논리 메모리(프로세스가 점유하는)는 Page라고 불리는 고정 크기의 블록으로 분리된다.

하나의 프로세스가 사용하는 공간은 여러 개의 페이지로 나뉘어 논리 메모리에서 관리되고, 개별 페이지는 순서에 관계 없이 물리 메모리 상의 프레임에 mapping 되어 저장된다.

장점
외부 단편화를 해결할 수 있다.

단점
내부 단편화가 발생한다.


Virtual Memory(가상 메모리)

멀티 프로그래밍을 실현하기 위해서는 많은 프로세스들을 동시에 메모리에 올려두어야 한다. 가상 메모리는 프로세스 전체가 메모리 내에 올라오지 않더라도 실행이 가능하도록 하는 기법으로, 프로그램의 크기가 물리 메모리보다 커도 실행이 가능하다는 것이 주요 장점이다.

프로그램의 일부만 메모리에 올릴 수 있다면

  • 물리 메모리 크기에 제약을 받지 않는다.
  • 더 많은 프로그램을 동시에 실행할 수 있다. 이에 따라 응답시간은 유지되고, CPU 이용률처리율은 높아진다.
  • swap에 필요한 입출력이 줄어들기 때문에 프로그램들이 빠르게 실행된다.

가상 메모리가 하는 일

가상 메모리는 실제의 물리 메모리 개념과 사용자의 논리 메모리 개념을 분리한 것으로 정리할 수 있다. 이로써 작은 메모리를 가지고도 얼마든지 큰 가상 주소 공간을 프로그래머에게 제공할 수 있다.

가상 주소 공간

  • 한 프로세스가 메모리에 저장되는 논리적인 모습을 가상 메모리에 구현한 공간이다. 프로세스가 요구하는 메모리 공간을 가상 메모리에서 제공함으로써 현재 직접적으로 필요하지 않은 메모리 공간은 실제 물리 메모리에 올리지 않는 것으로 물리 메모리를 절약할 수 있다.
  • 한 프로그램이 실행되면서 논리 메모리에 100KB를 요구하는 상황을 예로 들어 보자. 이때 프로그램을 실행하는 데에 필요한 실질적인 메모리 공간(Heap 영역, Stack 영역, Data 영역, Code 영역)의 합이 40KB라고 한다면, 실제 물리 메모리에는 40KB만 적재되고 나머지 60KB에 대해서는 필요 시 필요한 만큼만 물리 메모리에 적재하는 것이다.

가상 메모리의 페이지 공유

가상 메모리에서는 서로 다른 프로세스가 페이지를 공유할 수 있다.

  • 가상 메모리는 시스템 라이브러리를 여러 프로세스들 사이에 공유할 수 있도록 한다. 각 프로세스들은 공유 라이브러리를 자신의 가상 주소 공간에 두고 사용하는 것처럼 인식하지만, 실제로 라이브러리가 올라가 있는 물리 메모리 페이지들은 모두 프로세스에서 공유되고 있다.
  • 프로세스들이 메모리를 공유하는 것을 가능케 하고 프로세스들은 공유 메모리를 통해 통신할 수 있다. 이 또한 각 프로세스들은 각각 자신의 주소 공간처럼 인식하지만, 실제로는 물리 메모리를 공유하는 것이다.
  • fork()를 통한 프로세스 생성 과정에서 페이지들이 공유되는 것을 가능하게 한다.

Demand Paging(요구 페이징)

실행 초기에는 필요한 페이지만 물리 메모리에 적재하고, 추가적인 요구 페이지가 있을 때마다 해당 페이지를 물리 메모리에 적재한다.

프로그램 실행 시작 시에 프로그램 전체를 디스크에서 물리 메모리에 적재하는 대신 초기에 필요한 것들만 적재하는 전략을 요구 페이징(Demand Paging)이라고 하며, 가상 메모리 시스템에서 많이 사용한다. 가상 메모리는 대게 페이지로 관리된다. 요구 페이징을 사용하는 가상 메모리에서는 실행 과정에서 추가적인 페이지가 필요한 순간이 오면 해당 페이지를 적재한다. 한 번도 접근한 적이 없는 페이지는 물리 메모리에 적재하지 않는다.

프로세스 내의 개별 페이지들은 페이저(Pager)에 의해 관리된다. 페이저는 프로세스 실행에 실제 필요한 페이지들만 메모리로 읽어 옴으로써 사용하지 않을 페이지를 가져오기 위한 시간과 메모리의 낭비를 줄일 수 있다.

Page Fault Trap(페이지 부재 트랩)

Page Replacement(페이지 교체)

물리 메모리가 가득 찬 상태에서 Page Fault가 발생했다면 어떤 페이지를 쫓아내야 하는가.

가상 메모리 사용으로 프로그램 실행 시에 모든 항목이 물리 메모리에 올라가지 않기 때문에, 프로세스 동작에 필요한 페이지를 요청하는 과정에서 Page fault가 발생하면 원하는 페이지를 보조기억장치에서 추가로 불러오게 된다. 이때, 만약 물리 메모리가 모두 사용 중인 상황이라면 페이지의 교체가 이루어져야 한다. (또 다른 방법으로는 운영체제가 프로세스를 강제 종료하는 방법도 있다.)

기본적인 방법

  1. 디스크에서 필요한 페이지의 위치를 찾는다.
  2. 빈 페이지 프레임을 찾는다. i. Page Replacement Alorithm(페이지 교체 알고리즘)을 통해 희생될 페이지(victim)를 고른다. ii. 희생될 페이지를 디스크에 기록하고 관련 페이지 테이블을 수정한다.
  3. 새롭게 비워진 페이지 테이블 내 프레임에 새 페이지를 읽어오고 프레임 테이블을 수정한다.
  4. 사용자 프로세스를 재시작한다.

Page Replacement Algorithm(페이지 교체 알고리즘)

FIFO Page Replacement

가장 간단한 페이지 교체 알고리즘으로 FIFO(First-In First-Out)의 흐름을 가진다. 즉, 페이지 교체 시점이 오면 물리 메모리에 적재되었던 순서대로 교체되는 방식이다.

  • 장점
    • 이해하기 쉽고 구현하기도 쉽다.
  • 단점
    • 오래된 페이지에 지속적으로 필요한 정보들이 포함되어 있을 수 있다. (초기 변수 등)
    • 처음부터 계속 활발하게 사용하고 있는 페이지를 교체함으로써 페이지 부재율을 높이는 부작용을 초래할 수 있다.

Optimal Page Replacement(최적의 페이지 교체)

이 알고리즘의 핵심은 장래에 가장 오랫동안 사용하지 않을 페이지를 선별하여 교체하는 것이다. 실제로 구현할 수 있는 알고리즘은 아니지만, 다른 페이지 알고리즘들의 성능을 측정하는 척도로서 의의를 갖는다.

  • 장점
    • 알고리즘 중 가장 낮은 페이지 부재율을 보장한다.
  • 단점
    • 구현이 불가능하다. 모든 프로세스의 메모리 참조 계획을 미리 파악할 방법이 없기 때문이다.

LRU Page Replacement

LRU(Least-Recent-Used): 최적 알고리즘의 근사 알고리즘으로 가장 오랫동안 사용하지 않은 페이지를 선택하여 교체한다.

  • 특징
    • 대체로 FIFO 알고리즘보다 우수하고 OPT 알고리즘보다는 아쉬운 모습을 보인다.

LFU Page Replacement

LFU(Least-Frequently-Used): 참조 횟수가 가장 적은 페이지를 교체하는 방법. 활발하게 사용되는 페이지는 참조 횟수가 많아질 것이라는 가정에서 만들어진 알고리즘이다.

  • 특징
    • 어떤 프로세스가 특정 페이지를 집중적으로 사용하다가 다른 기능을 사용하게 되면, 더이상 사용하지 않아도 계속 메모리에 머물게 되어 초기 가정에 어긋나는 시점이 발생할 수 있다.

operatingsystem-summary's People

Contributors

elegantstar avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.