[26장. 병행성: 개요]

 

쓰레드들은 주소 공간을 공유하기 때문에 동일한 값에 접근할 수 있다.

두 개의 쓰레드가 하나의 프로세서에서 실행 중이라면 실행하고자 하는 쓰레드는 반드시 문맥 교환을 통해 실행중이 쓰레드와 교체되어야 한다.

프로세스가 교환될 때 사용된 PCB처럼 쓰레드도 TCB를 사용한다.

 

[28장. 락]

 

락은 프로그래머에게 스케줄링에 대한 최소한의 제어권을 제공한다.

쓰레드는 프로그래머가 생성하고 OS가 제어한다. 락은 쓰레드에 대한 제어권을 일부 이양 받을 수 있도록 해준다.

 

락 설계시 평가기준

  • 상호 배제를 제대로 지원하는가? (임계영역 진입 여부)
  • 쓰레드들이 락 획득에 대한 공정한 기회가 주어지는가? (기아현상 발생 여부)
  • 하나의 쓰레드가 실행 중에 락을 획득하고 해제하는 과정에서 발생하는 부하는 얼마나 되는가? (성능)

 

하드웨어 기법

  • Test and Set: 락의 값을 검사하고 새로운 값으로 설정하는 것을 나누어서 실행하면 상호 배제가 지켜지지 않을 수 있다. 원자적(동시) 연산으로 만든것을 스핀 락이라고 한다. 선점형 스케줄러에서만 사용할 수 있다.
  • Compare and Swap: 위와 비슷한 방식이지만 더 강력하다.
  • Load Linked & Store Conditional
  • 그 외.. 일단 패스함

 

락에서 가장 중요한 측면

  • 상호 배제의 정확성
  • 공정성: 단순한 스핀 락은 공정하지 않다.
  • 성능

 

락을 기다리며 스핀만 무한히 하는경우 할당된 CPU 시간을 다른 쓰레드에게 넘겨주어서 시간 낭비를 하지 않을 수 있지만 문맥 교환에 소모되는 비용과 해결하지 못한 기아 현상이 남게된다.

 

그 외 내용이 많은데 정리하기가 애매해서 일단 넘어감

 

[29장. 락 기반의 병행 자료 구조]

 

자료 구조에 락을 추가하면 경쟁 조건에서 안전한 자료 구조로 만들 수 있다.

 

병행 카운터

확장성 없는 카운팅: 간단하고 정확하게 동작하지만 확장성이 전혀 없다.

확장성 있는 카운팅: 근사 카운터를 이용한다.

 

이번장도 넘어감

 

[30장. 컨디션 변수]

 

쓰레드 실행 시, 특정 조건이 만족될 때 까지의 대기를 위한 컨디션 변수라는 개념을 사용할 수 있다. 일종의 큐 자료구조이다. 조건이 만족되기를 대기하는 큐이다. 조건이 만족되었을 때 대기중인 쓰레드를 깨워 실행을 계속 하도록 한다.

 

슬립에서 깨어난 프로세스는 리턴하기 전에 락을 재획득하여야 한다.

시그널을 보낼 때, 대기할 때는 항상 락을 획득하는 것이 좋다.

 

생산자-소비자(유한 버퍼) 문제

공유 변수에서 경쟁 조건(race condition)이 발생한다. 시그널을 받아서 깨어난 스레드가 실제 실행되는 시점에는 시그널을 받은 시점의 상태가 그대로 유지되어있는지를 다시 체크해야 한다.(Mesa semantic)

단일 버퍼 생산자-소비자의 경우에는 컨디션 변수를 2개 사용하면 된다.

 

포함 조건

다수의 쓰레드가 메모리 할당을 요청하고 대기중인 경우 컨디션 변수에서 대기중인 모든 쓰레드에게 시그널을 전송한다. 다수의 쓰레드를 불필요하게 깨워서 문맥 교환이 많이 발생하는 단점이 있다.

 

컨디션 변수는 프로그램 상태를 특정 조건이 만족될 때까지 대기하도록 하여 동기화를 매우 쉽게 해결한다.

 

[31장. 세마포어]

 

세마포어는 초기값에 의해 동작하기 때문에 반드시 제일 먼저 값을 초기화해야 한다.

 

이진 세마포어: 락의 상태가 두가지만 존재한다.

 

세마포어는 병행 프로그램에서 일어나는 사건의 순서를 정하는 데에도 유용하다.

 

생산자-소비자 문제

다수의 쓰레드가 존재하는 경우에 상호 배제가 지켜지지 않아 경쟁 조건이 발생한다.

 

교착 상태의 방지

락의 범위를 줄여야 한다.

 

식사하는 철학자

모든 포크는 누군가 다 잡고있고 서로 대기중이기 때문에 교착 상태가 발생한다.

단순한 해결방법으로는 최소 한명의 철학자가 다른 순서로 포크를 집도록 하면 된다.

 

쓰레드 제어: 과도하게 많은 쓰레드가 동시에 실행되면 성능이 나빠지므로 쓰레드 개수의 임계값을 정하고 동시에 실행하는 쓰레드 개수를 제한한다.

 

 

내용이 많아서 어느정도 쳐냈음. 다른 정보들도 더 찾아봐야 할 것 같음

 

[32장. 병행성 관련 버그]

 

비 교착 상태 오류

  • 원자성 위반 오류: 다수의 메모리 참조 연산들에 대해 예상했던 직렬성이 보장되지 않음. 락을 추가해서 해결할 수 있다.
  • 순서 위반 오류: 메모리 참조 간의 순서가 지켜지지 않음. 컨디션 변수를 추가해 동기화하여 해결할 수 있다.

 

교착 상태 오류

교착 상태 발생 조건

  • 상호 배제
  • 점유 및 대기
  • 비선점
  • 환형 대기

네 가지 조건중 하나라도 만족하지 않는다면 교착 상태는 발생하지 않는다.

 

교착 상태의 예방

  • 순환 대기: 락 획득을 하는 전체 순서를 정한다. 부분 순서만을 정의할 수도 있다.
  • 점유 및 대기: 원자적으로 모든 락을 한번에 획득하도록 한다. 실제 필요할 때 요청하는것이 아니라서 병행성이 저하되는 문제가 있다.
  • 비선점
  • 상호 배제

 

스케줄링으로 교착 상태 회피하기

병행성에 제약을 가져 올 수도 있는 문제가 있기때문에 보편적으로 사용되는 방법은 아니다.

 

발견 및 복구

교착 상태 발생을 허용하고 발견 시 복구하도록 하는 방법.

 

[33장. 이벤트 기반의 병행성(고급)]

'이론 > 운영체제' 카테고리의 다른 글

[OSTEP] III. 영속성  (0) 2022.08.21
[OSTEP] I. 가상화  (0) 2022.08.19
[OSTEP] 2장. 운영체제 개요  (0) 2022.08.19
쉽게 배우는 운영체제  (0) 2022.08.16

[4장. 프로세스의 개념]

 

프로세스: 실행 중인 프로그램

 

OS는 시분할 기법을 통해 CPU를 가상화시킨다. 시분할 기법은 CPU를 공유하기 때문에 각 프로세스의 성능은 낮아진다.

CPU 가상화의 효율적인 구현을 위해서는 도구(메커니즘)와 지능(정책)이 필요하다.

*정책: OS에서 어떤 결정을 내리는 데 사용하는 알고리즘

 

프로세스의 하드웨어 상태 중 가장 중요한 구성 요소는 메모리이다.

 

프로세스 생성

프로그램 코드와 정적 데이터를 메모리, 프로세스의 주소 공간에 load한다. 이후 스택과 힙을 생성하고 초기화 하고 입출력 셋업과 관계된 다른 작업을 마치게 되면 OS는 프로그램 실행을 위한 준비를 마치게 된다.

 

프로세스 상태

  • 실행: 프로세스가 명령어를 실행중인 상태
  • 준비: 프로세스가 실행할 준비가 되어있지만 OS가 다른 프로세스를 실행중인 등의 이유로 대기 중인 상태
  • 대기: 프로세스가 다른 사건을 기다리는 동안 프로세스의 수행을 중단시키는 연산 (예: 입출력 요청)

운영체제의 스케줄링 정책에 따라 실행과 준비 상태를 전이한다.

 

자료구조

OS는 프로세스 리스트, 레지스터 문맥 등의 자료구조가 있다.

프로세스 리스트는 시스템에 존재하는 모든 프로세스에 대한 정보를 갖고 있고 각 노드는 프로세스 제어 블록(PCB)이다.

 

[5장. 막간: 프로세스 API]

 

fork() 시스템 콜

프로세스 생성에 사용된다. 생성 성공시 0을 반환한다. 새로 생성된 프로세스는 자식 프로세스가 된다. 자식과 부모 프로세스는 완전히 동일하지는 않고 각자 주소공간, 레지스터, PC값을 따로 가진다.

 

wait() 시스템 콜

부모 프로세스가 자식 프로세스의 종료를 대기해야 하는 경우도 발생할 수 있다. wait() 시스템 콜은 자식 프로세스가 종료될 때까지 리턴하지 않는다.

 

exec() 시스템 콜

자기 자신이 아닌 다른 프로그램을 실행해야 할 때 사용한다. fork는 복사본, exec는 다른 프로그램이다.

 

[6장. 제한적 직접 실행 원리(Limited Direct Execution)]

 

시분할 시스템을 이용한 CPU 가상화 구현에는 성능 저하와 제어 문제를 해결해야 한다. 특히 제어 문제가 중요하다.

 

기본 원리

프로그램을 CPU 상에서 직접 실행시키는 것이다. 빠르게 실행된다는 장점이 있다.

문제점

 

제한된 연산: 사용자 모드와 커널 모드를 도입하여 프로세스가 할 수 있는 일을 제한한다. 제한된 작업의 실행을 허용하기 위해 시스템 콜이 제공된다. 시스템 콜을 실행하기 위해 프로그램은 trap 특수 명령어를 실행해야 한다. trap을 통해 커널 모드로 진입하여 프로세스가 요청한 작업을 처리한 후에 return-from-trap을 통해 사용자 모드로 돌아온다.

 

프로세스 간 전환

협조 방식: 시스템 콜 호출시 까지 대기.

협조 방식을 사용하는 OS는 yield 시스템 콜을 제공한다. 이 시스템 콜을 통해 OS가 CPU를 다른 프로세스에게 할당할 수 있는 기회를 제공한다. 협조 방식의 스케줄링 시스템은 근본적으로 수동적이다. 프로세스가 무한 루프에 빠져 시스템 콜을 호출할 수 없다면 문제가 발생할 수 있다.

 

비협조 방식: OS가 제어권 확보

시스템 콜이 없더라도 OS에게 제어권을 넘기려면 타이머 인터럽트를 이용하면 된다. 타이머는 인터럽트를 주기적으로 발생시키며 인터럽트가 발생하면 처리하는 과정에서 제어권이 자연스럽게 OS에게 넘어오게 된다.

 

문맥의 저장과 복원

어떤 방식으로든 OS가 제어권을 다시 획득하면 현재 실행중인 프로세스를 계속 실행할 것인지 아니면 다른 프로세스로 전환할 것인지를 스케줄러에 의해 결정해야 한다.

현재 실행중인 프로세스를 중단하고 다른 프로세스를 실행하기로 결정했다면 OS는 문맥 교환(context switching) 코드를 실행시킨다. 현재 실행중인 프로세스의 레지스터 값들을 저장하고 새로 실행될 프로세스의 레지스터 값을 복원하는 것이다.

 

[7장. 스케줄링 : 개요]

 

작업의 반환 시간 기준

  • FIFO: 단순하고 구현하기 쉽다. CPU를 많이 필요로 하지 않는 프로세스들이 CPU를 오랫동안 사용하는 프로세스가 끝날때 까지 기다리기 때문에(convoy effect) 이런 경우 평균 반환시간이 급격하게 늘어난다.
  • 최단 작업 우선(SJF): 가장 짧은 실행 시간을 가진 작업을 먼저 실행시키기 때문에 convoy effect를 해결할 수 있다. 모든 작업이 동시에 도착한다면 SJF가 최적의 스케줄링 알고리즘임을 증명할 수 있다. 하지만 동시에 도착하지 않고 실행시간이 긴 작업이 먼저 실행되는 도중 짧은 작업들이 도착해도 앞의 작업을 기다릴수밖에 없기때문에 convoy effect가 다시 발생하게 된다.
  • 최소 잔여시간 우선(STCF): SJF에 선점 기능을 추가했다. 새로운 작업이 도착하면 현재 실행중인 작업의 잔여 실행 시간과 새로운 작업의 잔여 실행 시간을 비교하여 작은 쪽의 작업을 스케줄한다.

 

작업의 응답 시간 기준

  • 라운드 로빈(RR): 작업이 끝날때까지 기다리지 않고 일정 시간 실행한 후 실행 큐의 다음 작업으로 전환한다. 타임 슬라이스의 길이는 타이머 인터럽트의 배수여야 한다. 타임 슬라이스의 길이가 짧을수록 응답 시간은 짧아지지만 문맥 교환이 그만큼 잦게 일어나서 성능 하락이 발생한다. 반환 시간만을 기준으로 삼을 경우 RR을 포함한 공정한 정책은 반환 시간이 좋지 않다.

 

프로세스가 입출력 요청으로 인해 대기 상태가 되는 경우 다른 프로세스가 CPU를 사용하여 연산을 중첩하면 시스템 사용률을 향상시킬 수 있다.

반환 시간과 응답 시간 중 하나를 최적화 하면 나머지 하나는 좋지 않은 특성을 나타낸다.

 

[8장. 스케줄링: 멀티 레벨 피드백 큐]

 

MLFQ가 해결하고자 하는 기본적인 문제는 두가지이다.

  • 짧은 작업을 먼저 실행시켜 반환 시간을 최적화하고자 한다
  • 응답시간을 최적화한다

 

MLFQ는 여러개의 큐로 구성되며 각각 다른 우선순위가 배정되고 실행 준비가 된 프로세스는 이 중 하나의 큐에 존재한다. 큐에 둘 이상의 작업이 존재할 수 있으며 같은 우선순위를 가지고 이 작업들 사이에는 RR 알고리즘이 사용된다.

MLFQ의 핵심은 우선순위를 정하는 방식이다.

기본 규칙은 우선순위가 높은 작업이 실행되고 우선순위가 같다면 RR 방식으로 실행된다.

스케줄러는 작업의 길이를 알 수 없기 때문에 일단 짧은 작업이라고 가정하여 높은 우선순위를 부여한다. 정말로 짧은 작업이면 금방 종료될것이고 긴 작업이라면 천천히 아래 큐로 이동하기 때문에 스스로 긴 배치형 작업이라는 것을 증명하게 되어서 MLFQ는 SJF를 근사할 수 있다.

 

현재 MLFQ의 문제점

  • 기아 상태가 발생할 수 있다.
  • 사용자가 스케줄러를 자신에게 유리하도록 동작시킬 수 있다.
  • 프로그램은 시간 흐름에 따라 특성이 변할수 있지만 현재 상태로는 반영하지 못한다.

일정 시간마다 우선순위를 일괄적으로 상향 조정하면 기아 상태와 변경된 특성에 적합한 스케줄링 방법을 적용할 수 있다. 하지만 적절한 일정 시간을 결정하는것은 매우 어렵다.

MLFQ의 각 단계에서 CPU 총 사용 시간을 측정하면 스케줄러를 자신에게 유리하게 동작시킬 수 있는 문제까지 막을 수 있다.

 

요약

우선순위가 높은 작업이 실행된다.

우선순위가 같으면 해당 작업들은 RR 방식으로 실행된다.

작업이 시스템에 들어가면 최상위 큐에 배치된다.

작업이 지정된 단계에서 배정받은 시간을 소진하면 작업의 우선순위는 한 단계 감소한다.

일정 주기가 지난 후 시스템의 모든 작업을 최상위 큐로 이동시킨다.

작업의 특성에 대한 정보 없이 작업의 실행을 관찰하여 생긴 과거 기록들을 토대로 우선순위를 지정한다.

 

[9장. 스케줄링: 비례 배분]

 

반환 시간이나 응답 시간을 최적화하는 대신 프로세스들에게 CPU의 일정 비율을 보장하는 것이 목적이다.

추첨권 이라는 무작위성 개념이 근간을 이룬다.

 

추첨 기법

  • 추첨권 화폐: 사용자가 추첨권을 자신의 화폐 가치로 추천권을 자유롭게 할당할 수 있도록 허용한다.
  • 추천권 양도: 일시적으로 다른 프로세스에게 추첨권을 넘겨줄 수 있다.
  • 추첨권 팽창: 일시적으로 자신이 소유한 추첨권의 수를 늘이거나 줄일 수 있다. 프로세스들이 상호 경쟁하는 상황에서는 의미가 없다.

구현이 매우 단순하다는 장점이 있다. 정렬 순서는 알고리즘의 정확성에 영향을 주지는 않지만 정렬된 경우 검색 횟수가 최소화되는 것을 보장한다.

 

무작위성을 이용하면 단순하게 만들 수 있지만 정확한 비율을 보장할 수 없다.

결정론적 공정 배분 스케줄러인 보폭 스케줄링을 이용한다.

 

리눅스 CFS(Completely Fair Scheduler)

효율성과 확장성이라는 장점을 가지고 있다. (추가내용들은 우선 생략)

 

[10장. 멀티프로세서 스케줄링 (고급)]

 

응용 프로그램을 병렬로 작성할 때 쓰레드를 이용한다.

캐시는 시간적 지역성과 공간적 지역성에 기반을 둔다.

 

단일 큐 멀티프로세서 스케줄링(SQMS): 단순함이 장점이다. 확장성과 캐시 친화성에 문제가 있다. 캐시 진화성은 SQMS 스케줄러가 가능한 한 프로세스가 동일한 CPU에서 재실행될 수 있도록 시도한다.

 

멀티 큐 멀티프로세서 스케줄링(MQMS): 일부 시스템은 멀티 큐로 둔다. 확장성이 좋다. CPU 개수가 증가할수록 큐의 개수도 증가하므로 락과 캐시 경합이 더이상 문제가 되지 않는다. 하지만 워크로드의 불균형이 발생한다. 지속적인 이주를 통해 해결할 수 있고 이주 필요 여부는 작업 훔치기라는 기술을 통해 결정한다.

 

[13장. 주소 공간의 개념]

 

여러 프로그램이 메모리에 동시에 존재하려면 보호가 중요한 문제가 된다.

 

주소 공간

주소 공간은 실행 프로그램의 모든 메모리 상태를 갖고 있다.

메모리 가상화: 물리 메모리를 공유하는 다수의 프로세스에게 프로세스 전용의 커다란 주소 공간이라는 개념을 제공하는 것

 

가상 메모리 시스템(VM)의 주요 목표

  • 투명성(transparency): OS는 프로세스가 가상 메모리의 존재를 인지하지 못하도록 구현 해야한다.
  • 효율성(efficiency): OS는 가상화가 시간과 공간 측면에서 효율적이도록 해야한다.
  • 보호(protection): OS는 프로세스를 자신 및 다른 프로세스들로부터 보호해야 한다.

 

[15장. 주소 변환의 원리]

 

주소 변환: 가상 주소를 물리 주소로 변환하는 것

 

동적(하드웨어 기반) 재배치

각 CPU마다 베이스와 바운드(한계)라는 2개의 하드웨어 레지스터를 사용한다.

물리 주소 = 가상 주소 + 베이스

바운드는 프로세스가 생성한 모든 주소가 합법적이고 프로세스의 범위 내에 있다는 것을 확인한다. 그렇지 않다면 예외를 발생시키고 예외 핸들러가 실행되도록 조치해야 한다.

주소 변환에 도움을 주는 프로세서의 일부를 메모리 관리 장치(MMU)라고 부르기도 한다.

 

운영체제 이슈

프로세스가 생성될 때 OS는 주소 공간이 저장될 메모리 공간을 찾아 조치를 취해야 한다.

프로세스가 종료될 때 메모리를 회수하여 다른 프로세스나 OS가 사용할 수 있게 해야 한다.

OS는 문맥 교환이 일어날 때도 몇 가지 추가 조치를 취해야 한다.

 

동적 재배치는 내부 단편화가 일어나서 비효율적이다.

 

[16장. 세그먼테이션]

 

세그먼테이션: 베이스/바운드의 일반화

MMU에 하나의 베이스와 바운드값이 존재하는게 아니라 세그먼트(코드, 스택, 힙)마다 베이스와 바운드 값이 존재한다.

가상 주소의 최상위 비트 몇 개를 세그먼트 종류를 나타내는 데 사용한다.

세그먼트 종류를 나타내는 데 최상위 2비트를 사용하고 세그먼트는 3종류만 존재하기 때문에 세그먼트 하나는 미사용으로 남게되어 전체 주소 공간의 1/4는 사용이 불가능하다.

 

스택

다른 세그먼트들과 다르게 반대 방향으로 확장된다.(LIFO) 다른 형식의 변환을 사용한다.

 

공유 지원

메모리를 절약하기 위해 주소 공간들 간에 특정 메모리 세그먼트를 공유하는 것이 유용하다. 특히, 코드 공유가 일반적이다. 공유를 지원하기 위해 세그먼트마다 protection bit를 추가하여 세그먼트를 읽거나 쓸 수 있는지, 혹은 세그먼트의 코드를 실행시킬 수 있는지 나타낸다. 코드 세그먼트를 읽기 전용으로 설정하면 주소 공간의 독립성을 유지하면서도 여러 프로세스가 주소 공간의 일부를 공유할 수 있다.

 

시스템이 각 주소 공간(세그먼트) 단위로 가상 주소 공간을 물리 메모리에 재배치하기 때문에 전체 주소 공간이 하나의 베이스-바운드 값을 갖는 방식에 비해 물리 메모리를 저장할 수 있다.

세그먼테이션 도입을 위해서 OS가 몇 가지 문제를 해결해야 한다.

  • 문맥 교환
  • 세그먼트 크기 변경
  • 미사용 중인 물리 메모리 공간의 관리: 각 세그먼트의 크기가 다를 때 작은 공간들이 남아서 새로운 세그먼트에 할당하기도 힘들고 확장하는데도 도움이 되지 않는다.(=외부 단편화) 기존의 세그먼트를 정리하여 물리 메모리를 압축하는것이 하나의 해결책이다.

 

세그먼테이션에 필요한 연산은 쉽고 구현에도 적합하기 때문에 속도가 빠르다. 오버헤드도 최소이다. 하지만 세그먼트의 크기가 일정하지 않아서 외부 단편화가 발생하고 유연하지 않다.

 

[17장. 빈 공간 관리]

 

공간의 크기가 가변적인 경우(세그먼테이션) 빈 공간을 관리하기가 어렵다.

 

저수준 기법들

  • 분할과 병합: 메모리 청크보다 큰 공간을 요청한 경우 요청이 실패하지만 더 작은 공간을 요청한 경우 공간을 분할하여 요청을 충족시킨다. 메모리 청크가 반환될 때 해제된 빈 공간이 기존에 존재하던 빈 공간과 인접한 경우 병합한다.
  • 할당된 공간의 크기 파악
  • 빈 공간 리스트 내장
  • 힙의 확장: 힙의 공간을 모두 사용하면 OS에 더 많은 메모리를 요청한다.

 

기본 전략

  • 최적 적합: 빈 공간 리스트를 탐색하여 요청한 크기와 같거나 더 큰 메모리 청크를 찾은 후 그 중에서 가장 작은 크기의 청크를 반환하여 사용한다. 공간의 낭비를 줄일 수 있지만 항상 완전탐색을 하기 때문에 성능이 저하된다.
  • 최악 적합: 마찬가지로 완전탐색으로 인해 성능이 저하된다.
  • 최초 적합: 요청보다 큰 첫번째 블럭을 찾아서 요청만큼 반환한다. 속도가 빠르다. 하지만 리스트의 앞쪽에 크기가 작은 블럭이 모여있다면 탐색 시간이 길어진다. 주소-기반 정렬을 사용하여 좀 더 효율적으로 관리할 수 있다.
  • 다음 적합: 리스트의 처음부터 탐색하는 대신 마지막으로 찾은 원소를 가리키는 추가의 포인터를 유지한다.

 

다른 접근법

  • 개별 리스트
  • 버디 할당: 요청이 들어오면 크기에 맞는 공간이 될 때까지 반으로 계속 분할한다. 블럭히 해제될 때 2의 배수 버디가 비어있는지 확인하여 전체 빈 공간이 복원되거나 버디가 사용 중이라는 것이 밝혀질 때까지 재귀적으로 계속 병합해나간다.

 

위의 방식들은 확장성에 문제가 있다.

 

[18장. 페이징: 개요]

 

페이지: 프로세스의 주소 공간을 가변 크기인 논리 세그먼트로 분할한 것이 아닌 고정 크기의 단위로 나눈것.

페이지 테이블: 주소 공간의 가상 페이지 주소 변환 정보를 저장한다.

 

페이징을 사용하면 유연성과 빈 공간 관리의 단순함을 얻을 수 있다.

 

페이지 테이블이 메모리 상에서 너무 커지면 이로 인해 처리 속도가 저하될 수 있다.

 

[19장. 페이징: 더 빠른 변환(TLB)]

 

주소 변환을 빠르게 하기 위해 변환 색인 버퍼(translation lookaside buffer, TLB)를 도입한다. 일종의 페이지 테이블에서의 캐시라고 보면 된다. 가상 메모리 참조 시, TLB에 원하는 정보가 있는지 먼저 확인하고 있다면(hit) 페이지 테이블에 접근하지 않고 변환을 바로 수행한다.

 

*TLB를 포함한 캐싱은 시간적 지역성과 공간적 지역성에 기반을 두고 있다.

 

TLB 미스가 발생한 경우 페이지 테이블에 접근하여 변환 정보를 찾아야 한다.

 

문맥 교환시 TLB에 이전 프로세스가 사용한 VPN이 남는다. 문맥 교환이 발생할 때마다 TLB를 비우면 해결이 되지만 또 다른 문제들이 야기된다. 그래서 주소 공간 식별자(ASID) 필드를 추가하여 VPN이 같더라도 ASID로 올바른 주소 변환을 할 수 있다.

 

[20장. 페이징: 더 작은 테이블]  / 차후 추가공부 예정

 

페이지 테이블의 크기를 줄일 수 있는 간단한 방법은 페이지의 크기를 증가시키면 된다. 단점으로는 내부 단편화가 증가한다. 페이지 테이블의 크기는 줄었을지언정 오히려 메모리 고갈을 더 가속화 시키게 된다.

 

세그먼테이션-페이징 혼용

프로세스의 전체 주소 공간을 위해 하나의 페이지 테이블을 두는 대신 논리 세그먼트마다 따로 페이지 테이블을 둔다.

TLB 미스 발생시 세그먼트 비트를 사용해서 어떤 베이스와 바운드 쌍을 사용할지 결정한다.

 

멀티 레벨 페이지 테이블

세그먼테이션을 사용하지 않는 방법이다. 선형 페이지 테이블을 트리 구조로 표현한다.

페이지 테이블을 페이지 크기의 단위로 나누고 페이지 내의 valid 비트가 모두 0이면 할당하지 않는다. 페이지 디렉터리라는 자료 구조를 사용한다. PDE의 valid가 1이라면 해당 PDE가 가리키는 페이지가 최소 하나 이상의 PTE의 valid bit가 1이라는 의미이다.

사용된 주소 공간의 크기에 비례해서 페이지 테이블이 할당된다는 장점이 있다. 또한 메모리 관리가 용이하다.

하지만 TLB miss 발생시 주소 변환을 위해 두 번의 메모리 로드가 발생한다.(페이지 디렉터리와 PTE 접근 각 한번)

페이지 테이블의 크기를 줄인 대신 TLB miss시의 비용이 커진 셈이다.

 

[21장. 물리 메모리 크기의 극복: 메커니즘]

 

프로세스에게 굳이 큰 주소 공간을 제공하는 이유는 편리함과 사용 용이성이 좋기 때문이다.

 

스왑 공간: 디스크에 페이지들을 저장할 수 있는 일정 공간.

 

TLB hit가 되면 물리 주소를 얻고 메모리로 바로 가져온다.

TLB miss가 되면 PTBR를 사용하여 PTE를 추출한다. 해당 페이지 항목이 유효하고 관련 페이지가 물리 메모리에 존재하면 PFN 정보를 추출하여 TLB에 탑재시킨 후 명령어를 재실행하면 TLB hit가 된다.

하지만 이것은 모든 페이지가 물리 메모리에 존재할때의 가정이다.

 

PTE에 해당 페이지가 물리 메모리에 존재하지 않는다는 것을 표현하려면 present bit를 이용한다. 해당 비트가 1이라면 위의 메커니즘이 적용되고 만약 0이라면 페이지 부재(page fault)가 발생한다. 이 때, 페이지 부재를 처리하기 위해 제어권이 OS에 넘어가고 페이지 부재 핸들러가 실행된다.

 

페이지 부재 발생시 OS는 페이지 테이블에서 해당 페이지의 디스크 상 위치를 파악하여 스왑 공간으로부터 페이지를 가져와(page in) 메모리로 탑재시킨다.

page in을 할 때, 물리 메모리에 빈 공간이 없다면 기존 페이지를 스왑 공간으로 내보내야 한다.(page out)

이 과정을 교체라고 하고 교체할 페이지를 선택하는 것을 페이지 교체 정책이라고 한다.

교체가 완료되면 OS는 페이지 테이블을 갱신하고 명령어를 재시도한다. TLB miss가 발생하게 되고 한번 더 재시도를 하면 TLB hit가 된다.

 

TLB 미스 발생 시 세가지의 경우가 있다

  • 페이지가 유효하고 존재하는 경우: TLB miss 핸들러가 PTE에서 PFN을 가져와 명령어를 재시도 한다.
  • 페이지가 유효하지만 존재하지 않는 경우: 페이지 부재가 발생했기 때문에 페이지 부재 핸들러가 실행되어 처리한다.
  • 페이지가 유효하지 않은 경우: OS의 트랩 핸들러에 의해 처리되도록 한다.

 

물리 메모리의 여유 공간이 고갈된 후에 교체 알고리즘이 작동하는 것은 그리 효율적이지 않다.

어느정도의 여유 공간을 가지고 있어야 하기 때문에 여유 공간의 크기가 최소값보다 작아지면 스왑 데몬(페이지 데몬)이라는 백그라운드 쓰레드를 실행하여 여유 공간의 크기가 최대값에 이를때까지 페이지를 제거한다.

다수의 페이지들을 클러스터나 그룹으로 묶어서 한번에 스왑 파티션에 저장하면(클러스터링) 오버헤드를 경감시켜서 디스크의 효율을 높일 수 있다.

 

[22장. 물리 메모리 크기의 극복: 정책]

 

메인 메모리는 가상 메모리의 페이지를 가져다 놓기 위한 캐시로 볼 수 있다.

페이지 교체 정책의 목표는 캐시 미스를 최소화 시키는 것이다.(페이지 부재 최소화) 한편으로는 캐시 히트를 최대화 하는 것도 목표라고 할 수 있다.

 

최적 교체 정책

가장 나중에 접근될 페이지를 교체하는것이 최적이라는 이론. 미래를 미리 알 수 없기 때문에 구현이 불가능하다. 다만 정책 수립시 비교 기준으로는 적합하다.

 

간단한 정책

  • FIFO: 큐에 먼저 들어온 페이지가 먼저 교체된다. 구현이 매우 쉽지만 페이지의 중요도를 판단하지 않고 무조건 선입선출을 하기때문에 성능이 좋지 않다.
  • 무작위: 구현이 매우 쉽지만 성능이 매번 다르다. 평균적으로는 FIFO보다 약간 좋다.

 

과거 이력 기반 알고리즘

지역성의 원칙에 기반을 두어서 빈도수와 최근성을 활용한다.

  • LRU(least recently used): 가장 오래전에 사용된 페이지 교체. 최적 기법과 근사한 성능이다.
  • LFU(least frequently used): 가장 적은 빈도로 사용된 페이지 교체

 

쓰래싱: 끊임없이 페이징이 발생하는 상황

 

[23장. 완전한 가상 메모리 시스템]

'이론 > 운영체제' 카테고리의 다른 글

[OSTEP] III. 영속성  (0) 2022.08.21
[OSTEP] II. 병행성  (0) 2022.08.21
[OSTEP] 2장. 운영체제 개요  (0) 2022.08.19
쉽게 배우는 운영체제  (0) 2022.08.16

CPU 가상화

하나 또는 소규모 CPU 집합을 무한개의 CPU가 존재하는 것처럼 변환하여 동시에 많은 수의 프로그램을 실행시키는 것.

API는 OS와 사용자가 상호작용할 수 있는 주된 방법이다.

 

메모리 가상화

각 프로세스는 자신만의 가상 주소 공간을 가진다. OS는 이 가상 주소 공간을 컴퓨터의 물리 메모리로 매핑한다.

실행중인 프로그램의 입장에서는 자기 자신만의 물리 메모리를 갖는것처럼 보인다.

 

병행성

프로그램이 동시에 많은 일을 하려 할 때 발생하는, 그리고 반드시 해결해야 하는 문제. 병행성 문제는 우선 OS 자체에서 발생한다. OS뿐만 아니라 멀티 스레드 프로그램에서도 동일한 문제가 발생한다. 명령어가 원자적(한꺼번에)으로 실행되지 않기 때문에 의도하지 않은 일이 발생할 수 있다.

 

영속성

CPU나 메모리 가상화와는 달리 OS는 프로그램 별로가상 디스크를 따로 생성하지 않는다.

OS는 syscall이라는 표준화된 방법으로 장치들을 접근할 수 있게 한다. OS는 마치 표준 라이브러리처럼 보이기도 한다.

 

설계 목표

OS는 CPU, 메모리, 디스크와 같은 물리 자원을 가상화 한다.

OS는 병행성과 관련된 복잡한 문제를 처리한다.

OS는 파일을 영속성으로 저장하여 아주 오랜 시간 안전한 상태에 있게 한다.

 

OS의 설계와 구현에 중요한 목표는 성능이다. (=오버헤드의 최소화)

또 다른 목표는 응용 프로그램 간의 보호, 그리고 OS와 응용 프로그램 간의 보호이다. 보호는 고립 원칙의 핵심이다.

에너지 효율성, 보안(보호의 확장), 이동성의 목표도 있다.

 

멀티프로그래밍 시대

작업들을 빠르게 번갈아가며 실행해서 CPU 사용률을 향상시킨다. 메모리 보호와 병행성 문제에 대한 이해가 중요하다.

'이론 > 운영체제' 카테고리의 다른 글

[OSTEP] III. 영속성  (0) 2022.08.21
[OSTEP] II. 병행성  (0) 2022.08.21
[OSTEP] I. 가상화  (0) 2022.08.19
쉽게 배우는 운영체제  (0) 2022.08.16

'운영체제: 아주 쉬운 세 가지 이야기' 책이 배송되기 전에 큰 그림을 잡기 위해 빠르게 훑어보려 도서관에서 빌려왔다.

'컴퓨터 구조 및 설계' 를 읽고나서 보니 겹치는 영역이 많아서 읽기 편하다.

아래의 내용은 '쉽게 배우는 운영체제' 책의 내용을 정리한 것이다.

 

[Chapter 1. 운영체제의 개요]

운영체제: 사용자에게 편리한 인터페이스 환경을 제공하고 컴퓨터 시스템의 자원을 효율적으로 관리하는 소프트웨어

운영체제의 역할

  • 자원 관리 (효율성)
  • 자원 보호 (안정성)
  • 하드웨어 인터페이스 제공 (확장성)
  • 사용자 인터페이스 제공 (편리성)

 

그리드 컴퓨팅: 분산 시스템의 한 분야.

 

운영체제의 구조

커널: 프로세스 관리, 메모리 관리 등 운영체제의 핵심적인 기능을 모아놓은 것

인터페이스: 커널에 서용자의 명령을 전달하고 실행 결과를 사용자에게 알려주는 역할

 

시스템 호출(system call): 커널이 자신을 보호하기 위해 만든 인터페이스. 커널이 제공하는 시스템 자원의 사용과 연관된 함수이다.

응용 프로그램이 하드웨어 자원에 접근하거나 운영체제가 제공하는 서비스를 이용하려 할 때는 sys-call을 사용해야 한다.

 

커널이 하는 일

  • 프로세스 관리
  • 메모리 관리
  • 파일 시스템 관리
  • 입출력 관리
  • 프로세스 간 통신 관리

 

단일형 구조 커널

커널의 핵심 기능을 구현하는 모듈들이 하나로 구성되어 있다.

 

계층형 구조 커널

비슷한 기능을 가진 모듈을 묶어서 하나의 계층으로 만들고 계층 간의 통신을 통해 운영체제를 구현하는 방식이다.

 

마이크로 구조 커널

기본적인 기능만 제공한다. 하나의 모듈이 실패하더라도 전체 운영체제가 멈추지 않는다.

 

[Chapter 2. 컴퓨터의 구조와 성능 향상]

폰노이만 구조: 모든 장치가 버스로 연결되어 있는 구조. 모든 프로그램은 메모리에 올라와야 실행할 수 있다.

 

CPU: 산술논리 연산장치, 제어장치, 레지스터로 구성됨

레지스터의 종류: DR, AR, PC, IR, MAR, MBR 등

 

버스는 CPU, 메모리, 주변장치 간에 데이터를 주고 받을 때 사용한다.

버스의 종류에는 제어 버스, 주소 버스, 데이터 버스가 있다.

버스의 대역폭은 한번에 전달할 수 있는 대역폭의 최대 크기를 말한다.

 

부팅: 운영체제를 메모리에 올리는 과정

 

버퍼: 속도에 차이가 있는 두 장치 사이에서 그 차이를 완화하는 역할

스풀: 버퍼와 비슷한 개념이지만 프로그램 간에 배타적이다

 

캐시: 메모리와 CPU간의 속도 차이를 완화하기 위한 장치

캐시의 변경된 데이터를 메모리에 반영하는 방법에는 즉시 쓰기(write-through)와 나중 쓰기(write-back)가 있다

 

인터럽트: 입출력 관리자가 CPU에게 보내는 작업 완료 신호. 신호를 받으면 CPU는 하던 일을 멈추고 옮겨진 데이터를 처리해야 한다. (컴퓨터 구조에서 봤던 내용이랑 조금 상이한것 같아서 확인이 필요)

 

직접 메모리 접근(DMA)

인터럽트 방식의 시스템을 구성하는 데 필수 요소이다.

메모리 매핑 입출력: DMA를 위한 공간을 분리하여 메모리의 일정 공간을 입출력에 할당하는 것.

사이클 훔치기: CPU와 DMA가 메모리에 동시에 접근시 DMA에게 메모리 사용 권한을 양보하는 것.

 

 

병렬 처리

여러개의 스레드를 동시에 처리하는 것을 멀티스레드라고 하며 파이프라인과 슈퍼스칼라가 있다.

 

병렬 처리시 고려 사항

  • 상호 의존성이 없어야 병렬 처리가 가능하다
  • 각 단계의 시간을 거의 일정하게 맞춰야 병렬 처리가 원만하게 이루어진다
  • 전체 작업 시간을 몇 단계로 나눌지 잘 따져보아야 한다

 

병렬 처리 기법

파이프라인: 명령어를 겹쳐서 실행하는 방법으로 하나의 프로세서에 여러개의 스레드를 실행하는 것

데이터 해저드, 구조적 해저드, 제어 해저드가 발생할 수 있다.

데이터 해저드는 전방전달(load-use data hazard의 경우 버블을 통해서만 해결할 수 있다), 구조적 해저드는 메모리를 두개로 나누어서, 제어 해저드는 지연과 예측 두가지로 해소할 수 있다. 특히 제어 해저드는 완전한 해결 방법이 없다.

 

슈퍼스칼라: 파이프라인이 처리할 수 있는 프로세서를 여러개 구성하여 여러개의 명령어가 동시에 실행되는 것. 대부분 슈퍼스칼라를 이용한다.

 

슈퍼파이프라인, 슈퍼파이프라인 슈퍼스칼라, VLIW(S/W적으로 병렬처리)기법도 있다

 

무어의 법칙: 반도체의 트랜지스터 집적도가 2년마다 2배씩 증가한다는 이론. 최근에는 정체되면서 잘 맞지 않게 되었다.

암달의 법칙: 컴퓨터 시스템의 일부를 개선할 때 전체적으로 얼마만큼의 최대 성능 향상이 있는지 계산하는데 사용한다.

 

[Chapter 3. 프로세스와 스레드]

프로그램과 프로세스: 프로그램은 저장장치에 저장되어있는 정적인 상태, 프로세스는 메모리에 올라온 동적인 상태.

프로그램을 메모리에 올릴때 운영체제 영역에 프로세스 제어 블록(PCB)를 생성한다.

 

간단한 프로세스의 네 가지 상태

  • 생성 상태: 프로세스 제어 블록(PCB) 생성
  • 준비 상태: CPU를 얻을 때 까지 기다리는 상태
  • 실행 상태: 작업을 수행. 시간내에 완료하지 못했다면 준비 상태로 돌아간다.
  • 완료 상태: PCB가 사라진 상태

입출력이 추가되면 준비와 실행 사이에 대기 상태가 추가된다. 이 다섯가지를 활성 상태라고 한다.

 

PCB의 구성

  • 포인터: PCB를 연결하여 큐를 구현할 때 사용
  • 프로세스 상태
  • PID
  • PC
  • 프로그램 우선순위
  • 각종 레지스터 정보
  • 메모리 관리 정보
  • 할당된 자원 정보
  • 계정 정보
  • PPID, CPID

 

문맥 교환(context switching): 기존의 프로세스가 나가고 새로운 프로세스를 받아들이는 작업

 

프로세스의 연산

 

프로세스의 구조

  • 코드 영역: 프로그래머가 작성한 코드가 탑재되는 영역. 읽기 전용으로 처리된다.
  • 데이터 영역: 코드가 실행되면서 사용하는 변수나 파일 등의 각정 데이터를 모아놓은 곳.
  • 스택 영역

 

프로세스의 생성과 복사

복사: fork() sys-call을 통해 복사한다. 프로세스의 생성 속도가 빠르고 추가 작업 없이 자원을 상속할 수 있다.

exec() sys-call시 프로세스는 그대로 둔 채 내용만 바꾼다. 이미 만들어진 프로세스의 구조를 재활용하는 것이다.

 

프로세스의 계층 구조는 동시에 여러 작업을 처리하고 종료된 프로세스의 자원을 회수하는 데 유용하다.

 

부모 프로세스는 자원을 회수하기 위해 자식 프로세스가 끝날 때까지 기다려야 하지만 부모 프로세스가 먼저 종료되거나 자식 프로세스가 비정상 적으로 종료될 시 프로세스가 비정상적으로 남게 되는데 이를 미아 프로세스(orphan process) 또는 좀비 프로세스(zombie process)라고 한다.

 

스레드

프로세스의 코드에 정의된 절차에 따라 CPU에 작업 요청을 하는 실행 단위이다.

프로세스는 여러개의 스레드로 구성되어 있다.

서로 독립적인 프로세스는 데이터를 주고받을 때 프로세스 간 통신(IPC)을 이용한다.

 

멀티스레드: 프로세스 내의 작업을 여러개의 스레드로 분할

멀티태스킹: 하나의 프로세서가 여러개의 스레드를 시분할 시스템으로 작업

멀티프로세싱: 슈퍼스칼라와 같다

CPU 멀티스레드: H/W적인 방법으로 하나의 프로세서에서 여러 스레드를 동시에 처리하는 병렬 처리 기법

 

프로세스 복사시 멀티태스킹은 정적인 영역까지 모두 복사하기 때문에 중복되는 영역이 많아져서 자원의 낭비가 있다. 하지만 멀티스레드는 정적인 영역은 공유하면서 동적인 영역만 따로 관리하기 때문에 자원을 아껴서 효율적이다.

 

멀티스레드의 장점

  • 응답성 향상: 한 스레드가 입출력으로 인해 작업이 진행되지 않더라도 다른 스레드가 작업을 계속하여 사용자의 작업 요구에 빨리 응답할 수 있다.
  • 자원 공유: 모든 스레드가 자원을 공유하게 되어 작업을 원활하게 진행할 수 있다.
  • 효율성 향상: 불필요한 자원의 중복을 막아서 시스템의 효율의 향상된다.
  • 다중 CPU 지원

 

멀티스레드의 단점

하나의 스레드에서 문제 발생시 프로세스 전체에 영향을 미친다.

 

멀티스레드 모델

커널 스레드: 커널이 직접 관리하는 스레드

사용자 스레드: 라이브러리에 의해 구현된 일반적인 스레드. 커널 스레드를 사용하려면 sys-call로 사용해야 한다.

  • 사용자 레벨 스레드: OS가 멀티스레드를 지원하지 않을 때 사용하는 방식
  • 커널 레벨 스레드: 커널이 멀티스레드를 지원하는 방식. 사용자 레벨 스레드와 장단점이 반대이다.
  • 멀티 레벨 스레드: 위의 두개를 혼합한 방식.

 

프로세스의 영역

정적 할당 영역: 코드, 데이터

동적 할당 영역: 스택, 힙

  • 스택: 함수 호출과 복귀시, 지역변수에 사용. 스레드가 작동하는 동안 추가되거나 삭제되는 동적 할당 영역.
  • 힙: 동적으로 할당되는 변수 영역.

 

wait() sys-call을 통해 부모-자식 프로세스 간의 동기화를 맞춰줄 수 있다.

 

[Chapter 4. CPU 스케줄링]

고수준 스케줄링: 시스템 내의 전체 작업 수를 조절한다(멀티프로그래밍 정도). 고수준 스케일링에 따라 시스템에서 동시에 실행 가능한 프로세스의 총 개수가 정해진다.

 

저수준 스케줄링: 고수준의 반대이다. 프로세스의 상태를 변경한다. 실제로 작업이 이루어지는 단계이다.

 

중간 수준 스케줄링: 고수준과 저수준 사이에서 중지와 활성화를 통해 프로세스 수를 조절하여 과부하를 막는다. 프로세스의 보류 상태에 해당한다. 저수준 스케줄링이 원만하게 이루어지는 buffer 역할을 한다.

 

고수준->중간수준->저수준 순으로 이루어진다.

 

스케줄링의 목적

  • 공평성: 모든 프로세스가 자원을 공평하게 배정받아야 한다
  • 효율성: 시스템 자원이 유휴 시간 없이 사용되도록 해야한다
  • 안정성: 우선순위를 통해 중요 프로세스가 먼저 작동하게 하고 나쁜 프로세스로부터 자원을 보호해야 한다
  • 확장성: 프로세스가 증가해도 시스템이 안정적으로 작동하도록 조치해야 한다
  • 반응 시간 보장: 시스템은 적절한 시간 안에 프로세스의 요구에 반응해야 한다
  • 무한 연기 방지: 특정 프로세스의 작업이 무한히 연기되어서는 안된다

하지만 안정성과 효율성을 높이기 위해 일정 부분 공평성을 희생한다.

 

선점형 스케줄링: 운영체제가 프로세서를 강제로 빼앗을 수 있다. (예: 인터럽트) 대부분의 저수준 스케줄러에서 사용한다.

비선점형 스케줄링: 다른 프로세스가 프로세서를 빼앗을 수 없다

 

CPU 집중 프로세스: CPU 버스트가 많은 프로세스

I/O 집중 프로세스: I/O 버스트가 많은 프로세스

두 개가 같이 있을 때는 I/O 집중 프로세스를 먼저 실행 상태로 옮기는 것이 효율적이다.

 

전면 프로세스: GUI 운영체제에서 화면의 맨 앞에 놓인 프로세스. 상호작용 프로세스라고도 한다.

후면 프로세스: 사용자와 상호작용이 없는 프로세스. 일괄 작업 프로세스라고도 한다.

 

(우선순위 높음) <-> (우선순위 낮음)

커널 프로세스           일반 프로세스

전면 프로세스           후면 프로세스

I/O 집중 프로세스     CPU 집중 프로세스

 

다중 큐

프로세스의 우선순위를 배정하는 방식

  • 고정 우선순위: 프로세스가 끝날때 까지 우선순위가 바뀌지 않는다. 구현은 쉽지만 시스템의 변화에 대응하기 어렵다.
  • 변동 우선순위: 프로세스 작업 도중에 우선순위가 변한다. 구현이 어렵다.

반전 우선순위를 통해 시스템의 효율성을 향상시킬 수 있다.

 

대기 상태에서도 다중 큐를 사용한다.

준비 상태의 다중 큐는 한번에 하나의 프로세스를 꺼내서 프로세서를 할당하는 반면, 대기 큐는 여러개의 PCB를 동시에 꺼내서 준비 상태로 옮긴다. 여기서 인터럽트 벡터라는 자료 구조를 사용한다.

 

스케줄링 알고리즘

스케줄링 알고리즘의 효율성을 파악하는 평가 기준은 다음과 같다.

  • CPU 사용률
  • 처리량
  • 대기 시간
  • 응답 시간
  • 반환 시간

스케줄링 알고리즘의 성능을 비교할 때는 주로 평균 대기 시간을 본다.

 

비선점형 알고리즘

  • FCFS 스케줄링: 준비 큐에 도착한 순서대로 프로세서를 할당하는 비선점형 방식. FIFO 스케줄링 이라고도 한다.
  • SJF 스케줄링: 준비 큐에 있는 프로세스 중에 실행시간이 가장 짧은 작업부터 프로세서를 할당한다. 하지만 운영체제가 프로세스의 종료 시간을 정확하게 예측하기 어렵고 공평성을 위배하는 문제가 있다. 만약 실행시간이 짧은 작업이 계속해서 들어와 특정 작업이 계속 연기된다면 기아(starvation) 현상이 발생해서 잘 사용하지 않는다.
  • HRN 스케줄링: SJF에서 발생할 수 있는 기아 현상을 해결하기 위해 만들어졌다. 하지만 공평성은 여전히 위배될 수 있다.

 

선점형 알고리즘

  • 라운드 로빈 스케줄링: 할당받은 시간 동안 작업을 하고 완료하지 못하면 준비 큐의 맨 뒤로 가서 차례를 기다리는 방식이다. FCFS 스케줄링과 유사하다. 문맥 교환 시간이 추가되어 실제 평균 대기시간은 더 길어진다.
  • SRT 스케줄링: SJF+라운드 로빈 스케줄링을 혼합한 방식이다. 남은 시간이 적은 프로세스에 프로세서를 먼저 할당한다. SJF 스케줄링과 마찬가지로 종료 시간을 예측하기 어렵고 기아 현상이 일어나기 때문에 잘 사용하지 않는다.
  • 다단계 큐 스케줄링: 우선순위에 따라 준비 큐를 여러개 사용한다. 큐는 라운드 로빈 방식으로 운영된다. 우선순위가 높은 상위 큐 프로세스의 작업이 끝나기 전까지 하위 큐 프로세스의 작업을 할 수 없다.
  • 다단계 피드백 큐 스케줄링: 다단계 큐 스케줄링의 문제점을 보완한 방식이다. 프로세서를 사용하고 난 프로세스의 우선순위가 낮아진다. 우선순위에 따라 타임 슬라이스가 다르다. 일반적으로 사용하는 방식이다.

 

우선순위 스케줄링: 선점형, 비선점형 알고리즘에 모두 사용할 수 있다.

 

인터럽트 처리

동기적 인터럽트: 프로세스가 실행중인 명령어로 인해 발생한다. (=사용자 인터럽트)

비동기적 인터럽트: 실행중인 명령어와 무관하게 발생한다. H/W적인 오류로 발생한다.

 

시스템에는 수많은 인터럽트가 존재하고 각각의 인터럽트에는 고유번호(IRQ)가 있다. 인터럽트가 여러개가 동시에 발생할수도 있는데 하나로 묶어서 처리하는 개념을 인터럽트 벡터라고 한다.

 

인터럽트 처리과정

  1. 인터럽트 발생시 현재 실행 중인 프로세스는 일시 정지가 되고 재시작을 위한 현재 프로세스 정보를 임시 저장한다
  2. 인터럽트 컨트롤러가 실행되어 처리 순서를 결정한다
  3. 먼저 처리할 인터럽트가 결정되면 인터럽트 벡터에 등록된 인터럽트 핸들러가 실행된다
  4. 인터럽트 핸들러가 처리를 마치면 일시 정지된 프로세서가 다시 실행되거나 종료된다

 

운영체제가 사용자 모드와 커널 모드를 전환하며 작업 처리를 하는 것을 이중 모드라고 한다. 이중 모드는 운영체제가 자원을 보호하기 위해 사용하는 기법이다.

 

[Chapter 5. 프로세스 동기화]

프로세스 간 통신(IPC): 같은 컴퓨터 내의 프로세스 뿐만 아니라 네트워크로 연결된 다른 컴퓨터와의 프로세스와의 통신도 포함된다.

 

바쁜 대기: 상태 변화를 살펴보기 위해 반복문을 무한 실행하며 대기하는 것

바쁜 대기를 해결하기 위해 동기화를 사용한다. 대기가 있는 통신은 동기화 통신, 대기가 없는 통신은 비동기화 통신이라고 한다.

 

IPC에서 가장 중요한 것은 프로세스 동기화이다.

 

IPC의 종류

  • 전역 변수: 동기화 문제가 있기때문에 바쁜 대기로 계속 주시해야한다.
  • 파일: 주로 부모-자식 관계 IPC에서 많이 사용된다. 운영체제가 프로세스 동기화를 제공하지 않아서 wait() sys-call을 이용해서 동기화를 한다.
  • 파이프: 운영체제가 제공하는 동기화 통신 방식이다. 단방향 통신이기 때문에 양방향 통신을 하려면 파이프 2개를 사용해야 한다. 이름 없는 파이프는 부모-자식같이 서로 관련있는 IPC에 사용되고 이름 있는 파이프는 FIFO라는 특수 파일을 이용하여 서로 관련없는 IPC에 사용된다.
  • 소켓: 네트워킹이라고 한다. 원격 프로시저 호출이나 소켓을 이용한다. 프로세스 동기화를 지원하고 하나만 사용해도 양방향 통신이 된다.

 

동기화를 지원하는 IPC에는 open()과 close()가 사용된다.

 

두개 이상의 프로세스가 공유 자원을 병행적으로 읽거나 쓰는 상황을 '경쟁 조건(race condition)이 발생했다' 고 한다.

 

임계구역(critlcal section): 공유 자원 접근 순서에 따라 실행 결과가 달라지는 프로그램의 영역. 임계구역에서는 프로세스들이 동시에 작업하면 안된다. 전역 변수 뿐만 아니라 하드웨어 자원을 사용할 때도 적용되는 개념이다.

 

생산자-소비자 문제(produce-consumer problem)

공유 변수에 두개 이상의 프로세스가 접근 타이밍을 맞추지 않고 동시에 접근해서 공유 변수의 값이 꼬여 race condition이 발생하는 것

 

임계구역 해결 조건

  • 상호 배제(mutual exclusion): 한 프로세스가 임계구역에 들어가면 다른 프로세스는 임계구역에 들어갈 수 없다. race conditon은 상호 배제를 지키지 않아서 발생하는 문제이다.
  • 한정 대기(bounded wating): 어떤 프로세스도 무한 대기하지 않아야 한다.
  • 진행의 융통성(progress flexibility): 한 프로세스가 다른 프로세스의 진행을 방해해서는 안 된다.

 

임계구역 문제를 해결하는 단순한 방법은 잠금(lock)을 이용하는 것이다.

임계구역 해결 조건을 고려한 코드 설계

  • 상호 배제 문제: 전역변수(lock)를 통해 임계구역을 잠구어 하나의 프로세스만 사용할 수 있게 한다. 하지만 임계구역에 진입 후 lock을 true로 바꾸기 직전에 타임아웃으로 준비상태에 들어간다면 이후 두 프로세스는 임계구역에 동시에 접근하게 된다. 또한 lock=true라면 다른 프로세스는 잠금이 풀리기를 기다리며 바쁜 대기를 하게된다.
  • 한정 대기 문제: 두 프로세스가 lock을 사용해 임계구역에 진입전에 lock을 true로 바꾼다. 이렇게 하면 상호 배제는 보장되지만 두 프로세스가 각자의 lock을 true로 바꾸고 타임아웃 된 경우 모두 임계구역에 진입하지 못하는 무한 대기 현상이 발생한다. 한정 대기 조건을 보장하지 못하는 상황으로 '교착 상태(deadlock)' 라고 한다. 프로세스가 늘어나면 검사해야하는 lock의 개수가 늘어나서 비효율적이다.
  • 진행의 융통성 문제: lock을 true/false가 아닌 정수값으로 둔다. 잠금을 확인하는 문장이 하나이므로 상호 배제와 한정 대기를 보장한다. 하지만 한 프로세서가 두 번 연달아서 임계구역에 접근하고 싶어도 그럴수 없다. 우선순위가 무시되고 이 때문에 특정 프로세스가 해당 프로세스를 방해하는 구조가 되고 이 현상을 경직된 동기화(lockstep synchronization)라고 한다.
  • 하드웨어적인 해결 방법: 하드웨어의 지원을 받아서 검사와 지정을 한꺼번에 실행하여 임계구역을 보호할 수 있다. 편리하지만 바쁜 대기를 사용하여 검사하기 때문에 자원의 낭비가 있다.

 

피터슨 알고리즘: 한정 대기 문제에서 turn이라는 공유 변수를 추가한 것과 같다. 임계구역 해결의 세 가지 조건을 모두 만족하지만 2개의 포르세스만 사용 가능하다는 한계가 있다. 여러프로세스가 하나의 임계구역을 사용하려면 공유 변수를 추가하고 코드를 변경해야 한다.

 

데커 알고리즘

 

세마포어: 임계구역에 진입하기전에 스위치를 사용 중으로 놓고 임계구역으로 들어간다. 이후 도착하는 프로세스는 앞의 프로세스가 작업을 마칠때까지 기다리고 프로세스가 작업을 마치면 다음 프로세스에 임계구역을 사용하라는 동기화 신호를 보낸다. 다른 알고리즘과 달리 임계구역이 잠겼는지 직접 확인하거나, 바쁜 대기를 하거나, 다른 프로세스에 동기화 메시지를 보낼 필요가 없다.

하지만 세마포어의 P()나 V() 내부 코드가 실행되는 도중에 다른 코드가 실행되면 상호 배제와 한정 대기 조건을 보장하지 못하므로 검사와 지정을 사용하여 분리 실행되지 않고 완전히 실행되게 해야 한다.

공유 자원과 프로세서가 여러개 일때도 사용할 수 있다.

사용자가 잘못 사용하면 임계구역이 보호받지 못할 수도 있다.

 

모니터: 공유 자원을 내부적으로 숨기고 인터페이스만 제공함으로써 자원을 보호하고 프로세스 간의 동기화를 시킨다. sys-call과 같은 개념이다. 임계구역 보호와 동기화를 위해 내부적으로 상태 변수를 사용한다.

 

[Chapter 6. 교착 상태]

2개 이상의 프로세스가 다른 프로세스의 작업이 끝나기만을 기다리며 작업을 더 이상 진행하지 못하는 상태

 

교착 상태의 발생

  • 시스템 자원: 다른 프로세스와 공유할 수 없는 자원을 사용할 때 발생한다.
  • 공유 변수
  • 응용 프로그램

 

교착 상태 필요조건

  • 상호 배제: 한 프로세스가 사용하는 자원은 다른 프로세스와 공유할 수 없는 배타적인 자원이어야 한다.
  • 비선점: 한 프로세스가 사용 중인 자원은 중간에 다른 프로세스가 빼앗을 수 없는 비선점 자원이어야 한다.
  • 점유와 대기: 프로세스가 어떤 자원을 할당받은 상태에서 다른 자원을 기다리는 상태여야 한다.
  • 원형 대기: 점유와 대기를 하는 프로세스 간의 관계가 원을 이루어야 한다.

위 네가지를 모두 충족해야 교착 상태가 발생하고 단 하나라도 만족하지 않으면 발생하지 않는다.

 

교착 상태와 기아 현상은 다르다. 기아 현상은 정책상 잘못이나 오류로 인해 발생하고 에이징으로 해결할 수 있지만 교착 상태는 자연적으로 발생하고 처리도 복잡하다.

 

교착 상태 해결 방법

  • 예방: 필요 조건중 하나라도 무력화를 시킨다. 실효성이 적어 잘 사용되지 않는다.
  • 회피: 자원 할당량을 조절하여 해결한다. 하지만 얼마만큼 할당해야 발생하지 않는다는 보장이 없기 때문에 실효성이 적다.
  • 검출과 회복: 자원 할당 그래프를 모니터링하면서 교착 상태가 발생하는지 살펴보고 발생 시 회복 단계가 진행된다. 현실적인 접근 방법이다.

 

교착 상태 예방

  • 상호 배제 예방: 독점적으로 사용할 수 있는 자원을 없애고 모든 자원을 공유시킨다. 임계구역이 보호받지 못하기 때문에 상호 배제 무력화는 사실상 어렵다.
  • 비선점 예방: 모든 자원을 빼앗을 수 있도록 만든다. 하지만 임계구역을 보호하기 위해 잠금을 사용시 자원을 빼앗을 수 없을 뿐더러 상호 배제도 보장할 수 없다. 게다가 기아 현상이 발생한다.
  • 점유와 대기 예방: 프로세스가 자원을 점유한 상태에서 다른 자원을 기다리지 못하게 하는 방법이다. 프로세스가 자원을 확보했는데 추가로 필요한 자원이 생기는 경우, 당장 사용하지도 않을 자원을 미리 선점하여 낭비하는 경우, 자원을 많이 사용하는 프로세스는 적게 사용하는 프로세스보다 자원을 동시에 확보하는게 어려워서 기아 현상이 발생하는 경우, 일괄 작업 방식으로 동작하는 여러 단점이 생긴다.
  • 원형 대기 예방: 점유와 대기를 하는 프로세스들이 원형을 이루지 못하도록 막는 방법이다. 프로세스 작업 진행에 유연성이 떨어지고 자원의 번호를 어떻게 부여할 것인지에 대한 문제가 발생한다.

 

교착 상태 회피

교착 상태가 발생하지 않는 범위 내에서만 자원을 할당한다. 자원의 총 수와 현재 할당된 자원의 수를 기준으로 시스템을 안정 상태와 불안정 상태로 나누고 시스템이 안정 상태를 유지하도록 자원을 할당한다. 안정 상태를 유지할 수 있는 범위 내에서 자원을 할당함으로써 교착 상태를 피한다.

은행원 알고리즘

최악을 경우를 기준으로 잡아서 문제 상황을 철저히 피하여 교착 상태를 막는다.

각 프로세스의 기대 자원과 비교하여 가용 자원이 크거나 같은 경우가 한 번 이상인 경우 안정 상태이다.

이 알고리즘을 사용하려면 프로세스가 자신이 사용할 모든 자원을 미리 선언해야 하고 시스템의 전체 자원 수가 고정적이어야 하며, 자원이 낭비되는 단점이 있다.

 

교착 상태 검출

예방은 구현하기 어렵고 회피는 자원을 낭비하는 문제가 있어서 검출이 가장 현실적인 해결 방법이다.

- 타임 아웃을 이용한 교착 상태 검출

일정 시간동안 작업이 진행되지 않은 프로세스를 교착 상태가 발생한 것으로 간주하여 처리하는 방법이다.

쉽게 구현할 수 있지만 엉뚱한 프로세스가 강제 종료되거나(타임아웃≠교착상태) 모든 시스템에 적용할 수 없다는(네트워크 등) 문제가 있다. 가벼운 교착 상태 검출이라고 한다.

DB의 경우 일관성이 깨지면 안되기 때문에 OS보다 처리가 복잡하다. 그래서 체크포인트와 롤백을 사용한다.

 

- 자원 할당 그래프를 이용한 교착 상태 검출

OS는 자원을 요청하거나 할당할 때마다 자원 할당 그래프를 갱신하는데 이때 사이클이 발생하면 교착 상태가 검출된 것으로 판단한다. 오버헤드가 발생한다는 단점이 있다.

 

교착 상태 회복

교착 상태를 유발한 프로세스를 강제로 종료한다. 교착 상태를 일으킨 모든 프로세스를 동시에 종료시키거나 하나를 골라 순서대로 종료시키며 나머지 프로세스의 상태를 파악하는 방법을 사용한다.

 

[Chapter 7. 물리 메모리 관리]

메모리 관리의 이중성: 프로세스는 메모리를 독차지하려 하고 메모리 관리자 입장에서는 되도록 관리를 효율적으로 하고 싶어 하는 것

컴파일러는 기계어로 번역 후 한꺼번에 실행하고 인터프리터는 한 줄씩 번역하여 실행한다.

컴파일러의 사용 목적은 오류 발견 및 코드 최적화이다.

(소스코드->컴파일러->목적코드->링커)->실행 (괄호 안은 컴파일 과정)

 

메모리 관리자의 역할

  • 가져오기: 프로세스와 데이터를 메모리로 가져온다
  • 배치: 가져온 프로세스와 데이터를 메모리의 어떤 부분에 올려놓을지 결정한다
  • 재배치: 메모리가 꽉 찬 경우 오래된 프로세스를 내보낸다 (LRU?)

배치 정책에서 메모리를 같은 크기로 자르는 것을 페이징, 프로세스의 크기에 맞게 자르는 것을 세그먼테이션이라고 한다.

메모리 관리자는 절대 주소를 사용하지만 사용자 입장에서 절대 주소(물리 주소)는 불편하고 위험하기때문에 사용자는 상대 주소(논리 주소)를 사용한다.

 

메모리 오버레이: 프로그램의 실제 크기가 메모리보다 클 때 전체 프로그램을 메모리에 가져오는 대신 적당한 크기로 잘라서(모듈) 가져오는 기법

모듈이 교체될 때 별도의 스왑영역(저장장치 공간)에 옮겨둔다. 저장장치는 장소만 빌려주고 관리는 메모리 관리자가 한다.

 

다중 프로그래밍 환경에서의 메모리 할당

가변 분할 방식: 연속된 공간에 배치할수 있다는 장점이 있지만 메모리 관리가 복잡하다.

빈 영역이 발생하더라도 서로 떨어져있으면 프로세스를 배정하지 못하기 때문에 큰 프로세스는 계속 할당받지 못해 기아 현상이 발생할 수 있다. 이로 인해 메모리에 작은 조각들이 발생하는 현상을 단편화/조각화 라고 한다. 이 문제를 해결하기 위해 메모리 배치 방식이나 조각 모음을 사용한다.

  • 메모리 배치 방식: 최초 배치(단편화 고려x), 최적 배치, 최악 배치, 버디 시스템이 있다.
  • 조각 모음: 단편화된 공간을 합치기 위해 프로세스의 동작을 멈추고 적당한 위치로 옮긴 후 상대 주소값을 바꾼다. 이후 프로세스를 다시 실행한다. 많은 시간이 걸린다.
  • 버디 시스템: 가변+고정의 특징을 모두 가지고 있다.

 

고정 분할 방식: 메모리 관리가 수월하지만 낭비가 발생할 수 있다. 프로세스가 메모리의 여러 조각에 나뉘어 저장되는 것이 문제이다. 프로세스가 메모리 영역보다 작은 경우 내부 단편화가 발생한다.

 

현대 OS에서는 기본적으로 고정 분할 방식을 사용하고 일부분은 가변 분할 방식을 혼합하고 있다.

 

[Chapter 8. 가상 메모리의 기초]

가상 메모리: 물리 메모리의 크기와 상관없이 프로세스에 커다란 메모리 공간을 제공하는 기술

가상 메모리 시스템의 모든 프로세스는 물리 메모리와 별개로 0번지부터 시작하는 연속된 메모리 공간을 가진다. 논리 주소와 유사하지만 물리 공간에 비례하지 않고 가상의 주소 공간을 가진다.

가상 메모리에서 메모리 관리자가 사용할 수 있는 메모리의 전체 크기는 물리 메모리+스왑 영역을 더한 크기이다. (=동적 주소 변환)

세그먼테이션(가변) 또는 페이징(고정) 기법을 단독으로 사용하기엔 단점이나 어려움이 있어서 잘 사용되지 않고 두 기법의 단점을 보완한 세그먼테이션-페이징 혼용 기법을 주로 사용한다.

 

매핑 테이블

가상 주소는 물리 주소나 스왑 영역 한 곳에 위치하며, 메모리 관리자는 가상 주소와 물리 주소를 일대일 매핑 테이블로 관리한다. 세그먼테이션, 페이징 어떤 것이든 분할된 경우 똑같은 방식으로 적용된다.

 

페이징 기법

주소 변환 과정

가상->물리: 가상 주소가 어느 페이지에 있는지 찾고 해당 페이지에 가서 어느 프레임에 있는지 알아낸다.

물리->가상: 위와 과정은 같다.

 

정형화된 주소 변환

페이징 기법에서는 가상 주소를 VA=<P,D>로 표현한다.(P:페이지, D:거리)

페이징 기법에서의 주소 변환은 VA=<P,D>를 PA=<F,D>로 변환하는 것이다.(F:프레임, D:거리)

페이지 테이블을 이용하면 간단하게 가상 주소를 물리 주소로 변환할 수 있다. 페이지 테이블은 페이지 번호, 프레임 번호로 구성되고 각각의 한줄은 페이지 테이블 엔트리(PTE)라고 부른다.

 

가상 주소를 <P,D>로 변환하는 공식

P=VA / PAGE_SIZE

D=VA % PAGE_SIZE

 

페이지 테이블 관리

페이지 테이블은 프로세스마다 하나씩 가지기 때문에 여러개의 프로세스가 실행시 전체 페이지 테이블의 크기는 그에 비례해서 커지기 때문에 사용할 수 있는 메모리 영역이 줄어든다. 따라서 페이지 테이블의 크기를 적정하게 유지하는 것이 페이지 테이블 관리의 핵심이다. 페이지 테이블에 빠르게 접근하기 위해 레지스터가(PTBR) 존재하고 각 페이지 테이블의 시작 주소를 PTBR에 보관한다. PTBR은 프로세스의 제어 블록에 저장된다.

 

페이지 테이블 매핑 방식

직접 매핑, 연관 매핑, 집합-연관 매핑, 역매핑이 있다.

  • 직접 매핑: 모든 페이지 테이블을 물리 메모리에 가지고 있는 가장 단순한 방식. 주소 변환 속도가 빠르다
  • 연관 매핑: 전체 페이지 테이블을 스왑 영역에 두고 페이지 테이블의 일부를 물리 메모리에 가져오는 방식. 원하는페이지가 TLB에 있는경우 곧바로 물리 주소로 변환되고 없는 경우 스왑 영역에 저장된 직접 매핑 테이블을 사용하여 프레임 번호로 변환한다. 전체 페이지 테이블을 물리 메모리에 보관하지 않아서 메모리를 절약할 수 있지만 TLB 미스가 빈번하게 발생하면 시스템의 성능이 떨어진다.
  • 집합-연관 매핑(디렉터리 매핑): 페이지 테이블을 같은 크기의 여러 묶음으로 나누고 각 묶음의 시작 주소를 가진 디렉터리 테이블을 새로 만들어 관리한다. 가상 주소를 VA=<P,D>가 아닌 VA=<P1,P2,D>로 표시한다. 두단계를 거쳐서 물리 주소로 변환된다.
  • 역매핑: 물리 메모리의 프레임 번호를 기준으로 테이블을 작성한다. 프로세스의 수와 상관없이 항상 일정 크기의 페이지 테이블을 유지하여 크기가 매우 작다. 연관 매핑과 마찬가지로 시스템의 성능이 떨어질 수 있다.

 

세그먼테이션 기법

세그먼테이션 기법도 매핑 테이블을 사용한다. 페이징 기법에서는 크기가 모두 똑같기때문에 매핑 테이블이 크기 정보를 갖고있을 필요가 없었지만 세그먼테이션 기법은 가변적이기 때문에 크기 정보를 포함한다.

가상 주소를 VA=<S,D>로 표현한다.(S:세그먼트 번호, D:거리)

세그먼테이션 테이블의 limit은 메모리를 보호하는 역할을 한다. limit을 넘어서 접근하려고 하면 트랩을 발생시키고 프로세스를 강제 종료시킨다. 트랩이 발생하면 OS는 사용자에게 segmentation fault 메시지를 보낸다.

(트랩의 반대는 시그널)

 

세그먼테이션-페이징 혼용 기법

메모리 접근 권한 검사는 가상 주소에서 물리 주소로 주소 변환이 일어날 때마다 시행된다.

페이징 기법에 세그먼테이션 테이블을 추가하여 권한 비트와 같이 중복되는 데이터를 세그먼테이션 테이블로 옮겨오면 테이블의 크기를 줄일 수 있다. 현재 대부분의 운영체제는 이 방식을 사용하고 있다.

 

세그먼테이션-페이징 혼용 기법의 주소 변환

가상 주소를 VA=<S,P,D>로 표현한다.(S:세그먼트 번호, P:페이지 번호, D:거리)

 

[Chapter 9. 가상 메모리 관리]

요구 페이징: 프로세스가 요청할 때 메모리로 가져오는 것

미리 가져오기: 앞으로 필요할 것이라 예상되는 페이지를 미리 가져오는 것. (캐시)

PTE의 플래그 비트에는 접근, 변경, 유효, 읽기, 쓰기, 실행 비트 등이 모여있다.

 

페이지 부재

프로세스가 페이지를 요청했을 때 그 페이지가 메모리에 없는 상황.

유효비트가 0일때 페이지가 메모리에 있고 1일때는 스왑 영역에 있다. 메모리에 빈 프레임이 있을 경우 스왑 영역에서 가져오기만 하면 되지만 빈 프레임이 없는 경우 메모리에 있는 프레임 중 하나를 스왑 영역으로 보낸 후에야 가져올 수 있다. 이것을 결정하는 알고리즘을 페이지 교체 알고리즘 이라고 한다.

 

지역성

  • 시간적 지역성: 한번 참조된 항목은 곧바로 다시 참조되는 경향이 있다.
  • 공간적 지역성: 참조된 항목 근처의 다른 항목들이 곧바로 다시 참조될 가능성이 높다.
  • 순차적 지역성: 여러 작업이 순서대로 진행되는 경향이 있다. 공간적 지역성의 특별한 경우로 본다.

 

페이지 교체 알고리즘

  • 무작위: 특별한 알고리즘 없이 무작위로 선정한다. 지역성을 전혀 고려하지 않기 때문에 성능이 좋지 않아 잘 사용하지 않는다.
  • FIFO: 큐로 구현한다. 큐의 맨 위의 페이지는 가장 오래된 페이지이고 메모리가 꽉 차면 맨 위의 페이지가 스왑 영역으로 나간다. 시간적 지역성을 고려하면 가장 오래된 페이지를 스왑 영역으로 내보내는게 맞지만 오래되었더라도 자주 사용되는 페이지도 스왑 영역으로 내보내게 된다.
  • 최적: 앞으로 사용하지 않을 페이지를 스왑 영역으로 옮긴다. 미래의 메모리 접근 패턴을 보고 대상 페이지를 결정하기 때문에 성능이 좋지만 미래의 접근 패턴을 안다는 것은 불가능하기 때문에 실제로 구현할 수 없다. 하지만 과거의 데이터를 바탕으로 미래를 추정하는것은 가능하기 때문에 최근 최소 사용(LRU), 최소 빈도 사용(LFU), 최근 미사용(NUR)이 개발되었고 성능도 유사하게 나오기에 이들을 최적 근접 알고리즘이라고 한다.
  • LRU(least recently used): 메모리에 올라온 후 가장 오랫동안 사용되지 않은 페이지를 스왑 영역으로 옮긴다. 페이지에 접근한 시간이나 카운터를 사용하여 구현하거나 참조 비트 시프트를 사용하여 구현할 수 있다. 실제로는 참조 비트 시프트를 이용하여 구현하고 참조 횟수가 아닌 참조 시간을 기준으로 대상 페이지를 선정한다. 적지 않은 공간을 사용하므로 낭비되는 메모리 공간이 생긴다.
  • LFU(least frequently used): 현재 프레임에 있는 페이지마다 그동안 사용된 횟수를 세어 횟수가 가장 적은 페이지를 스왑 영역으로 보낸다. LRU와 마찬가지로 낭비되는 메모리 공간이 많다.
  • NUR(not used recently): LRU, LFU와 성능이 거의 비슷하면서 불필요한 메모리 낭비 문제를 해결한 알고리즘이다. 페이지마다 참조 비트와 변경 비트를 가지므로 페이지마다 추가되는 공간이 2비트 뿐이다. 참조 비트는 PTE의 접근 비트를 가리키고 변경 비트는 PTE의 변경 비트를 가리킨다. 각 비트의 초기값은 0이고 페이지에 접근하면 참조 비트는 1이 되고 페이지가 변경되면 변경 비트는 1이 된다. 참조 비트->변경 비트순으로 고려하여 대상 페이지를 결정한다. 모든 페이지의 비트가 1,1이라면 정상적으로 이용할 수 없으므로 이런 경우 모두 0,0으로 초기화한다. 쉽게 구현할 수 있다는 장점 때문에 가장 많이 사용된다.

 

FIFO 변형: FIFO의 단점을 개선한 알고리즘

  • 2차 기회: 특정 페이지에 접근하여 페이지 부재 없이 성공할 경우 해당 페이지를 큐의 맨 뒤로 이동시켜 대상 페이지에서 제외시킨다. 성공한 페이지를 큐의 맨 뒤로 옮김으로써 기회를 한번 더 주는것과 같다. 성능은 LRU, LFU, NUR보다 약간 낮고 FIFO보다 약간 높다. 큐를 유지하는 비용이 높고 페이지가 성공하면 큐의 중간의 있는 값들을 뒤로 이동시키는 작업이 추가되는 단점이 있다.
  • 시계: 2차 기회와 유사하다. 큐 대신 원형 큐를 사용한다. 마찬가지로 페이지가 성공하면 기회를 한번 더 주기위해 참조 비트를 추가한다. 대상 포인터와 페이지당 참조 비트 하나만 추가하면 되기 때문에 NUR보다 추가 공간이 적게 들지만 복잡하고 계산량이 많다는 단점이 있다.

 

스레싱과 프레임 할당

스레싱: 하드디스크의 입출력이 너무 많아져서 잦은 페이지 부재로 인해 작업이 멈춘 것 같은 상태.

멀티프로그래밍 정도(degree of multiprogramming): 동시에 실행하는 프로그램의 수. 너무 높으면 스레싱이 발생한다.

 

프로세스에 프레임을 할당하는 방식

정적 할당: 프로세스 실행 초기에 프레임을 나누어준 후 그 크기를 고정하는 것

  • 균등 할당: 프로세스의 크기와 상관없이 사용 가능한 프레임을 모든 프로세스에 동일하게 할당한다. 크기가 큰 프로세스의 경우 필요한 만큼 프레임을 할당받지 못해 페이지 부재가 빈번하게 발생하고 작은 프로세스의 경우 메모리가 낭비된다.
  • 비례 할당: 프로세스의 크기에 비례하여 프레임을 할당한다. 균등 할당보다 조금 더 현실적이지만 프로세스가 실행중에 필요로 하는 프레임을 유동적으로 반영하지 못하고 사용하지도 않을 메모리를 처음부터 미리 확보하여 공간을 낭비한다.

 

동적 할당: 실시간으로 변하는 요청을 수용한다

  • 작업집합 모델: 지역성 이론을 바탕으로 최근 일정 시간동안 참조된 페이지들을 집합으로 만들고, 이 집합에 있는 페이지들을 물리 메모리에 유지하여 프로세스의 실행을 돕는다. 작업집합 윈도우의 크기에 따라 프로세스의 실행 성능이 달라진다.
  • 페이지 부재 빈도: 페이지 부재 횟수를 기록하여 페이지 부재 비율을 계산한다. 상한선과 하한선을 설정한 후 상한선을 초과하면 할당된 프레임이 적다는 것을 의미하므로 프레임을 추가하고 하한선 밑으로 내려가면 메모리가 낭비되므로 할당한 프레임을 회수한다.

 

[Chapter 10. 입출력 시스템과 저장장치]

여러 주변장치는 메인보드 내의 버스로 연결된다.

데이터가 지나다니는 하나의 통로를 채널이라고 부른다. 속도가 비슷한 장치끼리 묶어서 채널을 할당하면 전체 데이터 전송 속도를 향상시킬 수 있다.

폴링->I/O제어기->입출력 버스 분리

CPUㅡ메모리(메인 버스), CPUㅡVGA(그래픽 버스), 고속/저속 입출력 버스를 사용한다.

하나의 버스 채널은 주소 버스, 데이터 버스, 제어 버스로 구성된다.

 

 

직접 메모리 접근(DMA): CPU의 도움 없이도 메모리에 접근할 수 있도록 입출력 제어기에 부여된 권한

입출력 제어기는 여러 채널에 연결된 주변 장치로부터 전송된 테이터를 적절히 배분하여 하나의 데이터 흐름을 만들고 이 때 채널 선택기는 여러 채널에서 전송된 데이터 중 어떤 것을 메모리로 보낼지 결정하고 DMA 제어기를 거쳐 메모리로 올라간다. CPU와 DMA 제어기의 작업 공간이 겹치기 때문에 메인 메모리의 주소 공간 중 일부를 DMA 제어기에 할당하여 작업 공간이 겹치는 것을 막는다.

 

입출력 제어기와 DMA 제어기의 협업으로 작업이 완료되면 입출력 제어기는 CPU에 인터럽트를 보낸다.

외부에서 발생시 인터럽트, 내부에서 발생시 예외, 사용자가 임의로 발생시킨 경우 시그널이다. 예외 발생시 어떤 상황에서 오류가 발생했는지 파악하기 위해 프로세스가 종료되기 직전까지의 메모리와 레지스터 상태를 저장하는데 이를 코어 덤프라고 한다.

 

인터럽트 벡터: 여러 인터럽트 중 어떤 인터럽트가 발생했는지 파악하기 위해 사용하는 자료구조

인터럽트 핸들러: 인터럽트의 처리 방법을 함수 형태로 만든 것

 

버퍼: 바구니를 생각하면 된다. 단일 버퍼보다는 이중 버퍼가 버퍼 운용에 유리하다.

 

디스크 장치

디스크 장치를 사용하는 데에는 파티션, 포매팅, 조각 모음과 같은 관리 기법이 필요하다.

 

네트워크 저장 장치: DAS, NAS, SAN

 

디스크 스케줄링: 트랙의 이동을 최소화하여 탐색 시간을 줄이는 데 목적이 있다.

  • FCFS(first come, first service): 요청이 들어온 트랙 순서대로 서비스한다. 별다른 기법을 사용하지 않고 요청된 트랙 번호 순서대로 매우 큰 폭으로 바쁘게 움직인다.
  • SSTF(shortest seek time first): 현재 헤드가 있는 위치에서 가장 가까운 트랙부터 서비스한다. 효율성은 좋지만 기아 현상을 일으킬 수 있다.
  • 블록 SSTF: 에이징을 사용하여 SSTF의 기아 현상을 어느정도 해결한 방법으로 모든 트랙이 블록 안에서만 움직인다.
  • SCAN: SSTF의 공평성 위배 문제를 완화하기 위해 만들어졌다. 헤드가 한 방향으로만 움직이고 맨 마지막 트랙에 도착할 때 까지 뒤돌아가지 않는다. SSTF보다 성능이 조금 떨어지지만 FCFS보다 우수하다.
  • C-SCAN(circular SCAN): SCAN을 변형하여 헤드가 한쪽 방향으로 움직일때는 요청받은 트랙을 서비스하고 반대 방향으로 돌아올때는 서비스하지 않고 이동만 한다. 동일한 트랙 요청이 연속적으로 발생하면 마찬가지로 기아 현상이 발생한다.
  • LOOK: 더 이상 서비스할 트랙이 없으면 헤드가 끝까지 가지 않고 중간에서 방향을 바꾼다. 많이 사용된다.
  • C-LOOK(circular LOOK): C-SCAN의 LOOK 버전이다.
  • SLTF(shortest latency time first): 최소 지연 우선 기법. 헤드가 고정된 저장장치에 사용된다.

 

RAID(redundant array of independent disks): 자동으로 백업을 하고 장애가 발생하면 이를 극복하는 시스템. 동일한 규격의 디스크를 여러 개 모아 구성하며, 장애가 발생했을 때 데이터를 복구하는 데 사용된다.

  • RAID 0(스트라이핑): 같은 규격의 디스크를 병렬로 연결해서 이론적으로 입출력 속도가 빠르다. 복구 기능이 없다.
  • RAID 1(미러링): 같은 크기의 디스크를 최소 2개 이상 필요로 하고 짝수 개의 디스크로 구성된다. 비용이 증가하고 속도가 느려질 수 있다는 단점이 있다.
  • RAID 2: 오류 교정 코드(ECC)를 사용하여 오류를 찾고 교정을 한다. n개의 디스크에 대해 n-1개의 추가 디스크를 필요로 하고 오류 교정 코드를 계산하는 데 많은 시간을 소비하므로 잘 사용되지 않는다.
  • RAID 3: 오류 검출 코드를 사용하여 데이터를 복구한다. 섹터 단위로 데이터를 나누어 저장한다. N-way 패리티 비트를 구성하는 데 필요한 계산량이 많다는 단점이 있다.
  • RAID 4: RAID 3과 같은 구조이지만 처리 단위가 섹터가 아닌 블록이다. 데이터의 병목 현상때문에 사용하지 않는다.
  • RAID 5: RAID 4와 같은 방법을 사용하지만 병목 현상을 해결했다. 디스크 2개에 동시에 장애가 발생하면 복구가 불가능하다.
  • RAID 6: RAID 5와 같은 방식이지만 패리티 비트가 2개이다. RAID 5보다 계산량이 많고 4개의 디스크당 2개의 추가 디스크가 필요하다는 단점이 있다.
  • RAID 10: RAID 0과 RAID 1을 결합한 방식이다. 최소 4개의 디스크가 필요하다.
  • RAID 50, 60: RAID 0과 RAID 5, 6을 결합한 방식이다. RAID 10에 비해 추가되는 디스크의 수가 적지만 입출력 시 계산량이 증가한다

'이론 > 운영체제' 카테고리의 다른 글

[OSTEP] III. 영속성  (0) 2022.08.21
[OSTEP] II. 병행성  (0) 2022.08.21
[OSTEP] I. 가상화  (0) 2022.08.19
[OSTEP] 2장. 운영체제 개요  (0) 2022.08.19

앞의 내용은 필기로 따로 정리했고 시간 날때 추가할 예정

 

 

 

[Chapter5. 메모리 계층 구조]

 

지역성의 원칙

  • 시간적 지역성: 한번 참조된 항목은 곧바로 다시 참조되는 경향
  • 공간적 지역성: 근처의 다른 항목들이 곧바로 참조될 가능성이 높음

 

메모리 계층 구조를 만든 가장 큰 이유는 성능 향상이다.

 

블록/라인: 정보 전송의 최소 단위

 

SRAM: 주로 캐시에 사용

DRAM: 주로 메인 메모리에 사용

플래시 메모리: EEPROM의 한 종류

디스크 메모리: 자기 디스크(HDD)

 

주소 인터리빙: DRAM에서 하나의 주소를 여러 뱅크에 보내서 모든 뱅크가 동시에 읽고 쓰게 하는 순환 접근 방식

 

직접 사상(direct mapped): 메모리의 한 위치가 캐시 내에 정확히 한 곳에만 사상되는 것

 

캐시에 태그를 추가함으로써 요구하는 워드가 캐시 내에 존재하는지 알 수 있다.

 

캐시 블록이 크면 공간적 지역성을 잘 활용하기 때문에 실패율이 낮아지지만 너무 크면 캐시의 상당수를 차지하기 때문에 실패율이 오히려 떨어질수도 있다.

 

캐시 적중 실패시 파이프라인의 지연을 발생시킨다.

 

메인 메모리와 캐시를 일치시키는 방법은 즉시 쓰기(write-through)와 나중 쓰기(write-back)가 있다.

전자는 간단하지만 성능이 좋지 않지만 쓰기 버퍼를 통해 해결할 수 있다. 보통 2개 이상의 버퍼 엔트리를 가진다.

후자는 성능은 좋지만 즉시 쓰기에 비해 구현이 더 복잡하다.

 

캐시의 성능을 향상시키는 두 가지 방법

  • 2개의 다른 메모리 블럭이 캐시의 같은 장소를 두고 경쟁하는 확률을 줄여서 실패율을 낮추는 것
  • 메모리 계층구조에 새로운 계층을 추가해서 실패 손실을 줄이는 것(=다단계 캐싱)

 

블록을 캐시에 배치하는 방법

  • 직접 사상: 각 블록은 캐시의 딱 한곳에만 들어갈 수 있다
  • 완전 연관: 캐시의 어느곳에도 다 들어갈 수 있지만 탐색시간이 오래걸린다
  • 집합 연관: 직접 사상과 완전 연관을 합쳐둔 것

연관 정도를 늘리면 실패율은 줄어들지만 적중 시간이 증가한다.

적중 실패시 직접 사상에서는 해당 캐시 자리의 블록을 바로 교체하면 되지만 연관 방식의 캐시에서는 주로 LRU 방식을 통해 교체한다.

*LRU(least recently used): 가장 오랫동안 사용되지 않은 블록을 교체하는 방법

 

다단계 캐시를 이용한 실패 손실 줄이기

프로세서와 DRAM의 접근시간 차이를 줄이기 위해 캐시를 한 계층 더 지원한다.(2차 캐시)

1차 캐시에서 실패가 발생하면 2차 캐시에 접근하고 2차까지 실패시 더 큰 실패 손실이 발생한다.

그렇기 때문에 1차 캐시의 설계는 적중시간 최소화에 초점을 두고 2차 캐시의 설계는 실패율 최소화에 중점을 둔다.

2차 캐시는 실패율 최소화에 중점을 두었기 때문에 더 높은 연관 정도가 많이 사용된다.

 

가상 머신: 여러 개의 운영체제가 하드웨어 자원을 공유할 수 있다.

 

가상 메모리: 메인 메모리를 2차 저장 장치의 캐시로 사용하는 기술.

가상 메모리와 캐시에 적용된 개념이 같더라도 역사적 뿌리가 다르므로 서로 용어가 다르다

 

페이지 부재가 발생하면 OS가 제어를 넘겨받아서 인터럽트를 처리한다.

가상 메모리에서는 성능의 문제로 즉시 쓰기가 매우 비실용적이기 때문에 나중 쓰기를 사용한다.

 

변환 참조용 버퍼(translation-lookaside buf-fer): 페이지 테이블에 접근하는것을 피하기 위해 최근에 사용된 주소 사상을 보관하고 있는 캐시.

 

TLB 실패나 페이지 부재 예외는 메모리 접근 클럭 사이클이 종료되기 전에 탐지되어야 한다.

 

--

요약 및 정리

 

가상 메모리는 메인 메모리와 2차 메모리 사이의 캐시 역할을 담당하는 메모리 계층이다.

프로그램이 메인 메모리의 한계 이상으로 주소 공간을 확장시킬 수 있게 한다.

동시에 활성화된 다수의 프로세스들이 서호 보호된 상태에서 메인 메모리를 공유할 수있게 해준다.

2차 메모리는 쓰는데 시간이 많이 걸리기 때문에 나중 쓰기 기법을 사용한다.

프로세스들의 상호간 보호를 보장하기 위해서는 운영체제만이 주소 변환을 바꿀수 있어야 한다.

프로세서가 주소 변환을 할 때마다 페이지 테이블에 접근시 비용이 너무 커진다. 그 대신 TLB가 페이지 테이블의 캐시 역할을 한다.

 

캐시, TLB, 가상 메모리는 두 가지 지역성의 원칙에 기반을 둔다.

  • 블록을 넣을 수 있는 곳: 한 곳(직접 사상), 몇 곳(집합 연관), 아무데나(완전 연관)
  • 블록을 찾는 방법: 인덱싱(직접 사상 캐시), 제한적인 검색(집합 연관 캐시), 완전 검색(완전 연관 캐시), 별도의 검색 테이블(페이지 테이블)
  • 캐시 실패 발생시 교체블록: 무작위 또는 LRU
  • 쓰기 방법: 계층별로 즉시 쓰기나 나중 쓰기를 사용

+ Recent posts