[운영체제] 동기화를 하드웨어와 소프트웨어 방식으로 해결해보자

네민·2025년 5월 1일
0

우당탕탕 cs 공부

목록 보기
12/14

하드웨어 기반 동기화 방식

소프트웨어적으로 동기화를 보장한다고 해도 기계어 레벨에서 다른 프로세스에 선점될 경우 데이터의 불일치가 발생할 수 있다. 그래서 하드웨어 기반 해결안 제시한다.

다른 블로그 글들을 읽어보면 이거는 하드웨어 기반인데 왜 소프트웨어 코드로 알려주지?

이런 의문증을 가졌는데, 기계어로 뚝 - 딱하면 사용할 수 있는 걸 프로그래머가 알아보기 쉽게 하기 위해서 소프트웨어 코드로 비슷하게 구현한 것이라고 한다.

1. test_and_set

"현재 락이 비어 있는지 확인하고, 동시에 내가 그 락을 차지"하는 걸 처리한다.

대표적으로 Spinlock 구현에 사용된다.

Spinlock

공유 자원을 보호하기 위해 락이 풀릴 때까지 반복해서(lock을 점검하며) 대기하는 동기화 방식

💡 알고리즘 흐름

공유 변수에 접근하기 전에 lock의 true/false 상태를 확인한다.

  • lock이 false 상태면 내가 lock을 걸고 값을 변경한 후에 lock을 false로 변경한다.
  • 하지만 lock이 true 상태면 lock이 false가 될 때까지 기다린다. (while문)

Spinlock은 busy-wait를 하기 때문에, 짧은 시간이면 효율적이지만 긴 작업이면 CPU 낭비가 된다.


2. compare_and_swap

논블로킹 동기화 알고리즘의 핵심 연산으로,
락을 걸지 않고도 안전하게 공유 데이터를 수정할 수 있는 원자적 연산이다.

💡 알고리즘 흐름

  1. 메모리에서 현재 값을 읽는다.
  2. 내가 알고 있는 예상 값과 비교
  • 현재 값과 예상 값이 같다면 새 값으로 변경한다.
  • 다르다면 아무것도 하지 않고 false를 반환한다.
  • 실패하면 while 루프를 통해 재시도한다.

소프트웨어 기반 동기화 방식

1. 뮤텍스 (Mutex)

하나의 스레드만 공유 자원에 접근할 수 있도록 제한하는 락(lock) 메커니즘이다.

  • 한 스레드가 lock을 건다. → 그 스레드가 자원 주인
  • 다른 스레드는 대기 상태로 전환, 락을 뺏지 못한다.
  • 해당 스레드가 unlock하기 전까지 접근 불가능하다.

2. 세마포어 (Semaphore)

공유 자원에 접근할 수 있는 스레드의 수를 제한하기 위한 카운터 기반 동기화 도구이다.

  • 초기 값을 5로 설정하면, 최대 5개 스레드까지 자원에 접근 가능
  • 5 → 4 → 3 → … 이런식으로 카운트가 된다.
  • count가 0이면 대기한다.
  • 자원에 접근하면 count—, 자원 사용 끝나면 count++ 해서 다음 스레드가 진입한다.

3. Volatile

변수의 값을 모든 스레드가 항상 메인 메모리에서 읽고 쓰도록 강제하는 동기화 수단이다.

즉, 단순한 읽기/쓰기 작업에 대해 ‘가시성’을 보장해준다.

멀티스레드 환경에서는 각 스레드가 자기 CPU 코어의 캐시에 있는 값을 사용할 수 있기 때문에,

다른 스레드가 변경한 값을 즉시 확인하지 못하는데 volatile 키워드는 이 문제를 해결해준다.

어떻게?

하드웨어 수준에서 캐시 무효화하면서 캐시 값이 없어진 스레드가 메인 메모리를 읽게 된다.

특성

  1. 가시성 → 한 스레드가 volatile 변수에 쓴 값 즉시 다른 스레드가 읽을 수 있음
  2. 재정렬 금지 → volatile 변수 앞뒤 명령어는 컴파일러와 CPU가 순서 못바꿈

재정렬이 무엇인가?

컴퓨터는 더 빠른 실행을 위해서 코드 순서를 바꾸기도 함.

그래서 개발자가 작성한 코드의 순서와 실제 실행 순서가 다를 수도 있음.

예시.
volatile int count = 0;
count = 1;  // 다른 스레드에서 이 값을 바로 볼 수 있음

volatile을 붙인 변수가 값을 변경하게 되면 다른 스레드에서도 항상 바로 확인할 수 있다.

count++;

하지만 volatile 키워드는 원시성을 보장하지 않기 때문에,
count++ 라는 복합연산(read → modify → write)은 동기화를 보장할 수 없다.

이런 경우는 AtomicInteger 사용해야 한다.

volatile은 가시성과 재정렬 방지는 보장하지만 원자성은 보장하지 않는다. 따라서 동기화를 부분적으로만 보장한다.

캐시 무효화란?

  1. CPU A, B가 같은 공유 변수를 각자 캐시에 저장하고 있다.
  2. A가 공유 변수를 수정한다.
  3. 이 사실을 모르는 B는 예전 값을 계속 사용할 수 있기 때문에, A가 브로드캐스트를 통해 캐시 무효화 메세지를 보낸다.
  4. B의 캐시가 무효화된다. → B는 해당 변수를 사용할 때, 메인 메모리를 새 값을 읽는다.
profile
기록하자

0개의 댓글