피터슨의 알고리즘
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
피터슨의 알고리즘은 두 프로세스 간의 상호 배제를 구현하기 위해 `flag`와 `turn` 변수를 사용하는 알고리즘이다. 각 프로세스는 임계 구역에 진입하기 전에 자신의 `flag` 값을 true로 설정하고 `turn` 값을 상대방 프로세스에게 양보하며, 상대방 프로세스의 `flag` 값이 false이거나 `turn` 값이 자신의 차례가 될 때까지 대기한다. 이 알고리즘은 상호 배제, 진행, 한정된 대기 조건을 모두 만족하며, C 언어로 구현할 수 있다. 필터 알고리즘은 피터슨 알고리즘을 N개 이상의 프로세스에 적용할 수 있도록 일반화한 것으로, 각 프로세스의 레벨을 나타내는 `level` 변수와 각 레벨에서 마지막으로 들어온 프로세스를 추적하는 `last_to_enter` 변수를 사용한다. 최신 CPU 환경에서는 메모리 접근 순서를 보장하기 위해 메모리 장벽 명령을 사용해야 하며, 원자 연산을 지원하는 특수 명령어를 활용하여 더 효율적인 동기화를 구현할 수 있다.
더 읽어볼만한 페이지
- 동시성 제어 알고리즘 - 데커의 알고리즘
데커 알고리즘은 두 프로세스 간 상호 배제를 해결하기 위해 만들어진 최초의 소프트웨어 기반 알고리즘 중 하나로, 공유 자원에 대한 동시 접근을 막고 임계 영역 진입 우선순위를 조정하지만, 현대 운영체제에서는 효율성 문제로 잘 사용되지 않으나 상호 배제 개념 이해에 기여한다. - 동시성 제어 알고리즘 - 스핀락
스핀락은 락 획득을 위해 반복적으로 확인하며 대기하는 동기화 메커니즘으로, 성능 향상을 위한 최적화 기법과 하이퍼 스레딩 환경에서의 성능 향상 기법이 존재하지만, CPU 자원 낭비라는 단점으로 인해 다양한 대안 방식이 모색되고 있다.
피터슨의 알고리즘 | |
---|---|
기본 정보 | |
이름 | 피터슨 알고리즘 |
종류 | 동시성 제어 |
문제 영역 | 상호 배제 |
고안자 | 개리 L. 피터슨 |
발표 년도 | 1981년 |
세부 사항 | |
요구 사항 | 두 개의 프로세스만 해당 공유 메모리에 대한 읽기 및 쓰기 작업은 원자적이어야 함 |
복잡도 | O(1) |
특징 | 간단하고 효율적 교착 상태 및 기아 상태 방지 |
구현 | |
사용 언어 | C C++ Java Python C# |
2. 알고리즘
피터슨 알고리즘은 두 프로세스 간의 상호 배제를 구현하기 위해 `flag`와 `turn` 두 변수를 사용한다. `flag[n]`의 값이 `true`이면 프로세스 `n`이 임계 구역에 진입하려 함을 나타낸다. 프로세스 P0은 P1이 임계 구역에 진입하려 하지 않거나(flag[1] == false), P1이 `turn`을 `0`으로 설정하여 P0에게 우선순위를 부여한 경우에 임계 구역에 진입할 수 있다.[1]
이 알고리즘은 선점형 환경에서도 작동하며, 임계 구역 문제 해결에 필요한 세 가지 필수 조건(상호 배제, 진행 조건, 한계 대기)을 충족한다.[1][3]
`turn`은 0 또는 1의 값을 가질 수 있으므로, 단일 비트로 표현할 수 있다. 즉, 이 알고리즘은 3비트의 메모리만 필요로 한다.[4]
2. 1. 변수 설명
`flag`는 각 프로세스가 임계 구역에 진입할 의사가 있는지 나타내는 불린 배열이다. `flag[n]`이 `true`이면 프로세스 `n`이 임계 구역에 진입하려 함을 의미한다. `turn`은 임계 구역에 진입할 차례를 나타내는 정수형 변수이다. `turn` 값은 현재 우선순위를 가진 프로세스의 식별자를 나타낸다.[1]2. 2. 작동 원리
피터슨의 알고리즘은 `flag`와 `turn`이라는 두 변수를 사용한다. `flag[n]`의 값이 `true`이면 프로세스 `n`이 임계 구역에 진입하려 함을 나타낸다. 프로세스 P0은 P1이 임계 구역에 진입하려 하지 않거나, P1이 `turn`을 `0`으로 설정하여 P0에게 우선순위를 부여한 경우에 임계 구역에 진입할 수 있다.[1]P0 | P1 |
---|---|
각 프로세스는 임계 구역에 진입하기 전에 자신의 `flag` 값을 `true`로 설정하고, `turn` 값을 상대방 프로세스에게 양보한다. 프로세스는 `while` 루프를 통해 상대방 프로세스의 `flag` 값이 `false`이거나 `turn` 값이 자신의 차례가 될 때까지 대기한다. 임계 구역 작업을 마친 프로세스는 자신의 `flag` 값을 `false`로 설정하여 다른 프로세스가 임계 구역에 진입할 수 있도록 한다.
`turn`은 두 가지 값 중 하나를 가질 수 있으므로, 단일 비트로 대체될 수 있으며, 이는 알고리즘이 3비트의 메모리만 필요로 함을 의미한다.[4]
2. 3. 의사 코드
flag와 turn이라는 두 변수를 사용하는 피터슨 알고리즘의 의사 코드는 다음과 같다.`flag[n]`의 값이 `true`이면, 프로세스 `n`이 임계 구역에 진입하려 함을 나타낸다. 프로세스 P0은 P1이 임계 구역에 진입하려 하지 않거나, P1이 `turn`을 `0`으로 설정하여 P0에게 우선순위를 부여한 경우에 임계 구역에 진입할 수 있다.[1]
P0 | P1 |
---|---|
이 알고리즘은 선점형 멀티태스킹 환경에서도 작동하며, 상호 배제, 진행, 제한된 대기의 세 가지 조건을 만족한다.[3]
`turn` 변수는 0 또는 1의 값을 가지므로, 단일 비트로 표현할 수 있다. 즉, 이 알고리즘은 3비트의 메모리만 필요로 한다.[4]
2. 4. 필수 조건 충족
이 알고리즘은 임계 구역 문제 해결에 필요한 세 가지 조건을 충족한다.[1] 세 가지 조건은 상호 배제, 진행, 한정된 대기이다.[3]상호 배제 (Mutual exclusion)P0과 P1은 동시에 임계 구역에 들어갈 수 없다. 만약 P0이 임계 구역에 있다면, `flag[0]`은 참이다. 또한, 다음 조건 중 하나를 만족해야 한다.[5]
- `flag[1]`이 `false` (P1이 임계 구역을 벗어났음을 의미)
- `turn`이 `0` (P1이 막 임계 구역에 진입하려 하지만, 기다리고 있음을 의미)
- P1이 `P1_gate` 레이블에 있음 ( `flag[1]`을 `true`로 설정한 후 `turn`을 `0`으로 설정하기 전, 즉 바쁜 대기를 하려는 시점)
따라서 두 프로세스 모두 임계 구역에 있다면, `flag[0]`과 `flag[1]`이 모두 참이고, `turn`이 `0`인 동시에 `1`이어야 한다. 이는 불가능하므로, 두 프로세스가 동시에 임계 구역에 있을 수 없다.
진행 (Progress)임계 구역에서 실행 중인 프로세스가 없고, 일부 프로세스가 임계 구역에 들어가기를 원한다면, 나머지 구역에서 실행 중이지 않은 프로세스만 다음에 어떤 프로세스가 임계 구역에 들어갈지 결정하는 데 참여할 수 있다. 이 선택은 무기한 연기될 수 없다.[3] 다른 프로세스가 임계 구역에 들어가고 싶다는 플래그를 설정한 경우, 프로세스는 즉시 임계 구역에 다시 들어갈 수 없다.
한정된 대기 (Bounded waiting)제한된 우회라고도 하며, 임계 구역에 진입하려는 의사를 표시한 후 다른 프로세스에 의해 프로세스가 우회되는 횟수가 제한됨을 의미한다. 피터슨의 알고리즘에서 프로세스는 임계 구역에 진입하기 위해 한 번의 턴 이상을 기다리지 않는다.
3. C 언어 구현 예시 (POSIX 스레드)
c
/*
- 피터슨 알고리즘의 ANSI C89 소스, KNF 스타일 구현.
- 저작권 (c) 2005, 매튜 몬도르
- 퍼블릭 도메인으로 릴리스 (GFDL로 라이선스 될 수 있음).
- 필요에 따라 버그를 수정하고 KNF 스타일과 이 주석을 유지하십시오.
- 코드를 원하는 대로 사용할 수 없는 경우 불편하다고 간주됩니다.
- /
#include
#include
#include
#include
struct pa_desc {
volatile int *f0, *f1;
int last;
};
void pa_init(void);
void pa_desc_init(struct pa_desc *, int);
void pa_desc_lock(struct pa_desc *);
void pa_desc_unlock(struct pa_desc *);
void *thread0_main(void *);
void *thread1_main(void *);
void threadx_critical(void);
int main(int, char **);
static volatile int pa_f0, pa_f1, pa_last;
/* 공유 */
#define BUCKETS 100
int gi, g[BUCKETS];
/*
- pa 시스템을 초기화합니다. pa 디스크립터를 초기화하기 전에 프로세스에서 한 번 호출해야 합니다.
- /
void
pa_init(void)
{
pa_f0 = pa_f1 = pa_last = 0;
}
/*
- 스레드에서 사용할 pa 디스크립터를 초기화합니다.
- 한 스레드는 id 0을 사용하고 다른 스레드는 id 1을 사용해야 합니다.
- /
void
pa_desc_init(struct pa_desc *d, int id)
{
assert(id == 0 || id == 1);
if (id == 0) {
d->f0 = &pa_f0;
d->f1 = &pa_f1;
d->last = 1;
} else if (id == 1) {
d->f0 = &pa_f1;
d->f1 = &pa_f0;
d->last = 0;
}
}
void
pa_desc_lock(struct pa_desc *d)
{
for (*d->f0 = 1, pa_last = d->last;
- d->f1 == 1 && pa_last == d->last; ) ;
}
void
pa_desc_unlock(struct pa_desc *d)
{
- d->f0 = 0;
}
/*
- 첫 번째 동시 스레드의 메인 루프
- /
/* ARGSUSED */
void *
thread0_main(void *args)
{
struct pa_desc d;
pa_desc_init(&d, 0);
for (;;) {
pa_desc_lock(&d);
threadx_critical();
pa_desc_unlock(&d);
}
/* NOTREACHED */
return NULL;
}
/*
- 두 번째 동시 스레드의 메인 루프
- /
/* ARGSUSED */
void *
thread1_main(void *args)
{
struct pa_desc d;
pa_desc_init(&d, 1);
for (;;) {
pa_desc_lock(&d);
threadx_critical();
pa_desc_unlock(&d);
}
/* NOTREACHED */
return NULL;
}
/*
- 두 스레드가 잠금 없이 실행하면 일반적으로 동시성 문제가 발생하는 중요한 함수.
- /
void
threadx_critical(void)
{
int i;
/* 일반적으로 이중 동시성 문제가 발생하는 작업을 수행합니다. */
for (i = 0; i < BUCKETS; i++)
g[i] = gi++;
for (i = 0; i < BUCKETS; i++)
(void) printf("g[%d] = %d\n", i, g[i]);
}
/*
- 피터슨 알고리즘을 사용하여 잠금 없이 이중 동시성을 달성할 수 있음을 보여주는 테스트 프로그램입니다. 두 개의 동시 스레드를 실행하여 공유 메모리 영역(gi, g[BUCKETS])에서 동시성에 민감한 작업을 자발적으로 수행합니다.
- /
/* ARGSUSED */
int
main(int argc, char **argv)
{
pthread_t tid1, tid2;
pthread_attr_t tattr;
gi = 0;
pa_init();
pthread_attr_init(&tattr);
pthread_attr_setdetachstate(&tattr, 0);
/* 참고: 실제 응용 프로그램은 오류 검사를 수행합니다. */
pthread_create(&tid1, &tattr, thread0_main, NULL);
pthread_create(&tid2, &tattr, thread1_main, NULL);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
/* NOTREACHED */
return EXIT_SUCCESS;
}
4. 필터 알고리즘 (N개 프로세스)
필터 알고리즘은 피터슨의 알고리즘을 2개 이상의 프로세스(N > 2)에 대해 일반화한 것이다.[6] 이 알고리즘에서는 부울 플래그 대신 프로세스마다 정수 변수를 사용하며, 이는 단일 작성자/다중 읽기(SWMR) 원자 레지스터에 저장된다. N-1개의 추가 변수 또한 유사한 레지스터에 필요하며, 배열로 표현될 수 있다.
4. 1. 변수 설명
`level`은 각 프로세스의 현재 레벨을 나타내는 N개의 정수 배열이다. `last_to_enter`는 각 레벨에서 마지막으로 들어온 프로세스를 나타내는 N-1개의 정수 배열이다.[6]4. 2. 작동 원리
필터 알고리즘은 피터슨의 알고리즘을 2개 이상의 프로세스()에 대해 일반화한 것이다.[6] 부울 플래그 대신, 프로세스당 정수 변수가 필요하며, 이는 단일-작성자/다중-읽기(SWMR) 원자 레지스터에 저장된다. 또한, 개의 추가 변수가 유사한 레지스터에 필요하다. 레지스터는 배열로 표현될 수 있다.```
level: N개의 정수 배열
last_to_enter: N − 1개의 정수 배열
```
변수는 최대 값을 가지며, 각 변수는 임계 구역 앞에 있는 별개의 "대기실"을 나타낸다. 프로세스는 한 방에서 다음 방으로 진행하며, 임계 구역인 방 에서 완료된다. 구체적으로, 잠금을 획득하기 위해 프로세스 는 다음을 실행한다.
```
i ← 프로세스 번호
'''for''' ℓ '''from''' 0 '''to''' N − 1 '''exclusive'''
level[i] ← ℓ
last_to_enter[ℓ] ← i
'''while''' last_to_enter[ℓ] = i '''and''' k ≠ i가 존재하고, level[k] ≥ ℓ
'''wait'''
```
임계 구역을 종료하여 잠금을 해제하려면 프로세스 가 를 −1로 설정한다.
이 알고리즘이 상호 배제를 보장하는 이유는 다음과 같다. 프로세스 는 보다 높은 레벨의 프로세스가 없거나, 다음 대기실이 비어 있을 때, 또는 일 때(즉, 다른 프로세스가 해당 대기실에 들어왔을 때) 내부 루프를 종료한다. 레벨 0에서 모든 개의 프로세스가 동시에 대기실 0에 들어가더라도, 개 이하의 프로세스가 다음 방으로 진행하며, 마지막 방에 있는 프로세스는 해당 방에 마지막으로 들어온 프로세스가 된다. 마찬가지로, 다음 레벨에서는 개가 진행되며, 등등 마지막 레벨에서는 하나의 프로세스만 대기실을 떠나 임계 구역에 들어갈 수 있으므로 상호 배제가 보장된다.
두 프로세스 피터슨 알고리즘과 달리, 필터 알고리즘은 경계된 대기를 보장하지 않는다.
5. 하드웨어 수준 고려 사항
최근 CPU는 성능 향상을 위해 메모리 접근 순서를 재정렬(아웃 오브 오더 실행)할 수 있다. 이러한 환경에서 피터슨 알고리즘이 올바르게 작동하려면 메모리 배리어 명령어를 사용하여 메모리 접근 순서를 명시적으로 지정해야 할 수 있다.
대부분의 CPU는 원자적 연산을 지원하는 특수 명령어를 제공한다. 예를 들어, x86 프로세서의 `XCHG` 명령어와 알파, MIPS, PowerPC 및 기타 아키텍처의 Load-Link/Store-Conditional 명령어가 있다. 이러한 명령어는 순수한 공유 메모리 접근 방식보다 더 효율적으로 동기화 기본 요소를 구축하는 방법을 제공한다.
참조
[1]
논문
"Myths About the Mutual Exclusion Problem"
[2]
간행물
Proof of a Mutual Exclusion Algorithm
Operating Systems Review
1990-01
[3]
서적
Operating Systems Concepts: Seventh Edition
John Wiley and Sons
[4]
서적
Concurrent Programming: Algorithms, Principles, and Foundations
Springer Science & Business Media
[5]
서적
On Concurrent Programming
Springer Verlag
[6]
서적
The Art of Multiprocessor Programming
Elsevier
[7]
간행물
Proof of a Mutual Exclusion Algorithm
Operating Systems Review
1990-01
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com