* '혼자서 공부하는' 컴퓨터구조+운영체제 이지만, 혼자보단 여럿이하면 더 좋으니 혼공 학습단 13기와 함께합니다 :)
[4주차 학습목록] ʕo•ᴥ•ʔ✎
Ch09. 운영체제 시작하기
01. 운영체제를 알아야 하는 이유
02. 운영체제의 큰 그림
Ch10. 프로세스와 스레드
01. 프로세스 개요
02. 프로세스 상태와 계층 구조
03. 스레드
Ch11. CPU 스케줄링
01. CPU 스케줄링 개요
02. CPU 스케줄링 알고리즘
Ch09. 운영체제 시작하기
9-1. 운영체제를 알아야 하는 이유
운영체제
운영체제(operating system)는 실행할 프로그램에 필요한 요소들 자원을 담당하고, 프로그램이 올바르게 실행되도록 돕는 특별한 프로그램이다. 운영체제는 프로그램이다보니 메모리에 적재되어야 한다. 메모리에는 커널 영역(kernal space)과 사용자 영역(user space) 두 공간이 있는데, 그 중 운영체제는 커널 영역에 따로 적재된다.
운영체제는 실행할 프로그램을 메모리에 적재하고, 더 이상 실행되지 않는 프로그램을 메모리에서 삭제하며 지속적으로 메모리 자원을 관리한다. 또한 운영체제는 응용 프로그램과 하드웨어 사이에서 응용 프로그램에 필요한 자원을 할당하고, 응용 프로그램이 올바르게 실행되도록 관리하는 역할을 맡는다. 이때, 운영체제는 관리할 자원별로 기능이 나누어져 있다.
즉, 운영체제는 응용 프로그램에 자원을 효율적으로 배분하고, 실행할 프로그램들이 지켜야 할 규칙을 만들어 컴퓨터 시스템 전체를 관리한다.
운영체제를 알아야 하는 이유
개발자에게 운영체제는 없어서는 안되는 존재다. 운영체제가 없다면 하드웨어를 조작하는 코드까지 직접 모두 작성해야 하기 때문이다. 그렇다보니 그저 사용만해도 개발을 하는데 별 문제가 없어보이기에 굳이 운영체제를 알아야 하는가 싶은 생각이 들 수도 있다. 하지만, 운영체제를 깊게 이해한다면 운영체제가 우리에게 건네는 말을 잘 이해할 수 있게되고, 운영체제에게도 제대로 명령을 할 수가 있게 된다. 그리고 하드웨어와 프로그램을 더 깊이 이해하게 되어 문제 해결의 실마리를 찾을 수 있게 된다.
개발을 하다보면 오류를 많이 맞닥뜨리다보니 오류 메시지를 이해하여 해결을 하는 경우가 많다. 이때, 많이 접하게 되는 오류 메시지의 근원은 운영체제이다. 따라서 많이 만나게 되는 오류를 잘 이해하고 문제 해결을 잘 하기 위해서 운영체제에 대해 깊이 이해하고 잘 알아야 하는 것이 필요하다!
9-2. 운영체제의 큰 그림
커널
커널(kernal)은 운영체제의 핵심 기능을 담당하는 부분이다.
[커널에 포함되지 않는 대표적인 서비스]
- 사용자 인터페이스(UI:User Interface)
: 사용자가 컴퓨터와 상호작용할 수 있는 통로
ex) window의 바탕화면
[사용자 인터페이스 UI 종류]
1) 그래픽 유저 인터페이스(GUI:Graphical User Interface)
: 그래픽 기반으로 컴퓨터와 상호작용할 수 있는 인터페이스
ex) 마우스를 이용해 프로그램 실행하는 경우, 터치하여 실행하는 경우 등..
2) 커맨드 라인 인터페이스(CLI:Command Line Interface)
: 명령어를 기반으로 컴퓨터와 상호작용할 수 있는 인터페이스
ex) 명령어를 입력함으로써 프로그램 실행하는 경우 등..
이중 모드
운영체제는 응용 프로그램들이 자원에 접근하려고 할 때 오직 자신을 통해서만 접근하도록 자원을 보호한다. 운영체제는 커널 영역 내의 하드 디스크에 데이터를 저장하는 코드를 실행하여 응용 프로그램의 작업을 대신 수행해준다. 이런 운영체제의 역할은 이중 모드로써 구현된다. 이중 모드(dual mode)란, CPU가 명령어를 실행하는 모드를 사용자 모드와 커널 모드로 구분하는 방식을 말한다.
- 사용자 모드(user mode) : 운영체제 서비스를 제공받을 수 없는 실행 모드
--> 즉, 커널 영역의 코드를 실행할 수 없는 모드, 일반적인 응용 프로그램 실행이 해당함
- 커널 모드(kernal mode) : 운영체제 서비스를 제공받을 수 있는 실행 모드
--> 커널 영역의 코드를 실행할 수 있는 모드,
CPU가 커널 모드로 명령어를 실행하면 자원에 접근하는 명령어를 비롯한 모든 명령어를 실행할 수 있음.
이때, 사용자 모드로 실행되는 프로그램은 시스템 호출을 통해 커널 모드로 전환하여 운영체제 서비스를 제공받을 수 있다.
시스템 호출
- 시스템 호출(system call, 시스템 콜) : 운영체제 서비스를 제공받기 위해 커널 모드로 전환하는 방법
시스템 호출은 인터럽트의 일종이다. 구체적으로는 소프트웨어적인 인터럽트이다.
- 소프트웨어 인터럽트 : 특정 명령어에 의해 발생하는 인터럽트
따라서, CPU가 시스템 호출을 처리하는 순서는 인터럽트 처리 순서와 유사하다.
[순서]
시스템 호출 발생시키는 명령어 실행 -> CPU는 지금까지 하던 작업을 백업 -> 커널 영역 내에 시스템 호출을 수행하는 코드(인터럽트 서비스 루틴) 실행 -> 기존 실행하던 응용 프로그램으로 복귀하여 실행
운영체제 서비스
운영체제의 핵심 서비스 3가지
1. 프로세스 관리
- 프로세스(process) : 실행 중인 프로그램
CPU는 한 프로세스를 실행하다가 다른 프로세스로 실행을 전환하고, 그 프로세스를 실행하다가 또 다른 프로세스로 실행을 전환하는 것을 반복한다. 그러하다보니 운영체제는 다양한 프로세스를 관리하고 실행할 수 있어야 한다.
* 여러 프로세스가 동시에 실행되는 환경에서는 '프로세스 동기화'가 필수로 필요하고, 프로세스가 꼼짝도 못하고 더 이상 실행되지 못하는 '교착 상태' 문제 또한 해결해야 한다.
2. 자원 접근 및 할당
운영체제가 CPU, 메모리, (보조기억장치와) 입출력장치를 어떻게 관리하고 어떠한 기능을 제공하는지 다음과 같다.
1) CPU
- CPU 스케줄링을 통해 관리
- CPU 스케줄링 : 프로세스들에게 CPU를 공정하게 할당하기 위해 어떤 프로세스로부터 CPU를 이용하게 할 것인지, 얼마나 오래 이용하게 할지를 결정하는 것
2) 메모리
- 운영체제는 새로운 프로세스가 적재될 때마다 어느 주소에 적재해야 할지 결정해야 한다.
- 어떻게 프로세스에게 메모리를 할당하는지 등에 대한 자세한 얘기는 추후 14장에서 알아보자.
3) 입출력장치
- 운영체제는 인터럽트를 처리하는 프로그램, 즉 인터럽트 서비스 루틴을 제공함으로써 입출력 작업을 수행한다.
3. 파일 시스템 관리
- 컴퓨터를 사용하는 동안에는 여러 파일을 열고, 생성하고, 삭제를 하게 된다. 이러한 파일들을 한 곳에 묶어 폴더로 관리하게 된다.
이러한 파일 시스템(file system) 또한 운영체제가 지원하는 핵심 서비스이다.
보조기억장치 속 데이터를 운영체제가 어떻게 파일과 폴더(디렉터리)로 관리하는지는 추후 15장에서 알아보자.
Ch10. 프로세스와 스레드
10-1. 프로세스의 개요
프로세스
- 프로세스(process) : 실행 중인 프로그램
- 프로세스 확인하는 방법
1) 윈도우 : 작업 관리자 > [프로세스] 탭
2) 리눅스 : ps 명령어 > ps -ef 명령어 실행
프로세스 중에선, 사용자가 볼 수 있는 공간에서 실행되는 프로세스와 보이지 않는 공간에서 실행되는 프로세스가 있다.
- 포그라운드 프로세스(foreground process) : 사용자가 보는 앞에서 실행되는 프로세스
- 백그라운드 프로세스(background process) : 사용자가 보지 못하는 뒤편에서 실행되는 프로세스
* 백그라운드 프로세스 중 사용자와 상호작용할 수 있는 프로레스와 상호작용하지 않는 프로세스가 있다. 이렇게 사용자와 상호작용하지 않는 백그라운드 프로세스를 유닉스 운영체제에선 데몬(demon)으로, 윈도우 운영체제에선 서비스(service)라고 불린다.
프로세스 제어 블록
CPU 자원은 한정적이므로 모든 프로세스가 동시에 CPU를 사용할 수는 없다. 따라서 프로세스들은 차례대로 돌아가며 정해진 시간만큼 CPU를 이용하고, 시간이 끝났음을 알리는 인터럽트(타이머 인터럽트)가 발생하면 자신의 차례를 양보하고 다음 차례가 올 때까지 기다린다. 운영체제는 프로세스 제어 블록(PCB:Process Control Block)을 이용하여 프로세스의 실행 순서를 관리하고, 프로세스에 CPU를 비롯한 자원을 배분한다.
- 프로세스 제어 블록(PCB) : 프로세스를 식별하기 위해 필요한 프로세스와 관련된 정보를 저장하는 자료 구조
- PCB는 커널 영역에 생성됨.
- "새로운 프로세스가 생성되었다." = "운영체제가 PCB를 생성했다.", "프로세스가 종료되었다." = "운영체제가 해당 PCB를 폐기했다."
PCB에는 대표적으로 다음과 같은 정보들이 담긴다.
1. 프로세스 ID(PID:Process ID)
- 특정 프로세스를 식별하기 위해 부여하는 고유한 번호
- 윈도우 작업 관리자에서 확인 가능함
- 같은 일을 수행하는 프로그램이라도 두번 실행하면 PID가 다른 두 개의 프로세스가 생성된다.
ex) 학번, 사번 ..
2. 레지스터 값
- 프로세스는 자신의 실행 차례가 돌아오면 이전까지 사용했던 레지스터의 중간값들을 모두 복원한다.
그 이유는, 이전까지 진행했던 작업들을 이어서 실행하기 위함이다.
3. 프로세스 상태
- 현재 프로세스가 어떤 상태인지 PCB에 기록되어야 한다.
- 입출력장치를 사용하기 위해 기다리고 있는 상태인지, CPU를 사용하기 위해 기다리고 있는 상태인지, CPU를 이용하고 있는 상태인지 등 프로세스 상태 정보가 PCB에 저장된다.
4. CPU 스케줄링 정보
- 프로세스가 언제, 어떤 순서로 CPU를 할당받을지에 대한 정보를 PCB에 저장한다.
5. 메모리 관리 정보
- 프로세스마다 메모리에 저장된 위치가 다르기 때문에, 프로세스가 어느 주소에 저장되어 있는지에 대한 메모리 정보가 있어야 한다.
- 베이스 레지스터, 한계 레지스터 값과 같은 정보를 PCB에 저장한다.
6. 사용한 파일과 입출력장치 목록
- 어떤 입출력장치가 이 프로세스에 할당되었는지, 어떤 파일들을 열었는지에 대한 정보들이 PCB에 저장된다.
문맥 교환
프로세스 A와 B가 있다고 해자. 프로세스 A가 실행되다가 운영체제로부터 CPU를 할당받은 시간이 다 되어 프로세스 B에게 CPU 사용을 양보한다고 해보자. 이러한 상황에서 프로세스 A는 다음 차례가 다시 돌아왔을 때를 위해서 각종 레지스터 값, 메모리 정보, 실행을 위해 사용한 입출력장치 등의 중간 정보를 백업해둬야 한다. 그래야 다시 실행을 재개할 수 있을 것이다.
여기에서 중간 정보에 해당하는 것 즉, 하나의 프로세스 수행을 다시 재개하기 위해서 기억해야 하는 정보를 문맥(context)이라고 한다. 하나의 프로세스 문맥은 해당 프로세스의 PCB에 표현되어 있어 PCB에 기록되는 정보들을 문맥이라고 봐도 상관없다!
기존 프로세스의 문맥을 PCB에 백업하고, 새로운 프로세스를 실행하기 위해 문맥을 PCB로부터 복구해 새로운 프로세스를 실행하는 것을 문맥 교환(context switching)이라고 한다. 즉, 프로세스 간에 실행을 전환하는 것을 의미한다.
이를 그림으로 표현하면 아래와 같다.
이러한 문맥 교환은 여러개의 프로세스가 끊임없이 빠르게 번갈아 가면서 실행되는 원리이다. 우리들의 눈에 프로세스가 동시에 이루어지는 것처럼 보이는 것은 이 문맥 교환이 빈번하게 일어나 프로세스가 그만큼 빨리 번갈아 가면서 수행되기 때문이다.
* 문맥 교환을 너무 자주 하게되면 오버헤드가 발생한다..! 유의해야 한다..
프로세스 사용자 영역
프로세스가 생성되면 커널 영역에 PCB가 생성된다고 했는데, 그러면 사용자 영역에는 프로세스가 어떻게 배치가 될지 알아보자.
하나의 프로세스는 사용자 영역에 대표적으로 크게 4가지 영역으로 나뉘어 저장된다. 이는 프로그래밍에 있어 매우 중요한 내용이다!!
1. 코드 영역(code segment)
- 텍스트 영역(text segment)이라고도 부른다.
- 기계어로 이루어진 명령어가 저장된다. 즉, CPU가 실행할 명령어가 저장된다. -> 읽기 전용(read-only) 공간
2. 데이터 영역(data segment)
- 프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간
- 여기에 저장되는 대표적인 데이터는 * 전역 변수(global variable)가 있다.
* 전역 변수 : 프로그램 전체에서 접근할 수 있는 변수
3. 힙 영역(heap segment)
- 프로그래머가 직접 할당할 수 있는 저장 공간
- 프로그래밍 과정 중 힙 영역에 메모리를 할당했다면 나중에 그 해당 공간을 반환해야 한다.
- 메모리 공간 반환한다 = '더 이상 해당 메모리 공간을 사용하지 않는다'
* 여기서, 메모리 공간을 반환하지 않는다?
-> 할당한 공간은 메모리 내에 계속 남아 있게 되어 메모리 낭비를 초래하는 메모리 누수(memory leak) 문제가 발생한다.
4. 스택 영역(stack segment)
- 프로그램이 실행되는 동안 잠깐 사용되는 데이터를 일시적으로 저장하는 공간
- 여기에 저장되는 대표적인 데이터는 매개 변수, 지역 변수가 있다.
- 힙 영역과 스택 영역에 데이터가 쌓일 때 새롭게 할당되는 주소가 서로 겹치지 않기 위해서
힙 영역은 메모리의 낮은 주소 -> 높은 주소, 스택 영역은 높은 주소 -> 낮은 주소로 할당이 된다.
* 영역은 또한 크기의 변함 유무에 따라 다음과 같이 나뉘기도 한다!
- 정적 할당 영역 : 크기가 변하지 않는 고정된 영역
ex) 코드 영역, 데이터 영역
- 동적 할당 영역 : 크기가 변할 수 있는 영역
ex) 힙 영역, 스택 영역
10-2. 프로세스 상태와 계층 구조
프로세스 상태
프로세스는 모두 저마다의 상태가 있다. 운영체제는 이러한 프로세스의 상태들을 PCB에 기록하여서 관리를 한다.
그리고 여러 많은 운영체제는 동시에 실행되는 수 많은 프로세스를 계층적으로 관리한다.
컴퓨터를 사용할 때 여러 프로세스들이 빠르게 번갈아 가며 실행이 되는데, 이러한 과정 중에서 하나의 프로세스는 아래과 같은 여러 상태를 거치면서 실행이 된다. 프로세스 상태를 표현하는 방식은 운영체제마다 조금씩 다르지만 대표적인 상태는 다음와 같다.
1. 생성 상태(new)
- 프로세스를 생성 중인 상태
- 메모리에 막 적재되어 PCB를 할당 받은 상태를 의미
- 생성 상태를 거쳐 실행 할 준비가 완료된 프로세스는 준비 상태가 되어 CPU 할당을 기다린다.
2. 준비 상태(ready)
- CPU 할당을 받고 실행되기를 기다리고 있는 상태
- 준비 상태 프로세스는 차례가 되면 CPU를 할당받아 실행 상태가 된다.
* 준비 상태인 프로세스가 실행 상태로 전환되는 것 = 디스패치(dispath)
3. 실행 상태(running)
- CPU를 할당받아 실행 중인 상태
- 실행 상태 프로세스는 할당된 일정 시간 동안에만 CPU를 사용할 수 있다.
- 할당된 시간을 모두 사용한다면(타이머 인터럽트가 발생하면) 다시 준비 상태가 되고,
실행 도중 입출력장치를 사용하여 입출력 장치의 작업이 끝날 때까지 기다려야 한다면 대기 상태가 된다.
4. 대기 상태(blocked)
- 입출력장치의 작업을 기다리는 상태
- 입출력 작업은 CPU에 비하여 처리 속도가 느리다. 그러하여 입출력 작업을 요청한 프로세스는 입출력장치가 입출력을 끝낼 때까지(입출력 완료 인터럽트를 받을 때까지) 기다려야 한다.
- 입출력 작업 완료 -> 다시 준비 상태가 되어 CPU 할당을 기다림
5. 종료 상태(terminated)
- 프로세스가 종료된 상태
- 프로세스가 종료되면 운영체제는 PCB와 프로세스가 사용된 메모리를 정리한다.
이러한 프로세스의 상태들을 프로세스 상태 다이어그램(process state diagram)이라는 도표로 다음과 같이 나타낼 수 있다.
프로세스 계층 구조
프로세스는 실행 도중 시스템 호출을 통해서 다른 프로세스를 생성할 수 있는데, 이때 새로운 프로세스를 생성한 프로세스를 부모 프로세스(parent process), 생성된 프로세스를 자식 프로세스(child process)라고 한다.
부모 프로세스와 자식 프로세스는 각각 다른 프로세스이기 때문에 서로 다른 PID를 가진다.
일부 운영체제에선 자식 프로세스의 PCB에 부모 프로세스의 PID인 PPID(Parent PID)가 기록되기도 한다.
부모 프로세스로부터 생성된 자식 프로세스 또한 실행 과정 중에서 또 다른 자식 프로세스를 생성할 수 있다. 그리고 그 또 다른 자식 프로세스 또한 또 다른 자식 프로세스를 생성할 수가 있다. 그러하다 보니 많은 운영체제는 계층적인 구조로 프로세스들을 관리한다.
컴퓨터가 부팅이 될 때 실행되는 최초의 프로세스가 있을 것인데 이 최초의 프로세스가 자식 프로세스를 생성하고, 그 생성된 자식 프로세스들이 또 다른 새로운 프로세스들을 낳는 형식으로 여러 프로세스가 동시에 실행이 되는 것이다.
이렇게 설명한 과정을 도표로 그리게 되면 아래와 같이 트리 구조를 띄게 되는데 이것을 프로세스 계층 구조라고 한다.
사용자가 컴퓨터를 켜고 로그인 창을 통해 성공적으로 로그인해서 bash 쉘(사용자 인터페이스)로 Vim이라는 문서 편집기 프로그램을 실행했다고 예를 들어보자. 이러한 경우는 사용자가 컴퓨터를 켠 순간에 생성된 최초 프로세스는 자식 프로세스로 로그인을 담당하는 프로세스를 생성한 것이고, 이 로그인 프로세스는 자식 프로세스로 사용자 인터페이스 (bash 쉘) 프로세스를 생성한 것이고, 사용자 인터페이스 프로세스는 Vim 프로세스를 생성하게 된 것이다.
* 최초의 프로세스는 유닉스, 리눅스, macOS 운영체제에 따라 다르다.
- 유닉스 : init / 리눅스 : systemd / macOS : launched
- 최초의 프로세스는 모든 프로세스의 최상단에 있는 부모 프로세스이고, 이 최초 프로세스의 PID는 항상 1번이다.
- pstree : 프로세스 계층 보여주는 명령어
--리눅스에서 확인하는 명령어
[root@localhost ~]# pstree
--macOS에서 확인하는 명령어
hyunji-Mac-pro:~ hyunji$ pstree.
프로세스 생성 기법
부모 프로세스는 fork를 통해 자신의 복사본을 자식 프로세스로 생성해내고, 만들어진 복사본인 자식 프로세스는 exec를 통해 자신의 메모리 공간을 다른 프로그램으로 교체한다. 여기서 fork와 exec는 시스템 호출이다.
- fork : 자기 자신 프로세스의 복사본을 자식 프로세스로 생성하는 시스템 호출
* 복사된 자식 프로세스는 부모 프로세스의 자원들(메모리 내용 등)이 상속되지만, PID 값 또는 저장된 메모리 위치는 다르다.
- exec : 자신의 메모리 공간을 새로운 프로그램으로 덮어쓰는 시스템 호출, 새로운 프로그램 내용으로 전환하여 실행하는 호출
* exec를 호출하게 되면 코드 영역과 데이터 영역의 내용이 실행할 프로그램의 내용으로 바뀌고 나머지 영역은 초기화된다.
=> 부모 프로세스로부터 자식 프로세스가 복사되고(fork), 자식 프로세스는 새로운 프로그램으로 옷을 갈아입고(exec), 이 자식 프로세스로부터 새로운 자식 프로세스가 복사되고, 옷을 갈아입는 방식이 계속해서 반복되며 여러 프로세스가 계층적으로 실행되는 것!
이때, 부모 프로세스가 자식 프로세스를 fork한 뒤에 부모 프로세스, 자식 프로세스 누구도 exec를 호출하지 않는 경우도 있는데,
이러한 경우에는 부모 프로세스와 자식 프로세스가 서로 같은 코드를 병행하여 실행하는 프로세스가 된다!
10-3. 스레드
프로세스와 스레드
스레드(thread)는 실행의 단위이다. 조금 더 정확하게는 프로세스를 구성하는 실행의 흐름 단위이다.
하나의 프로세스는 여러 개의 스레드를 가질 수 있고, 이 스레드를 이용하면 하나의 프로세스에서 동시에 여러 부분을 실행할 수가 있다!
앞서 우리가 다룬 프로세스들은 한번에 하나의 부분만 실행되는 프로세스 즉, 실행의 흐름 단위가 하나로 실행되는 프로세스인 단일 스레드 프로세스였다.
그런데 '스레드' 개념이 들어오면서 하나의 프로세스가 한번에 여러 일을 동시에 처리할 수 있게 되었다!!
-> 프로세스를 구성하는 여러 명령어들을 동시에 실행 할 수 있다.
스레드는 프로세스 내에서 각기 다른 스레드 ID, 프로그램 카운터 값을 비롯한 레지스터 값, 스택으로 구성된다.
프로세스의 스레드들은 실행에 필요한 최소한의 정보(프로그램 카운터 포함한 레지스터, 스택)만을 유지한 채 프로세스 자원을 공유하며 실행된다!! 이것은 스레드의 핵심이다!
멀티프로세스와 멀티스레드
- 멀티프로세스(multiprocess) : 여러 프로세스를 동시에 실행하는 것
- 멀티스레드(multithread) : 여러 스레드로 프로세스를 동시에 실행하는 것
이때 멀티스레드와 단일 스레드 프로세스를 여러 개 실행하는 것이 무엇이 다른지는 스레드의 핵심을 생각해보면 알 수 있다.
단일 스레드 프로세스를 여러 개 실행할 때는 프로세스끼리는 기본적으로 자원을 공유하지 않는다.
하지만, 멀티스레드로 실행하게 되면 스레드끼리는 같은 프로세스 내의 자원을 공유한다.
-> 단일 스레드 프로세스를 여러개 실행하려고 하려면 코드 영역, 데이터 영역, 힙 영역 등을 비롯한 모든 자원들이 복제되어 메모리에 적재가 되어야 한다! 한마디로, PID와 저장된 메모리 주소를 제외한 모든 것이 메모리에 여러번 적재가 되게 된다. 이것은 어떻게 보면 메모리 낭비이다..!
프로세스의 자원을 공유한다는 특성은 마냥 장점만 있지는 않다.
때로는 이 특성은 단점이 될 수도 있는데, 멀티프로세스 환경에서는 하나의 프로세스에 문제가 생겨도 다른 프로세스에는 지장이 적거나 없지만, 멀티스레드 환경에서는 하나의 스레드에 문제가 생기면 프로세스 전체에 문제가 생길 수 있다..!
Ch11. CPU 스케줄링
11-1. CPU 스케줄링 개요
운영체제가 프로세스들에게 공정하고 합리적으로 CPU 자원을 배분하는 것을 CPU 스케줄링(CPU scheduling)이라고 한다.
CPU 스케줄링은 컴퓨터 성능과 직결되는 중요한 문제이다..!
프로세스 우선순위
프로세스마다 우선순위가 다르다. 우선순위가 높을수록 빨리 처리해야 하는 것을 의미한다.
대표적인 우선순위 높은 프로세스에는 입출력 작업이 많은 프로세스가 있다.
일반적인 프로세스가 실행되는 과정을 생각해보자. 대부분의 프로세스들은 CPU와 입출력장치를 모두 사용하면서 실행이 된다.
즉, 프로세스는 실행 상태와 대기 상태를 반복하며 실행된다.
그런데 이때, 프로세스의 종류마다 입출력장치를 이용하는 시간과 CPU를 이용하는 시간의 양에 차이가 있다!
비디오 재생 or 디스크 백업 등을 담당하는 프로세스와 같이 입출력 작업이 많은 프로세스를 입출력 집중 프로세스(I/O bound process)라고 하고, 복잡한 수학 연산/컴파일 등을 담당하는 프로세스와 같은 CPU 작업이 많은 프로세스를 CPU 집중 프로세스(CPU bound process)라고 한다. 입출력 집중 프로세스는 대기 상태에, CPU 집중 프로세스는 실행 상태에 더 많이 머무르게 된다.
상황에 따라, 프로세스의 중요도에 따라 프로세스가 CPU를 이용할 수 있도록 하기 위해 운영체제는 프로세스마다 우선순위(priority)를 부여한다. 각 프로세스의 PCB에 우선순위를 명시하고, 이를 기준으로 먼저 처리할 프로세스를 결정한다.
* CPU 버스트(CPU burst) : CPU를 이용하는 작업 / 입출력 버스트(I/O burst) : 입출력장치를 기다리는 작업
- ps -el : 프로세스 우선순위 확인 명령어
스케줄링 큐
CPU를 원하는 프로세스들은 한 두개가 아니다보니 운영체제가 모든 프로세스의 PCB를 다 확인하는 것은 비효율적이다.
그래서 운영체제는 프로세스들에게 줄 서서 기다리도록 요구를 한다. CPU를 사용하고 싶은 프로세스들, 메모리에 적재되고 싶은 프로세스들, 특정 입출력장치를 사용하고 싶은 프로세스들 등을 모두 줄 세우는 것이다!
이러한 줄을 운영체제는 스케줄링 큐(scheduling queue)로 구현하고 관리한다.
큐는 앞서 배웠듯이 선입선출(FIFO) 자료 구조이지만, 스케줄링에서 말하는 큐는 반드시 선입선출 방식일 필요는 없다!
-> 운영체제는 각 프로세스들을 큐에 삽입하여 줄을 세워 관리한다!
이렇게 운영체제가 관리하는 줄인 큐에는 다양한 종류가 있다.
대표적인 큐의 종류에는 준비 큐와 대기 큐가 있다.
- 준비 큐(ready queue) : CPU를 이용하고 싶은 프로세스들이 서는 줄, 준비 상태에 있는 프로세스들이 서는 줄
- 대기 큐(waiting queue) : 입출력장치를 이용하기 위해 대기 상태에 접어든 프로세스들이 서는 줄
* 준비 큐에서 우선순위가 낮은 프로세스드이 먼저 큐에 삽입되어도 우선순위가 높은 프로세스들은 그보다 먼저 처리될 수가 있다!
선점형/비선점형 스케줄링
- 선점형 스케줄링(preemptive scheduling) : 하나의 프로세스가 자원 사용을 독점할 수 없는 스케줄링 방식
-> 프로세스마다 정해진 시간만큼 CPU를 사용하고 정해진 시간을 모두 소비하여 타이머 인터럽트 발생 시, 운영체제가 해당 프로세스로부터 CPU 자원을 빼앗아 다음 프로세스에 할당하는 방식
+ 장점) 어느 한 프로세스의 자원 독점을 막고, 프로세스들에 골고로 자원을 배분 할 수 있다.
+ 단점) 문맥 교환 과정에서 오버헤드가 발생할 수 있다.
- 비선점형 스케줄링(non-preemptive scheduling) : 하나의 프로세스가 자원 사용을 독점할 수 있는 스케줄링 방식
-> 이 방식으로 자원을 이용하는 프로세스가 있을 시엔, 다른 프로세스들은 그 프로세스의 사용이 모두 끝날 때까지 기다려야 한다.
+ 장점) 문맥 교환 횟수가 비교적 적어 오버헤드는 적다.
+ 단점) 이미 프로세스가 자원을 사용 중인데 더 급한 프로세스가 끼어들어도 무작정 기다리는 수밖에 없어 모든 프로세스가 골고루 자원을 사용할 수가 없다.
11-2. CPU 스케줄링 알고리즘
스케줄링 알고리즘 종류
운영체제마다 다른 스케줄링 알고리즘을 사용하고 있다! 혼공컴운 책에서 배울 알고리즘은 7가지이다.
1. 선입 선처리 스케줄링 알고리즘
- FCFS 스케줄링(Fisrt Come Fisrt Served Scheduling)이라고도 부른다.
- 준비 큐에 삽입된 순서대로 CPU를 할당한다.
- 비선점형 스케줄링 방식
+) 단점 : 때로 프로세스들이 기다리는 시간이 매우 길어질 수 있다.
예시로, CPU를 오래 사용하는 프로세스가 먼저 큐에 도착하면 다른 프로세스는 그 프로세스가 CPU 사용을 다하는 동안 무작정 기다리는 방법밖에 없다. 이러한 현상을 호위 효과(convoy effect)라고 한다!
2. 최단 작업 우선 스케줄링 알고리즘
- SJF 스케줄링(Shortest Job First Scheduling)이라고도 부른다.
- 준비 큐에 삽입된 프로세스들 중에서 CPU 사용 시간의 길이가 가장 짧은 프로세스부터 CPU를 할당한다.
- 호위 효과를 방지하기 위한 알고리즘
- 기본적으로 비선점형 스케줄링 방식으로 분류되지만, 선점형 스케줄링 방식도 있는데 이는 우선 스케줄링에 해당한다.
3. 라운드 로빈 스케줄링 알고리즘
- 정해진 시간만큼만 돌아가며 CPU를 할당한다.
- 선입 선처리 스케줄링 + * 타임 슬라이스
- 선점형 스케줄링 방식
- 라운드 로빈 스케줄링 알고리즘은 타임 슬라이스 크기가 매우 중요하다!
지나치게 크면 선입 선처리 스케줄링과 다를 바가 없고, 작으면 문맥 교환에 발생하는 비용이 커진다.
* 타임 슬라이스 : 각 프로세스가 CPU를 사용할 수 있는 정해진 시간
4. 최소 잔여 시간 우선 스케줄링 알고리즘
- SRT 스케줄링(Shortest Remaining Time)이라고도 부른다.
- 최단 작업 우선 스케줄링 + 라운드 로빈 스케줄링 알고리즘
- 최소 잔여 시간 우선 스케줄링 하에서 프로세스들은 정해진 타임 슬라이스만큼 CPU를 사용하되, CPU를 사용할 다음 프로세스로는 남아있는 작업 시간이 가장 적은 프로세스가 선택된다!
5. 우선순위 스케줄링(priority scheduling) 알고리즘
- 프로세스들에게 우선순위를 부여하고, 가장 높은 우선순위를 가진 프로세스에 CPU를 할당한다.
* 우선순위가 같은 프로세스들은 선입 선처리로 스케줄링한다.
- 근본적인 문제점 : * 기아(starvation) 현상
* 기아 현상이란, 우선순위가 높은 프로세스만 계속 먼저 실행되다보니 우선순위가 낮은 프로세스들의 실행은 준비 큐에 먼저 삽입되었다고 해도 계속 뒤로 밀리게 되는 현상을 말한다.
이 현상을 방지하기 위한 대표적인 기법에는 에이징(aging)이 있다.
오랫동안 대기한 프로세스의 우선순위를 점차 높이는 방식을 에이징이라고 한다. 즉, 대기 중인 프로세스의 우선순위를 점차 증가시키는 방법이다. 그러면 기다리는 동안 점점 우선순위가 올라가 무작정 기다리는 현상을 방지할 수가 있게 될 것이다! 우선순위가 낮아도 대기하면서 점차 우선순위가 증가되어 실행이 될 것이다.
6. 다단계 큐 스케줄링(multilevel queue scheduling) 알고리즘
- 우선순위별로 준비 큐를 여러 개 사용하는 방식(우선순위 스케줄링의 발전된 형태)
- 해당 방식 하에서는 우선순위가 가장 높은 큐에 있는 프로세스들을 먼저 처리하고, 우선순위가 가장 높은 큐가 비어 있으면 그 다음 우선순위 큐에 있는 프로세스들을 처리한다. 즉, 각 우선순위마다 큐가 있다! 우선순위 0에 삽입된 프로세스들, 우선순위 1에 삽입된 프로세스들, .. 이러한 순서대로 CPU를 할당받아 실행된다.
+ 장점) 큐를 여러 개 두면 프로세스 유형별 우선순위를 구분하여 실행하기 편하다.
7. 다단계 피드백 큐 스케줄링(multilevel feedback queue scheduling)
- 다단계 큐 스케줄링의 발전된 형태
- 프로세스들이 큐 사이를 이동할 수 있는 다단계 큐 스케줄링
- 해당 방식에서 새로 준비 상태가 된 프로세스가 있다면 우선 우선순위가 가장 높은 우선순위 큐에 삽입되고 일정 시간(타임 슬라이스) 동안 실행이 된다. 즉, CPU를 비교적 오래 사용해야 하는 CPU 집중 프로세스들은 자연스레 우선순위가 낮아지고, CPU를 비교적 적게 사용하는 입출력 집중 프로세스들은 자연스레 우선순위가 높은 큐에서 실행이 끝난다.
해당 방식은 프로세스들이 큐 사이를 이동할 수 있기 때문에 낮은 우선순위 큐에서 너무 오래 기다리고 있는 프로세스들에 한해서 점차 우선순위가 높은 큐로 이동시키는 에이징 기법을 적용하여 기아 현상을 예방할 수가 있다...!
- 구현이 복잡하나, 가장 일반적인 알고리즘 방식이다.
[과제]
- 필수과제
01) 304p
01. 다음은 프로세스 상태를 보여주는 프로세스 상태 다이어그램이다. (1)부터 (5)까지 올바른 상태를 적어 보시오.
(1) : 생성 상태
(2) : 준비 상태
(3) : 실행 상태
(4) : 종료 상태
(5) : 대기 상태
'Studying > 혼공학습단' 카테고리의 다른 글
[혼공컴운]6주차_Ch.14 - 15 (0) | 2025.02.23 |
---|---|
[혼공컴운]5주차_Ch.12 - 13 (0) | 2025.02.18 |
[혼공컴운] 3주차_Ch.06 - 08 (0) | 2025.02.03 |
[혼공컴운] 2주차_Ch.04 - 05 (0) | 2025.01.20 |
[혼공컴운] 1주차_Ch.01 - 03 (0) | 2025.01.12 |