본문 바로가기

Computer Science/Operating System

프로세스 간 통신 Part1

1. 개요

프로세스는 독립적인 주소 공간을 가지기 때문에 원칙적으로는 다른 프로세스의 주소 공간을 참조하는 것이 불가능하다. 따라서 운영체제에서는 서로 다른 프로세스들이 데이터를 주고 받을 수 있도록 프로세스 간의 자원 접근을 위한 매커니즘인 프로세스 간 통신(IPC, Inter-Process Communication)을 제공한다. 그리고 이 IPC 방식은 공유메모리 모델과 메시지 전달 모델 2가지 모델로 나누어 볼 수 있다.

공유 메모리 모델은 프로세스들이 주소 공간의 일부를 공유하고 해당 영역에 읽기/쓰기 작업을 통해서 통신한다. 그리고 메시지 전달 모델은 커널을 통해서 send와 receive 연산을 통해 데이터를 전송한다.

1.1. 공유 메모리 모델 개요

위의 이미지를 보면 서로 통신을 하려는 프로세스 A와 프로세스 B가 있고 그 중간에 공유 메모리가 존재한다. 프로세스 A는 프로세스 B와 공유하려는 정보를 공유 메모리 영역에 write하고, 프로세스 B는 해당 영역에서 공유된 정보를 read한다. 이를 통해서 프로세스 B는 어떤 정보를 가지고 프로세스 A가 통신하려 했는지 알 수 있게 된다.

공유 메모리 모델은 시스템 호출이 필요하지 않고 보통의 메모리 속도로 액세스가 발생하기 때문에 한 번 셋업이 완료되면 빠르게 동작한다. 그러나, 운영체제에 공유 메모리 모델을 설정하는 것이 복잡하고 여러 컴퓨터가 연결된 상태에서 통신이 원활하지 않다는 문제가 있다.

공유 메모리의 이러한 특징 때문에 공유 메모리 모델은 동일한 컴퓨터에서 많은 양의 정보를 빠르게 공유해야 하는 환경에서 선호된다.

1.2. 메시지 전달 모델 개요

위의 이미지는 메시지 전달 모델을 설명하고 있다. 공유 메모리 모델에서와 마찬가지로 서로 통신하려는 프로세스 A와 B가 존재하고, 커널 영역이 추가로 존재한다. 그리고 프로세스 A와 B, 그리고 커널 오른쪽에 써있는 'M'은 메시지를 의미한다.

메시지 전달 모델은 시스템 콜을 통해 구현되는데, 프로세스 A가 커널로 메시지를 보내면 커널은 해당 메시지를 프로세스 B에게 전달하는 방식으로 작동한다.

모든 메시지 전송에 대해서 시스템 호출이 요구되기 때문에 속도는 느리지만 앞서 살펴본 공유 메모리 모델에 비해서 설정이 더 간단하고 여러 컴퓨터가 연결된 환경에서 잘 작동할 수 있다.

이러한 특징 때문에 메시지 전달 모델은 데이터 전송의 양 또는 빈도가 적거나 여러 대의 컴퓨터가 네트워크로 연결된 분산 환경에서 유용하다.

2. 공유 메모리(shared memory)

프로세스의 통신 방법 중 공유 메모리를 사용하는 경우, 가장 처음 공유 메모리 영역을 설정하기 위한 통신 프로세스가 필요하다. 이렇게 생성한 공유 메모리를 통해서 서로 다른 프로세스끼리 서로 통신이 가능해진다.

일반적으로 공유 메모리 영역은 해당 영역을 생성하는 프로세스의 주소 공간에 상주한다.

위에서 사용했던 이미지를 다시 가져와보자. 위의 상태에서 프로세스 A는 프로세스 B와 통신을 원하는 상태이다. 따라서 프로세스 A가 공유 메모리 영역을 생성하게 되는데, 일반적으로 공유 메모리 영역은 해당 영역을 생성하는 프로세스의 주소 공간에 자리잡는다.

그리고 공유 메모리 영역을 생성한 프로세스와 통신하고 싶은 다른 프로세스는 이미 생성된 공유메모리의 주소가 끝나는 지점 바로 뒤에 주소가 이어져야 한다.

일반적으로 프로세스는 각자 자신만의 독립적인 주소공간을 가지기 때문에 다른 프로세스가 이 주소 공간을 참조하는 것은 허용되지 않는다. 그래서 운영체제에 의해서 프로세스 A가 프로세스 B의 메모리 영역에 접근하는 것과 그 반대의 경우 모두 "일반적으로" 거부된다. 그러나 IPC 기법으로 공유메모리 모델을 사용할 수 있는 환경이라면 서로 통신하려는 프로세스 모두 운영체제에 의한 제한을 거부하고 공유 메모리 영역을 생성하게 된다.

참고로 운영체제는 공유 메모리 영역을 생성하는 것에 대해서 관여하지 않는다. 공유 메모리 영역을 생성하는 것은 오직 서로 통신을 하려는 프로세스들에 의해서 결정된다.

2.1. 생산자-소비자 문제(producer-consumer problem)

생산자-소비자 문제는 여러 개의 프로세스를 어떻게 동기화할 것인가에 관한 고전적인 문제이다. 이 문제에는 고정된 크기의 버퍼와 생산자 프로세스 그리고 소비자 프로세스가 존재한다.

생산자 프로세스는 아이템을 생산하고 공유 버퍼(shared buffer)에 추가한다. 소비자 프로세스는 공유된 버퍼에서 아이템을 가져가 소비한다.

예를 들어, 컴파일러는 자바나 C로 작성된 고급 언어를 바탕으로 어셈블리 코드를 생산하고, 이 코드는 어셈블러에 의해서 소비된다. 이 때, 생산자는 컴파일러, 소비자는 어셈블러가 된다.

그런데, 생산자-소비자 문제에서 말하는 "문제"는 무엇일까?

생산자-소비자 문제에서 생산자와 소비자는 동기화된 상태로 동작해야 한다. 따라서 생산자는 생산만, 소비자는 소비만 담당하며 소비자는 반드시 생산자에 의해 생산된 것만 소비해야 하고 생산되지 않은 것을 소비하면 안된다.

그렇다면 생산자 프로세스와 소비자 프로세스가 동기화된 상태로 작업을 하기 위해서는 어떻게 해야 할까?

이것에 대한 한 가지 해결책은 계속 다루고 있던 주제인 공유 메모리를 사용하는 것이다. 생산자 프로세스와 소비자 프로세스가 동시에 실행될 수 있도록 생산자가 채우고 소비자가 비울 수 있는 버퍼를 사용할 수 있어야 한다. 그리고 이 버퍼는 생산자와 소비자 프로세스가 공유하는 메모리 영역에 있다. 이로써 두 개의 프로세스는 공유 메모리 영역에 액세스해서 동시에 작업할 수 있게 된다.

소비자 프로세스가 버퍼에 담긴 아이템을 소비하는 동안 생산자 프로세스는 새로운 아이템을 생산할 수 있다. 이러한 방식으로 통신을 할 경우 생산자와 소비자 프로세스는 반드시 서로 동기화된 상태여야 소비자 프로세스가 아직 생산되지 않은 아이템을 소비하려 하지 않는다.

버퍼의 종류 2가지: Unbounded buffer, Bounded buffer

위에서 생산자 프로세스와 소비자 프로세스가 사용하는 버퍼에 대해서 다루었다. 이 때 버퍼는 무한 버퍼와 유한 버퍼로 나누어진다.

무한 버퍼(Unbounded buffer)

무한 버퍼에서는 그 이름 처럼 버퍼의 사이즈에 제한이 없다. 즉, 생산자 프로세스는 계속해서 아이템을 생산할 수 있다. 반대로 소비자 프로세스는 오직 버퍼가 비어 있을 때만 새로운 아이템을 위해 기다리게 된다.

유한 버퍼(Bounded buffer)

유한 버퍼는 무한 버퍼와 반대로 버퍼의 사이즈에 한계가 존재한다. 유한 버퍼에서는 버퍼의 사이즈가 고정되어 있기 때문에 버퍼가 비어 있을 때 소비자 프로세스는 새로운 아이템이 버퍼에 채워질 때까지 대기해야 하고, 버퍼가 가득찬 상태일 때 생산자 프로세스는 버퍼에 여유 공간이 생길 때까지 대기해야 한다.

3. 메시지 전달(Message Passing)

메시지 전달은 프로세스들이 동일한 주소 공간을 공유하지 않고 통신과 작업 동기화를 할 수 있는 메커니즘을 제공한다. 이 시스템은 특히 통신 프로세스가 네트워크로 연결되어 여러 컴퓨터에 상주할 수 있는 분산 환경에서 유용하다. 이러한 환경은 인터넷을 통한 채팅 서비스를 생각하면 좋다.

예를 들어, 서울에 사는 A 유저와 샌프란시스코에 사는 B 유저가 서로 채팅을 하려 한다고 해보자. 이러한 경우 유저 A의 시스템 속 프로세스와 유저 B의 시스템 속 프로세스가 통신해야 한다. 그러나 이 두 프로세스는 같은 시스템 속에 있는 것이 아니기 때문에 두 프로세스는 공유 메모리 영역을 할당해서 통신하는 것이 불가능하다. 둘은 네트워크를 통해서 연결되어 있는 상태이므로 이때는 메시지 전달 시스템을 활용하는 것을 생각해 볼 수 있다.

메시지 전달 시스템에서 메시지 전달을 위한 IPC 함수는 최소한 2가지를 지원해야 하는데, 메시지 송신(send(message))과 메시지 수신(receive(message))이 있다.

send는 한 프로세스가 통신을 희망하는 다른 프로세스에게 메시지를 보낼 수 있도록 한다. 그리고 receive는 수신하는 측의 프로세스가 다른 프로세스가 보낸 메시지를 수신할 수 있도록 한다.

이렇게 메시지를 주고 받을 때 메시지의 사이즈는 고정 길이(fixed size) 일 수도 있고 가변 길이(variable size) 일 수도 있다. 그리고 이 크기가 중요한 이유는 한 번에 전송 또는 수신할 수 있는 메시지의 길이가 제한되기 때문이다. 만약 메시지의 길이가 길다면 제한된 길이로 분할해서 여러 번 전송하거나, 여러 번 수신해서 조각들을 합해야 한다. 이러한 작업은 메시지의 길이에 따라서 작업하는 주체가 달라진다.

만약 메시지의 길이가 고정되어 있다면 프로그래머가 이 작업을 수행하기 때문에 프로그래밍 과정은 복잡하지만 메시지 전달 "시스템" 구현은 간단해질 수 있다.

반면, 메시지의 길이가 고정되어 있지 않다면 IPC 함수에서 이 작업을 진행하기 때문에 "시스템" 구현은 복잡해지지만, 프로그래밍은 더욱 간단해질 수 있다.

3.1. IPC의 Communication Link(통신 링크)

메시지 전달 시스템에서 두 개의 프로세스 P와 Q가 서로 통신하려면 두 프로세스 사이에 communication link가 설정되어 있어야 한다. 그리고 Communication link를 구현하는 방법은 물리적인 방법과 논리적인 방법이 있는데, 물리적인 방법으로는 공유 메모리, 하드웨어 버스, 네트워크 등이 있고 운영체제 입장에서는 논리적인 방법이 중요 관심 사항이다.

논리적인 구현 방법으로는 아래의 방법들이 존재한다.

  • Direct / Indirect communication (직접 / 간접)
  • Synchronous / Asynchronous communication (동기 / 비동기)
  • Automatic / Explicit buffering (자동 / 명시적)

그리고 각각의 논리적인 구현 방법에는 차례대로 명명방법(naming), 동기화(synchronization), 버퍼링(buffering)과 같은 문제들이 있다.

3.2. Naming

통신하려는 프로세스끼리는 서로를 어떻게 참조할지에 대한 방법이 있어야 하는데, 직접 참조하는 방법과 간접적으로 참조하는 방법 2가지가 존재한다.

3.2.1. Direct Communication 1

Direct communication 방식에서 서로 통신하려는 프로세스는 각자의 이름을 명시적으로 나타내야 한다. 프로세스 P와 Q를 가지고 예를 들면 아래와 같다.

  • send(P, message) : 프로세스 P에게 메시지를 전송
  • receive(Q, message) : 프로세스 Q로부터 메시지를 수신

이처럼 Direct Communication에서는 전송과 수신 프로세스에 수신자와 발신자의 이름을 명시하는 것을 확인할 수 있다. 그리고 서로의 이름을 명시하는 이러한 쳬계에서 communication link는 다음과 같은 특성을 가진다.

  • 서로 통신하려는 프로세스의 쌍 사이에는 자동적으로 링크가 설정된다. 따라서 각 프로세스들은 통신을 위한 서로의 ID값만 알고 있으면 된다.
  • 링크는 오직 두 개의 프로세스 사이에서만 연관된다.
  • 따라서, 두 개의 프로세스 사이에는 정확히 1개의 링크만 존재한다.

현재 다룬 Direct communication 체계에서는 서로의 이름을 명시하기 때문에 주소 지정에서 대칭되는 모습을 나타낸다. 즉, 위의 예시에서도 보았듯이 sender process와 receiver process 모두 서로 통신할 이름을 지정해야 한다.

3.2.2. Direct Communication 2

앞에서 다룬 Direct communication 체계에서와 같이 항상 주소 지정 시 대칭되는 모습을 나타내야 하는 것은 아니다. 또 다른 종류의 Direct communication은 오직 sender process만 수신자의 이름을 명시한다.

  • send(P, message) : 프로세스 P에게 메시지 전송
  • receive(id, message) : 아무 프로세스로부터 메시지를 받을 준비가 되어 있음

Sender process는 수신자의 이름과 메세지를 담고 있지만, Receiver process는 송신자의 이름을 가지고 있지 않다. 대신, receiver process의 id는 초기에 이름이 설정되지 않지만 이후에 통신이 이루어진 프로세스의 이름으로 설정된다.

결과적으로, 앞서 살펴본 Direct Communication과 달리 이 체계는 주소 지정 시 비대칭되는 모습을 나타낸다.

3.2.3. Direct Communication의 문제점

앞서 살펴본 2가지 종류의 Direct Communication 체계는 주소가 대칭이든 아니든 최소한 sender process는 receiver의 이름을 프로세스에 명시한다. 따라서 어떤 프로세스의 이름을 변경하게 되면 sender 또는 receiver에 연결된 모든 다른 프로세스 정의에 대한 검사가 필요해진다. 이러한 문제에 대한 해결책이 Indirect Communication(간접통신) 이다.

3.2.4. Indirect Communication

간접통신 방식에서는 메일박스(mailbox) 또는 포트(port) 를 통해서 메시지를 송신하고 수신한다. 그리고 이 메일박스는 프로세스에 의해서 메시지를 넣거나 제거할 수 있는 추상적인 객체로 볼 수 있다.

각 메일박스는 고유의 ID를 가지고 있어 sender 프로세스는 어떤 메일박스로 보내야 하는지, 그리고 receiver 프로세스는 어떤 메일박스로부터 수신해야 하는지를 알 수 있다. 그리고 프로세스들은 서로 공유하는 메일박스가 있을 때 메일박스를 통해서 통신할 수 있다.

프로세스 P와 Q가 있고 이 둘이 메일박스 A를 공유하고 있을 때 프로세스 간의 Indirect Communication은 다음과 같이 이루어질 수 있다.

  • send(A, message) : 메일박스 A로 메시지를 송신
  • receive(A, message) : 메일박스 A에서 메시지를 수신

3.2.5. Indirect Communication 체계에서 communication link의 특성

  1. Indirect Communication에서 프로세스들은 서로 공유하는 메일박스가 있을 때만 통신하는 것이 가능하다. 따라서, 이렇게 공유하는 메일박스가 있을 때 통신 링크가 설정될 수 있다.
  2. Indirect Communication에서 communication link는 두 개 이상의 프로세스와 연관가능하다. 앞서 살펴본 Direct Communication 체계에서 오직 두 개의 프로세스끼리만 링크를 설정할 수 있던 점과 차별되는 점이다.
  3. 서로 통신하는 프로세스들 사이에는 다수의 링크가 설정되어 있을 수 있지만, 모든 링크들은 하나의 메일박스에 대응된다.

3.2.6. Indirect Communication: 3개의 프로세스가 하나의 메일박스를 공유할 때

예를 들어 위와 같이 프로세스 P1, P2, P3가 있고 이 셋이 공유하는 메일박스 A가 있다고 가정해보자. 이 상황에서 P1 프로세스는 메일박스로 메시지를 보냈고, P2와 P3 프로세스는 메일박스 A에 대해서 receive() 연산을 실행했다. 그렇다면 어떤 프로세스가 P1으로부터 메시지를 수신할 수 있을까?

이 질문에 대한 해답은 어떤 방법을 택하는지에 따라서 달라진다.

첫 번째로 생각할 수 있는 방법은 링크가 최대 2개의 프로세스와 연결되도록 허용하는 것이다. 앞서 살펴본 바와 같이 indirect communication에서 communication link는 두 개 이상의 프로세스와 연결가능하다고 했다. 현재 위의 상황을 살펴보면 P1이 P2와 P3에 모두 연결되어 있는 모습을 하고 있다. 이러한 상황에서 P3와의 연결은 끊고 오직 P1과 P2 사이에만 링크를 설정하는 것이다. 결과적으로 P2만 메시지를 수신할 수 있다.

두 번째 방법은 한 번에 최대 하나의 프로세스가 receive() 연산을 실행하도록 하는 것이다. 이러한 경우 특정 시간에 receive() 연산을 가장 먼저 수행한 프로세스가 메시지를 수신할 수 있다.

세 번째 방법은 메시지를 수신할 프로세스를 시스템이 임의로 선택하도록 허용하는 것이다. 이 때, 어떤 프로세스가 수신할지에 대한 선택 알고리즘을 따로 정의할 수 있다. 즉, 시스템이 임의로 P2와 P3 중 누가 메시지를 수신할지 정할 수 있고, 이 때 선택 기준으로 다양한 알고리즘(e.g., 라운드 로빈)을 적용할 수 있다.

3.2.7. Indirect Communication: 메일박스의 소유권

Indirect Communication을 위해 사용하는 메일박스는 프로세스 또는 운영체제가 소유할 수 있다.

만약 프로세스가 메일박스를 소유할 경우, 메일박스가 누구의 소유인지는 명확하다. 이것은 곧, 메일박스가 수신하는 모든 메시지가 어떤 프로세스에서 출발한 메시지인지 명확하게 알 수 있다는 것을 의미한다. 그런데, 메일박스를 소유한 프로세스가 종료된다면(terminate) 해당 메일박스도 함께 사라진다.

한편, 운영체제는 메일박스에 대한 생성, 송수신, 삭제 기능을 가지고 있다. 따라서 운영체제도 자체의 메일박스를 소유할 수 있다. 이렇게 생성된 메일박스는 메시지를 송수신할 때 활용하길 원하는 프로세스와 공유해서 사용할 수 있다.

Reference.