한빛출판사 정성호 저 <쉽게 배우는 운영체제>를 참고하여 작성하였습니다.
1. 프로세스 간 통신
1) 개요
한 프로세스가 같은 컴퓨터 내의 프로세스끼리 혹은 네트워크로 연결 된 다른 컴퓨터에 있는 프로세스와 데이터를 주고 받는 것을 프로세스 간 통신 (IPC:Inter-Process Communication) 이라고 한다.
프로세스 내부 데이터 통신 | 하나의 프로세스 내에 2개 이상의 스레드가 존재하는 경우의 통신 |
프로세스 간 데이터 통신 | 같은 컴퓨터의 프로세스끼리 통신 |
네트워크를 이용한 데이터 통신 | 여러 컴퓨터가 네트워크로 연결 되어 있을 때 통신 |
프로세스간 통신의 종류는 위 처럼 크게 세 가지로 나눌 수 있으며, 프로세스 내부 데이터 통신의 경우는 전역 변수나 파일을 이용해 이루어지며, 프로세스 간 데이터 통신은 공용 파일 혹은 파이프를 이용하여 통신이 이루어진다. 서로 다른 기기가 네트워크를 이용하여 통신을 할 때는 주로 소켓을 이용하여 통신을 한다.
전역 변수나 파일, 파이프를 이용한 통신은 단방향 통신으로 한쪽 방향으로만 데이터를 전송할 수 있으며 소켓의 경우는 양방향 통신으로, 데이터를 동시에 양쪽 방향으로 동시에 전송 할 수 있다.
2) 프로세스 간 통신의 종류 상세
① 전역 변수를 이용한 통신
전역 변수를 이용한 통신은 공동으로 관리하는 메모리를 사용하여 데이터를 주고 받는 방법이다. 데이터를 보낼 때는 쓰기 연산을 통해 전역 변수에 값을 쓰고, 받는 쪽에서는 읽기 연산을 통해 전역 변수의 값을 읽어온다.
전역 변수를 이용하여 통신할 때는 단방향으로만 통신이 가능하다. 따라서 양방향으로 통신하기 위해서는 경우를 프로세스 A에서 프로세스 B로 통신할 때와 프로세스 B에서 프로세스 A로 통신할 때 두 가지로 나누어야 하고, 전역변수 역시 두 개가 필요하다.
이 때 데이터를 받는 입장에서는 전역 변수의 값을 제때 읽지 못하면 보내는 입장에서 전역 변수에 값을 덮어 쓸 수 있기 때문에 짧은 주기로 전역 변수를 계속해서 확인해야 한다.
② 파일을 이용한 통신
파일을 이용한 통한 통신의 주요한 연산에는 파일 열기(open()), 파일 닫기(close()), 파일 쓰기(write()), 파일 읽기(read())가 있다. 프로세스가 입출력 관리 프로세스에 쓰기를 요구하면 데이터가 저장되고, 읽기를 요구하면 입출력 관리 프로세스로부터 데이터를 가져온다.
파일을 이용한 통신은 운영체제가 동기화를 제공하지 않아 프로세스가 스스로 동기화를 해야하는데, 이떄 wait() 함수를 이용한다.
③ 파이프를 이용한 통신
파이프를 이용한 통신은 open() 연산으로 기술자를 얻고 작업을 한 후 close() 연산으로 마무리 한다는 점에서는 파일을 이용한 통신과 유사하지만, 파일을 이용한 통신과 다르게 운영체제가 동기화를 지원한다. 또한 단방향 통신 방식으로, 양방향 통신을 하기 위해서는 파이프가 두 개 필요하다.
데이터를 보내는 프로세스는 파이프에 쓰기 연산을 통해 전송하고, 받는 프로세스는 읽기 연산을 통해 받는다. 예를 들어 프로세스 A가 프로세스 B에 데이터를 보낸다고 가정하면, 프로세스 A가 파이프 1에 쓰기 연산을 하고 프로세스 B는 파이프 1의 데이터를 읽는다. 프로세스 B는 파이프 1의 데이터를 읽은 후 대기 상태로 진입하고, 프로세스 A가 다시 파이프 1에 데이터를 쓰면 대기 상태가 풀리면서 동기화가 이루어진다. 이러한 방식으로 동기화가 지원되므로, 전역 변수를 이용한 통신처럼 데이터를 받는 프로세스가 파이프를 바쁘게 확인해 줄 필요가 없다.
④ 소켓을 이용한 통신
소켓을 이용한 통신은 서로 다른 기기에 존재하는 프로세스가 네트워크를 이용하여 통신할 때 사용하는 방법이다. (소켓은 프로세스가 네트워크를 통해 데이터를 보내기 위한 창구 역할을 한다.) 이들은 통신을 하기 위해 자신의 소켓과 상대의 소켓을 연결하고, 이를 통해 프로세스끼리도 연결이 되는데 이러한 작업을 바인딩(binding) 이라고 한다. 프로세스가 소켓을 바인딩한 후 소켓에 쓰기 연산을 하면 데이터가 전송되고, 읽기 연산을 하면 데이터를 받을 수 있다.
한편 소켓은 프로세스 동기화를 지원하므로 전역 변수를 이용한 통신처럼 프로세스가 짧은 주기로 계속해서 소켓을 확인할 필요가 없다.
2. 공유자원과 임계구역
1) 개요
공유 자원은 여러 프로세스가 공동으로 이용하는 변수, 메모리, 파일 등을 의미한다. 공유 자원은 공동으로 이용되므로 누가 언제 데이터를 읽거나 쓰느냐에 따라 결과가 달라 질 수 있으므로 자원 접근 순서를 잘 조정하여 의도치 않게 결괏값이 바뀌는 상황을 방지하여야 한다.
임계구역은 공유 자원 접근 순서에 따라 실행 결과가 달라지는 프로그램의 영역을 의미한다. 임계 구역에서는 프로세스들이 동시에 작업해서는 안 되며, 어떤 프로세스가 한 번 임계구역에 진입하면 작업이 끝날 때까지 다른 프로세스가 임계구역에 들어오는 것을 막아야 한다.
임계구역을 안전하게 보호하기 위해서는 상호배제(한 프로세스가 임계 구역에 들어가면 다른 프로세스는 임계 구역에 들어갈 수 없음), 한정 대기(한 프로세스가 무한 대기에 빠지면 안 됨), 진행의 융통성(한 프로세스가 다른 프로세스의 작업을 방해해서는 안 됨) 조건을 충족해야 한다.
2) 임계 구역의 종류
① 전역 변수로 잠금을 구현
전역 변수로 잠금을 구현하는 첫 번째 방법이다.
프로세스 A와 B는 공유변수 'lock=false'를 공유한다. 프로세스 A와 B는 모두 'lock=false' 인 상태에서 임계구역에 진입할 수 있으며, 진입한 뒤에 'lock=true' 를 실행하여 다른 프로세스가 임계구역에 진입하지 못하도록 한다.
그러나 동시 진입 상황이 발생하는 경우가 있다. 프로세스 A가 'while(lock==true)' 구문을 반복해서 실행하다가 'lock=false' 조건이 발생하여 반복문을 끝냈다고 가정하자. 그런데 이 때 프로세스 A가 'lock=true;' 구문을 실행하기 전 타임아웃이 발생하고, 이 상태에서 프로세스 B가 'while (lock==true);' 문을 끝내고 'lock=true;'를 걸고 임계구역에 진입하였다. 그리고 타임아웃이 끝난 뒤 프로세스 A도 'lock=true;'를 걸고 임계구역에 진입하였다. 이 경우, 프로세스 A와 B가 모두 임계구역에 동시에 진입하게 된다. 상호 배제 조건이 위배되는 것이다.
전역 변수로 잠금을 구현하는 두 번째 방법이다.
프로세스 A와 B는 공유 변수 'lock_A=false;'와 'lock_B=false;'를 공유한다. 프로세스 A와 B는 진입하기 전에 먼저 'lock_A(또는 B)=true' 문을 이용하여 잠금을 설정한다. 첫 번째 방법과는 다르게 일단 잠금을 하고 다른 프로세스가 잠금이 걸렸는지 확인하므로 동시에 진입하여 문제가 발생할 걱정이 없다. 그러나 위와 같은 방법에서는 또 다른 문제가 발생한다.
이러한 방법에서는 무한 루프 문제가 발생한다. 무한 루프 문제는 프로세스 A는 'lock_A=true'문을, 프로세스 B는 'lock_B=true' 구문을 수행하고 각각 타임아웃에 빠질 경우에 발생한다. 이러한 상황에서는 프로세스 A와 B 모두 계속해서 임계구역에 진입하지 못하고 반복문만 돌고 있게 된다.
전역변수로 잠금을 구현하는 세 번째 방법이다.
세 번째 방법은 비교적 간단한다. 최초에 'lock=1;' 이라는 공유변수를 공유한다. 이때 프로세스 A가 반복문을 끝내고 진입하여 작업을 한 뒤 'lock=2;' 구문을 실행하고, 그 뒤 프로세스가 반복문을 끝내고 진입하여 작업을 수행한 뒤 'lock=1;' 구문을 실행하고 또 다시 프로세스 A가 작업을 수행하는 식이다. 이 경우 상호 배제와 한정 대기 조건을 모두 충족하게 되지만 같은 프로세스가 연속적으로 임계구역에 진행할 수 없다는 문제점이 있다.
② 피터슨 알고리즘
피터슨 알고리즘은 임계구역 문제 해결에 필요한 세 가지 조건 (상호 배제, 한정 대기, 진행의 융통성)을 모두 충족한다. 단, 두 개의 프로세스에서만 사용이 가능하며 세 개 이상의 프로세스부터는 적용이 불가능하다는 한계점이 있다.
피터슨 알고리즘은 전역 변수로 잠금을 구현의 두 번째 방법과 매우 유사한데, 여기에 무한 루프에 빠지는 상황을 대비하여 'turn'이라는 변수를 하나 더 추가했다는 차이점이 있다.
공유변수 'int turn='A';'을 가진 상태에서 두 프로세스가 프로세스 A, B의 차례로 lock을 걸고 타임아웃에 처했다고 가정하자. turn 변수가 없다면 두 프로세스 모두 무한 루프에 빠져 작업을 할 수 없는 상태가 될 것이다. 그러나 turn 변수를 추가함으로서 무한 루프에 빠지지 않고 다른 프로세스에게 순서를 양보하며 작업 처리가 가능하다. (물론 프로세스 A와 B가 'turn='N';' 구문까지 실행하고 타임아웃에 처했는지 아닌지에 따라 어떤 프로세스가 먼저 임계구역에 진입하는지가 달라지기는 하겠다.)
③ 데커 알고리즘
데커 알고리즘 역시 피터슨 알고리즘과 마찬가지로 임계 구역 문제 해결에 필요한 세 가지 조건을 모두 만족한다. 다만 과정이 복잡하여 잘 사용하지는 않는다. (피터슨 알고리즘도 마찬가지이다.) 프로세서 A 입장에서의 데커 알고리즘의 전개 과정을 순서도로 나타내보면 아래와 같다.
만약 프로세스 A가 임계구역에서 작업을 끝마치고 나면 'turn='B';' 문장을 통해 프로세스 B에게 순서를 넘겨준 뒤 'lock_A='false';' 문장을 통해 프로세스 A의 잠금을 해제할 것이다. 그리고 이후 프로세스 B가 임계구역에 진입할 수 있게 된다.
④ 세마포어 (Semaphore)
세마포어 역시 임계 구역이나 공유 자원에 여러 프로세스가 동시에 접근하여 혼란을 초래하는 것을 막는 방법 중 하나인데, 위에서 서술한 방법보다 비교적 간단하며 사용하기도 쉽다.
RS는 임계 구역에 동시 진입이 가능한 프로세스의 수를 의미한다. 따라서 처음에 세마포어를 설정할 때 RS의 수를 입력받는다. P();는 잠금을 수행하는 코드이다. 프로세스가 P();를 통해 임계구역에 진입할 때, RS의 값이 0보다 크면 RS의 수를 1 감소시키고 임계구역에 진입하며, 만약 RS가 0보다 작거나 같으면 진입하지 않고 대기한다. V();는 잠금을 해제하고 동기화를 진행하는 코드로, 프로세스는 임계구역에서 나올 때 V();를 통해 빠져나오며 RS를 1 증가시킨다.
'운영체제' 카테고리의 다른 글
8. 가상 메모리 - 페이징 기법/세그먼테이션 기법 (0) | 2022.06.08 |
---|---|
7. 메모리 관리 기법 - 절대주소와 상대주소/메모리 오버레이와 스왑/가변분할방식과 고정분할방식 (0) | 2022.06.08 |
4. CPU 스케줄링 - 개요/우선순위/종류 (0) | 2022.06.04 |
3. 프로세스와 스레드 - 프로세스의 정의/구조/상태/생성과 복사/스레드 (0) | 2022.06.03 |
2. 운영체제 - 운영체제의 정의/역할/구조/가상머신 (0) | 2022.06.03 |