스레드는 프로세스의 실행 가능한 가장 작은 단위입니다.
프로세스는 최소 한개 부터 여러개의 스레드를 가질 수 있습니다.
여러 스레드를 가질때에는 코드, 데이터, 힙은 공유하고
스택 영역을 각각 생성하여 스레드 간의 독립성을 유지합니다.
프로세스안에 스레드가 하나라면 작업을 하나씩 진행해야합니다.
여러 작업을 동시에 하고 싶다면 스레드가 하나이기 때문에
여러개의 프로세스를 사용하면 되는데 몇가지 아쉬운 점이 있습니다.
위의 문제로 스레드라는 개념이 나왔고
하나의 프로세스 안에 여러개의 스레드를 두어서 작업을 동시에 처리하도록 만들었습니다.
이로써 프로세스간의 이동이 아닌 프로세스 내의 이동으로 해결 가능.
->같은 프로세스의 스레드들끼리의 컨텍스트 스위칭이 가볍다
->스레드들은 자신들이 속한 프로세스의 메모리 영역을 공유
즉, 멀티스레딩은 프로세스 내 작업을 멀티스레드로 처리하는 기법입니다.
✔공유 자원(shared resource)은 시스템 안에서 각 프로세스/스레드가 함께 접근할 수 있는 자원이나 변수를 의미합니다.
자원의 예는 모니터, 프린터, 메모리, 파일, 데이터 등이 있습니다.
✔경쟁 상태(race condition)은 공유 자원을 두 개 이상의 프로세스가 동시에 읽거나 쓰는 상황입니다.
동시에 데이터를 조작할때, 타이밍이나 접근 순서에 따라 결괏값이 달라질 수 있습니다.
임계 영역(critical section)은 경쟁상태로 인해 결과가 달라지는 코드 영역입니다.
임계 영역을 해결하기 위한 방법은 뮤텍스, 세마포어, 모니터 3가지 입니다.
3가지 방법은 모두 상호 배제, 한정 대기, 융통성이란 조건을 만족합니다.
이 방법에 토대가 되는 메커니즘은 잠금(lock)입니다.
lock을 사용하는 대표적인 프로그래밍 언어는 주로
멀티스레딩 또는 병렬 처리를 지원하는 언어들 입니다.
Java, C++, C#, Python, Go, Rust, Ruby 등이 있습니다.
*언어마다 lock의 형태는 다름
💡상호 배제(mutual exclustion)
한 프로세스가 임계 영역에 들어갔을 때 다른 프로세스는 들어갈 수 없다
💡한정 대기(bounded waiting)
특정 프로세스가 영원히 임계 영역에 들어가지 못하면 안 된다
💡융통성(progress)
만약 어떠한 프로세스도 임계 영역을 사용하지 않는다면 임계 영역 외부의 어떠한 프로세스도 들어갈 수 있으며 이 때 프로세스끼리 서로 방해하지 않는다.
뮤택스(mutex)는 프로세스/스레드가 공유 자원을 lock()을 통해 잠금 설정하고 사용한 후에는 unlock()을 통해 잠금 해제하는 객체입니다.
잠금이 설정되면 다른 프로세스/스레드는 접근 불가.
해제 시, 접근 가능
뮤텍스는 잠금 또는 잠금 해제라는 상태만을 가집니다.
세마포어(semaphore)는 일반화된 뮤텍스입니다.
코드 구조가 정형화 되어있다고 볼 수 있고
간단한 정수 값과 두가지 함수 (wait, signal)로 공유 자원에 대한 접근을 처리합니다.
P(wait)연산 : 세마포어의 값을 감소시키고, 값이 0 이하이면 대기
V(signal)연산 : 세마포어의 값을 증가시키고, 대기 중인 프로세스가 있으면 이를 깨워 실행.
📌 바이너리 세마포어
정수 값 0,1만 가지는 세마포어.
구현의 유사성으로 인해 뮤텍스는 바이너리 세마포어라고 할 수 있지만
(잠금 == 0 / 해제 ==1)
뮤텍스는 락을 가진 자만 락을 해제 할 수 있지만 세마모어는 아닙니다.
뮤텍스가 강한 자물쇠라면 세마포어는 약한 자물쇠라고 할 수 있습니다.
📌 카운팅 세마포어
여러 개의 값을 가지는 세마포어, 여러 자원에 대한 접근을 제어하는데 사용 됩니다.
모니터는 둘 이상의 프로세스/스레드가 공유 자원에 안전하게 접근할 수 있도록 공유 자원을 숨기고 해당 접근에 대해 인터페이스만 제공합니다.
모니터는 모니터큐를 통해 공유 자원에 대한 작업들을 순차적으로 처리합니다.
모니터는 세마포어보다 구현하기 쉽고, 상호 배제(하나만 들어갈 수 있는 것)는 자동인 반면 세마포어는 상호 배제를 명시적을 구현이 필요합니다.
교착 상태(deadlock)는 두 개 이상의 프로세스들이 서로가 가진 자원을 기다리며 중단된 상태입니다.
✔상호 배제: 리소스를 공유해서 사용할 수 없다
✔점유 대기: 프로세스가 이미 하나 이상의 리소스를 취득한 상태에서 다른 프로세스가 사용하고 있는 리소스를 추가로 기다린다.
✔비선점: 리소스 반환은 오직 그 리소스를 취득한 프로세스만 할 수 있다.
✔환형 대기: 프로세스들이 순환 형태로 서로의 리소스를 기다린다.
🤬4가지 조건이 갖춰지면 데드락, 교착상태가 발생한다.
OS의 데드락 해결 방법
1. 데드락 방지 - 네 가지 조건 중 하나가 충족되지 않게 시스템을 디자인
2. 데드락 회피 - 데드락이 발생할 것 같은 상황을 회피하는 것(은행원 알고리즘 사용)
3. 데드락 감지와 복구 - 데드락을 허용하고 데드락이 발생하면 복구하는 전략 (한 개씩 프로세스를 종료)
4. 데드락 무시 - 아.몰.랑 시전 사용자가 작업을 종료하게한다.
(4가지 방법 모두 속 시원하게 해결하지는 않는다.🤢)
💡은행원 알고리즘:리소스 요청을 허락해줬을 때 데드락 발생할 가능성이 있으면, 리소스를 할당해도 안전할 때 까지 계속 요청을 거절하는 알고리즘
스케줄러는 CPU가 쉬지 않고 일하게 하기 위해 실행할 프로세스를 선택하는 역할을 합니다
실행 중인 프로세스가 종료, 중단, 대기 상태로 가게되면 CPU가 비워있게 되는데 이때, 비어있지 않게 바로 이어서 다른 프로세스가 실행되어야 합니다.
그럼 어떤 프로세스가 실행되어야 할까요.
이것을 정해 주는 것이 스케줄러라고 합니다.
ready 단계에 있는 프로세스들을 모아둔 것이 레디큐이며
프로세스들이 실행 대기 중입니다
레디큐의 프로세스들중 스케줄러가 프로세스를 선택
디스패쳐는 스케줄러가 선택한 프로세스를 실제로 cpu에 실행 되도록하는것
1. 컨텍스트 스위칭 (커널 모드에서 실행됨)
2. user mode로 바꿔주는것
3. 적절한 위치로 프로세스를 이동
(스케줄러와 디스패쳐를 같게 보는 관점도 있으니 두루 알고계시면 좋을 것 같습니다.)
프로세스가 종료, I/O 작업, 양보 (자발적) 준비단계로감
프로세스가 자발적으로 실행중에서 다른 상태로 넘어가는것을
비선점형 방식이라함
신사적, 협력적(프로세스가 스스로 양보), 느린 응답성
강제적으로 프로세스를 중지하는게 아니라서 프로세스가 자발적으로 멈출때까지 기다리기 때문에 느린 응답성
먼저 도착한 순서대로 처리
프로세스의 다음 CPU burst(실행 시간)가 가장 짧은 프로세스부터 실행
우선순위가 높은 순서부터 처리
(우선순위는 도착 순서의 역순이 될수도,
실행 시간이 긴 것 부터가 될 수 있다.)
처리 도중 우선순위가 먼저인 프로세스가 생긴다면
비선점형은 일단 하던 프로세스는 마무리 될 때까지 기다리고
선점형은 우선순위가 먼저인 프로세스를 실행시킨다.
비선점형 방식을 기본적으로 행합니다.
추가적으로 프로세스가 끝나지 않았을때도 개입하는 것
실행 중인 프로세스를 ready 상태 (레디큐)로 보내는 것
프로세스가 충분히 cpu를 다 쓰지 않았음에도
다른 프로세스를 실행 시키기 위해 현재 프로세스를 강제로 중단.
적극적, 강제적, 빠른 응답성
time slice로 나눠진 CPU time을 번갈아가며 실행
Ex)
Process1 = 10ms
Process2 = 6ms
Process3 = 3ms
time slice = 4ms
=> P1 (10 - 4) / P2 ( 6 - 4 ) / P3 ( 3 - 3 ) / P1 ( 6 -4 ) ~
time slice가 길어지면 FCFS가 되고 짧아질수록 컨텍스트 스위칭이 잦아지는 현상 발생합니다.
실행 시간이 짧은 프로세스 부터 수행하는데
수행 도중 더 짧은 프로세스가 들어오면 현재 프로세스를 중지 후
실행 시간이 더 짤은 프로세스를 수행하는 알고리즘
프로세스들을 그룹화해서 그룹마다 큐를 두는 방식
어떤 큐를 먼저 실행시킬지와
각 큐들을 어떤 순서로 실행시킬지 정해야합니다.