운영체제#4 동기화

성찬홍·5일 전

Computer Science

목록 보기
14/17

1. 동기화

동시다발적으로 실행되는 프로세스들은 서로 협력하며 영향을 주고 받는다 .
이 과정에서 자원의 일관성을 보장해야한다.
→ 프로세스나 스레드는 동시에 실행되면서 공유 자원(변수, 파일, 메모리 등) 을 사용한다.

  • 공동의 목적을 위해 동시에 수행되는 프로세스이다.
  • 여러 프로세스나 스레드가 공유 자원에 접근할 때 서로 간섭하지 않도록 실행 순서를 조정하는 것을 의미한다.
  • 쉽게 말해 공유 데이터를 안전하게 사용하기 위한 규칙이라고 보면 된다.

& 프로세스들의 수행 시기를 맞추는 것

  • 실행 순서 제어 : 프로세스를 올바른 순서대로 실행하기
  • 상호 배제: 동시에 접근해서는 안 되는 자원에 하나의 프로세스만 접근하게 하기

실행 순서 제어를 위한 동기화

: “스레드 또는 프로세스가 특정 순서대로 실행되도록 강제하는 기술”을 의미한다.

Ex )
Writer : Book.tsx 파일에 값을 저장하는 프로세스
Reader : Book.tsx 읽는 프로세스
→ Reader와 Writer 프로세스는 무작정 아무렇게나 실행되어선 안된다.
→ 실행의 순서가 있기 때문이다.
Reader 프로세스는 ‘Book.tsx 안에 값이 존재한다’는 특정 조건이 만족되어야만 실행이 가능하다.

상호 배제를 위한 동기화

  • 공유가 불가능한 자원의 동시 사용을 피하기 위한 동기화
  • 한 번에 하나의 프로세스만 접근해여 하는 자원에 동시 접근을 피하기 위한 동기화

Ex ) 문제가 생기는 경우 예시

공유자원

: 여러 프로세스 혹은 스레드가 공유하는 자원

→ 전역 변수 , 파일, 입출력장치 , 보조기억장치 등등

임계 구역

: 동시에 실행되면 문제가 발생하는 자원에 접근하는 코드 영역

→ 여러 스레드가 동시에 접근하면 문제가 발생하는 코드 영역

→ 반드시 한 번에 하나의 스레드만 실행해야 한다.

→ 앞선 예시의 ‘총합’ 변수 , ‘잔액’변수 ,등등

→ 임계 구역에 진입하고자 하면 진입한 프로세스 이외에는 대기해야 한다.

→ 임계 구역에 동시에 접근하면 자원의 일관성이 깨질 수 있다

→ 이를 레이스 컨디션이라고 한다.

운영체제가 임계구역 문제를 해결하는 세 가지 원칙

  • 상호 배제
    • 한 프로세스가 임계 구역에 진입했다면 다른 프로세스는 들어올 수 없다.
  • 진행
    • 임계 구역에 어떤 프로세스도 진입하지 않았다면 진입하고자 하는 프로세스는 들어갈 수 있어야 한다.
  • 유한 대기
    • 한 프로세스가 임계 구역에 진입하고 싶다면 언젠가는 임계 구역에 들어올 수 있어야 한다.
      ( 임계 구역에 들어오기 위해 무한전 대기해서는 안 된다. )

동기화 기법

뮤텍스 락

: 상호 배제를 위한 동기화 도구(자물쇠 역할) , 뮤텍스 락

→ 공유 자원을 동시에 접근하는 스레드들 사이의 충돌을 막기 위해 사용되는 동기화 도구

오직 하나의 스레드만 접근할 수 있도록 보장하는 기법

→ 전역 변수 하나 , 함수 두개

  • 자물쇠 역할 : 프로세스들이 공유하는 전역 변수 lock
  • 임계 구역을 잠그는 역할 : acquire 함수
  • 임계 구역의 잠금을 해제하는 역할 : release 함수

acquire 함수

  • 프로세스가 임계 구역에 진입하기 전에 호출
  • 임계 구역이 잠겨 있다면
    • 임계 구역이 열릴 때까지( lock이 false가 될 때까지 ) 임계 구역을 반복적으로 확인
  • 임계 구역이 열려 있다면
    • 임계 구역을 잠그기 ( lock을 true로 바꾹 )

release 함수

  • 임계 구역에서의 작업이 끝나고 호출

  • 현재 잠긴 임계 구역을 열기( lock을 false로 바꾸기 )

  • 바쁜 대기

while(lock == true) /* 만약 임계 구역이 잠겨 있다면 */
		;               /* 임계 구역이 잠겨 있는지를 반복적으로 확인 */
     Thread A                    Thread B
   -------------              ---------------
   lock(mutex)     ----lock(mutex)  (대기)
   임계구역 실행                 (기다림)
   unlock(mutex)   ----→      lock 획득 → 임계구역 실행

세마포어

→ 정수 변수(S)로 표현되는 동기화 기법이며, wait(P)와 signal(V) 연산으로 자원 접근을 제어한다.

  • 좀 더 일반화된 방식의 도기화 도구
  • 공유 자원이 여러 개 있는 경우에도 적용 가능
  • 임계 구역 앞에서 멈춤 신호를 받으면 잠시 기다리기
  • 임계 구역 앞에서 가도 좋다는 신호를 받으면 임계 구역 진입

→ 매번 임계구역 앞듸로 wait(),signal() 호출하기엔 사용자 입장에선 불편함.

세마포어의 종류

(1) 이진 세마포어(Binary Semaphore)

  • 값: 0 또는 1
  • 사실상 뮤텍스와 유사한 상호 배제 역할
  • 단, 소유권이 없음 → 아무 스레드나 signal 가능 → 뮤텍스와의 중요한 차이점!

(2) 카운팅 세마포어(Counting Semaphore)

  • 값: 0 이상 N
  • 자원이 여러 개 있을 때 사용 예: DB connection pool, 주차장, 스레드풀 등 → N개의 자원을 동시에 최대 N명이 사용

  • 전역변수 S : 임계 구역에 진입할 수 있는 프로세스의 개수를 나타내는 변수
  • wait() : 자원 사용 요청
    • 임계 구역에 들어가도 좋은지 , 기다려야할지 알려주는 함수
    • S > 0이면 S를 하나 줄이고 입장
    • S = 0이면 자원이 없으므로 대기(block)
  • signal() : 자원 사용 완료
    • 임계구역 앞에서 기다리는 프로세스에 ‘이제 가도 좋다’를 알려주는 함수

ex) 레스토랑에 테이블이 3개 있다면

S = 3  (테이블 3)

손님1wait()S=2 → 식사 시작  
손님2wait()S=1 → 식사 시작  
손님3wait()S=0 → 식사 시작  
손님4wait()S=0 → 테이블 없음 → 대기

손님1이 떠나면

signal()S=1 → 대기 중 손님4가 식사 시작

코드 예시

class Semaphore {
  constructor(max) {
    this.max = max;           // 동시에 실행 가능한 개수
    this.current = 0;         // 현재 실행 중인 작업 수
    this.queue = [];          // 대기 중인 resolve 함수들
  }

  async acquire() {
    if (this.current < this.max) {
      this.current++;
      return Promise.resolve();
    }

    // 대기 상태 → resolve가 push되어, signal(=release) 때 호출됨
    return new Promise(resolve => this.queue.push(resolve));
  }

  release() {
    // 대기 중인 작업이 있다면 깨워주기
    if (this.queue.length > 0) {
      const resolve = this.queue.shift();
      resolve();
    } else {
      this.current--;
    }
  }
}

사용

const semaphore = new Semaphore(2);

async function task(name, delay) {
  await semaphore.acquire();  // wait
  
  console.log(`${name} 시작`);
  await new Promise(r => setTimeout(r, delay));
  console.log(`${name} 종료`);

  semaphore.release();        // signal
}

async function run() {
  task("A", 2000);
  task("B", 1000);
  task("C", 500);
  task("D", 800);
}

run();

모니터

: 상호 배제를 위한 동기화와 실행순서 제어를 위한 동기화 2개를 모두 제공한다.

상호 배제를 위한 동기화

  • 인터페이스를 위한 큐
  • 공유자원에 접근하고자 하는 프로세스를 (인터페이스를 위한) 큐에 삽입
  • 큐에 삽입된 순서대로 ( 한 번에 하나의 프로세스만 ) 공유자원을 이용

정리

동기화란 여러 프로세스나 스레드가 동시에 실행되며 공유 자원(변수, 파일, 메모리 등) 을 사용할 때, 서로 간섭하지 않도록 실행 순서와 접근 방식을 조정하는 것을 의미한다. 이를 통해 데이터의 일관성을 보장하며, 실행 순서를 강제하거나(예: Reader–Writer 문제) 동시에 접근해서는 안 되는 자원에 한 번에 하나의 프로세스만 접근하도록 제한하는 상호 배제(Mutual Exclusion) 를 달성한다. 특히 임계 구역(critical section)에서 발생할 수 있는 레이스 컨디션을 방지하기 위해 운영체제는 상호 배제, 진행, 유한 대기라는 세 가지 원칙을 통해 임계구역 문제를 해결한다.

이러한 동기화를 구현하기 위해 다양한 기법이 사용되는데, 대표적으로 뮤텍스(Mutex) 와 세마포어(Semaphore) 가 있다. 뮤텍스는 단 하나의 스레드만 자원에 접근할 수 있도록 잠금(lock)을 제공하는 반면, 세마포어는 정수 값을 사용해 여러 개의 자원을 동시에 관리할 수 있어 더 일반적인 형태의 동기화 도구이다. 세마포어는 이진(Binary) 형태로 뮤텍스와 유사하게 사용되거나, 카운팅 형태로 자원이 여러 개일 때 wait/signal 연산을 통해 접근을 제어한다. 또한 모니터(Monitor) 는 상호 배제와 실행 순서 제어 기능을 모두 제공하는 고수준 동기화 도구로, 내부 큐를 통해 공유 자원 접근을 자동으로 관리하여 보다 안전하고 간편한 동기화 환경을 제공한다.

참고

https://www.inflearn.com/course/%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EC%BB%B4%ED%93%A8%ED%84%B0%EA%B5%AC%EC%A1%B0-%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C/dashboard

profile
꾸준한 개발자

0개의 댓글