운영체제 용어 사전
동시성원자 연산 · 경쟁 조건

뮤텍스

임계 구역을 보호하는 자물쇠들. 대기 방식과 개수 제한에서 서로 갈린다.

공유 자원을 한 번에 하나만 건드리게 하는 장치들.

왜 필요한가 — counter++의 비밀

counter++ 는 기계어로 세 단계다

  • ① 메모리 → 레지스터로 읽는다
  • ② 레지스터를 1 증가시킨다
  • ③ 레지스터 → 메모리로 쓴다

두 스레드가 ①을 동시에 하면

  • 둘 다 같은 값을 읽고 같은 값을 쓴다 → 한 번의 증가가 사라진다

이 '중간에 끼어들 수 있는 구간' 이 임계 구역이다

자물쇠들의 차이

뮤텍스     한 번에 하나. 못 얻으면 '잠들었다가' 깨어난다
           → 대기 중 CPU 를 쓰지 않는다
           → 잠들고 깨는 데 컨텍스트 스위치 비용이 든다
           → 소유자 개념이 있다 (잠근 스레드만 풀 수 있다)

세마포어   N 개까지 허용하는 카운터
           → 자원 풀 · 생산자-소비자에 쓴다
           → 소유자 개념이 없다 (다른 스레드가 풀어도 된다 → 신호 전달에 유용)
           → N=1 이면 뮤텍스와 비슷하지만 소유자가 없다는 차이가 남는다

스핀락     못 얻으면 '계속 확인하며 돈다' (busy-wait)
           → 대기 시간이 매우 짧을 때만 유리하다
           → 단일 코어에서는 절대 쓰면 안 된다 (락 쥔 쪽이 못 도는데 CPU 를 태운다)
           → 커널 내부의 짧은 구간에 주로 쓰인다

원자 연산   락 없이 하드웨어 명령으로 (CAS · fetch-and-add)
           → 가장 빠르지만 단순한 연산에만 쓸 수 있다

무엇을 고르나

  • 대기가 길다 (I/O · 복잡한 처리) — → 뮤텍스
  • 대기가 매우 짧다 (몇 줄) — → 스핀락 (멀티코어에서만)
  • 단순 증감·교체 — → 원자 연산
  • 자원 개수 제한 · 신호 전달 — → 세마포어

가시성이라는 별도의 문제

락은 '동시 접근' 을 막는다. 그런데 문제가 하나 더 있다

각 코어에는 자기 캐시가 있다

  • 한 코어가 바꾼 값이 다른 코어에 즉시 안 보일 수 있다
  • 컴파일러·CPU 가 명령 순서를 바꾸기도 한다 (재정렬)

메모리 배리어가 이를 막는다

  • 락의 획득·해제에는 배리어가 포함돼 있다
  • 그래서 락을 제대로 쓰면 가시성도 함께 해결된다

락을 쓸 때의 원칙

  • 범위를 최대한 좁게 (락 안에서 I/O 를 하지 않는다)
  • 항상 같은 순서로 잠근다 (데드락 예방)
  • 락을 쥔 채 다른 락을 요구하지 않는다
  • 예외가 나도 반드시 풀리게 한다

면접 함정

  • "세마포어(N=1) = 뮤텍스" → 소유자 개념이 없어 다른 스레드가 풀 수 있다.
  • "스핀락이 뮤텍스보다 빠르다" → 대기가 길면 CPU만 태운다.

락 경합 측정

perf lock record ./app && perf lock report
perf stat -e 'syscalls:sys_enter_futex' ./app   # 락 대기가 커널로 내려간 횟수

리눅스의 뮤텍스는 futex 위에 구현돼 있다

경합이 없으면 → 유저 공간에서 원자 연산만으로 끝난다 (시스템 콜 없음) 경합이 있으면 → futex 시스템 콜로 커널에 잠들기를 요청한다

"경합이 없으면 락은 거의 공짜" 라는 말의 근거다

읽기가 압도적일 때

읽기-쓰기 락 (RWLock)

  • 읽기끼리는 동시에 허용, 쓰기는 배타적
  • 읽기가 많으면 처리량이 오른다
  • 대신 쓰기 기아가 생길 수 있고, 구현이 복잡해 경합이 낮으면 일반 뮤텍스가 낫다

RCU (Read-Copy-Update)

  • 읽기는 락 없이, 쓰기는 새 사본을 만들어 교체
  • 리눅스 커널이 널리 쓴다. 읽기 비용이 사실상 0

조건 변수

"조건이 만족될 때까지 기다린다" 를 표현한다

  • 뮤텍스와 짝으로 쓴다 (락을 놓고 잠들었다가, 깨어나면 다시 잡는다)
  • 깨어난 뒤 조건을 '반드시 다시 확인' 해야 한다 (spurious wakeup)
    • while (!condition) wait(cv, mutex); ← if 가 아니라 while

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 동시성 — 경쟁 조건·뮤텍스/세마포어·CAS