[운영체제] 프로세스 간 통신
운영체제 내에서 실행되는 병행 프로세스들은 독립적이거나 또는 협력적인 프로세스들일 수 있다. 프로세스가 시스템에서 실행 중인 다른 프로세스들과 데이터를 공유하지 않는 프로세스는 독립적이다. 프로세스가 시스템에서 실행 중인 다른 프로세스들에 영향을 주거나 받는다면 이는 협력적인 프로세스들이다. 분명히 다른 프로세스들과 자료를 공유하는 프로세스는 상호 협력적인 프로세스이다.
- 정보 공유: 여러 응용 프로그램이 동일한 정보에 흥미를 느낄 수 있으므로, 그러한 정보를 병행적으로 접근할 수 있는 환경을 제공해야 한다.
- 계산 가속화: 만일 우리가 특정 태스크를 빨리 실행하고자 한다면 우리는 그것을 서브태스크로 나누어 이를 각각이 다른 서브태스크들과 병렬로 실해오디게 해야 한다. 이러한 가속화는 복수 개의 처리 코어를 가징 경우에만 달성할 수 있다.
- 모듈성: 시스템 기능을 별도의 프로세스들 또는 스레드들로 나누어 모듈식 형태로 시스템을 구성하기를 원할 수 있다.
협력적 프로세스들은 데이터를 교환할 수 있는 즉 서로 데이터를 보내거나 받을 수 있는 프로세스 간 통신 기법이 필요하다. 프로세스 간 통신에는 기본적으로 공유 메모리와 메시지 전달의 두 가지 모델이 있다. 공유 메모리 모델에서는 협력 프로세들에 의해 공유되는 메모리의 영역이 구축된다. 프로세스들은 그 영역에 데이터를 읽고 쓰고 함으로써 정보를 교환할 수 있다.
메시지 전달 모델: 충돌을 회피할 필요가 없기 때문에 적은 양의 데이터를 교환하는데 유용하다. 공유 메모리보다 분산 시스템에서 구현하기 쉽다. 통상적으로 시스템 콜을 사용하여 구현되므로 커널 간섭등의 부가적인 시간 소비 작업이 필요하기 때문에 공유 메모리 모델이 메시지 전달보다 더 빠르다. 공유 메모리 시스템에서는 공유 메모리 영역을 구축할 때만 시스템 콜이 필요하다. 공유 메모리 영역이 구축되면 모든 접근은 일반적인 메모리 접근으로 취급되어 커널의 도움이 필요 없다.
공유 메모리 시스템에서의 프로세스 간 통신
공유 메모리를 사용하는 프로세스 간 통신에서는 통신하는 프로세스들이 공유 메모리 영역을 구축해야 한다. 통상 공유 메모리 영역은 공유 메모리 세그먼트를 생성하는 프로세스의 주소 공간에 위치한다. 이 공유 메모리 세그먼트를 이용하여 통신하고자하는 다른 프로세스들은 이 세그먼트를 자신의 주소 공간에 추가하여야 한다. 일반적으로 운영체제는 한 프로세스가 다른 프로세스의 메모리에 접근하는 것을 금지한다. 공유 메모리는 두개의 프로세스 이상이 해당 제약 조건을 제거하는 것에 동의하는 것이 필요하다. 그 후에 프로세스들은 공유 영역에 읽고 씀으로써 정보를 교환할 수 있다. 데이터의 형식과 위치는 이들 프로세스에 의해 결정되며 운영체제의 소관이 아니다. 또한 프로세스들은 동시에 동일한 위치에 사용되지 않도록 책임져야 한다.
협력하는 프로세스의 개념을 설명하기 위해서 협력하는 프로세스의 일반적인 패러다임인 생산자-소비자 문제를 생각해보자. 생산자 프로세스는 정보를 생산하고 소비자 프로세스는 정보를 소비한다. 예를 들어 컴파일러는 어셈블리 코드를 생산하고 에셈블러는 이것을 소비한다. 어셈블러는 이어 목적 모듈을 생산할 수 있고 적재기는 이들을 소비한다. 일반적으로 우리는 서버를 생산자로 클라이언트를 소비자로 생각한다. 예를 들면 웹 서버는 HTML 파일과 이미지와 같은 웹 콘텐츠를 생산하고 이 자원들을 요청한 클라이언트 웹 브라우저가 소비하게 된다.
생산자-소비자 문제의 하나의 해결책은 공유 메모리를 사용하는 것이다. 생산자와 소비자 프로세스들이 병행으로 실행되도록 하려면, 생산자가 정보를 채워 넣고 소비자가 소모할 수 있는 항목들의 버퍼가 반드시 사용 가능해야 한다. 이 버퍼는 생산자와 소비자가 공유하는 메모리 영역에 존재하게 된다. 생산자가 한 항목을 생산하고 그동안에 소비자는 다른 항목을 소비할 수 있다. 생산자와 소비자가 반드시 동기화되어야 생산되지 않은 항목들을 소비자가 소비하려고 시도하지 않을 것이다.
무한 버퍼: 생산자-소비자 문제에서는 버퍼의 크기에 실질적인 한계가 없다. 소비자는 새로운 항목을 기다려야만 할 수도 있지만 생산자는 항상 새로운 항목을 생산할 수 있다.
유한 버퍼: 버퍼 크기가 고정되어 있다 가정한다. 이 경우 버퍼가 비어 있으면 소비자는 반드시 대기해야 하며 모든 버퍼가 채워져 있으면 생산자가 대기해야 한다.
메시지 전달 시스템에서의 프로세스간 통신
메시지 전달 방식은 동일한 주소 공간을 공유하지 않고도 프로세스들이 통신하고 그들의 동작을 동기화할 수 있도록 허용하는 기법을 제공한다. 메시지 전달 방식은 통신하는 프로세스들이 네트워크에 의해 연겨로딘 다른 컴퓨터들에 존재할 수 있는 분산환경에서 특히 유용하다. 채팅 프로그램이 이에 해당하는데 send(Message), receive(Message) 프로세스가 보낸 메시지는 고정 길이일 수도 있고 가변 길이일 수도 있다. 고정 길이 메시지만 보낼 수 있다면 시스템 수준의 구현은 직선적이다. 하지만 가변 길이 메시지의 경우 보다 복잡한 시스템 수준의 구현이 필요하다. 만약 A와 B가 통신을 원하면 서로 메시지를 주고 받아야하고 이 사이에 통신 연결이 설정되어 있어야 한다.
- 직접 또는 간접 통신
- 동기식 또는 비동기식 통신
- 자동 또는 명시적 버퍼링
명명
통신을 원하는 프로세스들은 서로를 가르킬 방법이 있어야 한다. 이들은 직접 통신과 간접 통신을 사용할 수 있다.
직접 통신
통신을 원하는 각 프로세스는 통신 수신자 또는 송신자의 이름을 명시해야 한다.
- send(A, Message) 프로세스 A에 메시지를 전송한다.
- receive(B, Message) 프로세스 B로부터 메시지를 수신한다.
직접 통신 특성
- 통신을 원하는 각 프로세스의 쌍들 사이에 연결이 자동으로 구축된다. 프로세스들은 통신하기 위해 상대방의 신원만 알면 된다.
- 연결은 정화히 두 프로세스 사이에만 연관된다.
- 통신하는 프로세들의 각 쌍 사이에는 정화하게 하나의 연결이 존재해야 한다.
이 기법은 주소 방식에서 대칭성을 보인다. 즉 송신자, 수신자 프로세스가 모두 통신하려면 상대방의 이름을 제시해야 한다. 이 기법의 변형으로 주소 지정 시에 비대칭을 사용할 수도 있다. 송신자만 수신자 이름을 지명하여 수신자는 송신자 이름을 제시할 필요가 없다.
- send(A, Message) 메시지를 프로세스 A에 전송
- recieve(id, Message) 임의의 프로세스로 부터 메시지를 수신한다. id는 수신 프로세스 이름으로 설정
대칭성, 비대칭성 모두 프로세스를 지정하는 방식 때문에 모듈성을 제한한다는 것이 단점이다. 프로세스의 이름을 바꾸면 다른 프로세스 지정부분을 검사할 필요가 있을 것이다. 옛 이름들에 대한 모든 참조를 반드시 찾아서 새로운 이름으로 변경해야 할 것이다. 일반적으로 이러한 하드 코딩 기법은 신원을 명시적으로 표시해한다.
간접 통신
메시지들은 메일박스 또는 포트로 송신되고 그것으로부터 수신된다. 메일박스는 추상적으로 프로세스들에 의해 메시지들이 넣어지고 메시지들이 제거될 수 있는 객체라고 볼 수 있다 . 각 메일박스는 고유 id를 가진다. 예를 들어 POSIX 메시지 큐는 메일박스를 식별하기 위하여 정수 값을 사용한다. 이 기법에서 프로세스는 다수의 상이한 메일박스를 통해 다른 프로세스들과 통신할 수 있다. 두 프로세스들이 공유 메일박스를 가질 때만 이들 프로세스가 통신할 수 있다.
- send(A, Message) 메시지를 메일박스 A로 송신한다.
- receive(A, Message) 메시지를 메일박스 A로부터 수신한다.
간접 통신 기법 특징
- 한 쌍의 프로세스들 사이의 연결은 이들 프로세스가 공유 메일박스를 가질 때만 구축된다.
- 연결은 두 개 이상의 프로세스들과 연관될 수 있다.
- 통신하고 있는 각 프로세스 사이에는 다수의 서로 다른 연결이 존재할 수 있고 각 연결은 하나의 메일박스에 대응된다.
그럼 3개의 프로세스가 공유 메일박스를 가진다고 가정하면 어떻게 될까?
- 하나의 링크는 최대 두 개의 프로세스와 연관되도록 허용한다.
- 한순간에 최대로 하나의 프로세스가 receive() 연산을 실행하도록 허용한다.
- 어느 프로세스가 메시지를 수신할 것인지 시스템이 임의로 선택. 알고리즘을 이용해 정의할 수 있다. 시스템은 송신자에게 수신자를 알려 줄 수 있다.
메일박스는 한 프로세스 또는 운영체제에 의해 소유될 수 있다. 메일박스가 한 프로세스에 의해 소유된다면 우리는 소유자와 메일박스의 사용자를 구분할 수 있다. 각 메일박스가 고유한 소유자를 가지고 있기 때문에 이 메일박스로 보내진 메시지를 어느 프로세스가 수신할지에 정확히 정의된다. 메일박스를 소유하고 있는 프로세스가 종료할 때 메일박스는 사라진다 이후 이 메일박스로 메시지를 송신하는 프로세스는 더는 메일박스가 존재하지 않는다는 사실을 통보 받아야 한다.
메일박스가 운영체제에 소유된다면 메일박스는 자체적으로 존재하기 된다. 이것은 독립적인 것으로 특정 프로세스에 예속되지 않는다.
새로운 메일박스를 생성하는 프로세스는 기본저긍로 메일박스의 소유자가 된다. 초기에는 소유자만이 메일박스를 통해 메시지를 수신할 수 있는 유일한 프로세스이다. 그러나 소유권과 수신 특권은 적절한 시스템 콜을 통해 다른 프로세스에 전달될 수 있다.
동기화
프로세스 간의 통신은 send와 receive 호출에 의해 발생한다. 봉쇄형, 비봉쇄형 방식으로 전달된다. 이 두 방식은 각각 동기식, 비동기식이라고도 알려져 있다.
- 봉쇄형 보내기: 송신하는 프로세스는 메시지가 수신 프로세스 또는 메일박스에 의해 수신될 때까지 봉쇄된다.
- 비봉쇄형 보내기: 송신하는 프로세스가 메시지를 보내고 작업을 재시작한다.
- 봉쇄형 받기: 메시지가 이용 가능할 때까지 수신 프로세스가 봉쇄된다.
- 비봉쇄형 받기: 송신하는 프로세스가 유효한 메시지 또는 Null을 받는다.
수신, 송신자 모두 봉쇄형일 때 사소한 문제가 발생하는데 송신자, 수신자 모두 준비될 때까지 서로 대기하는 상황이 발생한다. 봉쇄형 송신에서 메시지 준비가 완료되더라도 봉쇄형 수신에서 준비가 완료될 때까지 대기해야한다. 반대로 봉쇄형 수신자 또한 메시지를 받을 준비가 되더라도 봉쇄형 송신에서 메시지 전송 준비가 완료되지 않았다면 대기해야한다.
버퍼링
통신이 직접적이든 간접적이든 간에 통신하는 프로세스들에 의해 교환되는 메시지는 임시 큐에 들어 있다. 기본적으로 이러한 큐를 구현하는 방식은 세 가지가 있다.
- 무용량: 큐의 최대 길이가 0이다. 즉 링크 자체 안에 대기하는 메시지들을 가질 수 없다. 이 경우 송신자는 수신자가 메시지를 수신할 때까지 기다려야 한다.
- 유한 용량: 큐는 유한한 길이 N을 가진다. 최대 N개의 메시지가 그 안에 들어갈 수 있다. 새로운 메시지가 전송될 때 큐가 만원이 아니라면 메시지는 큐에 놓이며 송신자는 대기하지 않고 실행을 계속한다. 그렇지만 링크는 유한용량을 가지기 때문에 만원이라면 송신자는 큐안에 공간이 이용 가능할 때까지 반드시 봉쇄해야 한다.
- 무한 용량: 큐는 잠재적으로 무한한 길이를 가진다. 따라서 메시지들이 얼마든지 큐 안에서 대기할 수 있다. 송신자는 절대 봉쇄되지 않는다.
무용량의 경우 떄떄로 버퍼가 없는 메시지 시스템이라고 부른다. 다른 경우들은 자동 버퍼링이라 불린다.