반응형

안녕하세요구르트

이번 포스팅은 [CS] 프로세스와 스레드 :: #공부중 (tistory.com)에서 아주 간략하게 다룬 PCB와 Context Switching에 대해 좀 더 자세히 다뤄보겠습니다.


PCB란 무엇인가

PCB(Process Control Block)는 프로세스가 생성될 때 운영체제에 의해 생성되는 것으로, 생성된 프로세스에 대한 메타데이터를 저장하고 있습니다. PCB는 프로세스의 중요한 정보를 포함하고 있기 때문에 유저 영역에서 접근하지 못하도록 커널 스택의 가장 앞부분에서 관리됩니다.


●PCB의 구조

 

  • 프로세스 식별자 (PID: Process ID)
    각 프로세스를 유일하게 구분하기 위한 고유 식별자입니다.
  • 프로세스 상태 (Process State)
    프로세스가 현재 어떤 상태에 있는지 나타냅니다. 프로세스는 다음과 같은 상태 중 하나에 있을 수 있습니다:
    • New: 프로세스가 생성되었지만 아직 실행되지 않음.
    • Running: 프로세스가 CPU에서 실행 중임.
    • Waiting (Blocked): 프로세스가 어떤 이벤트(입출력 등)를 기다리는 중임.
    • Ready: 프로세스가 실행 준비 상태이며, CPU를 사용할 수 있기를 기다리는 중임.
    • Terminated: 프로세스가 실행을 마치고 종료된 상태임.
  • 프로그램 카운터 (Program Counter)
    다음에 실행할 명령어의 주소를 저장합니다. 문맥 교환이 발생할 때 이 값을 이용해 이전 프로세스의 상태를 복구할 수 있습니다.
  • CPU 레지스터 상태
    문맥 교환을 할 때, CPU 레지스터의 내용을 저장하고 복구하는 데 사용됩니다. 이를 통해 중단된 프로세스가 나중에 재개될 때 이전 상태를 유지할 수 있습니다.
  • 메모리 관리 정보
    프로세스의 메모리 공간과 관련된 정보들을 담고 있습니다. 예를 들어, 페이지 테이블, 세그먼트 테이블, 메모리 한계 레지스터 등이 포함될 수 있습니다.
  • 프로세스 상태 (Process State)
    프로세스의 실행 중, 대기 중, 종료 상태 등 프로세스의 현재 상태에 대한 정보입니다.
  • 계정 정보 (Accounting Information)
    CPU 사용 시간, 프로세스의 우선순위, 실행된 시스템 호출 수 등 프로세스의 성능 및 자원 사용에 관한 정보입니다.
  • 입출력 상태 정보 (I/O Status Information)
    프로세스가 사용하는 입출력 장치, 열려 있는 파일 목록, 입출력 요청 큐 등의 정보를 담고 있습니다.

●PCB의 역할

운영체제는 PCB를 사용해서 여러 프로세스를 관리할 수 있습니다. CPU의 각 코어는 여러 프로세스를 동시에 처리할 수 없기 때문에 짧은 시간동안 여러 프로세스를 하나씩 빠르게 처리하게 되는데요, 프로세스의 전환 과정에서 다음 프로세스의 정보를 읽어와야 작업을 진행할 수 있기 떄문에 프로세스의 정보, 즉 PCB를 필요로 합니다. 


Context Switching란 무엇인가

컨텍스트 스위칭은 운영체제(OS)에서 현재 실행 중인 작업의 상태(context)를 저장하고, 다른 작업의 상태를 복원하여 실행을 전환하는 과정을 말합니다. 운영체제에서 하는 작업이기 때문에 당연히 커널영역으로의 전환이 이루어집니다.

프로세스나 스레드와의 전환만이 컨텍스트 스위칭이 일어난다고 생각하실 수도 있는데, 뿐만 아니라 여러 상황에서 이루어집니다.


●Context Switching이 일어나는 상황

컨텍스트 스위칭은 여러 상황에서 일어날 수 있습니다. 대표적인 4가지 경우가 있는데요.

  • 프로세스 전환: 하나의 프로세스가 실행 중일 때, OS가 다른 프로세스로 전환해야 하면, 현재 프로세스의 상태를 저장하고 다른 프로세스의 상태를 불러와 실행합니다.
  • 스레드 전환: 멀티스레드 환경에서, 하나의 스레드에서 다른 스레드로 전환할 때도 컨텍스트 스위칭이 일어납니다.
  • 시스템 호출: 프로그램이 시스템 리소스에 접근하기 위해 커널 모드로 전환하는 순간에도 컨텍스트 스위칭이 발생합니다. 이때는 사용자 모드에서 커널 모드로, 다시 커널 모드에서 사용자 모드로 돌아가는 과정에서 전환이 이루어집니다.
  • 인터럽트 처리: 외부 장치나 타이머 등에서 인터럽트가 발생하면, 현재 작업의 상태를 저장하고 인터럽트 핸들러로 전환되면서도 컨텍스트 스위칭이 일어날 수 있습니다.

●Context Switching에서 다루는 정보

컨텍스트 스위칭에서 다루는 정보들은 뭐가 있을까요? GPT쌤한테 물어봤습니다.

더보기

1. 프로그램 카운터 (Program Counter)

  • 역할: 현재 실행 중인 명령어의 위치를 저장.
  • 설명: 프로세스 또는 스레드가 멈췄던 지점에서 다시 실행을 재개하려면, 중단된 지점의 명령어 위치를 정확히 알아야 해. 프로그램 카운터는 이 위치 정보를 저장하고 있기 때문에, 이를 복원하여 다음 실행 지점에서부터 이어서 실행할 수 있어.

2. CPU 레지스터 (CPU Registers)

  • 역할: 연산 중인 데이터와 결과 값을 저장.
  • 설명: 프로세스나 스레드가 작업 중에 CPU 레지스터에는 여러 변수 값이나 중간 계산 결과가 담겨 있어. 컨텍스트 스위칭 시에 이 레지스터의 값을 저장하고, 전환된 작업의 레지스터 값을 복원하여 이어서 연산을 수행할 수 있도록 해.

3. 스택 포인터 (Stack Pointer) 및 프레임 포인터 (Frame Pointer)

  • 역할: 함수 호출의 스택 정보와 지역 변수를 관리.
  • 설명: 함수 호출 시 함수의 실행 정보를 스택에 쌓고, 함수가 끝나면 스택에서 꺼내는 구조야. 스택 포인터는 현재 스택의 최상단 위치를 가리키고, 프레임 포인터는 특정 함수 호출의 시작 지점을 가리켜. 이 정보가 저장되고 복원됨으로써 함수 호출이 끊김 없이 연결될 수 있어.

4. 메모리 관리 정보 (Memory Management Information)

  • 역할: 프로세스의 메모리 맵 및 페이지 테이블 정보를 관리.
  • 설명: 프로세스마다 사용하는 메모리 주소 공간이 다르기 때문에, 커널은 프로세스 전환 시 해당 프로세스의 가상 메모리 맵과 페이지 테이블을 관리해. 이를 통해 해당 프로세스가 접근해야 하는 메모리 자원을 올바르게 연결해줄 수 있어.

5. 프로세스 상태 (Process State)

  • 역할: 프로세스가 현재 어떤 상태에 있는지를 저장.
  • 설명: 프로세스는 실행 상태, 준비 상태, 대기 상태 등 다양한 상태를 가질 수 있어. 이 정보는 커널이 프로세스를 정확한 상태로 전환하고 처리할 수 있도록 해주는 중요한 정보야.

6. 파일 디스크립터 테이블 (File Descriptor Table)

  • 역할: 프로세스가 열어둔 파일의 정보와 네트워크 소켓 등을 관리.
  • 설명: 프로세스는 실행 중에 파일이나 소켓 같은 리소스를 열어 사용할 수 있어. 이러한 파일 디스크립터 정보는 전환된 프로세스에서도 동일하게 유지되어야 하기 때문에, 커널은 이 정보를 저장하고 복원해.

7. 입출력 상태 (I/O State)

  • 역할: 프로세스가 대기 중인 입출력 작업의 상태 정보를 관리.
  • 설명: 만약 프로세스가 어떤 입출력 작업(예: 파일 읽기/쓰기, 네트워크 통신 등)을 기다리는 상태라면, 커널은 해당 작업이 어느 단계에 있는지 그 상태를 기록하고 있어. 이를 통해 나중에 다시 프로세스를 복원할 때 그 입출력 작업을 이어서 수행할 수 있게 해.

8. 특권 레벨 (Privilege Level)

 

  • 역할: 프로세스가 사용자 모드인지, 커널 모드인지를 저장.
  • 설명: 시스템 호출이나 인터럽트가 발생할 때, CPU는 사용자 모드에서 커널 모드로 전환돼. 이 특권 레벨 정보가 정확하게 유지되어야 올바른 권한으로 작업을 수행할 수 있어.

 


●각 상황마다의 Context Switching

컨텍스트 스위칭은 작업의 전환이란 큰 틀에선 똑같지만, 상황에 따라 다루는 정보들이 달라집니다.

각각의 상황에서 커널이 어떤 정보를 읽고 저장하는지 알아봅시다.

1. 프로세스 전환 (Process Switching)

  • 읽는 정보: 프로세스는 독립된 실행 단위이기 때문에 상태를 완전히 저장하고 복원해야 합니다.
    • 프로그램 카운터 (Program Counter): 프로세스가 중단된 위치.
    • CPU 레지스터 (CPU Registers): 프로세스에서 사용 중이던 모든 레지스터 값.
    • 스택 포인터 (Stack Pointer) 및 프레임 포인터 (Frame Pointer): 프로세스의 함수 호출 및 지역 변수 상태.
    • 메모리 관리 정보 (Memory Management Information): 가상 메모리 주소 공간, 페이지 테이블 정보.
    • 프로세스 상태 (Process State): 실행 상태, 준비 상태, 대기 상태 등의 현재 상태.
    • 파일 디스크립터 테이블 (File Descriptor Table): 프로세스가 열어 둔 파일 및 소켓 정보.
    • 입출력 상태 (I/O State): 프로세스가 기다리고 있는 입출력 작업의 상태.

프로세스 전환은 완전히 다른 메모리 주소 공간과 자원을 사용하는 독립적인 작업을 전환하는 것이기 때문에, CPU 레지스터 및 메모리 관련 정보 전부를 저장 및 복원해야 합니다.

2. 스레드 전환 (Thread Switching)

  • 읽는 정보: 스레드는 같은 프로세스 내에서 실행되므로 프로세스의 정보는 공유되지만, 스레드 고유의 정보는 저장 및 복원해야 합니다.
    • 프로그램 카운터 (Program Counter): 스레드가 중단된 지점.
    • CPU 레지스터 (CPU Registers): 스레드에서 사용 중이던 레지스터 값.
    • 스택 포인터 (Stack Pointer) 및 프레임 포인터 (Frame Pointer): 스레드의 호출 스택 상태.

스레드는 프로세스 내에서 실행되기 때문에 메모리 관리 정보와 파일 디스크립터 테이블은 공유합니다. 그래서 프로세스 전환보다는 상대적으로 적은 양의 정보를 저장하고 복원하지만, CPU 상태와 스택 정보는 개별적으로 관리해야 합니다.

3. 시스템 호출 (System Call)

  • 읽는 정보: 시스템 호출은 사용자 모드에서 커널 모드로 전환될 때 발생하는데, 이때는 사용자 모드에서 커널 모드로 전환하는 정보만 필요합니다.
    • 프로그램 카운터 (Program Counter): 사용자 모드에서 커널 모드로 전환되기 직전의 위치.
    • CPU 레지스터 (CPU Registers): 현재 사용 중이던 레지스터 값.
    • 스택 포인터 (Stack Pointer): 커널 모드에서 사용할 새로운 스택.
    • 특권 레벨 (Privilege Level): 사용자 모드에서 커널 모드로 전환하는 권한 정보.

시스템 호출은 같은 프로세스 내에서 일어나는 일이기 때문에, 메모리 관리 정보나 파일 디스크립터는 변경되지 않고 그대로 유지됩니다. 사용자 모드에서 커널 모드로 전환되기 위한 최소한의 상태 정보만 저장하고 복원합니다.

4. 인터럽트 처리 (Interrupt Handling)

  • 읽는 정보: 인터럽트는 외부 사건에 의해 발생하며, 현재 작업을 잠깐 중단하고 인터럽트 처리 루틴으로 전환하는 것이기 때문에 비교적 적은 정보를 저장합니다.
    • 프로그램 카운터 (Program Counter): 인터럽트 발생 시점의 명령어 위치.
    • CPU 레지스터 (CPU Registers): 작업 중이던 레지스터 값.
    • 특권 레벨 (Privilege Level): 인터럽트가 발생할 때, 커널 모드로의 전환 여부.

인터럽트는 매우 짧은 시간 동안 실행되기 때문에 메모리 관리 정보나 입출력 상태는 그대로 유지되고, 필요한 정보만 빠르게 저장하고 원래 작업으로 복원합니다.


 

반응형

'프로그래밍 > CS' 카테고리의 다른 글

[CS] 물리 메모리와 가상 메모리  (0) 2024.11.04
[CS] 페이징(Paging)  (0) 2024.10.22
[CS] 인터럽트  (0) 2024.10.22
[CS] CPU 스케줄링  (4) 2024.10.21
[CS] CPU의 메모리 계층 구조  (4) 2024.10.13
반응형

하이헬로올라

오늘은 인터럽트에 대해서 알아볼건데용?

가벼운 주제기 때문에 전래동화 듣는 느낌으로 멍때리고 보시면 될 것 같습니다.


인터럽트(Interrupt)란?

인터럽트는 CPU가 작업을 하는 도중 예기치 않은 상황이 발생할 때, 현재 정상적으로 작업중인 프로세스를 잠시 중단하고, 해당 상황에 대한 우선 처리를 한 후 원래 작업중인 프로세스로 복귀하는 것을 말합니다. 이를 통해 시스템이 예기치 않은 상황에 대해 대응을 할 수 있습니다.

 

여기서 예기치 않은 상황이란 것은 뭘까요?


● 외부 인터럽트, 내부 인터럽트

예기치 않은 상황이란 말을 듣게되면 당연히 에러부터 생각하실 겁니다. 하지만 CPU의 입장에선 여러 가지 경우가 있습니다. 단순히 분류하자면 소프트웨어 측에서 나는 예외 상황이 있고, 하드웨어 측에서 나는 예외 상황이 있습니다.

 

소프트웨어 측에서 발생한 인터럽트를 내부 인터럽트(혹은 소프트웨어 인터럽트)라고 하고,

하드웨어 측에서 발생한 인터럽트를 외부 인터럽트(혹은 하드웨어 인터럽트)라고 합니다.

 

● 내부 인터럽트 (Internal Interrupt, 또는 Exception)

내부 인터럽트(소프트웨어 인터럽트)란 CPU 내부에서 발생하는 예외 상황이나 오류에 대한 인터럽트를 말합니다. 내부 인터럽트는 주로 소프트웨어 또는 하드웨어의 잘못된 동작으로 인해 발생하며, 일반적으로 CPU가 스스로 감지해냅니다.

 

내부 인터럽트의 주요 예시를 살펴보자면,

  • 디바이드 바이 제로(0 나누기): 프로그램이 숫자를 0으로 나누려고 할 때 발생하는 예외.
  • 페이지 폴트: 메모리 관리에서 잘못된 메모리 주소에 접근할 때 발생하는 예외.
  • 잘못된 명령어: CPU가 알 수 없는 명령어를 처리하려고 할 때 발생.
  • 오버플로우: 산술 연산이 CPU 레지스터의 최대값을 초과할 때 발생.
  • 시스템 콜(System Call): 운영체제의 기능을 호출할 때 발생하는 인터럽트. 사용자가 커널 모드로 진입하기 위해 주로 사용.

● 외부 인터럽트 (External Interrupt)

외부 인터럽트(하드웨어 인터럽트)란 CPU 외부를 통해 발생하는 이벤트에 대한 인터럽트를 말합니다. 외부 인터럽트는 주로 입출력 장치나, 타이머 등의 외부장치가 특정 조건을 충족할 때 발생하는 이벤트를 통해 발생합니다.

 

외부 인터럽트의 주요 예시를 살펴보자면,

주요 외부 인터럽트 예시:

  • 키보드 입력: 사용자가 키보드를 눌렀을 때 발생하는 인터럽트.
  • 마우스 클릭: 마우스 클릭 등의 입력이 있을 때 발생.
  • 타이머 인터럽트: 타이머가 설정된 시간이 다 되었을 때 발생. 보통 운영체제에서 시간 기반의 스케줄링에 사용.
  • I/O 인터럽트: 네트워크 카드, 하드 드라이브 등에서 데이터 전송이 완료되었을 때 발생.
  • 전원 관리 인터럽트: 배터리 수준이 낮거나 전원이 꺼지기 직전에 발생.

●인터럽트의 처리 과정

인터럽트는 어떤 과정을 통해 처리를 할까요?

1. 인터럽트가 발생할 시, CPU는 현재 상태를 저장하고 실행 중인 작업을 잠시 중단합니다.

2. 인터럽트 벡터 테이블을 통해 해당 인터럽트를 확인하고, 인터럽트를 처리하는 핸들러로 점프해 예외 처리를 합니다.

3. 예외 처리를  다 하면 저장해놓은 상태를 참조해 원래 작업으로 복귀합니다.

인터럽트 벡터 테이블 이란?

인터럽트 벡터 테이블은 인터럽트가 발생했을 때, 어떤 인터럽트가 발생했는지 식별하고, 해당 인터럽트에 맞는 처리 코드(인터럽트 핸들러)의 주소를 저장해놓은 테이블입니다.

 


● 내부 인터럽트와 외부 인터럽트의 주요 차이점

내부 인터럽트와 외부 인터럽트의 주요 차이점을 보고 마무리 해보겠습니다.

발생 원인 CPU 내부에서 발생 (프로그램 실행 중) 외부 하드웨어 장치에서 발생
예시 디바이드 바이 제로, 페이지 폴트 등 키보드 입력, 타이머, 네트워크 카드 등
발생 시점 예측 불가능한 오류나 특정 조건이 발생할 때 장치에서 특정 이벤트가 발생할 때
CPU 개입 여부 CPU 스스로 감지 외부 장치에 의해 발생

 

인터럽트들은 시스템이 다양한 작업을 동시에 처리하고, 예기치 않은 상황에 대처할 수 있도록 하기 위해 만들어진 매커니즘입니다.


 

반응형

'프로그래밍 > CS' 카테고리의 다른 글

[CS] 페이징(Paging)  (0) 2024.10.22
[CS] PCB와 ContextSwitching  (2) 2024.10.22
[CS] CPU 스케줄링  (4) 2024.10.21
[CS] CPU의 메모리 계층 구조  (4) 2024.10.13
[CS] 커널(Kernel)과 그 종류에 대해  (0) 2024.10.13
반응형

하이안뇽방가

오늘은 운영체제의 핵심개념인 CPU 스케줄링에 대해 알아보겠스빈다

렛츠 고도리

 


CPU 스케줄링이란?

CPU의 코어는 한 코어당 하나의 프로세스밖에 처리하지 못합니다.

따라서 하나의 코어에서 여러 개의 프로세스들을 짧은 시간동안 처리하려면 시분할로 작업을 처리해야 하는데요.

여러 프로세스를 효율적으로 처리하려면 각 프로세스를 언제, 얼마나 작업을 처리할지 신중히 선택해야합니다. (저희가 해야하냐고요? 아뇨 커널이 해줍니다 ㅋ)

커널은 각 프로세스마다 형평성과 효율성에 맞게 작업 순서와 시간을 배분하는데요. 이를 CPU 스케줄링이라고 말합니다.

 


CPU 스케줄링의 주요 목표

CPU 스케줄링의 목표는 다음과 같습니다.

  • 공정성 : 각 프로세스나 스레드가 공정하게 CPU를 사용할 수 있어야 합니다.
  • 응답 시간 최소화 : 프로세스의 요청에 대한 응답 시간을 줄여야 합니다.
  • 처리량 최적화 : 주어진 시간 동안 최대한 많은 작업을 처리하는 것이 중요합니다.
  • 대기 시간 최소화 : 각 프로세스의 작업이 대기하는 시간을 줄여야 합니다.
  • CPU사용율 최적화 : 유휴 상태에 빠진 CPU가 없이 최대한 많이 사용되도록 관리해야 합니다.

프로세스의 우선순위

CPU 스케줄링은 프로세스의 우선순위를 바탕으로 작업 배분을 하게 됩니다.

그렇다면 프로세스의 우선순위는 어떠한 기준으로 정해질까요?

  • 커널 프로세스 vs 일반 프로세스 : 커널 프로세스는 운영체제의 중요한 작업을 처리하기 때문에, 일반 프로세스보다 우선순위가 높습니다.
  • CPU 집중 프로세스 vs 입출력 집중 프로세스 : CPU를 많이 사용하는 작업을 CPU 집중 프로세스, 입출력(I/O)작업을 많이 수행하는 작업을 입출력 집중 프로세스라고 합니다. 입출력 집중 프로세스는 빠르게 입출력 대기 상태로 전환되기 때문에 시스템 효율성을 높일 수 있어 CPU 집중 프로세스보다 우선순위가 높습니다. 이를 사이클 훔치기라고 합니다.
  • 전면 프로세스 vs 후면 프로세스 : 전면 프로세스는 사용자와 상호작용하는 프로세스, 후면 프로세스는 사용자와 상호작용이 없는 프로세스입니다. 전면 프로세스는 사용자와 상호작용을 통해 즉각적인 응답을 요구하므로 후면 프로세스보다 우선순위가 높습니다.

프로세스 상태의 다중 큐

 

프로세스의 우선순위는 프로세스 제어 블록(PCB)에 표시됩니다.

CPU스케줄러는 프로세스의 제어 블록을 탐색해서 우선순위가 가장 높은 프로세스를 작업하게 되는데요, 하지만 매번 모든 프로세스를 탐색하기는 부담이 상당히 많이 갑니다.

때문에 다중 큐 (Multiple Queue) 라는 것을 사용하게 되었습니다.

준비 상태의 다중 큐

왼쪽 - 정리 X , 오른쪽 - 우선순위 정렬

왼쪽 그림은 우선순위 별로 정렬하지 않은 상태입니다.

이러면 우선순위가 높은 프로세스를 찾기 위해 일일히 찾아야 해서 비용이 큽니다 ㅠ.

때문에 오른쪽 그림처럼 우선순위 별로 큐를 만든 후, 우선순위 단위로 큐를 처리하면 편합니다.

 

준비 상태의 다중 큐는 프로세스 우선순위를 배정하는 방식이 2가지가 존재하는데, 바로  고정 우선순위 방식 동적 우선순위 방식입니다.

 

고정 우선순위란, 프로세스가 생성될 때 우선순위가 결정되며, 실행 중에 바꿀 수 없는 방식입니다. 보통 시스템이 자동으로 설정하거나 사용자가 지정합니다.

ㄴ ex) 운영체제의 중요한 작업이나 시스템 프로세스는 높은 우선순위를 가지고 실행됩니다.

 

동적 우선순위란, 프로세스가 실행되는 도중에 우선순위가 변동될 수 있는 방식입니다. 우선순위가 낮은 프로세스가 우선순위가 높은 프로세스에 밀려 실행이 밀리는 기아 문제 (Starvation)현상을 방지하기 위해 사용합니다.

ㄴ ex) 오래 대기하는 프로세스의 우선순위를 점점 높이는 에이징(Aging) 기법을 사용할 때 자주 씁니다.

 

 

●대기 상태의 다중 큐 

입출력 단위로 큐를 만듬.

대기 상태의 다중 큐란, 위 사진처럼 각각의 입출력에 대해 대기 중인 프로세스들의 큐를 말합니다. 대기 큐는 여러 Pcb를 동시에 꺼내어 준비 상태로 옮기는 역할을 합니다.

입출력 장치들이 동시에 많이 처리될 경우 여러 개의 인터럽트를 한번에 처리해야 하는데, 이를 위해 인터럽트 벡터를 사용합니다.

 

인터럽트 벡터란,  인터럽트가 발생했을 때 어떤 인터럽트가 발생했는지 식별하고, 해당 인터럽트에 맞는 처리를 하기 위한 일종의 테이블입니다.

 

대기 큐는 사실 이름과 달리 무조건 선입선출이 아닙니다. 작업 속도를 높이기 위해 작업 순서를 뒤바꾸는 경우가 있기 때문입니다.


선점형 스케줄링과 비선점형 스케줄링

CPU 스케줄러는 스케줄링을 작성할 때 2가지 방식을 고려합니다. 바로 선점형 스케줄링과 비선점형 스케줄링인데요.

 

선점형 스케줄링이란, 어떤 프로세스가 작업중일 때 다른 프로세스가 작업을 위해 현재 프로세스의 작업을 중단하고 해당 프로세스의 작업을 할당받는 방식입니다. 즉 한 코어가 여러 프로세스를 작업하는 방식입니다.

 

비선점형 스케줄링이란, 선점형 스케줄링의 반대로 어떤 프로세스가 작업중일 때 다른 프로세스가 작업을 할당받지 못하도록, 즉 한 프로세스가 한 코어의 작업을 독점하는 방식입니다.


스케줄링 알고리즘

CPU 스케줄러는 스케줄링을 효율적으로 작성하기 위해 여러 알고리즘을 사용한다.

당연히 효율적인 알고리즘을 사용하기 위해선 평가 기준이 있어야한다. 


●스케줄링 알고리즘 평가 기준

CPU 스케줄러의 알고리즘 평가(목표) 기준은 다음과 같다.

  • 대기 시간 (Waiting Time) : 각 프로세스가 CPU할당을 기다리는 시간을 최소화 하는 것.
  • 반환 시간 (Turnaround Time) : 프로세스가 시작부터 끝날 때까지 걸리는 시간을 최소화 하는 것.
  • 응답 시간 (Response Time) : 사용자가 요청을 시작한 후 첫 번째 응답을 받는 시간을 최소화하는 것.
  • CPU 이용률 (CPU Utilization) : CPU가 유휴없이 최대한 많은 시간을 작업 처리에 사용하도록 하는 것.
  • 문맥 전환 오버헤드(Context Switching Overhead) : 문맥 전환에 소모되는 자원을 최소화하는 것.
  • 공정성(Fairness) : 기아(Starvation) 문제 없이 모든 프로세스가 공평하게 CPU를 할당받는 것.
  • 처리량(Throughput) : 단위 시간당 처리할 수 있는 프로세스의 수를 최대화하는 것.
  • 우선순위(priority handling) : 우선순위가 높은 프로세스에 더 많은 CPU 자원을 할당하는 것.

●스케줄링 알고리즘 종류

스케줄링 알고리즘에는 다양한 종류가 있지만, 크게 선점형 알고리즘과 비선점형 알고리즘을 사용하는지에 따라 나눌 수 있다.

 

●비선점형 알고리즘

 

1. FCFS 스케줄링(First Come First Served)

준비 큐에 도착한 순서대로 CPU에 작업을 할당하는 방식입니다. 준비 큐가 하나라 모든 프로세스의 우선순위가 동일합니다. 때문에 처리 시간이 긴 프로세스가 CPU를 차지하면 다른 프로세스가 작업을 못하기 때문에 효율성이 낮아진다.

 

2. SJF 스케줄링(Shortest Job first)

준비 큐에 있는 프로세스 중에 실행 시간이 가장 짧은 작업부터 처리하는 방식입니다. 최단 작업 우선 스케줄링이라고도 합니다. 하지만 현대의 프로세스는 사용자와 빈번하게 상호 작용하기 때문에 프로그램의 실행 시간을 파악하기가 어려워 이 알고리즘을 사용하기 힘듭니다. 작은 작업부터 처리하기 때문에 효율적일 수 있으나, 실행 시간이 긴 프로세스의 CPU할당이 연기될 수 있는 기아 문제가 발생할 수 있으므로 공평성에 위배된다. 이 때, 대기 시간이 길수록 우선 순위를 높이는 에이징 방식으로 완화할 수 있다.

 

3. HRN 스케줄링(Highest Response Ratio Next)

SJF 스케줄링에서 발생할 수 있는 기아 상태를 기아 상태를 해결하기 위해 만들어진 알고리즘입니다. 최고 응답률 우선 스케줄링이라고도 합니다. SJF의 우선순위 판단 기준이 프로세스의 실행 시간이었다면, HRN은 대기 시간과 사용 시간에 대한 연산을 통하여 우선순위를 정하는 방식입니다.

HRN 스케줄링의 연산 기준

 

●선점형 알고리즘

1. 라운드 로빈 스케줄링(Round Robin)

한 프로세스가 할당받은 시간(타임 슬라이스)동안 작업을 하다가 완료하지 못하면 다시 큐에 집어 넣습니다. 우선순위를 적용하지 않는 단순한 방식의 알고리즘입니다.

 

2. SRT 스케줄링(Shortest Remaining Time)

SJF 스케줄링과 라운드 로빈 스케줄링을 혼합한 방식의 알고리즘입니다. 최소 잔류 시간 우선 스케줄링이라고도 합니다.

라운드 로빈 스케줄링의 방식을 기본적으로 쓰되, CPU가 다음 프로세스를 할당할 때, 남은 작업 시간이 가장 적은 프로세스를 선택합니다. 하지만 이 또한 기아 문제때문에 잘 사용하지 않습니다.

 

3. 다단계 큐 스케줄링(Multi-Level Queue)

우선순위에 따라 준비 큐를 여러 개 사용하는 방식입니다. OS로부터 부여받은 우선순위에 따라 각 우선순위에 맞는 큐에 삽입됩니다. 각 우선순위 큐는 라운드 로빈 알고리즘 방식으로 운영되며, 고정 우선순위 방식을 사용합니다. 제일 상위 우선순위 큐의 모든 프로세스 작업이 끝나야 다음 우선순위 큐 작업이 시작됩니다. 때문에 하위 우선순위 큐의 작업들이 연기되는 문제가 있습니다.

3. 다단계 피드백 큐 스케줄링(Multi-Level Feedback Queue)

다단계 큐 스케줄링에서 에이징 기법을 적용해 단점을 보완한 방식입니다.

다단계 피드백 큐 방식의 특징은, CPU 할당이 끝난 프로세스가 원래 우선순위의 큐에 되돌아 가지 않고 한 단계 아래의 우선순위 큐에 들어가게 됩니다. 때문에 변동 우선순위 방식을 사용합니다. 또한 우선순위에 따라 할당하는 시간(타임 슬라이스)가 다릅니다. 우선순위가 낮은 큐의 프로세스는 CPU를 좀 더 사용할 수 있도록 타임 슬라이스를 크게 부여합니다.

요즘 운영체제가 많이 채용하는 방식입니다.


오늘은 CPU스케줄링에 대해 포스팅 해봤는데요?

생각보다 적을게 많아서 고생했습니다.. ;;;;;

그럼 20000 좋은 하루 되십시오

반응형

'프로그래밍 > CS' 카테고리의 다른 글

[CS] PCB와 ContextSwitching  (2) 2024.10.22
[CS] 인터럽트  (0) 2024.10.22
[CS] CPU의 메모리 계층 구조  (4) 2024.10.13
[CS] 커널(Kernel)과 그 종류에 대해  (0) 2024.10.13
[CS] 유저영역, 커널영역 그리고 SystemCall  (1) 2024.10.13
반응형

ㅎㅇ헬로봉주르

이번 포스팅은 CPU의 메모리 계층 구조에 관해 작성해볼까 합니다.

특히 캐시와 레지스터라는 용어는 개발자의 입장에선 정말 많이 접하게 되는데요.

그만큼 중요한 개념이고, 알게 많기 때문에 글이 길어질듯 합니다.

같이 알아봅시다 ㄱㄱ싱


CPU의 메모리 계층 구조는 어떻게 생겼을까?

 

CPU의 메모리는 계층 구조로 이루어져 있습니다.

어떻게 생겼을까요?

 

캐시와 레지스터는 주 기억장치가 아니랍니다. 참고

(이미지 참고 : [CS/운영체제] 메모리 계층 구조도, 그리고 캐시 메모리(Cache memory) (velog.io))

 

[CS/운영체제] 메모리 계층 구조도, 그리고 캐시 메모리(Cache memory)

메모리 계층은 아래와 같은 구성 요소로 이루어져 있습니다. 메모리 계층 구조(Memory hierarchy)란 메모리를 필요에 따라 여러 가지 종류로 나누어 둠을 의미합니다. 그 이유는 메모리를 필요에 따

velog.io

바로 이렇게 생겼습니다.

 

메인 메모리는 주 기억장치, 혹은 저희가 친숙하게 부르는 RAM이라고도 부릅니다.

하드 디스크는 보조 기억장치라고도 부릅니다.

 

사진에서의 핵심 포인트가 있습니다. 한 번 알아볼까요?

 

1. 처리속도와 저장용량이 반비례한다.

 

 사진을 자세히 보시면 레지스터>캐시(L1, L2, L3)>주 기억장치(RAM)>보조 기억장치(HDD)순으로 속도가 빠르다고 나와있는데요. 이는 CPU와의 물리적 거리가 해당 순서대로 가깝기 때문입니다.또한 고속 메모리를 만들기 위한 설계와 비용적 제약으로 인해 처리속도와 저장용량이 반비례하게 됩니다.

 

2. 휘발성 메모리와, 비휘발성 메모리로 나뉜다.

 

레지스터, 캐시, RAM은 휘발성 메모리, HDD는 비휘발성 메모리입니다.

여기서 휘발성 메모리와 비휘발성 메모리는 데이터 유지 특성에 따라 구분하는데요.

 - 휘발성 메모리 : 전원이 공급될 때만 데이터를 유지하는 메모리입니다. CPU에 접근해 빠른 속도로 데이터를 읽고 쓰기가 가능해 작업 중인 데이터를 임시로 저장하는 용도로 사용합니다.

 - 비휘발성 메모리 : 전원이 꺼져도 데이터를 유지하는 메모리입니다. CPU에 직접 접근할 수 없고 커널(파일 시스템)을 통해 RAM에 로드하기 때문에 속도가 느립니다. 주로 영구 저장용 메모리로 사용됩니다.

 

이런 특징들로 인해 각자의 역할이 나뉘어져 있습니다. 하나씩 차근차근 알아보겠습니다.


레지스터(Register)

 

레지스터는 CPU에서 가장 가까운 위치에 있는 메모리입니다.


1. 레지스터의 특징

 

레지스터의 주요 특징을 살펴보겠습니다.

  • CPU가 연산 등의 요청을 처리하는데 필요한 데이터를 일시적으로 저장합니다.
  • 속도가 매우 빠릅니다. (CPU와 가장 가까운 메모리이기 때문)
  • 휘발성 메모리입니다.
  • 용량이 매우 작습니다.

2. 레지스터가 데이터를 읽고 쓰는 과정

 

사실 로우레벨 프로그래밍을 하지 않으면 접할 일이 거의 없을겁니다.

굳이 볼 수 있긴한데, 디버그 중 VS의 디스어셈블리 창을 열어보시면 해당 사진과 비슷한 광경이 나옵니다.

eax, ecx가 레지스터

그 전에 먼저, CPU에는 산술연산장치(ALU)라고 CPU로부터 들어온 연산요청을 처리하는 장치가 있습니다.

해당 연산에 사용할 데이터를 주로 레지스터에서 꺼내오게 되는데요. 연산을 수행한 후, 결과를 다시 레지스터나 RAM에 저장하게 됩니다.

 

해당 코드를 순서대로 설명해보자면,

1. mov dword ptr [a], 2 (변수 a에 값 2를 저장합니다. (a의 주소는 현재 스택 프레임에 위치합니다.)

2. mov dword ptr [b], 3 (변수 b에 값 3을 저장합니다.)

3. mov eax, dword ptr [b] (b의 값을 eax 레지스터에 로드합니다.)

4. mov ecx, dword ptr [a] (a의 값을 ecx 레지스터에 로드합니다.)

5. add ecx, eax (eax(b의 값)와 ecx(a의 값)를 더하여 ecx에 저장합니다.(이 때, ALU를 통해 연산을 거치게 됩니다))

6. mov eax, ecx (결과를 eax 레지스터에 저장합니다.)

7. mov dword ptr [c], eax (c 변수에 eax의 값을 저장합니다.)

이로써 c는 a + b의 결과값이 됩니다.


캐시(Cache)

 

캐시는 CPU 내부에 위치하는 레지스터와 RAM의 중간 사이의 메모리입니다.


1. 캐시의 특징

 

캐시의 주요 특징을 살펴보겠습니다.

  • 자주 사용하는 데이터를 캐시에 저장하여 메인 메모리(RAM)와의 접근 시간을 줄입니다.(병목현상 ↓)
  • 레지스터보다는 느리지만, RAM보다는 훨신 빠릅니다.
  • 휘발성 메모리입니다.
  • L1, L2, L3 캐시로 계층이 나뉘어 있으며, L1이 상대적으로 가장 빠르지만 용량이 적고, L3가 상대적으로 가장 느리지만 용량이 큽니다.

작업관리자의 "성능"탭의 CPU항목에서 확인할 수 있다. 용량을 보면 L1이 제일 작고 L3가 제일 큰 걸 볼 수 있다.

 


2. 캐시의 역할

 

캐시는 레지스터와 RAM의 속도 차이에서 발생하는 병목현상을 줄이기 위해 만들어진 메모리입니다.

캐시는 자주 사용되는 데이터나 명령어를 저장하여 CPU가 메모리에 접근할 필요를 줄입니다. CPU가 특정 데이터를 여러 번 참조할 때, 해당 데이터가 캐시에 존재하면 RAM보다 훨씬 빠르게 접근할 수 있습니다.

캐시는 내부적으로 구현되어있는 캐시 관리 정책과 구조에 따라 자주쓰는 데이터를 효율적으로 적재하여 CPU가 데이터를 읽을 때 캐시에 있는 데이터를 먼저 탐색하여 쓰게됩니다.


3. 캐시 데이터 쓰이는 과정

 

캐시의 동작 과정을 알아보기 전에 알아가야 할 두 키워드가 있습니다.

 

캐시 미스와 캐시 히트

 - 캐시 미스 : CPU가 특정 데이터를 사용하려고 할 때, 먼저 캐시를 확인합니다. 이때 캐시에 사용할 데이터가 없을 경우 캐시 미스라고 합니다.

 - 캐시 히트 : CPU가 특정 데이터를 사용려고 캐시를 확인한 결과, 캐시에 사용할 데이터가 있는 경우 캐시 히트라고 합니다.

(캐시 미스와 캐시 히트는 정말 많이 접하게 되는 용어니까 알아두면 좋습니다.)

 

그럼 캐시의 데이터는 어떤식으로 쓰일까요?

캐시의 데이터는 CPU가 RAM에 접근하는 과정에서 자동으로 관리됩니다.

CPU가 특정 데이터를 사용하려고 할 때, L1 → L2 → L3순으로 캐시를 확인하게 됩니다.

이 때, 캐시에 데이터가 없는 경우(캐시 미스), 캐시에 데이터가 있는 경우(캐시 히트)로 총 두 가지로 나눌 수 있는데요.

1. 캐시 미스가 발생한 경우 : 캐시에 데이터가 없을 경우 RAM에 접근하여 데이터를 가져와 캐시에 저장하고, 이 데이터를 사용하여 연산을 수행합니다.

2. 캐시 히트가 발생한 경우 : 캐시에 데이터가 있을 경우 CPU는 RAM에 접근하지 않고 캐시에서 바로 해당 데이터를 가져와 연산을 수행합니다.


4. 캐시가 쌓이는 과정

 

캐시가 어떤 방식으로 쓰이는지를 봤는데요, 반대로 캐시가 쌓이는 과정도 알아봅시다.

캐시는 제한된 크기에서 데이터를 효율적으로 쌓고, 교체하기 위해 다양한 캐시 알고리즘을 통해 데이터를 관리합니다.

캐시 교체 알고리즘은 무엇이 있을까요?

 

1. LRU (Least Recently Used)

LRU는 메모리에 남아있는 캐시 중 가장 오랫동안 사용되지 않은 데이터를 교체하는 알고리즘입니다. 오랫동안 사용하지 않았던 데이터는 앞으로도 사용할 확률이 적다고 판단하기 때문입니다.

LRU의 동작 원리

많은 운영체제가 이 방식을 쓰고 있다고 합니다.

 

2. LFU (Least Frequently Used)

LFU는 메모리에 남아있는 캐시 중 가장 적게 사용되고 있는, 즉 참조 횟수가 가장 적은 데이터를 교체하는 알고리즘입니다. 참조 횟수를 알아야 하므로 각 캐시마다 카운터가 필요합니다.

LFU의 동작 원리

 

3. FIFO (First In, First Out)

FIFO는 캐시가 꽉 찼을 때, 먼저 들어온 데이터가 먼저 교체 되는 선입선출 구조의 알고리즘입니다.

설명만큼이나 구현도 쉽고 복잡하지 않습니다.

 

 

++ 가장 좋은 알고리즘이란??

사실 어느 상황에나 가장 좋고 빠른 알고리즘이란건 없다고 생각합니다. 그럴거면 여러 알고리즘이 존재하지 않았겠죠?

어떤 캐시가 일을 더 잘합니까? — FIFO가 LRU보다 낫습니다요 | by scalalang2 | 취미로 논문 읽는 그룹 | Medium

해당 사이트는 많은 캐시 알고리즘들을 성능 비교분석한 글입니다. 읽어보면 좋을 것  같습니다.


5. 캐시 적중률을 높이는 방법

 

캐시 히트가 일어나는 비율을 캐시 적중률(or 캐시 히트율)이라고 합니다. 캐시 적중률을 높이는 것은 CPU가 RAM에 접근하는 빈도가 줄어드는 것이므로 성능향상을 꾀할 수 있습니다.

때문에 캐시 메모리의 이점을 제대로 활용하려면 CPU가 사용할 법한 데이터를 예측해서 코드를 짜는 것이 바람직합니다.

CPU가 사용할 법한 데이터를 어떻게 예측하려면 CPU의 메모리 접근 원리를 알아야 하는데요?

CPU의 메모리 접근 원리를 참조 지역성의 원리라고 합니다.

 

●참조 지역성의 원리

참조 지역성의 원리는 캐시 적중률에 있어서 핵심 개념입니다.

공간적으로나 시간적으로 데이터가 가깝게 사용될 경우 캐시 적중률이 높아질 수 있습니다.

 

1. 시간 지역성

자주 사용하는 데이터를 반복적으로 접근하는 경우 캐시 적중률이 높아집니다.

CPU는 최근에 접근했던 메모리 공간에 다시 접근하려는 경향이 있기 때문입니다.

시간 지역성을 극대화하기 위해선 루프문 내에 같은 변수를 여러 번 사용하는 구조가 이상적입니다.

2. 공간 지역성

데이터가 캐시 데이터들의  메모리 공간에서 가까운 경우 캐시 적중률이 높아집니다.

CPU는 접근한 메모리 공간 근처를 접근하려는 경향이 있기 때문입니다.

공간 지역성을 극대화하기 위해선 배열이나 구조체같은 연속적인 메모리를 통해, 인접한 데이터를 접근하도록 설계하는 구조가 인상적입니다.

 

++ 캐시 적중률의 중요함(std::vector vs std::list)

벡터와 리스트의 차이는 연속적인 메모리냐 아니냐의 차이입니다.

벡터는 연속적인 메모리의 배열이기 때문에 캐시 적중률이 높습니다.

둘의 순회 시간차이가 얼마나 날까요?

더보기

구현 코드)

#define COUNT 10000000
double Process1(std::vector<int>& vec)
{
	auto start = std::chrono::steady_clock::now();
	for (auto o : vec)
	{
		++o;
	}
	auto end = std::chrono::steady_clock::now();
	std::chrono::duration<double> elapsed_seconds = end - start;
	return elapsed_seconds.count();
}

double Process2(std::list<int>& list)
{
	auto start = std::chrono::steady_clock::now();
	for (auto o : list)
	{
		++o;
	}
	auto end = std::chrono::steady_clock::now();
	std::chrono::duration<double> elapsed_seconds = end - start;
	return elapsed_seconds.count();
}

int main()
{
	std::vector<int> vec;
	std::list<int> list;
	for (int i = 0; i < COUNT; i++)
	{
		vec.push_back(i);
		list.push_back(i);
	}
	std::cout << "vector : " << Process1(vec) << '\n';
	std::cout << "list : " << Process2(list) << '\n';
}

 

(막상 해보니까 차이가 너무 많이나는데...머지...)

어쨋든 차이가 심합니다. 캐시 적중률을 높이는 것은 개발자에 있어서 숙명이라고 할 수 있겠습니다.

 

6. 캐시 일관성

CPU는 각기마다 개별 캐시 메모리를 소유하고 있습니다.

때문에 멀티코어 시스템 환경에선 각 코어가 캐시를 사용하는 동안, 서로 동일한 메모리 주소에 대한 데이터가 일관되게 유지되도록 해야합니다. 이를 캐시 일관성이라고 하는데요.

 

어떤 상황에서 캐시 일관성 문제가 발생할까요?

CPU1과 CPU2가 있다고 가정해봅시다.

  1. CPU1은 메모리 주소 X에 저장된 값을 자신의 캐시에 저장했습니다.
  2. CPU2도 메모리 주소 X에 저장된 값을 자신의 캐시에 저장했습니다.
  3. CPU1이 캐시에서 값을 읽어와 변경하고, 메모리에 쓰지 않았습니다. 
  4. CPU1이 캐시에서 값을 변경한 후 메모리에 쓰지 않았기 때문에  CPU2는 여전히 이전 값을 자신의 캐시에 유지하게 됩니다.
  5. 이 경우 CPU1과 CPU2는 동일한 메모리 주소 X에 대한 서로 다른 값을 가지게 됩니다.

이러한 문제가 발생할 시 데이터 무결성에 문제가 생깁니다.

이를 방지하기 위해 캐시 일관성 프로토콜이 사용된다는데요? 나중에 알아봅시다.


메인 메모리(RAM)

프로세스의 주소공간에 할당되는 메모리는 메인 메모리에 해당됩니다.


1. 메인 메모리의 특징

메인 메모리의 주요 특징에 대해 살펴보겠습니다.

  • 프로그램이 실행 중일 때 필요한 데이터와 명령어를 임시로 저장합니다.
  • CPU에게 처리할 데이터를 전달해줍니다.
  • 레지스터, 캐시보단 속도가 느리지만, HDD보다 빠릅니다.
  • 휘발성 메모리입니다.
  • HDD에 비해 상대적으로 작은 용량을 가지고 있습니다. (통상 8~32GB)

2. RAM과 HDD의 관계

RAM은 HDD의 데이터를 로드해 CPU가 빠르게 데이터를 처리할 수 있도록 도와줍니다. RAM이 부족할 경우, 가상 메모리나 페이지 파일을 통해 HDD를 메모리처럼 사용할 수 있습니다.

예를 들자면, 운영체제나 프로그램을 실행할 때, HDD에서 RAM으로 데이터를 로드하여 프로그램이 원할하게 실행되도록 해줍니다.



하드 디스크(HDD, SSD)

메모리 계층 구조에서 CPU가 데이터를 접근하는 데 가장 느린 장치입니다.


1. 하드 디스크의 특징

하드 디스크의 주요 특징에 대해 살펴보겠습니다.

  • 장기적인 데이터 저장을 위해 사용합니다. 
  • 속도가 느립니다.
  • 비휘발성 메모리입니다.
  • 대용량 저장이 가능합니다.

2. HDD vs SSD

HDD와 SSD는 다들 들어보셨을겁니다.

용량은 HDD가 SSD보다 많지만, 속도는 SSD가 훨 빠르다는 사실을 대부분 아실겁니다. 그 이유가 뭘까요?

 

1. HDD와 SSD의 구조적 차이

HDD :  데이터를 읽고 쓰기 위해서 기계적인 부품(플래터)와 읽기/쓰기 헤드를 사용합니다. 데이터를 읽거나 쓰기 위해서는 플래터가 특정 위치로 회전해야 하고, 읽기/쓰기 헤드가 해당 위치로 이동해야 합니다. 이 과정에서 데이터 접근 시간이 길어집니다. 또한 데이터를 병렬적으로 처리하지 못하기 때문에 동시에 여러 데이터에 접근하기 어렵습니다.

SSD : 데이터를 전자적으로 처리하는 플래시 메모리 칩을 사용하여 데이터를 읽고 씁니다. 기계적 부품이 없기 때문에 데이터에 접근하는 속도가 매우 빠릅니다. 또한 내부에서 병렬 처리가 가능하기 때문에 여러 데이터에 빠르게 접근할 수 있습니다.



 

반응형

'프로그래밍 > CS' 카테고리의 다른 글

[CS] 인터럽트  (0) 2024.10.22
[CS] CPU 스케줄링  (4) 2024.10.21
[CS] 커널(Kernel)과 그 종류에 대해  (0) 2024.10.13
[CS] 유저영역, 커널영역 그리고 SystemCall  (1) 2024.10.13
[CS] 프로세스와 스레드  (2) 2024.10.06

+ Recent posts