[운영체제] 프로세스 동기화

ideal dev·2022년 12월 24일
0

운영체제

목록 보기
8/9

운영체제에서 가장 중요한 건 프로세스 관리이다.
프로세스 관리

  • CPU 스케줄링 : Ready Queue 에 있는 프로그램 중 어떤 것을 선택할 것인 지
  • 프로세스 동기화 (thread) : content switching이 되는 기본 단위

프로세스 동기화

Cooperating Process

현대 컴퓨터의 메모리에는 여러 프로세스가 존재하는데,

  • 한 프로세스가 다른 프로세스에게 영향을 받거나 주는 프로세스를 Cooperating Process
  • 반대로 아무런 영향을 미치지 않는 독립적인 프로세스는 Independent Process
    -> 현대 컴퓨터 환경에는 Cooperating Process이 많이 존재하고, 이들은 서로 영향을 미치기 때문에 데이터나 흐름에 대한 동기화(Synchronization) 가 매우 중요

Process Synchronization (Thread synchronization)

  • Cooperating 시스템 : 공통된 자원을 공유하기 때문에, 영향을 주고 받음
  • 프로세스간 통신의 예) 전자우편, 파일전송 등
  • 프로세스간 자원 공유의 예) 메모리 상의 자료들 , DB, 하나의 서버에 여러개의 프로세스가 있는 경우(명절 기차표 예약, 대학 온라인 수강신청, 실시간 주식거래 등)
    하나의 기차 자리를 여러명이 예약해서는 안 됨 !

‼️ 문제점

  • 동시에 접근하기 때문에 데이터가 불확실해질 수 있음.
    -> 여러 프로세스가 공유하는 자원의 일관성을 유지해야 함.
    -> 여러 프로세스가 동시에 하나의 공유된 자원에 접근하려고 할 때 이 프로세스들의 순서를 정하여 데이터의 일관성을 유지시켜주어야 함

은행 계좌 문제 예시

  • 하나의 계좌에 입금, 출금을 한다
  • 계좌 : 공유하는 자원, 입금.출금 : 각각 프로세스
    자바코드
class Test {
	public static void main(String[] args) throws InterruptedException {
		BankAccount b = new BankAccount();
		Parent p = new Parent(b);
		Child c = new Child(b);
		p.start();   // start(): 쓰레드를 실행하는 메서드
		c.start();
		p.join();    // join(): 쓰레드가 끝나기를 기다리는 메서드
		c.join();
		System.out.println("balance = " + b.getBalance());
	}
}

// 계좌
class BankAccount {
	int balance;
	void deposit(int amount) {
		balance = balance + amount;
	}
	void withdraw(int amount) {
		balance = balance - amount;
	}
	int getBalance() {
		return balance;
	}
}

// 입금 프로세스
class Parent extends Thread {
	BankAccount b;
	Parent(BankAccount b) {
		this.b = b;
	}
	public void run() {   // run(): 쓰레드가 실제로 동작하는 부분(치환)
		for (int i = 0; i < 100; i++)
		  b.deposit(1000);
	}
}

// 출금 프로세스
class Child extends Thread {
	BankAccount b;
	Child(BankAccount b) {
		this.b = b;
	}
	public void run() {
		for (int i = 0; i < 100; i++)
		  b.withdraw(1000);
	}
}

이를 실행하면 1000원을 100번 입금하고(입금쓰레드), 1000원을 100번 출금하기에 (출금쓰레드)
balance = 0 의 결과값을 얻을 수 있음.

하지만,2개의 쓰레드에 시간 지연을 시켜본다면?

// 계좌
class BankAccount {
	int balance;
	void deposit(int amount) {
		int temp = balance + amount;
		System.out.print("+");
		balance = temp;
	}
	void withdraw(int amount) {
		int temp = balance - amount;
		System.out.print("-");
		balance = temp;
	}
	int getBalance() {
		return balance;
	}
}

이를 실행하면

+++++++---------++++-----------------------------++++++++++++++++++++----------------+++++++++++++++++++++++++++++++++++++++++----------------------------------------------++++++++++++++++++++++++++++

balance = 100000 의 값이 나옴.

  • 약간의 시간 지연을 준 것만으로도 여러 쓰레드가 하나의 공유 자원을 사용하는 프로그램은 망가지게 됨
  • 원인 : 2개 이상의 복수 쓰레드가 공통적으로 사용하는 변수에 대한 동시 업데이트를 하는 경우

은행 계좌 문제에서의 공통변수 balance

balance = balance + amount;   // 입금
balance = balance - amount;   // 출금

임계구역 문제

  • 공통변수를 변경하는 코드 영역
  • 은행계좌 문제에서의 임계 구역
void deposit(int amount) {
  balance = balance + amount;
}
void withdraw(int amount) {
  balance = balance - amount;
}

해결하기 위한 3가지 조건

  • Mutual exclusion(상호배타): 오직 한 쓰레드만이 진입 가능, 한 쓰레드가 임계구역에서 수행 중인 상태에서는 다른 쓰레드는 절대 이 구역에 접근할 수 없음
  • Progress(진행): 한 임계구역에 접근하는 쓰레드를 결정하는 것은 유한 시간 이내에 이루어져야함
  • Bounded waiting(유한대기): 임계구역으로 진입하기 위해 대기하는 모든 쓰레드는 유한 시간 이내에 해당 임계구역으로 진입할 수 있어야 함.

프로세스/쓰레드 동기화 목적

원하는 결과값을 도출하도록 임계구역 문제를 해결
프로세스의 실행 순서를 원하는대로 제어
Busy wait 등과 같은 비효율성을 제거

세마포

  • 동기화 문제 해결을 위한 소프트웨어 도구
  • 구조 : 정수형 변수 + 두 개의 동작 (P,V) P,V는 네덜란드어에서 따온 약자

-> P: Proberen (test) → acquire()
-> V: Verhogen (increment) → release()

  • 자바 코드를 통해 나타낸 세마포 구조
class Semaphore {
  int value;      // number of permits
  Semaphore(int value) {
    // ...
  }
  void acquire() {
    value--;
    if (value < 0) {
      // add this process/thread to list
      // block
    }
  }
  void release() {
    value++;
    if (value <= 0) {
      // remove a process P from list
      // wakeup P
    }
  }
}

acquire()

  • value값을 감소시키고, 만약 value값이 0보다 작으면 이미 해당 임계구역에 어느 프로세스가 존재한다는 의미로 현재 프로세스는 접근을 하지 못하도록 막음
  • 이를 List라는 기다리는 줄에 추가한 뒤 block을 걸어줌

release()

  • value값을 증가시키고, 만약 value값이 0보다 같거나 작으면 임계구역에 진입하려고 대기하는 프로세스가 list에 남아있다는 의미이므로 그 중에서 하나를 꺼내 임계구역을 수행
  • Mutual exclusion을 위해 사용

은행 계좌 문제에 세마포 적용

import java.util.concurrent.Semaphore;  // 세마포를 사용하기 위해 파일 가장 위에 추가해야 한다.

class BankAccount {
	int balance;

	Semaphore sem;
	BankAccount() {   // BankAccount 클래스의 생성자가 호출되면 세마포를 만든다.
		sem = new Semaphore(1);  // value 값을 1로 초기화한다.
	}

	void deposit(int amount) {
		try {
			sem.acquire();   // 임계구역에 들어가기를 요청한다.
		} catch (InterruptedException e) {}
	    /* 임계 구역 */  
		int temp = balance + amount;
		System.out.print("+");
		balance = temp;

		sem.release();   // 임계구역에서 나간다.
	}
	void withdraw(int amount) {
		try {
			sem.acquire();
		} catch (InterruptedException e) {}
	    /* 임계 구역 */  
		int temp = balance - amount;
		System.out.print("-");
		balance = temp;

		sem.release();
	}
	int getBalance() {
		return balance;
	}
}

세마포의 value값을 1로 설정해두어, 패런트가 공통변수값을 계산할 때 value=0, 끝나면 value = 1

패런트가 계산 중일 때, 차일드가 들어오려해도

임계구역의 문제를 해결하였으므로, 이를 실행하면 balance = 0 의 결과값을 얻을 수 있다.

Ordering

  • 세마포는 mutual exclusion뿐 아니라 ordering을 하기 위해서도 사용한다. 즉, 프로세스의 실행 순서를 원하는 순서로 설정

참고
강의 : http://www.kocw.net/home/search/kemView.do?kemId=978503
정리 : https://velog.io/@codemcd/운영체제OS-8.-프로세스-동기화-1

0개의 댓글