December 17, 2020
✍︎ 운영체제 내부구조 및 설계원리 제8판 (Operating Systems Internals and Design Principles) 을 정리하는 글입니다.
멀티프로그래밍, 멀티프로세싱, 분산처리 등의 설계의 핵심은 병행성이다.
* 멀티프로그래밍: 단일 처리기 시스템에서 다수 프로세스 관리
* 멀티프로세싱: 멀티 프로세서 시스템에서 다수 프로세스 관리
* 분산처리: 분산된 시스템에서 수행되는 다수의 프로세스 관리 예) Cluster System
{프로세스 사이의 통신, 프로세스 활동들의 동기화, 자원에 대한 공유와 경쟁, 프로세스에 대한 처리기 시간 할당 등의 이슈} ⊂ 병행성
단일 처리기 멀티 프로그래밍 시스템에서 프로세스들은 인터리빙(interleaving)된다. 인터리빙은 실제로 병렬 처리를 하는 것도 아니고, 프로새스 교환시 문맥 교환 비용이 발생하지만 시스템 처리 효율과 구조적 프로그래밍에 큰 장점을 제공한다.
멀티 프로세서 시스템은 인터리빙과 함께 오버래핑(overlapping)도 지원한다. 인터리빙과 오버래핑, 두 기법 모두 병행 처리의 예이며 동일한 문제를 야기한다.
단일 처리기 시스템과 멀티 프로세서 시스템에서 발생하는 문제의 본질은 “프로세스들의 수행 상대 속도를 예측할 수 없다”는 특징으로부터 기인한다. 수행 속도는 OS의 스케줄링 정책, 다른 프로세스들의 활동, OS의 인터럽트 처리 방법에 의해 영향을 받아 동적으로 변한다. 이로 인해 프로그램 개발 시 다음과 같은 어려움들이 발생한다.
void echo(){
chin = getchar();
chout = chin;
putchar(chout);
}
👉 단일 처리기 기반 멀티프로그래밍 시스템이라고 가정했을 때,
echo()
함수를 전역 메모리 공간에 적재하는 경우, 한 개의 함수 이미지가 메모리에 존재하도록 하여 공간을 절약할 수 있다. 즉, 프로세스간 메모리 공유는 공간 절약 측면에서 효율성을 높이고 프로세스간 긴밀한 상호작용을 가능하게 한다는 장점이 있지만 이로 인해 몇 가지 문제가 야기될 수 있다.
문제 상황
echo()
함수 호출로 인해 getchar()
가 호출된다. 사용자가 ‘x’를 입력하고 이 값이 chin에 저장되는 동안 OS는 P1을 선점하고 P2를 수행하기로 결정했다.echo()
함수를 호출했고 1번과 같은 절차로 chin에 ‘y’가 저장된 후 출력된다.이 문제는 전역변수 chin을 두 개 이상의 프로세스가 공유하기 때문에 발생한다.
∴ 한 시점에 오직 한 프로세스만이 그 함수 내부에 존재하도록 해야 한다.
해결 상황
echo()
함수 호출로 인해 getchar()
가 호출된다. 사용자가 ‘x’를 입력하고 이 값이 chin에 저장되는 동안 OS는 P1을 선점하고 P2를 수행하기로 결정했다.echo()
를 호출하지만 아직 P1이 echo()
함수 내에 있기 때문에 P2는 echo()
함수 내부로 진입할 수 없다. P2는 echo()
함수 사용이 가능할 때까지 일시 중지된다.echo()
함수 내부에서 나왔을 때 수행이 재개되고 사용자가 ‘y’를 입력하면 ‘y’가 출력된다.👍 위의 예시 상황을 통해 전역 변수를 공유(또는 자원을 공유)할 때 문제가 발생할 수 있고, 이를 막기 위해서는 전역변수를 보호해야함을 알았다. 이때 전역변수를 보호하는 유일한 방법은 공유자원에 접근하는 프로그램의 코드 부분을 제어하는 것이다.
👆위의 문제는 멀티 프로세서 시스템에서도 동일하게 발생되어 공유 자원 보호의 필요성을 보여준다. 이 경우에도 동일한 방법을 이용하여 해결할 수 있다.
단일 처리기 시스템 | 멀티 프로세서 시스템 | |
---|---|---|
원인 | 인터럽트가 프로세스 수행 중 어떤 위치에서든 수행을 중지시키며 발생할 수 있기 때문이다. | 인터럽트의 비동기적 발생 뿐만 아니라 두 개의 프로세스들이 동시에 수행하여 가은 전역변수에 접근하려 하기 때문이다. |
해결책 | 공유 자원에 대한 접근 제어 |
ex) 프로세스 A가 특정 입출력 자원을 할당받은 채로 block 또는 일시 중지된다면 다른 프로세스들도 해당 입출력 자원 사용 불가. 즉, 가용 자원이 실행 순서에 따라 사용될 수 없는 상황 발생. -> 이 경우가 악화되면 교착 상태 발생 가능
다수의 프로세스나 쓰레드가 공유자원을 동시에 읽거나 쓰려고 하는 상태를 경쟁 상태(race condition)라고 한다. 경쟁 상태가 발생하면 최종 수행 결과는 프로세스의 수행 순서에 따라 달라진다.
a
를 프로세스 P1, P2가 공유한다. P1은 a=1
, P2는 a=2
로 수정하려 하는데 이는 경쟁 상태로 볼 수 있다. 경쟁에서 져서 마지막으로 a에 접근한 프로세스의 수정결과가 a에 최종적으로 저장된다.b=1
, c=2
로 초기화된 전역 변수를 공유하는 P3와 P4를 가정해보자. P3은 b = b+c
연산을, P4는 c = c+b
연산을 수행할 때, 이 연산들의 결과 또한 수행 순서에 따라 결정된다.분류 기준: 프로세스가 다른 프로세스들의 존재를 어떤 수준으로 인식하고 있는가
협동 작업을 하지 않는 서로 독립적인 프로세스들 ex) 멀티 프로그래밍 환경
인식 정도(Degree of Awareness) | 서로를 인식하지 못함 |
관계(Relationship) | 경쟁(자원에 대한 프로세스 사이의 경쟁) Competition |
한 프로세스가 다른 프로세스에게 미치는 영향 | - 한 프로세스의 수행 결과는 다른 프로세스의 행위와는 독립적 - 프로세스의 타이밍에 영향 받을 수 있음 |
잠재적인 제어 문제 | - 상호 배제 - 교착 상태 - 기아 상태 |
🖇 경쟁(자원에 대한 프로세스 사이의 경쟁) Competition
경쟁 관계에 있는 프로세스들이 존재할 경우 발생하는 제어 문제들
상호 배제: 한 시점에 단 하나의 프로세스만 임계 영역에 들어갈 수 있다. 상호배제 보장은 교착상태
와 기아
라는 제어 문제를 발생시킨다. 프로그래머에 의해 아래와 같이 직접 작성된다.
void P1{
while (true){
//임계영역 이전 코드
entercritical(Ra);
//임계영역
exitcritical(Ra);
//임계영역 이후코드
}
}
void P2{
while (true){
//임계영역 이전 코드
entercritical(Ra);
//임계영역
exitcritical(Ra);
//임계영역 이후코드
}
}
...
void Pn{
while (true){
//임계영역 이전 코드
entercritical(Ra);
//임계영역
exitcritical(Ra);
//임계영역 이후코드
}
}
다른 프로세스들의 이름(식별자, ID)을 알지는 않지만 자원을 공유하는 프로세스
인식 정도(Degree of Awareness) | 서로를 간접적으로 인식 |
관계(Relationship) | 공유를 이용한 프로세스간 협력 Cooperation |
한 프로세스가 다른 프로세스에게 미치는 영향 | - 한 프로세스의 수행 결과는 다른 프로세스들로부터 얻은 정보에 의해 영향 받을 수 있음 - 프로세스의 타이밍에 영향 받을 수 있음 |
잠재적인 제어 문제 | - 상호 배제 - 교착 상태(재사용 가능 자원) - 기아 상태 - 데이터 일관성 |
🖇 공유를 이용한 프로세스간 협력 Cooperation
간접적인 협력
이 관계에 있는 프로세스들이 존재하는 경우 발생하는 제어 문제
Data 일관성 유지
P1은 a = a+1;//1 b = b+1;//2
이고
P2는 b = 2*b;//3 a = 2*a;//4
일 때,
프로세스들이 동시에 수행된다면, 상호배제 조건이 지켜지더라도 일관성이 깨질 수 있다.
예) 실행 순서가 1-3-2-4일 때
이 예시를 통해, 상호배제 보장 시에는 각 공유자원에 접근하는 코드(프로그램 부분)를 개별 임계 영역으로 정의하는 것이 아니라, 연관된 공유 자원에 접근하는 프로그램 부분 전체를 단일 임계영역으로 정의해야 한다는 것을 알 수 있다.
경쟁 제어 시 사용하는 entercritical()/exitcritical()
사용 가능하다
다른 프로세스들의 이름(식별자, ID)을 알고, 이를 이용해 직접 통신하는 프로세스들. 공통된 작업을 함께 수행하기 위해 설계되었음.
인식 정도(Degree of Awareness) | 서로를 직접적으로 인식 |
관계(Relationship) | 통신을 이용한 프로세스간 협력 Cooperation |
한 프로세스가 다른 프로세스에게 미치는 영향 | - 한 프로세스의 수행 결과는 다른 프로세스들로부터 얻은 정보에 의해 영향 받을 수 있음 - 프로세스의 타이밍에 영향 받을 수 있음 |
잠재적인 제어 문제 | - 교착 상태(소모성 자원) - 기아 상태 |
🖇 통신을 이용한 프로세스간 협력 Cooperation
이 관계에 있는 프로세스들이 존재하는 경우 발생하는 제어 문제
운영체제나 프로그래밍 언어 차원의 지원 없이 프로세스간 협력을 통해 직접 상호 배제를 보장한다.
수행 부하가 크고, 잘 설계 되지 않았을 경우 오동작 가능성이 높다. 하지만 이 방법을 앎으로써 병행 처리의 복잡도와 상호 배제 윤리를 잘 이해할 수 있다.
상호 배제를 보장할 수 있는 가장 간단한 방법은 프로세스가 인터럽트 되지 않도록 하는 것이다.
∵ 단일 처리기에서 병행 처리(concurrent processing)되는 프로세스들은 인터리빙(interleaving)되기 때문이다. 또한 프로세스는 운영체제 서비스를 호출하거나 인터럽트가 될 때까지 계속 실행하기 때문에 인터럽트가 발생하지 않으면 한 프로세스의 지속적인 실행을 보장 가능
이를 위해 시스템 커널에서 인터럽트를 허용하거나 비허용할 수 있는 기본 인터페이스를 제공한다.
while(true){
// 인터럽트 금지
// 임계 영역
// 인터럽트 허용
// 임계 영역 이후 코드
}
단점
멀티 프로세서 환경에서 여러 처리기들은 공통의 주기억 장치를 공유하며, 이때 모든 처리기는 동등한 관계에서 독립적으로 동작한다.
∴ 인터럽트 기법으로 상호 배제 보장 불가
하드웨어 수준에서 특정 메모리 주소가 접근되고 있을 때, 같은 위치에 대한 접근 요청은 차단된다는 것에 기반해 상호 배제를 보장하기 위한 두 가지 명령어가 구현된다. 이때 기계 명령어는 두 개의 기능을 원자적으로 처리한다.
원자적: 명령어 수행 도중 인터럽트 되지 않고 마치 하나의 단위인 것처럼 처리될 수 있다.
Compare & Swap 명령어
compare_and_swap()
: 테스트 하려는 값과 저장된 값을 비교한다.
int compare_and_swap(int *word, int testval, int newval){
int oldval;
oldval = *word;
if(oldval == testval) *word = newval;
return oldval;
}
이 함수는 원자적으로 수행되기 때문에 중간에 중단되는 경우는 없다.
대부분 모든 처리기에서 지원하며, 대부분의 OS에서 병행성을 위해 이 명령어를 사용한다.
Compare & Swap 명령어를 이용한 상호 배제 보장
const int n = /*프로세스 개수*/;
int bolt;
void P(int i){
while(true){
while(compare_and_swap(bolt, 0, 1) == 1){ // 제일 처음 진입한 프로세스가 bolt를 1로 변경한 뒤
/*대기*/;
}
/*임계영역*/; // 임계영역에서 수행, 이때 진입을 하려는 프로세스들은 bolt가 1이기 때문에 위의 while문에서 대기해야한다.
bolt = 0; // 수행이 끝나면 bolt를 0으로 바꾸고 임계영역을 나간다.
/*임계영역 이후의 코드*/;
}
}
void main(){
bolt = 0;
parbegin(P(1), P(2), ... , P(n));
}
Exchange 명령어
exchange()
: 레지스터 값과 메모리에 들어있던 값을 서로 교체하는 기능 수행
void exchange(int *register, int *memory){
int temp;
temp = *memory;
*memory = *register;
*register = temp;
}
Exchange 명령어를 이용한 상호 배제 보장
int const n = /*프로세스 개수*/;
int bolt;
void P(int i){
while(true){
int keyi = 1;
do exchange(&keyi, &bolt)
while(keyi != 0);
/*임계영역*/;
bolt = 0;
/*임계영역 이후의 코드*/;
}
}
void main(){
bolt = 0;
parbegin(P(1), P(2), ... , P(n));
}
key
는 1, bolt
는 0일 때 exchange를 수행하면 key
는 0, bolt
는 1로 바뀐다. 임계영역에 진입해 있는 프로세스가 있는 경우, 새로 진입하려는 프로세스가 exchange(&keyi, &bolt)
를 수행하고자 하면 수행 결과 key
는 1, bolt
는 0이 되어 계속 do while문을 돌게 된다. 진입했던 프로세스가 임계영역에서 빠져나올 때 bolt
를 0으로 바꾸고 나서야 새로운 프로세스가 다시 임계영역에 진입가능하다.
기계 명령어 접근 방법의 특성
장점
단점
교착 상태 빠질 수 있음 ⇢ 가능한 시나리오
소프트웨어적 방법과 하드웨어적 방법은 모두 단점을 갖고 있기 때문에 새로운 해결책(운영체제, 프로그래밍 언어 수준에서 상호 배제를 보장하는 방법)이 필요하다.