한양과학기술고등학교

에듀테크 프로젝트 대시보드

우선순위 역전 (Priority Inversion)

우선순위 역전 (Priority Inversion)

낮은 우선순위 태스크가 잡고 있는 자물쇠(뮤텍스) 하나 때문에, 가장 급한 태스크가 하염없이 기다리게 되는 스케줄링의 함정 — 그리고 우선순위 상속으로 푸는 법.

한 줄 요약

선점형 우선순위 스케줄러에서, 고순위 H가 원하는 공유 자원을 저순위 L이 이미 잠갔다. L이 자원을 풀어야 H가 진행되는데, 그 사이 중순위 M이 끼어들어 L을 계속 밀어낸다. 결과적으로 H는 자기보다 낮은 M 때문에 무한정 밀린다.

1. 인터랙티브 타임라인

같은 시나리오를 상속 없음과 우선순위 상속 두 모드로 비교해 보자. ▶︎를 눌러 시간이 흐르는 것을 지켜보고, 각 태스크가 언제 자원을 쥐는지(초록 테두리), 언제 막히는지(빗금) 확인한다.

H
M
L
실행 중(CPU 점유) 자원(뮤텍스) 보유 자원 대기로 블로킹 준비됐지만 선점당함
현재 시각
0
H 응답 지연
–
상태
–
핵심 차이: 상속 없음 모드에서 H의 지연은 M의 실행 시간에 비례해 늘어난다(이론적으로 무한). 상속 모드에서는 L이 잠깐 H의 우선순위를 물려받아 M을 밀어내고 임계 구역을 빨리 빠져나오므로, H의 지연은 L의 임계 구역 길이만큼으로 유한하게 제한된다.

2. 왜 생기나 — 세 박자

조건설명
공유 자원 + 상호 배제H와 L이 같은 뮤텍스를 두고 경쟁한다. 한 번에 하나만 들어갈 수 있다.
선점형 우선순위 스케줄링항상 준비 상태인 태스크 중 우선순위가 가장 높은 것이 CPU를 가져간다.
중간 우선순위 태스크의 존재H도 L도 아닌 M이 끼어들어 L을 밀어내면, L이 자원을 못 푼다 → H도 못 푼다.

M이 없다면? L은 계속 돌아 임계 구역을 곧 빠져나오고 H는 짧게만 기다린다. 이 짧고 유한한 대기는 우선순위 역전이 아니라 정상적인 bounded blocking이다. 문제는 M 때문에 그 대기가 경계 없이 길어질 때다.

3. 화성에서 실제로 터진 버그

1997년 NASA Mars Pathfinder 탐사선이 화성 표면에서 반복적으로 리셋됐다. 원인은 VxWorks 실시간 OS에서의 전형적인 우선순위 역전이었다:

  • 고순위 bc_dist(정보 버스 관리) 태스크가 뮤텍스로 보호된 공유 버스에 접근하려 함
  • 저순위 ASI/MET(기상 데이터) 태스크가 그 뮤텍스를 쥔 채 중순위 통신 태스크에 선점당함
  • 고순위 태스크가 제때 못 돌자 워치독 타이머가 시스템을 통째로 리셋

JPL 엔지니어들은 지구에서 원격으로 pthread_mutexattr의 우선순위 상속 플래그를 켜서(1로) 문제를 해결했다. 디버깅이 가능했던 건 같은 코드가 지상 테스트 장비에서도 돌고 있었기 때문이다.

4. 해결책 비교

기법아이디어비고
우선순위 상속
(Priority Inheritance)
H가 L이 쥔 뮤텍스에서 블로킹되면, L이 임시로 H의 우선순위를 물려받는다. L이 뮤텍스를 풀면 원래 우선순위로 복귀. 가장 널리 쓰임(PTHREAD_PRIO_INHERIT). 연쇄·중첩 락에서 상속 전이 처리가 까다롭다.
우선순위 실링
(Priority Ceiling)
각 뮤텍스에 "그 락을 쓸 수 있는 태스크 중 최고 우선순위"를 천장값으로 미리 지정. 락을 잡는 순간 태스크가 그 천장으로 올라간다. 데드락·연쇄 블로킹을 원천 차단. 천장을 정적으로 알아야 함(PTHREAD_PRIO_PROTECT).
인터럽트/선점 비활성화 임계 구역 동안 아예 스케줄러를 멈춘다. 구현은 간단하지만 짧은 구역에만. 길면 실시간성 전체를 해친다.
락 없는 설계 공유 자원 자체를 없애거나(태스크별 사본), 원자 연산·메시지 큐로 대체. 가능하면 최선. 항상 가능하진 않음.

5. 코드로 보기 (POSIX)

// 우선순위 상속을 켠 뮤텍스 초기화
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);

// 기본값 PTHREAD_PRIO_NONE → 역전 방어 없음
pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);

pthread_mutex_t bus_lock;
pthread_mutex_init(&bus_lock, &attr);

// 이제 저순위 태스크가 bus_lock 을 쥔 채
// 고순위 태스크에게 블로킹을 유발하면,
// 커널이 저순위 태스크의 우선순위를 자동으로 끌어올린다.
고등학생용 비유: 매점에 딱 하나뿐인 전자레인지(뮤텍스)를 3학년(L)이 쓰고 있다. 급식 검사하러 온 선생님(H)이 전자레인지를 써야 하는데 3학년이 끝내야 쓸 수 있다. 그런데 2학년(M)들이 계속 3학년을 새치기로 밀쳐내면(선점), 3학년은 영영 못 끝내고 선생님도 계속 기다린다. 우선순위 상속 = "선생님이 기다리니까 지금 쓰는 사람은 선생님 순번으로 쳐준다" → 2학년도 새치기 못 함 → 3학년이 빨리 끝냄.

6. 정리