스케줄링 프로세스 관리 서비스 중 가장 중요한 2가지는 스케줄링과 동기화라고 할 수 있다. 오늘은 여기에 대해 공부해 보았다.
스케줄링이 중요한 이유는 프로세스 유형에 따라 프로세스를 수행해야 리소스를 더 효율적으로 사용할 수 있게 되기 때문이다. 예를 들어, IO bound가 높은 process와 CPU bound가 높은 process가 있다면, 당연히 IO bound가 높은 process를 먼저 실행하는 편이 더 효율적일 것이다. CPU를 얼마 사용하지 않고 요청만 한 뒤 WAITING 상태로 전이하기 때문이다.
따라서 IO bound process의 경우 CPU bound process보다 우선순위를 높게 주는 편이 더 효율적으로 스케줄링 할 수 있게 할 수 있다.
ps -el 명령어를 사용해서 우선순위 확인이 가능하고, nice 명령어로 프로세스 우선순위를 변경할 수 있다
PCB에는 스케줄링 우선순위에 대한 정보가 적혀 있다. 그렇다고 해서 스케줄러가 PCB를 일일이 검사하는 것은 아니다. 스케줄링 큐라는 다단계 우선순위 큐를 활용한다.
스케줄링 큐에는 CPU를 이용하고 싶은 프로세스, 메모리에 적재하고 싶은 프로세스, IO 장치를 사용하고 싶은 프로세스들에 대한 큐가 각각 존재하고, PCB를 넣어서 관리할 수 있도록 한다.
ready queue : CPU를 이용하고 싶은 프로세스들이 줄을 선다
waiting queue : IO 장치를 이용하기 위해 대기 상태의 프로세스들이 줄을 선다
스케줄링은 2가지로 분류할 수 있다
선점형 스케줄링 : 프로세스 A가 CPU 및 다른 리소스를 사용중 OS가 자원을 빼앗아 다른 프로세스 B에게 할당할 수 있다
장점 : 한 프로세스가 자원을 독점하는 현상을 막을 수 있다
단점 : Context Switching에서 오버헤드가 발생한다
비선점형 스케줄링 : 프로세스 A가 자원을 사용하고 있다면 A가 놓아줄 때까지 다른 프로세스 B가 끼어들 수 없다
장점 : Context Switching 횟수가 적다
단점 : 자원을 골고루 사용할 수 없다
스케줄링 방법에는 FCFS(선입 선처리), SJFC(최단 작업 우선), 라운드로빈, 우선순위, 다단계 피드백 큐 등이 있다
동기화동기화란 프로세스들 간 실행 순서와 자원의 일관성을 보장하는 걸 말한다. 이러한 현상이 일어나는 이유는, 우리가 소스코드로 입력할 때는 한 줄이지만, 바이트코드로 만들면 여러 줄이 되고, 그 여러 줄들은 atomic하게 실행되지 않고 중간에 context switching될 수 있기 때문이다.
동기화에는 실행 순서 제어를 위한 동기화와 상호 배제를 위한 동기화가 존재한다
실행 순서 제어 : 프로세스를 올바른 순서대로 실행한다
한 트랜잭션 내에서 데드락이 발생할 때 DB 리소스 접근 순서를 재배치해서 해결하는 경우 이에 해당한다고도 볼 수 있을 것 같다
상호 배제 : 동시에 접근해서는 안 되는 자원에 하나의 프로세스만 접근할 수 있도록 한다
공유 자원들을 격리하기 위해 공유 자원에 접근하는 코드 영역을 critical section라고 부른다. 이 critical section 문제를 다음 3가지 원칙 하에 해결하며, 이를 만족해야 상호 배제를 위한 동기화가 가능하다
상호 배제 mutual exclusion : 한 프로세스가 critical section에 들어갔을 때 다른 프로세스가 접근할 수 없다
진행 : critical section에 들어가 있는 프로세스가 없다면, 어떤 프로세스는 들어갈 수 있어야 한다
유한 대기 : 한 프로세스가 critical section에 들어가고 싶다면, 언젠가 들어갈 수 있어야 한다 (무한 대기 x)
또한 동시에 여러 프로세스가 critical section의 코드를 실행하는 경우를 race condition이라고 부른다
대표적인 동기화 기법에는 크게 뮤텍스 / 락 / 세마포어가 존재한다.
mutex lock(MUTual EXclusive lock, 애초에 이름에 상호 배제를 위한 lock임을 명시한다) : 동시에 접근해선 안 되는 자원에 동시접근할 수 없도록 만드는 자물쇠. 현재 내가 critical section에 있음을 알리는 수단이다
lock : 자물쇠 역할. 프로세스들이 공유하는 전역변수
acquire : 임계 구역을 잠그는 함수
release : 임계 구역의 잠금을 해제하는 함수
이 방식은 while (lock == true); 이런 모양새로 잠금이 열릴 때까지 계속 확인하는데 이를 busy waiting, 바쁜 대기라고 부른다. 이걸 보고 polling 혹은 spin lock이 떠올랐다.
semaphore : 공유 자원이 여러 개 있을 때, 여러 프로세스가 공유 자원에 접근 가능하도록 하는 매니저 역할이다. 탈의실이 여러 개 있고, 점원이 여기 들어갈 수 있어요 하고 중간에서 알려주는 거라고 볼 수 있다.
S : 임계 구역에 접근할 수 있는 프로세스 수(사용 가능한 공유 자원 수)를 나타내는 전역변수
wait : 임계 구역에 들어가도 좋은지, 기다려야 하는지 알려주는 함수
signal : 임계 구역 앞에서 기다리는 프로세스에 '이제 가도 좋다'고 신호를 주는 함수
while(S <= 0)과 같은 꼴로 들어갈 수 있는지 확인한다
semaphore의 경우 mutex보다 나은게, wait에서 busy waiting하지 않고 PCB를 바로 waiting queue에 넣는다. (이걸 non-blocking하다고 볼 수 있다고 생각한다)
monitor : 인터페이스로만 공유 자원에 접근할 수 있도록 만든 형태이다. 인터페이스는 일종의 API라고 볼 수 있을 것이고, 나는 일종의 래퍼 객체 같은 것이라고 이해했다. 이게 등장한 이유는 semaphore의 경우 critical section 전후에 wait / signal을 명시해야 하는데 누락하면 장애로 이어지기 때문에 더 편리하게 다루기 위해서라고 한다.
추가적으로 다음 주소의 글을 재밌게 읽었는데, 여기서도 mutex 경합이 CPU를 많이 소진해서 문제가 발생한 걸 spin_loop를 조정해 해결했다는 내용이였다. 오늘 배운 내용이라 이해도 잘되었다.