13 분 소요

본 글은 Operating System Concepts 10th (운영체제) 책을 보며 내용을 개인 공부에 목적으로 정리했습니다.
이전에 운영체제 관련 강의들을 들으면서 정리한 시리즈 글들이 있는데,
지식을 습득하는 데 있어 가장 느리지만 가장 빠른 방법이 원본책을 자세히 보는 것이라 생각됩니다.
책 내용들을 최대한 이해하기 위해 거의 모든 내용을 담고 있습니다.

책 pdf 링크 : Operating System Concepts 10th Edition by Abraham Silberschatz Peter B Galvin Greg Gagne pdf free download
연습 문제 정답지 : Solutions of Practice Exercises, Tenth Edition of Operating System Concepts, AVI SILBERSCHATZ


3. Process

  • 오늘날의 컴퓨터 시스템들은 메모리에 다수의 프로그램이 적재(load)되어 동시(concurrent) 실행되는 것을 허용한다.
  • 이러한 발전은 다양한 프로그램을 보다 견고하게 제어하고 보다 구획화(compartmentalization)할 것을 필요로 했고, 프로세스의 개념이 등장했다.
    • 프로세스는 실행 중인 프로그램을 말하며, 현대 컴퓨터 시스템에서 작업의 단위이다.


  • 목표
    1. 프로세스의 개별 구성요소를 식별하고 운영체제에서 해당 구성요소가 어떻게 표현되고 스케줄 되는지 기술한다.
    2. 운영체제에서 프로세스를 생성하고 종료하는 방법을 설명한다. 이러한 작업을 수행하는 적절한 시스템 콜을 사용하여 프로그램의 개발 등이 포함된다.
    3. 공유 메모리 및 메시지 전달을 사용하는 프로세스 간 통신을 설명하고 대조한다.
    4. 파이프와 POSIX 공유 메모리를 사용하여 프로세스 간 통신을 수행하는 프로그램을 설계한다.
    5. 소켓과 원격 프로시저 호출을 사용하여 클라이언트-서버 통신을 설명한다.
    6. Linux 운영체제와 상호 작용하는 커널 모듈을 설계한다.


3.1 Process Concept

  • 초창기 컴퓨터는 CPU 활동들을 작업(job)을 실행하는 일괄처리(batch) 시스템이었고, 사용자 프로그램(user program) 또는 태스크(task)를 실행하는 시분할 시스템이 뒤를 이었다.
  • 현대에 와서 이러한 모든 활동을 프로세스라고 부른다.


3.1.1 The Process

  • 프로세스의 현재 활동의 상태(status)는 프로그램 카운터(program counter) 값과 프로세서 레지스터의 내용으로 나타낸다.
    • 프로그램 카운터(program counter) : 마이크로프로세서 내부에 있는 레지스터 중의 하나로서, 다음에 실행될 명령어의 주소를 가지고 있어 실행할 기계어 코드의 위치를 지정한다. (=명령어 포인터) 1
  • 프로세스의 메모리 배치는 다음 그림과 같이 여러 섹션으로 구분된다.

image

  • Text section : 실행가능한 코드
  • Data section : 전역 변수(global variable)
  • Heap section : 프로그램 실행 중에 동적으로 할당되는 메모리
    • 동적(dynamic) : 그때 그때 생성하는 것을 말한다.
    • 동적 메모리 할당 : 실행 시간 동안 사용할 메모리 공간을 할당하는 것을 말한다.
  • Stack section : 함수를 호출할 때 임시 데이터 저장장소(함수 파라미터, 리턴 주소, 지역 변수)


  • 텍스트 및 데이터 섹션의 크기는 고정되기 때문에 프로그램 실행 시간 동안 크기가 변하지 않는다. 그러나 스택 및 힙 섹션은 프로그램 실행 중에 동적으로 줄어들거나 커질 수 있다.
    • 함수가 호출될 때마다 함수 파라미터, 지역 변수 및 리턴 주소를 포함하는 활성화 레코드(activation record)가 스택에 푸시(push)된다.
    • 함수로부터 제어(control)가 리턴되면 스택에서 활성화 레코드가 팝(pop) 된다.
    • 메모리가 동적으로 할당됨에 따라 힙이 커지고 메모리가 시스템에 반환되면 축소된다.


  • 실행 파일이 메모리에 적재될 때 프로그램은 프로세스가 된다.
  • 프로세스가 실행되는 과정에서 많은 프로세스를 생성하는 것이 보통이다.
  • 예를 들어 대부분의 상황에서 실행 가능한 Java 프로그램은 JVM 안에서 실행된다. JVM은 적재된 Java 코드를 해석하고 그 코드를 대신하여 원 기계어를 이용하여 행동을 취하는 프로세스로서 프로그램을 실행한다.
  • 다음은 C 프로그램의 메모리 배치 그림이다.

image


3.1.2 Process State

image

  • 프로세스는 실행되면 그 상태(state)가 변한다.
  • 프로세스의 상태는 부분적으로 그 프로세스의 현재의 활동에 따라서 정의된다.
  • 새로운(new) : 프로세스가 생성중이다.
  • 준비(ready) : 프로세스가 프로세서에 할당되기를 기다린다.
  • 실행(running) : 명령어들이 실행되고 있다.
  • 대기(waiting) : 프로세스가 어떤 이벤트(입출력 완료 등)가 일어나기를 기다린다.
  • 종료(terminated) : 프로세스의 실행이 종료되었다.


3.1.3 Process Control Block

  • 각 프로세스는 운영체제에서 프로세스 제어 블록(process control block, PCB)에 의해 표현된다.

image

  • PCB는 특정 프로세스와 연관된 여러 정보를 수록하며, 다음과 같은 것들을 포함한다.
    • 프로세스 상태(process state) : new, ready, running, waiting, halted(정지) 상태 등이다.
    • 프로그램 카운터(program counter) : 이 프로세스가 다음에 실행할 명령어의 주소를 가리킨다.
    • CPU 레지스터들(registers) : 레지스터에는 누산기(accumulator), 인덱스 레지스터, 스택 레지스터, 범용 레지스터들과 상태 코드 정보가 포함된다.
    • CPU-스케줄링 정보 : 이 정보는 프로세스 우선순위, 스케줄 큐에 대한 포인터와 다른 스케줄 파라미터를 포함한다.
    • 메모리 관리 정보(memory-management information) : 이 정보는 운영체제에 의해 사용되는 메모리 시스템에 따라 기준(base) 레지스터와 한계(limit) 레지스터의 값, 운영체제가 사용하는 메모리 시스템에 따라 페이지 테이블(page table) 또는 세그먼트 테이블(segment table) 등과 같은 정보를 포함한다.
    • 회계(accounting) 정보 : 이 정보는 CPU 사용 시간과 경과된 실시간, 시간 제한, 계정 번호, 잡 또는 프로세스 번호 등을 포함한다.
    • 입출력 상태 정보 : 이 정보는 이 프로세스에 할당된 입출력 장치들과 open 파일의 목록 등을 포함한다.
  • 즉, PCB는 약간의 회계 데이터와 함께 프로세스를 시작시키거나 다시 시작시키는 데 필요한 모든 데이터를 위한 저장소의 역할을 한다.


3.1.4 Threads

  • 싱글 제어 스레드는 프로세스가 한 번에 한 가지 일만 실행하도록 허용한다.
  • 현대 운영체제는 프로세스 개념을 확장하여 한 프로세스가 다수의 실행 스레드를 가질 수 있도록 한다.
    • 프로세스가 한 번에 하나 이상의 일을 수행할 수 있도록 허용한다.
    • ex. 워드 프로세서는 하나의 스레드에 사용자 입력 관리를 맡기는 동안 다른 스레드가 철자 검사기를 수행하도록 만들 수 있다.


3.2 Process Scheduling

  • 멀티 프로그래밍의 목적은 CPU 이용을 최대화하기 위하여 항상 어떤 프로세스가 실행되도록 하는 데 있다.
  • 시분할(time sharing)의 목적은 각 프로그램이 실행되는 동안 사용자가 상호 작용할 수 있도록 프로세스들 사이에서 CPU 코어를 빈번하게 교체하는 것이다.
  • 이 목적을 달성하기 위해 프로세스 스케줄러(process scheduler)는 코어에서 실행 가능한 여러 프로세스 중에서 하나의 프로세스를 선택한다.
    • 스케줄링(scheduling) : 시스템의 목표를 달성할 수 있도록 프로세서를 할당하는 일련의 과정
    • 스케줄러(scheduler) : ready 상태의 프로세스 중에서 이번에 CPU를 줄 프로세스를 결정하는 프로그램


3.2.1 Scheduling Queue

image

  • 프로세스가 시스템에 들어가면 준비(ready) 큐에 들어가서 준비 상태가 되어 CPU 코어에서 실행되기를 기다린다.
  • 이 큐는 연결 리스트로 저장된다.
  • 준비 큐 헤더(header)에는 리스트의 첫 번째 PCB에 대한 포인터가 저장되고 각 PCB에는 준비 큐의 다음 PCB를 가리키는 포인터 필드가 포함된다.
  • I/O 완료와 같은 특정 이벤트가 발생하기를 기다리는 프로세스는 대기 큐에 삽입된다.

작업 큐(Job Queue) : 현재 시스템 내에 있는 모든 프로세스의 집합
준비 큐(Ready Queue) : 현재 메모리 내에 있으면서 CPU를 잡아서 실행되기를 기다리는 프로세스의 집합
장치 큐(Device Queue) : I/O 디바이스의 처리를 기다리는 프로세스의 집합 2


image

  • 프로세스 스케줄링의 일반적인 표현은 Figure 3.5와 같은 큐잉 다이어그램(queueing diagram)이다.
  • 준비 큐와 대기 큐의 집합의 2가지 유형의 큐가 제시되어 있다.
  • 원은 큐에 서비스를 제공하는 자원을 나타내고 화살표는 시스템의 프로세스의 흐름을 나타낸다.
  • 새 프로세스는 처음에 준비 큐에 놓인다. 프로세스는 실행을 위해 선택되거나 또는 디스패치(dispatch) 될 때까지 기다린다.
  • 프로세스에 CPU 코어가 할당되고 실행 상태가 되면, 여러 이벤트 중 하나가 발생할 수 있다.


3.2.2 CPU Scheduling

  • 프로세스는 수명주기 동안 준비 큐와 다양한 대기 큐를 이동한다(migrate).
  • CPU 스케줄러(cpu scheduler)의 역할은 준비 큐에 있는 프로세스 중에서 선택된 하나의 프로세스에 CPU 코어를 할당하는 것이다.
    • CPU 스케줄러는 CPU를 할당하기 위한 새 프로세스를 자주 선택해야 한다.
  • 일부 운영체제는 스와핑(swapping)으로 알려진 중간 형태의 스케줄링을 가지고 있다.
    • 핵심 아이디어는 메모리에서 프로세스를 제거하여 멀티 프로그래밍의 정도를 감소시키는 것이 유리할 수 있다는 것이다.
    • 프로세스를 메모리에서 디스크로 스왑아웃(swapped out)하고 현재 상태를 저장하고, 이후 디스크에서 메모리로 스왑인(swapped in)하여 상태를 복원할 수 있기 때문에 이 기법을 스와핑(swapping)이라 한다.
    • 스와핑은 일반적으로 메모리가 초과 사용되어 가용공간을 확보해야 할 때만 필요하다.


3.2.3 Context Switch

  • 인터럽트는 운영체제가 CPU 코어를 현재 작업에서 뺏어 내어 커널 루틴을 실행할 수 있게 한다.
  • 인터럽트가 발생하면 시스템은 인터럽트 처리가 끝난 후에 컨텍스트(context)를 복구할 수 있도록 현재 실행 중인 프로세스의 현재 컨텍스트를 저장할 필요가 있다.
    • 컨텍스트(context) : 현재 CPU를 사용중인 프로세스의 CPU 제어권이 다른 프로세스로 이양되는 과정 3
  • 컨텍스트는 프로세스의 PCB에 표현된다.
  • 컨텍스트는 CPU 레지스터의 값, 프로세스의 상태, 메모리 관리 정보 등을 포함한다.
    • 일반적으로 커널 모드이건 사용자 모드이건 CPU의 현재 상태를 저장하는(state save) 작업을 수행하고, 나중에 연산을 재개하기 위하여 상태 복구 작업(state restore)을 수행한다.


  • CPU 코어를 다른 프로세스로 교환하려면 이전의 프로세스의 상태를 보관하고 새로운 프로세스의 보관된 상태를 복구하는 작업이 필요한데, 이 작업을 컨텍스트 교환(context switch)라 한다.

image

  • 컨텍스트 교환이 일어나면, 커널은 과거 프로세스의 컨텍스트를 PCB에 저장하고, 실행이 스케줄된 새로운 프로세스의 저장된 컨텍스트를 복구한다.
    • 컨텍스트 교환이 진행될 동안 시스템이 아무런 유용한 일을 못 하기 때문에 컨텍스트 교환 시간은 순수한 오버헤드이다.
    • 오버헤드(overhead) : 컨텍스트 교환에 필요한 시간, 메모리 등을 말한다.


3.2 Operation on Processes

  • 대부분 시스템 내의 프로세스들은 동시 실행될 수 있으며, 반드시 동적(그때 그때)으로 생성되고, 제거되어야 한다.
  • 그러므로 운영체제는 프로세스 생성 및 종료를 위한 기법을 제공해야 한다.


3.3.1 Process Creation

  • 실행되는 동안 프로세스는 여러 개의 새로운 프로세스들을 생성할 수 있다.
  • 생성하는 프로세스를 부모(parent) 프로세스라고 부르고, 새로운 프로세스는 자식(child) 프로세스라고 부른다.
  • 이 새로운 프로세스들은 각각 다시 다른 프로세스들을 생성할 수 있으며, 그 결과 프로세스의 트리(tree)를 형성한다.
  • 운영체제는 고유한 프로세스 식별자(process identifier, pid)를 사용하여 프로세스를 구분한다.
  • 이 식별자를 통하여 커널이 유지하고 있는 프로세스의 다양한 속성에 접근하기 위한 인덱스로 사용된다.

image


  • 프로세스가 새로운 프로세스를 생성할 때, 두 프로세스를 실행시키는 데 두 가지 가능한 방법이 존재한다.

    1. 부모는 자식과 동시하게 실행을 계속한다.
    2. 부모는 일부 또는 모든 자식이 실행을 종료할 때까지 기다린다.
  • 새로운 프로세스들의 주소 공간 측면에서 볼 때 다음과 같은 두 가지 가능성이 있다.

    1. 자식 프로세스는 부모 프로세스의 복사본이다.
    2. 자식 프로세스가 자신에게 적재될 새로운 프로그램을 가지고 있다.


image

  • UNIX 시스템 콜을 봐본다. (figure 3.9)
  • 동일한 프로그램의 복사본을 실행하는 두 개의 서로 다른 프로세스를 갖는다.
    • 유일한 차이점은 자식 프로세스에 보이는 pid의 값은 0이고, 반면에 부모 프로세스에게 보이는 pid 값은 0보다 큰 정수 값이라는 것이다.
  • 자식 프로세스는 오픈 파일과 같은 리소스뿐 아니라 특권과 스케줄링 속성을 부모 프로세스로부터 상속받는다.
  • 그런 후에 자식 프로세스는 exec() 시스템 콜을 사용하여 자신의 주소 공간을 UNIX 명령 /bin/ls로 덮어쓴다.
  • 부모는 wait() 시스템 콜로 자식 프로세스가 끝나기를 기다린다.
  • 자식 프로세스가 끝나면, 부모 프로세스는 wait() 호출로부터 재개하여, exit() 시스템 콜을 사용하여 끝낸다.


3.3.2 Process Termination

  • 프로세스가 마지막 문장의 실행을 끝내고, exit 시스템 콜을 사용하여 운영체제에 자신의 삭제를 요청하면 종료한다.
  • 이 시점에서, 프로세스는 자신을 기다리고 있는 부모 프로세스에 상태 값을 반환할 수 있다.
  • 물리 메모리와 가상 메모리 오픈 파일, 입출력 버퍼를 포함한 프로세스의 모든 자원이 할당 해제되고 운영체제로 반납된다.
  • 부모는 다음과 같이 여러 가지 이유로 인하여 자식 중 하나의 실행을 종료할 수 있다.
    1. 자식이 자신에게 할당된 리소스를 초과하여 사용할 때, 이때는 부모가 자식들의 상태를 검사할 수 있는 방편이 주어져야 한다.
    2. 자식에게 할당된 태스크가 더 이상 필요 없을 때
    3. 부모가 exit를 하는데, 운영체제는 부모가 exit 한 후에 자식이 실행을 계속하는 것을 허용하지 않는 경우


  • 몇몇 시스템에서는 부모 프로세스가 종료한 이후에 자식 프로세스가 존재할 수 없다.
  • 그러한 시스템에서는 프로세스가 종료되면 그로부터 비롯된 모든 자식 프로세스들도 종료되어야 한다. 이것을 연쇄식 종료(cascading termination)이라 하며, 이 작업은 운영체제가 시행한다.
  • 좀비(zombie) 프로세스 : 종료되었지만 부모 프로세스가 아직 wait() 호출을 하지 않은 프로세스
  • 고아(orphan) 프로세스 : 부모 프로세스가 wait()를 호출하는 대신 종료할 때 부닥친 자식 프로세스

3.4 Interprocess Communication

  • 운영체제 내에서 실행되는 동시 프로세스들은 독립적이거나 또는 협력적인 프로세스들 일 수 있다.
  • 프로세스가 시스템에서 실행 중인 다른 프로세스들과 데이터를 공유하지 않는 프로세스는 독립적(independent)이다.
  • 프로세스가 시스템에서 실행 중인 다른 프로세스들에 영향을 주거나 받는다면 협력적(cooperating)인 프로세스들이다.
  • 프로세스 협력을 허용하는 환경을 제공하는 데는 다음과 같은 이유가 있다.
    1. 정보 공유(information sharing) : 여러 응용 프로그램이 동일한 정보에 흥미를 느낄 수 있으므로, 그러한 정보를 동시적(concurrent)으로 접근할 수 있는 환경을 제공해야 한다.
    2. 계산 가속화(computation speedup) : 만일 우리가 특정 태스크를 빨리 실행하고자 한다면, 그것을 서브태스크로 나누어 이들 각각이 다른 서브태스크들과 병렬(parallel)로 실행되게 해야 한다.
    3. 모듈성(modularity) : 시스템 기능을 별도의 프로세스들 또는 스레드들로 나누어, 모듈식 형태로 시스템을 구성하기를 원할 수 있다.


image

  • 협력적 프로세스들은 데이터를 교환할 수 있는, 즉 서로 데이터를 보내거나 받을 수 있는 프로세스 간 통신(interprocess communication, IPC) 기법이 필요하다.
  • 프로세스 간 통신에는 기본적으로 공유 메모리(shared memory)메시지 전달(message passing)의 두 가지 모델이 있다.
  • 공유 메모리 모델에서는 협력 프로세스들에 의해 공유되는 메모리의 영역이 구축된다.
    • 프로세스들은 그 영역에 데이터를 읽고 쓰고 함으로써 정보를 교환할 수 있다.
  • 메시지 전달 모델에서는 통신이 협력 프로세스들 사이에 교환되는 메시지를 통하여 이루어진다.
  • 운영체제에서는 통상적으로 두 모델을 쓴다.
  • 메시지 전달 모델은 충돌을 회피할 필요가 없기 때문에 적은 양의 데이터를 교환하는 데 유용하다.
    • 메시지 전달은 또한 분산 시스템에서 공유 메모리보다 구현하기 쉽다.
  • 메시지 전달 시스템은 시스템 콜을 사용하여 구현되므로 커널 간섭 등의 부가적인 시간 소비 작업 필요하기 때문에 공유 메모리 모델이 메시지 전달보다 더 빠르다.
  • 공유 메모리 시스템에서는 공유 메모리 영역을 구축할 때만 시스템 콜이 필요하다.
  • 공유 메모리 영역이 구축되면 모든 접근은 일반적인 메모리 접근으로 취급되어 커널의 도움이 필요 없다.


3.5 IPC in Shared-Memory Systems

  • 공유 메모리를 사용하는 프로세스 간 통신에서는 통신하는 프로세스들이 공유 메모리 영역을 구축해야 한다.
  • 공유 메모리 영역은 공유 메모리 세그먼트(segment)를 생성하는 프로세스의 주소 공간에 위치한다.
  • 이 공유 메모리 세그먼트를 이용하여 통신하고자 하는 다른 프로세스들은 이 세그먼트를 자신의 주소 공간에 추가하여야 한다.
  • 공유 메모리는 둘 이상의 프로세스가 제약 조건을 제거하는 것에 동의하는 것을 필요로 한다.
    • 제약 조건은 운영체제는 한 프로세스가 다른 프로세스의 메모리에 접근하는 것을 금지한다.
  • 그런 후에 프로세스들은 공유 영역에 읽고 씀으로써 정보를 교환할 수 있다.
  • 데이터의 형식과 위치는 이들 프로세스에 의해 결정되며 운영체제의 소관이 아니다.
  • 또한 프로세스들은 동시에 동일한 위치에 쓰지 않도록 책임져야 한다.


3.6 IPC in Message-Passing Systems

  • 메시지 전달(message passing) 방식은 동일한 주소 공간을 공유하지 않고도 프로세스들이 통신을 하고, 그들의 동작을 동기화할 수 있도록 허용하는 기법을 제공한다.
  • 메시지 전달 방식은 통신하는 프로세스들이 네트워크에 의해 연결된 다른 컴퓨터들에 존재할 수 있는 분산 환경에서 특히 유용하다.
  • 메시지 전달 시스템은 최소한 2가지 연산을 제공한다.
    • send(message)
    • receive(message)
  • 프로세스가 보낸 메시지는 고정(fixed) 길이일 수도 있고 가변(variable) 길이일 수도 있다.
    • 고정 길이 메시지만 보낼 수 있으면 시스템 수준의 구현은 직선적(straightforward)이지만 이러한 제한은 프로그래밍 작업을 더욱 힘들게 한다.
    • 가변 길이 메시지는 보다 복잡한 시스템 수준의 구현이 있어야 하지만, 프로그래밍 작업은 더 간단해진다.
  • 만약 프로세스 $P$와 $Q$가 통신을 원하면, 반드시 서로 메시지를 보내고 받아야 한다.
  • 이들 사이에 통신 연결(communication link)이 설정되어야 한다.
  • 하나의 링크와 send()/receive() 연산을 논리적으로 구현하는 방법은 다음과 같다.
    • direct 또는 indirect 통신(communication)
    • synchronous 또는 asynchronous 통신(communication)
    • automatic 또는 explicit 버퍼링(buffering)


3.6.1 Naming

  • 통신을 원하는 프로세스들은 서로를 가리킬 방법이 있어야 한다. 이들은 직접(direct) 또는 간접(indirect) 통신을 사용할 수 있다.
  • 직접(direct) 통신 하에서, 통신을 원하는 각 프로세스는 통신의 수신자(recipient) 또는 송신자(sender)의 이름을 명시해야 한다.
    • send(P, message) : 프로세스 P에 메시지를 전송한다.
    • receive(Q, message) : 프로세스 Q로부터 메시지를 수신한다.
  • 이 기법에서 통신 연결은 다음의 특성을 가진다.

    1. 통신을 원하는 각 프로세스의 쌍들 사이에 연결이 자동으로 구축된다. 프로세스들은 통신하기 위해 상대방의 신원(identity)만 알면 된다.
    2. 연결은 정확히 두 프로세스 사이에만 연관된다.
    3. 통신하는 프로세스들의 각 쌍 사이에는 정확하게 하나의 연결이 존재해야 한다.
  • 위 기법은 주소 방식에서 대칭성(symmetry)을 보인다.
  • 기법의 변형으로서 주소 지정 시에 비대칭(asymmetry)을 사용할 수도 있다.
    • 송신자만 수신자 이름을 지명하며, 수신자는 송신자의 이름을 제시할 필요가 없다.
    • send(P, message) : 프로세스 P에 메시지를 전송한다.
    • receive(id, message) : 임의의 프로세스로부터 메시지를 수신한다. 변수 id는 통신을 발생시킨 프로세스의 이름으로 설정된다.


  • 간접(indirect) 통신에서 메시지들은 메일박스(mailbox) 또는 포트(port)로 송신되고, 그것으로부터 수신된다.
    • 메일박스(mailbox) : 추상적으로 프로세스들에 의해 메시지들이 넣어지고, 메시지들이 제거될 수 있는 객체라고 볼 수 있다.
    • 포트(port) : 운영체제 통신의 종단점. 네트워크 서비스나 특정 프로세스를 식별하는 논리 단위 1
  • 각 메일박스는 고유의 id를 가진다.
  • 두 프로세스들이 공유 메일박스를 가질 때만 이들 프로세스가 통신할 수 있다.
    • send(A, message) : 메일박스 A로 메시지를 송신한다.
    • receive(A, message) : 메일박스 A로부터 메시지를 수신한다.
  • 이 방법에서 통신 연결은 다음의 성질을 가진다.

    1. 한 쌍의 프로세스들 사이의 연결은 이들 프로세스가 공유 메일박스를 가질 때만 구축된다.
    2. 연결은 두 개 이상의 프로세스들과 연관될 수 있다.
    3. 통신하고 있는 각 프로세스 사이에는 다수의 서로 다른 연결이 존재할 수 있고, 각 연결은 하나의 메일박스에 대응된다.


  • 메일박스는 한 프로세스 또는 운영체제에 의해 소유될 수 있다.
  • 메일박스가 한 프로세스에 의해 소유된다면, 소유자와 메일박스의 사용자를 구분할 수 있다.
    • 메일박스는 프로세스의 주소 공간의 일부이다.
    • 소유자는 이 메일박스로부터 메시지를 수신만 가능한 프로세스이다.
    • 메일박스의 사용자는 메일박스에 메시지를 송신만 할 수 있는 프로세스이다.
  • 메일박스를 소유하고 있는 프로세스가 종료할 때 메일박스는 사라진다.
  • 그 후에 이 메일박스로 메시지를 송신하는 모든 프로세스는 더는 메일박스가 존재하지 않는다는 사실을 반드시 통보받아야 한다.
  • 반면에, 운영체제가 소유한 메일박스는 자체적으로 존재한다.
    • 이것은 독립적인 것으로 어떤 특정한 프로세스에 예속되지 않는다.


3.6.2 Synchronization

  • 메시지 전달은 블록형(blocking)이거나 비블록형(nonblocking) 방식으로 전달된다. 이 두 방식은 각각 동기식(synchronous)비동기식(asynchronous)이라고도 알려져 있다.
    • blocking send : 송신하는 프로세스는 메시지가 수신 프로세스 또는 메일박스에 의해 수신될 때까지 블록된다.
    • nonblocking send : 송신하는 프로세스가 메시지를 보내고 작업을 재시작한다.
    • blocking receive : 메시지가 이용 가능할 때까지 수신 프로세스가 블록된다.
    • nonblocking receive : 송신하는 프로세스가 유효한 메시지 또는 null을 받는다.
  • send()와 receive()가 모두 블록형일 때, 송신자와 수신자 간에 랑데부(rendezvous)를 하게 된다.
    • 랑데부(rendezvous) : 두 개 이상의 프로세스를 네트워크에 어떠한 수동 설정 없이 연결한다는 의미이다. 3


3.6.3 Buffering

  • 통신이 직접적이든 간접적이든 간에, 통신하는 프로세스들에 의해 교환되는 메시지는 임시 큐(queue)에 들어 있다.
  • 이러한 큐를 구현하는 방식은 3가지가 있다.
    1. 무용량(zero capacity) : 큐의 최대 길이가 0이다. 즉, 링크는 자체 안에 대기하는 메시지들을 가질 수 없다. 이 경우, 송신자는 수신자가 메시지를 수신할 때까지 기다려야 한다.
    2. 유한 용량(bounded capacity) : 큐는 유한한 길이 n을 가진다. 즉, 최대 n개의 메시지가 그 안에 들어 있을 수 있다. 링크가 만약 만원이면, 송신자는 큐 안에서 공간이 이용 가능할 때까지 반드시 블록되어야 한다.
    3. 무한 용량(unbounded capacity) : 큐는 잠재적으로 무한한 길이를 가진다. 따라서 메시지들이 얼마든지 큐 안에서 대기할 수 있다. 송신자는 절대 블록되지 않는다.


3.7 Examples of IPC Systems

  • 이 부분은 예시이므로 생략한다.


3.8 Communication in Client-Server Systems

  • 클라이언트 서버에서 사용할 수 있는 두 가지 다른 통신 전략에 대해 설명한다.


3.8.1 Socket

  • 소켓(socket)은 통신의 극점(endpoint)을 뜻한다.
  • 두 프로세스가 네트워크상에서 통신을 하려면 양 프로세스마다 하나씩, 총 2개의 소켓이 필요해진다.
  • 각 소켓은 IP 주소와 포트 번호 2가지를 접합(concatenate)해서 구별한다.
  • 일반적으로 소켓은 클라이언트-서버(client-server) 구조를 사용한다.
    • 서버는 지정된 포트에 클라이언트 요청 메시지가 도착하기를 기다리게 된다.
    • 요청이 수신되면 서버는 클라이언트 소켓으로부터 연결 요청을 수락함으로써 연결이 완성된다.
  • Telent, ftp 및 http 등의 특정 서비스를 구현하는 서버는 well-known 포트로부터 메시지를 기다린다.
    • SSH 서버 : 22번 포트
    • FTP 서버 : 21번 포트
    • HTTP 서버 : 80번 포트
    • 1024 미만의 모든 포트는 well-known 포트로 간주되며 표준 서비스를 구현하는 데 사용된다.

image


  • Java는 3가지 종류의 소켓을 제공한다. 연결 기반(connection-oriented) TCP(Transmission Control Protocol) 소켓은 Socket 클래스로 구현된다. 비연결성(Connectionless) UDP(User Datagram Protocol) 소켓은 DatagramSocket 클래스를 사용한다. MulticastSocket 클래스는 DatagramSocket 클래스의 서브클래스이다.
    • Multicast 소켓은 데이터를 여러 수신자에게 보낼 수 있다.
  • 다음 예제 프로그램은 연결 기반 TCP 소켓을 사용하는 data 서버를 설명한다.
// Data server
import java.net.*;
import java.io.*;
public class DateServer
{
    public static void main(String[] args) {
        try {
            // 서버는 포트 6013을 listen 한다는 것을 가정하는 ServerSocket를 생성한다.
            ServerSocket sock = new ServerSocket(6013);

            /* now listen for connections */
            while (true) {
                /*
                accept() 메소드를 이용하여 listen 하게 된다.
                서버는 accept() 메소드에서 클라이언트가 연결을 요청할 때까지 블록된다.
                연결 요청이 들어오면 accept()는 클라이언트와 통신하기 위해 사용할 수 있는 소켓을 반환한다.
                */
                Socket client = sock.accept();
                
                // PrintWriter 객체는 추후 클라이언트와 통신하는 데 사용된다.
                PrintWriter pout = new
                 PrintWriter(client.getOutputStream(), true);

                // PrintWriter 객체는 서버가 print()나 println()과 같은 루틴을 서서 소켓에 데이터를 쓸 수 있게 한다.
                pout.println(new java.util.Date().toString());
                /* close the socket and resume */
                /* listening for connections */
                client.close();
            }
        }
        catch (IOException ioe) {
            System.err.println(ioe);
        }
    }
}


  • 클라이언트는 소켓을 생성하고 서버가 listen 하는 포트와 연결함으로써 서버와 통신을 시작한다.
    • listen() : 클라이언트의 연결요청을 기다리는 함수 [^6]
  • IP 주소 127.0.0.1은 루프백(loopback)을 나타내는 특별한 IP 주소이다.
    • 컴퓨터가 IP 주소 127.0.0.1을 참조하면 그 자신 기계를 지칭하는 것이다.
    • 이처럼 하면 같은 기계에 있는 클라이언트와 서버가 TCP/IP 프로토콜을 사용하여 통신하게 된다.
// Date Client
import java.net.*;
import java.io.*;
public class DateClient
{
    public static void main(String[] args) {
        try {
            /* 
            클라이언트는 Socket을 생성하고 IP 주소 127.0.0.1에 있는 포트 6013의 서버와 연결해주기를 요청한다.
            */
            Socket sock = new Socket("127.0.0.1",6013);
            InputStream in = sock.getInputStream();

            BufferedReader bin = new
             BufferedReader(new InputStreamReader(in));

            /* read the date from the socket */
            String line;
            while ( (line = bin.readLine()) != null)
                System.out.println(line);

            /* close the socket connection*/
            sock.close();
        }   
        catch (IOException ioe) {
            System.err.println(ioe);
        }
    }
}


  • 소켓을 이용한 통신은 분산된 프로세스 간에 널리 사용되고 효율적이기는 하지만 너무 낮은 수준이다.
  • 우선 소켓은 스레드 간에 구조화되지 않은 바이트 스트림만을 통신하도록 하기 때문이다.


3.8.2 Remote Procedure Calls, RPC

  • RPC는 네트워크에 연결된 두 시스템 사이의 통신에 사용하기 위하여 프로시저 호출 기법을 추상화(abstract)하는 방법으로 설계되었다.
    • 원격 프로시저 호출(Remote Procedure Call, RPC) : 별도의 원격 제어를 위한 코딩 없이 다른 주소 공간에서 함수나 프로시저를 실행할 수 있게 하는 프로세스 간 통신 기술이다. 즉, 원격 프로시저 호출을 이용하면 프로그래머는 함수가 실행 프로그램에 로컬 위치에 있든 원격 위치에 있든 동일한 코드를 이용할 수 있다. 2
    • 프로시저(procedure) : 명령 단위가 수행하는 절차
  • RPC는 IPC 기반 위에 만들어졌지만 여기서는 프로세스들이 서로 다른 시스템 위에서 돌아가기 때문에 원격 서비스를 제공하기 위해서는 메시지 기반 통신을 해야 한다.
  • IPC 방식과는 달리 RPC 통신에서 전달되는 메시지는 구조화되어 있고, 따라서 데이터의 패킷 수준을 넘어서게 된다.
  • 각 메시지에는 원격지 포트에서 listen 중인 RPC 데몬의 주소가 지정되어 있고 실행되어야 할 함수의 식별자, 그리고 그 함수에게 전달되어야 할 파라미터가 포함된다.
  • 그런 후에 요청된 함수가 실행되고 어떤 출력이든지 별도의 메시지를 통해 요청자에게 반환된다.
    • 포트(port)는 단순히 메시지 패키지의 시작 부분에 포함되는 정수이다.
    • 원격 프로세스가 어떤 서비스를 받고자 하면 그 서비스에 대응되는 적절한 포트 주소로 메시지를 보내야 한다.



이미지출처 [^4]

  • RPC는 클라이언트가 원격 호스트의 프로시저 호출하는 것을 마치 자기의 프로시저 호출하는 것처럼 해준다.
  • RPC 시스템은 클라이언트 쪽에 스텁(stub)을 제공하여 통신을 하는 데 필요한 자세한 사항들을 숨겨 준다.
  • 클라이언트가 원격 프로시저를 호출하면 RPC는 그에 대응하는 스텁을 호출하고 원격 프로시저가 필요로 하는 파라미터를 건네준다.
  • 그러면 스텁이 원격 서버의 포트를 찾고 파라미터를 정돈(마샬, marshall)한다.
  • 이에 대응되는 스텁이 서버에도 존재하여 서버 측 스텁이 메시지를 수신한 후 적절한 서버의 프로시저를 호출한다.
  • 파라미터 정돈(parameter marshalling)은 클라이언트와 서버 기기의 데이터 표현 방식의 차이 문제를 해결한다.
    • 이런 차이를 해결하기 위해 대부분의 RPC 시스템은 기종 중립적인(machine-independent) 데이터 표현 방식을 정의한다. XDR(external data representation)이 이러한 표현 방식 중 하나이다.
  • 클라이언트 측에서는 서버에게 데이터를 보내기 전 파라미터 정돈(marshalling) 작업의 일환으로 전송할 데이터를 기종 중립적인 XDR 형태로 바꾸어서 보낸다.
  • 수신측 기계에서는 XDR 데이터를 받으면 파라미터를 풀어내면서(unmarshaled) 자기 기종의 형태로 데이터를 바꾼(convert) 후 서버에게로 넘겨준다.


  • 클라이언트와 서버 간의 통신 문제를 고려해봐야 한다.
  • 일반적인 프로시저 호출의 경우, 바인딩(binding)이라는 작업이 링킹/적재/실행 시점에 행해진다.
    • 바인딩(binding) : 프로그램에 사용된 구성 요소의 실제 값 또는 속성을 결정짓는 행위를 의미한다. 예를 들어 함수를 호출하는 부분에서 실제 함수가 위치한 메모리를 연결하는 것도 바인딩에 해당한다. [^5]
  • 이때 프로시저의 이름이 프로시저의 메모리 주소로 변환된다.
  • 이와 마찬가지로 RPC도 클라이언트와 서버의 포트를 바인딩 해야 하는데, 클라이언트는 서버의 포트 번호를 어떻게 알 수 있는가?
  • 이를 위해 2가지 방법이 사용된다.
  1. 고정된 포트 주소 형태로 미리 정해 놓는 방법이다.
    • 컴파일 할 때 RPC에는 이 고정된 포트 번호를 준다.
    • 컴파일 되고 나면 그 후에는 서버가 그 포트 번호를 임의로 바꿀 수 없다.
  2. 랑데부(rendezvous) 방식에 의해 동적(dynamic)으로 바인딩한다.
    • 보통 운영체제는 미리 정해져 있는 고정 RPC 포트를 통해 랑데부용 데몬(matchmaker라고 불림)을 제공한다.
    • 그러면 클라이언트가 자신이 실행하기를 원하는 RPC 이름을 담고 있는 메시지를 랑데부 데몬에게 보내서, RPC 이름에 대응하는 포트 번호가 무엇인지 알려달라고 요청한다.
    • 그러면 포트 번호가 클라이언트에게 반환되고, 클라이언트는 그 포트 번호로 RPC 요청을 계속 보낸다.

image


  • RPC는 분산 파일 시스템(DFS)을 구현하는 데 유용하다.


References

댓글남기기