728x90
4th
📌 4주차 : 프로세스 동기화
- 데드락
- 병행성과 병렬성
- RaceCondition
- 세마포어
- 뮤텍스
- 모니터
스레드 동기화
스레드 동기화의 필요성
- 스레드 동기화: 공유 데이터에 대한 다수 스레드의 동시 접근을 해결하는 방법
- 다수의 스레드가 공유 데이터를 동시에 쓰는 충돌 상황에서 공유 데이터가 훼손되지 않도록 스레드의 실행을 제어하는 기법
- 동기화를 통해 스레드가 공유 데이터에 대해 배타적이고 독점적으로 접근하도록 허용해야한다.
공유 데이터 액세스 문제와 해결방법
- 문제점: 여러 스레드가 공유 데이터에 동시에 쓰기를 수행하면 공유 데이터가 훼손된다.
- 해결책: 스레드 동기화 → 한 스레드가 공유 데이터에 대한 사용을 마칠 때까지 다른 스레드가 접근하지 못하도록 제어
임계구역과 상호배제
- 임계구역: 사용자가 작성한 프로그램 중 공유 데이터에 접근하는 코드 블록
- 상호배제: 임계구역은 반드시 한 스레드만 배타적 독점적으로 실행하도록 관리하는 것을 의미
- 핵심: 임계구역에 먼저 진입한 스레드가 임계구역의 실행을 끝날 때까지 다른 스레드가 집입하지 못하도록 보장하는 것
- 임계구역 설정은 멀티스레드 응용 프로그램의 개발자의 판단에 따라 이루어짐
상호배제
- 임계구역 전후에 진입코드, 진출코드 있음
- 진입코드: 임계구역에 진입하기 전 필요한 코드 블록, 현재 임계구역을 이미 실행중인 스레드가 있는지 검사하고, 없는 경우 다른 스레드 들어오지 못하도록 조치한다. 들어오려면 들어오려는 스레드 대기시키고, 진입한 스레드 끝날때까지 대기한다.
- 진출코드: 스레드가 임계구역 실행을 마칠 때 실행되어야 하는 코드 블록, 대기중인 스레드 임계구역 진입할 수 있도로 entry 코드에서 취한 조치 해제
- 임계구역 코드: 한번에 한 스레드만 실행하도록 보장, 임계구역은 짧을 수록 좋음
상호배제 구현
- 방법
- 소프트웨어적: Peterson’s 알고리즘
- 실제 구현에 잇어 여러 문제를 노출하기 때문에 사용 ㄴ
- 하드웨어적: 인터럽트 서비스 금지, 원자명령 활용
- 현재는 원자명령을 활용
- 소프트웨어적: Peterson’s 알고리즘
- 관건: 임계구역의 entry 코드와 exit 코드를 어떻게 구현하는가
방법1 - 인터럽트 서비스 금지
- 임계구역으로 진입할 때 entry 코드에서 인터럽트 서비스를 금지하고 exit 코드에서 인터럽트 서비스를 허용하는 CPU 명령들을 실행하는 방법 → 스레드 선점되지 않는다.
- cli와 sti
- cli 명령: CPU 내부의 인터럽트 플래그를 0으로 리셋 → 인터럽트 무시
- sti 명령: CPU의 인터럽트 플래그를 1로 설정 → 인터럽트 발생시 인터럽트 서비스 루틴 실행
- 두 가지 문제점
- 임계구역 실행하는 동안 모든 인터럽트 무시된다
- 인터럽트 서비스 금지 방법은 단일 CPU 시스템에서는 활용가능, 다중 CPU를 가진 시스템에서는 활용하지 못한다.
방법2 - 원자명령 사용
- 상호배제를 위해 만들어진 CPU 명령
- 원자명령없이 lock 변수를 이용한 상호배제 시도
- 임계구역들어갈때 lock 변수 1, 나올때 0으로 변경
- 문제점: entry 코드
- lock 변수 값을 읽어 들이는 명령과 lock 변수를 1로 바꾸는 명령 사이에 컨택스트 스위치이 발생할때 문제 발생
- 해결 방법 - 원자 명령 도입
- 위 두 명령어를 하나의 명령으로 만드는 것 → 원자명령, TSL
- TSL ax, lock
멀티스레드 동기화 기법
- 동기화 프리미티브
- 멀티스레드 동기화 방법중 3가지 방법
- 락 방식 - 뮤텍스, 스핀락
- wait-signal 방식 - 세마포
뮤텍스
- 잠금/열림중 한 상태를 가지는 락 변수를 이용하여, 한 스레드만 임계구역에 진입시키고 다른 스레드들을
큐에 대기시키는 방법 - 락 변수: 락을 잠근다, 락을 여다
- lock/unlock 연산
- lock 연산: 스레드가 임계구역에 들어가기 전 실행하는 entry 코드, 잠겨 있을 경우 현재 스레드를 블록 상태로 만들어 대기 큐에 삽입, 열린 상태라면 락을 잠그고 임계구역 진입
- unlock 연산: 임계구역 나올때 실행하는 exit 코드, 락을 열림 상태로 바꾸고 대기 큐에 있는 스레드 하나 깨워 준비 상태로 만들기
- 원자 명령 사용됨
- 뮤텍스: 블록킹 락, 수면 대기 락
- 특징
- 임계구역의 실행 시간이 짧은 경우에는 비효율적
- 락이 잠겨있는 경우, 스레드는 대기 큐에 들어가고 락이 풀리면 cpu 얻어서 실행되는데 임계 구역 실행시간이 짧을 경우 락이 잠겨 있는 시간보다 컨텍스트 스위치하는 시간이 더 크기때문에 비효율적
- 임계구역의 실행 시간이 짧은 경우에는 비효율적
스핀락
- 락 기반이지만 뮤텍스와 다르게 대기 큐가 없다
- 락 변수
- lock/unlock 연산
- lock 연산: 락이 열려있으면 임계구역 들어가고, 잠겨 있으면 열릴 때까지 락 검사를 무한 반복하고, 락 열리면 즉각 락을 잠그고 임계 구역 들어간다.
- unlock 연산: 락을 열림으로 변경
- 원자명령 사용
- 타임 슬라이스가 소진될때 스레드는 컨텍스트 스위치되고, 다시 스케줄되면 다시 락이 풀릴 때까지 검사를 반복
- = 공격적인 뮤텍스, 바쁜 대기 락
- 특징
- 뮤텍스 기법의 바쁜 대기 모형
- 단일 CPU를 가진 운영체제에서 스핀락은 비효율적
- lock이 잠겨있을때 현재 스레드가 계속 락을 검사하는 코드는 의미 없는 검사를 계속 하는 거여서 CPU 낭비가 심하고, 다른 스레드의 실행 기회마저 뺏는 것이다.
- 반대로 멀티 CPU인 경우, 락을 경쟁하는 스레드들을 서로 다른 코어에서 실행시키면 상당히 효과적
- 스핀락은 임계구역 코드가 짧아서 락이 빨리 열리는 응용에 매우 효과적
- 스핀락은 락을 얻기 위해 무한 경쟁하기 때문에 기아 발생할 수 있다.
- 리눅스는 동기화 기법으로 스핀락 사용한다.
뮤텍스와 스핀락은 어떤 경우에 적합할까?
- 락이 잠기는 시간이 긴 응용의 경우 뮤텍스가 효과적, 짧은 경우 스핀락이 효율적
- 단일 CPU일 경우 뮤텍스가 적합, 멀티코어인 경우 스핀락이 효율적
- 뮤텍스는 사용자 응용프로그램에서 주로 이용, 스핀락은 커널 코드나 인터럽트 서비스 루틴에서 주로 사용
세마포
- n개의 자원을 다수의 스레드가 공유하여 사용하도록 돕는 자원 관리 기법
- 자원의 개수 n을 알고, 스레드의 요청을 받아 사용을 허락하고, 자원이 모자랄 때 요청한 스레드는 대기큐에서 잠을 재우며, 자원 사용을 끝낸 스레드가 세마포에게 알리면 세마포가 대기큐에서 잠을 자는 스레드를 깨워 자원을 사용하도록 허락하는 방식
- 구성 요소: 자원, 대기큐, counter 변수, P/V 연산
- counter 변수: 사용가능한 자원의 개수를 나타내는 정수형 변수
- 변수가 음수이면 자원을 기다리는 스레드의 개수
- P/V 연산
- P 연산: 스레드에게 자원 사용을 허가하는 과정
- counter변수 1 감소
- V 연산: 스레드가 자원 사용이 끝났음을 세마포에게 알리는 과정
- counter 변수 1 증가
- 종류
- 수면 대기 세마포
- P 연산 중 자원 사용을 허가받지 못한 스레드를 대기큐에서 잠을 재우고, V 연산에서 사용 가능한 자원이 생기게 되면 스레드를 깨워 자원 사용을 허락
- 바쁜 대기 세마포
- P 연산에서 가용 자원이 생길 때까지 무한 루프를 돌아 검사하는 방식
- 대기 큐가 없다
- 수면 대기 세마포
- = wait/signal 연산: 자원을 사용하려는 스레드 대기하고, 자원 사용끝난 스레드는 대기 스레드에게 알려주기 때문에
- P 연산: 스레드에게 자원 사용을 허가하는 과정
이진 세마포
- 카운터 세마포: 자원이 여러 개인 경우
- 이진 세마포: 자원이 1개인 경우
- 세마포 변수 S: 0 또는 1
- 대기 큐
- P 연산: S를 1 → 0 으로 감소, 0보다 작으면 대기큐 잠들게 하기
- V 연산: S를 1 증가시키고 0보다 크면 그냥 리턴, 0보다 작거나 같으면 대기큐에 있는 스레드 깨우기
동기화 이슈: 우선순위 역전
- 스레드 동기화로 인해 우선순위가 높은 스레드가 순위가 낮은 스레드보다 늦게 실행되는 우선순위 역전이 발생할 수 있다.
- 실시간 시스테에서 높은 순위의 스레드가 오래 대기하는 것은 심각한 문제
- 해결책
- 우선순위 올림: 스레드가 공유 자원을 소유하게 될 때 우선순위를 일시적으로 미리 정해진 우선순위로 높이는 방법
- 공유 자원을 소유한 스레드가 다른 스레드에 의해 선점되지 않고 빨리 실행
- 우선순위 상속: 낮은 순위의 스레드가 공유 자원을 획득하고 실행하는 동안 높은 순위의 스레드가 공유 자원을 요청하면 낮은 순위의 스레드를 우선순위를 요청한 스레드보다 높게 변경하여 실행
- 두 방법은 모두 구현이 쉽지 않고 오버헤드 존재함
- 우선순위 올림: 스레드가 공유 자원을 소유하게 될 때 우선순위를 일시적으로 미리 정해진 우선순위로 높이는 방법
생산자-소비자 문제
- 전형적인 동기화 문제
- 문제점
- 입력스레드와 재생 스레드가 버퍼에 동시에 접근하는 경우의 상호배제
- 재생스레드가 읽으려고 하는데 버퍼가 비어있는 경우
- 입력스레드가 쓰려고하는데 버퍼가 꽉 찬 문제
- 유한한 크기의 공유버퍼에 데이터를 공급하는 생산자와 공유버퍼에서 데이터 읽고 소비하는 소비자가 공유버퍼를 문제없이 사용하도록 생산자와 소비자를 동기화시키는 문제(실행 순서 제어)
- = 유한 버퍼 문제
- 해결책
- 상호배제 해결: 뮤텍스나 세마포를 이용
- 비어있는 공유버퍼 문제 해결: 세마포
- 꽉 찬 공유버퍼 문제 해결: 세마포, 소비자 스레드가 데이터를 읽은 후 생산자 스레드 깨우기
교착상태
교착상태 문제 제기
무한 대기와 교착 상태
- 무한적 대기하는 상태
식사하는 철학자 문제
- 5명의 철학자 5개의 포크, 스파게티를 먹기위해서는 2개의 포크가 필요함
- 모든 철학자가 동시에 자신의 왼쪽 포크를 들고 오른쪽 포크를 집으려고 하는 경우 모든 철학자가 자신의 오른쪽 포크를 무한정 기다리게 된다.
- 즉, 영원히 대기하는 교착 상태
- 원인: 환형 요청/대기
- 대기의 모형이 환형 고리를 형성하고 있으며 스스로 해체할 수 없어서 교착 상태에 빠진 것
- 해결: 환경 요청/대기가 생기지 않게 규칙 변경
- 나머지 한 철학자만 오른쪽 포크를 먼저 잡은 뒤 왼쪽 포크를 잡도록 규칙 변경
식사하는 철학자와 컴퓨터 시스템
- 철학자 - 스레드
- 포크 - 자원
- 스파게티 - 스레드가 처리할 작업
교착상태
교착상태 정의
- banker’s algorithm research
- = 풀지 못하는 포옹
- 자원을 소유한 스레드들 사이에서 각 스레드는 다른 스레드가 소유한 자원을 요청하여 모든 스레드가 무하정 대기하는 현상
- 커널 코드 내에서는 거의 발생하지 않고, 사용자 응용프로그램 내에서 주로 발생한다.
- 커널은 교착상태를 고려하여 매우 정교하게 작성되어 있지만 사용자 응용프로그램은 아니다.
- 그럼에도 교착상태를 막도록 운영하는 컴퓨터 시스템은 거의 없다. → 많은 시간과 비용을 치러야 하기 때문
컴퓨터 시스템에 잠재된 교착상태 유발 요인
- 자원 - 자원은 교착상태의 발생지
- 소프트웨어/데이터 자원 - 뮤텍스, 스핀락, 세마포, 파일, 데이터베이스, 파일락 등
- 하드웨어 자원 - 프린터, 메모리, 프로세서 등
- 자원과 스레드 - 한 스레드가 동시에 여러 개의 자원을 필요로 하는 경우
- 자원과 운영체제 - 운영체제는 한 번에 하나씩 자원을 할당
- 만약 스레드가 운영체제로부터 필요한 자원을 한 번에 모두 할당 받는다면 교착상태 발생하지 않을 수도 있다.
- 자원 비선점 - 할당된 자원은 스레드가 자발적으로 내놓지기 전에 강제로 뺏지 못한다.
교착상태 모델링
- 자원 할당 그래프: 컴퓨터 시스템에 존재하는 자원과 스레드들의 상태를 나타내는 방향성 그래프
- 꼭짓점: 스레드는 원으로, 자원은 사각형
- 간선
- 할당 간선: 자원 → 스레드, 스레드가 자원 소유
- 요청 간선: 스레드 → 자원, 스레드가 자원 기다림
- 교착 상태가 발생한 자원 할당 그래프의 모양
- 환형 고리가 나타난다
교착상태 해결
코프만 조건
- 교착상태를 유발할 수 있는 4가지의 필요충분조건 증명
- 상호 배제: 자원은 한 번에 한 스레드에게만 할당
- 소유하면서 대기; 스레드가 자원을 소유하면서 다른 자원 대기
- 강제 자원 반환 불가: 스레드에게 할당된 자우넝르 강제로 빼앗지 못함
- 환형 대기: 한 그룹의 스레드에게 각 스레드가 다른 스레드가 소유한 자원을 요청하는 환형 고리 형성
- 한 가지라도 성립되지 않게 된다면 교착상태 빠지지 않게 되는 것
교착상태 해결 방법
- 예방
- 하나 이상의 조건이 아예 성립하지 못하도록 시스템을 설계하고 구성하여 교착상태가 발생할 여지가 없도록 예방하는 것
- 회피
- 운영체제가 자원을 할당할 때 교착상태에 빠지지 않을 것이라고 확신하는 경우에만 자원을 할당
- 자원을 할당할때마다 교착상태 가능성을 검사 → 시스템 성능 많이 저하
- 감지 및 복구
- 교착상태 감지하는 프로그램 백그라운드 구동시켜 발견하면 교착상태 해체
- 교착상태 감지 작업 주기로 실행해야해서 부담 크다.
- 무시
- 아무런 대비책 없이 교착상태 발생하도록 내버려 두는 방법
- 기본 철학은 웬만해서 교착상태 발생하지 않으며, 발생할 경우 스레드 종류, 부팅시키기
- 대부분의 범용 운영체제에서 사용중이다.
- 타조 알고리즘이라 불림
교착상태 예방
- 교착상태 처음부터 발생하지 않는 시스템 환경으로 만드는 것
- 코프만 4가지 조건중 최소 한 가지 이상 성립못하도록 만들기
- 상호배제 → 상호배제 없애기
- 자원이 한 스레드에 의해 독점되지 않게 막기 → 2개 이상의 스레드가 동시에 자원을 사용할 수 있도록 허용 ⇒ 불가능
- 출력물 엉망, 하나의 락을 동시에 2개 스레드가 점유? 말이 안돼
- 소유하면서 대기 → 기다리지 않게
- 스레드가 필요한 자원을 처음부터 모두 가지게하면 된다.
- 운영체제는 스레드 실행 전에 스레드가 필요한 자원을 모두 알고, 스레드 실행시작과 함께 모든 자원을 할당 → 실행 중에 자원을 요청하여 대기하는 일이 없도록 하는 것
- 스레드가 자원을 소유한 상태로 새로운 자원을 필요로 하면, 현재 할당한 모든 자원을 반환하고 필요한 모든 자원을 한꺼번에 모두 요청하는 방법
- 자원 강제 반환 불가 → 자원의 선점 허용
- 더 높은 순위의 스레드가 자원 요청하면 강제로 자원 뺏기
- 자원 강제 반환된 스레드가 자원을 다시 사용하게 될때 이전 상태로 되돌아갈 수 있도록 상태를 관리
- 어려운 방법
- 환형 대기 → 환형 대기 제거
- 모든 자원에 번호를 매기고, 스레드에게 반드시 번호 순으로 자원을 할당받게 하는 방법
교착상태 회피
- 자원할당 알고리즘을 이용하여 교착상태를 방지하는 방법
- 자원을 할당할때마다 환형 대기가 발생할 것인지 판단하는 작업이 실행되는 큰 단점
- banker’s 알고리즘
- 시스템을 안전한 상태와 불안전한 상태로 나누고, 자원을 할당하였을 때 안전한 상태로 갈때만 자원을 할당
- 각 스레드는 실행 전에 필요한 전체 자원의 수를 운영체제에 알려줘야하는데 아는게 불가능함
- 실행중인 스레드 개수도 동적으로 변함
교착상태 감지 및 복구
- 교착상태를 감지하는 백그라운드 프로그램을 상시적으로 실행시켜 교착상태를 감지하고 교착상태를 해제하는 방법
- 자원 강제 선점
- 교착상태 빠진 스레드중 하나 선택하고 소유 자원 강제 빼앗아 이 자원을 기다리는 다른 스레드에 할당 → 환형 대기 고리 끊기
- 롤백
- 교착상태 발생할 것이라고 예측되는 스레드 상태 주기적으로 저장해두었다가, 교착상태 발생하면 가장 최근에 저장해둔 상태로 복구시켜 가장 최근에 실행하던 상태로 돌아가기
- 주기적으로 스레드들의 상태가 저장되어야 하기 때문에, 스시템의 시간과 저장 공간을 소모 → 시스템 성능 저하
- 스레드 강제 종료
- 어떤 스레드를 희생시킬지 결정하는 문제 → 사용자나 시스템 관리자가 흔히 사용하는 방법
- 교착상태 감지 복구 방법도 백그라운드 프로그램이 실행되면서 자원 할당 그래프를 분석하여 교착상태가 있는지 판단 → 시스템 부담, 교착상태 해제방법 시스템 많이 부담
교착상태 무시: 타조 알고리즘
- 교착상태가 발생할 가능성은 극히 적다 → 교착상태를 피하기 위한 비용은 많다 → 굳이 교착상태 예방, 회피, 감지 및 복구를 할 필요가 있을까?
- 현재 대부분의 운영체제는 교착상태를 무시한다.
- 교착상태에 대한 아무런 대책 없이 컴퓨터 시스템을 가동시키고, 교착상태가 의심되면 사용자나 관리자가 시스템을 재시작하거나 의심가는 스레드를 강제종료하는 방법
- 데이터 잃어버리는 손실 발생하나 비용 면에서 더 나은 방법
- 실시간 시스템에서는 타조 알고리즘 부적절
참조
운영체제 책
'CS > 운영체제' 카테고리의 다른 글
| 프로세스 메모리 배치 (0) | 2025.10.20 |
|---|---|
| CPU 스케줄링 (1) | 2024.08.07 |
| 프로세스와 쓰레드 (3) | 2024.07.30 |
| 운영체제 기본 개념 (0) | 2024.07.30 |