프로세스 동기화
동기화의 의미
프로세스 동기화 : 프로세스 사이의 수행 시기를 맞추는 것
※ 프로세스뿐 아니라 스레드 또한 동기화의 대상
실행 순서 제어를 위한 동기화 : 동시에 실행되는 프로세스를 올바른 순서대로 실행하는 것.
※프로세스들에 따라서 아무 순서대로 실행되어선 안되는 프로세스가 있음.
밑의 사진을 예시로 Reader프로세스는 Writer가 먼저 txt 파일을 써야 읽을 수 있으며
Reader가 먼저 실행되면 아무것도 읽을수 없습니다.
(book.txt 파일에 글이 있어야하는 특정한 조건이 필요하다)
-->실행 순서가 필요하다
상호 배제를 위한 동기화 : 공유가 불가능한 자원의 동시 사용을 피하기 위해 사용하는 알고리즘.
동시에 접근하지 못하게 하는 것.
한번에 하나의 프로세스만 접근가능
공유 자원과 임계 구역
공유 자원 : 여러 프로세스 혹은 스레드가 공유하는 자원
임계 구역 : 동시에 실행하면 문제가 발생하는 자원에 접근하는 코드 영역.
임계구역에 진입하려면 진입한 프로세스 외에는 대기해야한다!
레이스 컨디션 : 여러 프로세스가 동시에 접근하여 문제가 발생한 상황
임계 구역 문제를 해결하기 위한 세 가지 원칙
상호 배제 : 한 프로세스가 임계 구역에 진입했다면 다른 프로세스는 임계 구역에 들어올 수 없다.
진행 : 임계 구역에 어떤 프로세스도 진입하지 않았다면 임계 구역에 진입하고자 하는 프로세스는 들어갈 수 있어야 한다.
유한 대기 : 한 프로세스가 임계 구역에 진입하고 싶다면 그 프로세스는 언젠가는 임계 구역에 들어올 수 있어야 한다.
동기화 기법
뮤텍스 락
뮤텍스 락 : 상호 배제를 위한 동기화 도구
=> 자물쇠 기능을 코드로 구현한 동기화 기법.
간단 비유 설명 : 딱 1개의 화장실만이 존재해서 누군가 화장실을 사용하고 있다면 그 사람은 열쇠를 갖고 있고,
화장실을 사용하고 열쇠를 반납할 때까지는 누구도 그 화장실을 사용할 수 없는것
예제
전역 변수 lock: 프로세스들이 공유
acquire (): 임계 구역을 잠그는 역할
release (): 임계 구역의 잠금을 해제하는 역할
acquire()
{
while ( lock == 1 ); // lock 이 잠겨있는지 확인. lock이 nonzero 라면 잠겨있는 상태
lock = 1; // lock이 zero라면 while 문 실행 x. 임계 구역 잠금
}
release()
{
lock = 0; // 임계 구역 잠금 해제
}
< acquire()와 release()로 구현한 뮤텍스 락 >
acquire();
// 임계 구역
release();
임계 구역 진입 불가능 --> while 확인하며 로 대기.
임계 구역 진입 가능 --> acquire()의 lock =1로 임계 구역을 잠근 뒤 임계 구역에서 작업 진행.
임계 구역에서 나올 시 --> release ()의 lock =0으로 임계 구역의 잠금 해제
바쁜 대기 : 프로세스가 임계 구역의 잠금 상태를 계속 확인하는 것과 같은 대기 방식.
뮤텍스 락에서는 while문을 계속 반복하는 행위.
좋은 방식이 아님 (화장실 사용하고있는데 계속 문을 두들기면.....)
세마포
세마포 : 조금더 일반화 된 방식의 동기화 기법
공유 자원이 여러 개 있을 때의 상호 배제를 위한 동기화 기법.
바쁜 대기방식을 해결할 수 있음
간단 비유 설명 : 여러대를 주차할 수 있는 주차장과 주차 가능한 대수를 표시한 전광판이 존재
전광판을 이용해 실시간으로 확인하며 주차를 기다릴 수 있음
불필요한 낭비를 막기위해 전광판을 보면서 차량을 시동을 끄거나 킴 (바쁜대기 낭비를 해소)
예제
전역변수 S: 임계 구역에 진입할 수 있는 프로세스의 개수
wait(): 임계 구역에 들어가도 되는지 여부를 알리는 역할
signal(): 임계 구역을 진입하려는 프로세스에게 신호를 보내는 역할
wait();
// 임계 구역
signal();
//임계 구역 전후로 wait()와 signal()을 호출.
wait()
{
while( s <= 0 ); // 사용 가능한 자원개수 확인
s--; // 사용가능 개수가 있다면 ,1감소 시키고 내가 들어감
}
signal()
{
s++; // 임계 구역의 작업을 끝내고 S 증가
}
세마포의 바쁜 대기를 해소 방법
사용할 수 있는 자원이 없을경우 대기상태로 만듬 (대기 큐에 삽입)
사용 가능한 자원이 생기면 준비상태로 만듬 (준비 큐에 삽입)
wait();
// 임계 구역
signal();
//임계 구역 전후로 wait()와 signal()을 호출.
wait()
{
s--; // 사용가능 개수가 있다면 ,1감소 시키고 내가 들어감
if(S<0){
add this process to Queue
sleep()
}
}
signal()
{
s++; // 임계 구역의 작업을 끝내고 S 증가
if(S<0){
remove a process p from Queue
wakeup(p)
}
}
세마포 실행순서 동기화
세마포 변수 S의 0으로 두고
먼저 실행할 프로세스뒤에 signal(),
--> 바로 임계 구역 사용
다음에 실행할 프로세스 앞에 wait()을 붙임.
--> s값 확인 으로 임계구역 바로 사용 불가
모니터
모니터 : 세마포에 비해 개발자에게 더 편리하도록 조건 변수를 활용한 방식
(언제 signal(),wait() 붙이고 있나...,오류도 발생할 수 있다)
큐를 이용하여 상호 배제를 위한 동기화를 제공
실행 순서 제어를 위한 동기화도 제공하는 동기화 도구.
실행 순서 제어를 위한 동기화 : 조건 변수를 이용하여 실행 순서를 제어할 수 있음
조건 변수 : 실행될 조건이 되지 않으면 wait 함수를 통해 실행을 중단
특정 프로세스가 실행될 조건을 충족되었을 경우 signal 함수를 통해 실행을 재개
간단 비유 설명 : 주차장을 사용하는데 있어 관리인이 존재하여 특정 조건에 따라 순서대로 주차할 수 있게 만드는 기법
교착 상태란
식사하는 철학자 문제 : 교착 상태를 설명하는 대표적 문제 상황
모든 철학자가 동시에 포크를 집어 식사를 하면 어떤 철학자도 식사를 할 수 없고 영원히 생각만 하는 상황 발생.
즉, 교착 상태발생.
교착 상태 : 서로의 자원을 기다리는 상태로 계속해서 대기하며 진행이 멈춰버리는 현상.
(철학자들 : 포크 좀 내려놔바.... 밥 좀 먹자)
교착상태를 해결하기 위해서는
1. 교착상태가 발생했을떄의 상황 정확히 표현하기
2. 교착 상태가 일어난 근본원인 이해하기
자원 할당 그래프 -- 교착상태 조건 확인가능
어떤 프로세스가 어떤 자원을 할당받아 사용하는지
어떤 프로세스가 어떤 자원을 기다리고 있는지
프로세스는 원, 자원의 종류는 사각형으로 표현
사용할 수 있는 자원의 개수는 자원 사각형 내 점으로 표현
프로세스가 어떤 자원을 할당받아 사용 중이라면 자원에서 프로세스를 향해 화살표 표시
프로세스가 어떤 자원을 기다리고 있다면 프로세스에서 자원으로 화살표 표시
자원 할당 그래프로 표현한 식사하는 철학자 문제
교착 상태 발생 조건
상호 배제 : 한 프로세스가 사용하는 자원을 다른 프로세스가 사용할 수 없을 때
점유와 대기 : 자원을 할당받은 상태에서 다른 자원을 할당받기를 기다리는 상태
비선점 : 다른 프로세스의 자원을 강제적으로 빼앗을 수 없는 상태
원형 대기 : 프로세스들과 프로세스가 요창 및 할당받은 자원이 원의 형태를 이루어 대기하는 상태
교착 상태 해결 방법
교착 상태 예방 (4가지 조건 제거)
자원의 상호 배제 : 모든 자원 공유 가능 상태
이론적으로 교착 상태를 없앨 수 있으나, 현실적으로 무리가 있는 방법
점유와 대기 : 특정 프로세스에 자원을 모두 할당하거나 아예 할당하지 않는 방식.
이론적으로는 교착 상태 해결 가능하나 자원의 활용률이 낮아질 우려가 있음
(지금 당장 필요한 프로세스는 못쓰거나 낭비가 됨)
비선점 조건 : 자원을 이용중인 프로세스로부터 자원을 빼앗을 수 있는 방식 -> 교착 상태 해결 가능
선점하여 사용할 수 있는 자원에 대해서는 유효하지만 선점이 불가능한 자원은 빼앗을 수 없으므로 범용성이 떨어짐.
원형 대기 조건 : 모든 자원에 번호를 붙여 순서대로 자원 할당
자원들에 번호를 부여하는 것은 어려운 작업
어떤 자원에 어떤 번호를 붙이냐에 따라 활용률이 달라짐
교착 상태 회피
안전 순서열 : 교착 상태 없이 안전하게 프로세스들에 자원을 할당할 수 있는 순서
안전 상태 : 교착 상태가 발생하지 않고 모든 프로세스가 정상적으로 자원을 할당받고 종료될 수 있는 상태
불안전 상태 : 교착 상태가 발생할 수도 있는 상태. 안전 순서열이 없는 상황
교착상태 회피 : 안전상태에서 안전상태로 움직이는 경우에만 할당하는 방식
항상 안전상태에만 움직이는 방식
교착 상태 검출 후 회복
선점을 통한 회복 : 교착 상태가 해결될 때 까지 한 프로세스씩 자원을 몰아주는 방식
프로세스 강제 종료를 통한 회복 : 교착 상태에 놓인 프로세스를 모두 강제 종료 하거나 (작업내역 잃어버림)
교착 상태가 없어질 때 까지 한 프로세스씩 강제 종료 (오버헤드)
교착상태 무시!
타조 알고리즘 : 잠재적 문제를 무시로 대처하는 방식
실습
만일 뮤텍스 락이나 세마포어 모니터가 없다면?
import threading
# 공유자원
shared_var = 0
# 1씩 증가 시키는함수
def increment_shared_var():
global shared_var
threading.currentThread()
threading.currentThread()
shared_var += 1
# 스레드 생성
threads = []
for _ in range(5):
t = threading.Thread(target=increment_shared_var)
threads.append(t)
t.start()
for t in threads:
t.join()
print("공유 자원 값:", shared_var)
공유자원값 : 2
스레드가 동시에 사용하면서 값을 증가 시키지 못한걸 확인 가능!
뮤텍스락
import threading
# 공유자원
shared_var = 0
# 뮤텍스락 생성
mutex = threading.Lock()
# 1씩 증가 시키는함수
def increment_shared_var():
global shared_var
# Acquire()
mutex.acquire()
try:
shared_var += 1
finally:
# Release()
mutex.release()
# 스레드 생성
threads = []
for _ in range(5):
t = threading.Thread(target=increment_shared_var)
threads.append(t)
t.start()
for t in threads:
t.join()
print("공유 자원 값:", shared_var)
공유 자원 값 : 5
threading.Lock()를 통해 자물쇠를 만들고
mutex.acquire() , mutex.release() 로 자물쇠를 풀어가며 동작하는걸 확인 할 수있다.
https://docs.python.org/ko/3/library/threading.html#lock-objects
세마포어
import threading
# 공유자원
shared_var = 0
# 세마포어 생성
semaphore = threading.Semaphore(1)
# 1씩 증가 시키는함수
def increment_shared_var():
global shared_var
# Acquire
semaphore.acquire()
try:
shared_var += 1
finally:
# Release()
semaphore.release()
# 스레드 생성
threads = []
for _ in range(5):
t = threading.Thread(target=increment_shared_var)
threads.append(t)
t.start()
for t in threads:
t.join()
print("공유 자원 값:", shared_var)
공유 자원 값 : 5
뮤텍스 락과 마찬가지로 semaphore.acquire(), semaphore.release() 사용하며 잘 진행이 된걸 확인가능하다.
https://docs.python.org/ko/3/library/threading.html#semaphore-objects
모니터
import threading
# 공유자원
shared_var = 0
# 모니터 조건 객체 생성
monitor = threading.Condition()
# 1씩 증가 시키는함수
def increment_shared_var():
global shared_var
with monitor:
shared_var += 1
# 대기중인 프로세스를 깨우고 진행시킴
monitor.notify()
# 공유 자원이 목표 값보다 작은지 확인 함수
def wait_for_shared_var(target_value):
with monitor:
while shared_var < target_value:
#알람 받을때까지 기다림
monitor.wait()
# 스레드 생성
threads = []
for _ in range(5):
t = threading.Thread(target=increment_shared_var)
threads.append(t)
t.start()
for t in threads:
t.join()
# 공유자원이 특정조건(5)에 도착할때까지 기다리는 함수
wait_for_shared_var(5)
print("공유 자원 값:", shared_var)
threading.Condition()로 모든 스레드를 lock을 잡은것처럼 대기상태로 만들고 조건에 따라 동작하게 합니다.
notify() 대기상태를 깨우고 동작하게 하고 wait()로 알람을 받을때 까지 기다립니다
with 문으로 자동으로 프로세스 할당 / 해제를 진행할 수 있음
https://infinity-infor-age.tistory.com/entry/python-inter-thread-comm
https://superfastpython.com/thread-condition/
'컴퓨터 사이언스 > 컴퓨터 구조 & 운영체제' 카테고리의 다른 글
[혼공컴운] 운영체제 chapter 14 (0) | 2024.02.09 |
---|---|
[혼공컴운] 5주차 미션 (1) | 2024.02.07 |
[혼공JS] Express 서버 시작하기 (0) | 2024.02.07 |
[혼공컴운] 4주차 미션 (1) | 2024.01.30 |
[혼공컴운] 파이썬을 지우면..... (0) | 2024.01.28 |