Home [OS] 상호 배제 알고리즘
Post
Cancel

[OS] 상호 배제 알고리즘

상호 배제 알고리즘


SW solutions

  1. Dekker’s algorithm
    • 두 개의 프로세스 간 상호 배제를 보장하는 최초의 알고리즘
    • Flag : 임계 구역에 들어가있는지 여부를 알려주는 변수
    • Turn : 어느 프로세스가 임계 영역에 들어가겠다고 요구할 권한이 있는지 알려주는 변수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 while(1) {               // 프로세스i의 진입 영역

  flag[i] = ture;        // 프로세스i가 임계구역에 진입하기 위해 진입을 알림

  while(flag[j]) {       // 프로세스j가 임계구역에 진입하려고 하는지 확인
    if (turn == j) {     // 프로세스j가 임계구역에 있다면
      flag[i] = flase;   // 프로세스i가 진입을 취소하고

      while (turn == j); // 프로세스i의 차례가 올 때까지 대기 함.

      flag[i] =ture;     // 차례가 넘어왔다면 진입을 알림.
    }
  } // Critical Section

  turn = j;              // 임계구역의 사용이 끝났다면 차례를 프로세스j에게 넘김.
  flag[i] = false;       // 진입을 취소하여 임계구역 사용완료를 알림.
}
  1. Peterson’s algorithm
    • 프로세스가 두 개일 때 상호 배제를 보장하는 알고리즘
1
2
3
4
5
6
7
8
9
10
11
12
while(1) {                        // 프로세스i의 진입 영역

  flag[i] = ture;                 // 프로세스i가 임계구역에 진입하기 위해 진입을 알림.
  turn = j;                       // 프로세스j에게 진입을 양보함.
                                  //  (두 프로세스중 먼저 양보한쪽이 먼저 임계구역에 들어가게 됨.)
  while (flag[j] && turn = j);    // 프로세스i의 차례가 될 때까지 대기를 함.

// critical section

  flag[i] = false                 // 임계구역 사용완료를 알림.

}
  1. Dijkstra’s algorithm
    • 프로세스 n개의 상호배제 문제를 해결한 최초의 알고리즘
    • flag[i] 변수 상태 idle : 프로세스가 임계 구역에 진입 시도를 하고 있지 않은 상태 want-in : 프로세스의 임계구역 진입 시도 1단계 in-CS : 프로세스의 임계구역 진입 시도 2단계 및 임계구역 내에 있을 때
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
while(1) {                                // 프로세스i의 진입 영역

	do {
	// 임계구역 진입시도 1단계
	  flag[i] = want-in                     // 1단계 진입시도를 한다고 알림
	  while (turn != i ) {                  // 자신의 차례가 될 때까지 대기를 함.
	    if (flag[turn] == idle) {           // 임계구역에 프로세스가 없다면,
	      turn = i;                         // 자신의 차례로 변경함.
	    }
	  }

	// 임계구역 진입시도 2단계
	  flag[i] = in-CS                      // 임계구역에 진입하겠다고 알림.
	  j = 0;
	  while ((j < n) && (j == i|| flag[j] != in-CS ){ // 자신을 제외한 in-CS 상태의 프로세스가 있는지 검사 함.
	    j = j + 1;
	  }
	} while(j < n)   // 자신 외에 2단계 진입을 시도하는 프로세스가 있다면 다시 1단계로 돌아감.

	// critical section   // in-CS 상태의 프로세스가 자신밖에 없다면 임계영역에 진입함.

	flag[i] = idle;  // 임계구역 사용완료를 알림.

}
  1. Knuth’s algorithm
    • 이전 알고리즘 관계 분석 후 일치하는 패턴을 찾아 패턴의 반복을 줄여 프로세스에 프로세서 할당
    • 무한정 연기할 가능성을 배제하는 해결책을 제시했으나, 프로세스들이 오래 기다려야 한다.
  2. Eisenberg and McGuire’s algorithm
  3. Lamport’salgorithm
    • 프로세스 n개의 상호 배제 문제를 해결한 알고리즘
    • 프로세스에게 고유한 번호를 부여하고, 번호를 기준으로 우선순위를 정하여 우선순위가 높은 프로세스(번호가 낮은 프로세스)가 먼저 임계 구역에 진입하도록 구현되었습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
while(1) {              // 프로세스i의 진입 영역

  choosing[i] = ture;   // 번호표 받을 준비
  number[i] = max(number[0], number[1], ..., number[n-1]) + 1;  // 번호표 부여
                                                                // (번호표 부여중 선점이 되어 같은 번호를 부여 받는 프로세스가 발생할 수 있음)
  chossing[i] = false;  // 번호표를 받음

  for (j = 0; j < n; j++) { // 모드 프로세스와 번호표를 비교함.
    while (choosing[j]);    // 프로세스j가 번호표를 받을 때까지 대기

    while ((number[j] != 0) &&
            ((number[j] < number[i])               // 프로세스 j가 프로세스 i보다 번호표가 작거나(우선순위가 높고)
            || (number[j] == number[i] && j < i)); // 또는 번호표가 같을 경우 j 가 i 보다 작다면
                                                   // 프로세스 j가 임계구역에서 나올 때까지 대기.
  }


// Critical Section

  number[i] = 0;  // 임계구역 사용완료를 알림.
}
소프트웨어 솔루션의 문제점
  • 속도가 느리고 구현이 복잡하다.
  • 수행 도중 preemption이 발생할 수 있으며, 운영체제가 이런 인터럽트를 막아준다고 하더라도 오버헤드가 발생한다.
  • busy waiting이 발생하여 비효율적이다.

HW solution

  1. TestAndSet(TAS) instruction
    • Test와 Set을 한번에 수행하는 기계어
      TAS를 이용한 상호배제 구현
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      boolean TestAndSet(boolean &target) {
       boolean rv = target;
       target = true;
       return rv;
      }
      // lock의 초기값은 false, 처음 실행한 프로세스는 첫 반복문 (while)을 통과
      do {
       while(TestAndSet(lock)); // lock을 걸어줌, lock이 풀릴떄까지 다른 프로세스는 CS에 접근 불가
         // critical section
       lock = false;
         // remainder section
      }
      
N개의 프로세스에서 상호배제 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
boolean lock = false;

do {
    waiting[i] = true; // 프로세스 i를 대기열에 넣음.
    key = true;
    while (waiting[i] && key) // 대기가 풀리거나 lock이 풀릴 때까지 대기함
        key = TestAndSet(&lock); // lock 값을 key에 반환

    waiting[i] = false; // CS에 접근할 수 있으므로 대기를 풀어줌.

    // Critical Section
    j = (i + 1) % n;
    while((j != i) && !waiting[j]) // 대기 중인 프로세스를 찾음
        j = (j + 1) % n;

    if(j == i) // 대기 중인 프로세스가 없으면
        lock = false; // 다른 프로세스의 진입을 허용할 수 있게 lock을 풀어줌.
    else // 대기 프로세스가 있으면 다음 순서로 임계 영역에 진입하게 함.
        waiting[j] = false; // 임계 영역에 진입할 수 있도록 대기를 품.
} while (true);
하드웨어 솔루션의 문제점
  • 하드웨어가 한번에 수행되는 것을 보장해 주기 때문에 소프트웨어 솔루션에 비해 구현이 간단하지만 Busy waiting 문제가 있어 비효율적이다.

OS supported SW solution

  1. Spinlock
    • 임계 구역에 진입이 불가능할 때 진입이 가능할 때까지 루프르르 돌면서 재시도하는 방식으로 구현된 락을 가리킨다.
    • 운영체제의 스케줄링 지원을 받지 않기 때문에, 해당 스레드에 대한 문맥 교환이 일어나지 않는다. 멀티 프로세서 시스템에서만 사용할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 자원이 없다면 while 루프를 돌면서 기다리는 방식(busy-waiting)
wait(S) {
  while (S <= 0);  // 자원이 없다면 while 루프를 돌며 대기를 함.

  S--;  // 자원을 획득함.
}

signal(S) {
  S++;  // 자원을 해제함.
}

// 자원이 없다면 blocked 상태에서 기다리는 방식
typedef struct
{
  int value;            /* semaphore */
  struct process *list; /* process wait queue */
} semaphore;

wait(semaphore *S) {
  S->value--;
  if (S->value < 0 ) {              // 자원이 없다면
    add this process to S->list;    // 프로세스를 큐에 넣고
    block();                        // block 시킴
  }
}

signal(semaphore *S) {
  S->value++;
  if (S->value <= 0) {               // 자원이 0이하라면 block중인 프로세스가 있다는 의미임.
    remove a process P from S->list; // 대기하고 있는 프로세스를 가져옴.
    wakeup(P);                       // 가져온 프로세스를 깨움.
  }
}
  1. Semaphore
    • Busy waiting 문제를 해결함
    • 이진 세마포어
      • 0 또는 1 값만 가질 수 있는 세마포어
      • 임계 구역 문제를 해결하는데 사용하며 자원이 하나이기 때문에 뮤텍스로도 사용할 수 있다.
    • 개수 세마포어
      • 도메인이 0이상인 임의의 정수값인 세마포어
      • 여러개의 자원을 가질 수 있으며 제한된 자원을 가지고 액세스 작업을 할 때 사용한다.
  2. Eventcount/sequencer
    • 은행의 번호표와 비슷한 개념
    • Sequencer
      • 정수형 변수
      • 생성 시 0으로 초기화, 감소하지 않음
      • 발생 사건들의 순서 유지
      • ticket() 연산으로만 접근 가능
    • ticket(S)
      • 현재까지 ticket() 연산이 호출 된 횟수를 반환
    • eventcount
      • 정수형 변수
      • 생성시 0으로 초기화, 감소하지 않음
      • 특성 사건의 발생 횟수를 기록
      • read(E), advance(E), await(E, v) 연산으로만 접근 가능
    • read(E)
      • 현재 Eventcount 값 반환
    • advance(E)
      • E <- E + 1
      • E를 기다리고 있는 프로세스를 깨움(wake-up)
    • await(E, v)
      • V는 정수형 변수
      • if(E < v)이면 E에 연결된 Qe에 프로세스 전달(push) 및 CPU scheduler 호출
  • No busy waiting
  • No starvation
  • Semaphore보다 더 low-level control이 가능 (순서를 컨트롤 할 수 있음)

Language-Level solution

  1. Monitor
    • 공유 데이터와 Critical section 의 집합
    • 한 번에 하나의 프로세스만 모니터 개체 내에 프로시저를 사용할 수 있습니다. 모니터에 접근하지 못한 프로세스는 entry queue에서 대기하게 된다. (상호배제)
    • 모니터 개체 내 공유 자원은 직접 접근할 수 없으며 프로시저를 통해서만 가능합니다. (정보은닉)
This post is licensed under CC BY 4.0 by the author.