[CS] Chapter 05 프로세스 간 통신

Ringo·2024년 7월 4일

Computer Science

목록 보기
8/16
post-thumbnail

📌 프로세스 간 통신

1. 프로세스 간 통신의 개념

운영체제는 프로세스와 프로세스끼리 쉽게 데이터를 주고받을 수 있는 통신 방법을 제공하는데 이를 프로세스 간 통신(IPC; Inter Process Communication) 이라고 합니다.

공유 메모리나 공유 파일을 이용한 통신

프로세스 간 통신 중 가장 원시적인 방식입니다. 이 방식에서는 일정한 메모리 영역이나 파일을 공유하고 이를 통하여 데이터를 주고받습니다.

파이프를 이용한 통신

하나의 컴퓨터 내에서 프로세스 간 통신에 많이 사용되는 수단은 파이프(pipe) 입니다. 파이프는 프로세스 간 통신을 위하여 운영체제가 제공하는 통신 기법입니다.보통의 경우에 파이프는 fork()로 만들어진 부모-자식 간 통신에 사용됩니다.

소켓을 이용한 통신

컴퓨터와 컴퓨터가 네트워크로 연결된 경우의 통신에 주로 사용되는 수단은 소켓(socket)입니다. 그러나 파이프와 비교하면 소켓 방식은 초기화할 내용도 많고 시스템 자원도 많이 사용합니다. 따라서 같은 컴퓨터 내에서 소켓으로 통신하는 것은 비효율적입니다.

2. 프로세스 간 통신의 분류

통신 방향에 따른 분류

프로세스 간 통신은 동시에 실행되는 프로세스끼리 데이터를 주고받는 작업입니다.

양방향 통신

데이터를 양쪽 방향으로 동시에 전송할 수 있는 구조입니다. 일반적인 통신은 모두 양방향 통신입니다. 프로세스 간 통신에서는 소켓을 이용한 통신이 양방향 통신에 해당합니다.

반양방향 통신

데이터를 양쪽 방향으로 전송할 수 있지만 동시 전송은 불가능하고 특정 시점에 한쪽 방향으로만 전송할 수 있는 구조입니다. 대표적인 예는 무전기입니다.

단방향 통신

모스 부호처럼 한쪽 방향으로만 데이터를 전송할 수 있는 구조입니다. 프로세스 간 통신에서는 공유 메모리나 공유 파일을 이용한 통신과 파이프를 이용한 통신이 단방향 통신에 해당합니다.

종류통신 기법통신 기기
양방향(duplex) 통신소켓 이용 통신전화
반양방향(half duplex) 통신무전기
단방향(simplex) 통신공유 메모리나 공유 파일 이용 통신, 파이프 이용 통신모스 부호

통신 구현 방식에 따른 분류

공유 메모리를 사용하는 통신 방식의 가장 큰 문제는 상대방이 데이터를 언제 보낼지 받는 쪽에서 모른다는 것입니다. 데이터를 받는 쪽에서는 반복적으로 공유 메모리를 점검하는 수밖에 없습니다. 상태 변화를 살펴보기 위해 반복문을 무한 실행하며 기다리는데 이것을 바쁜 대기(busy waiting) 이라고 합니다.

바쁜 대기 문제를 해결하기 위해 데이터가 도착했음을 알려주는 동기화(synchronization)를 사용합니다. 메신저에서 메시지가 도착했다고 알려주는 알림은 동기화의 대표적인 예입니다.

대기가 있는 통신

동기화를 지원하는 방식입니다. 데이터를 받는 쪽은 데이터가 도착할 때까지 자동으로 대기 상태에 머물러 있습니다.

대기가 없는 통신

동기화를 지원하지 않는 방식입니다. 데이터를 받는 쪽은 바쁜 대기를 사용하여 데이터가 도착했는지 여부를 직접 확인합니다.

종류통신 기법통신 기기
대기가 있는 통신(동기화 통신)파이프 이용 통신, 소켓 이용 통신전화
대기가 없는 통신(비동기 통신)공유 메모리나 공유 파일 이용 통신전보

3. 프로세스 간 통신의 종류

파일을 이용한 통신

파일 입출력 코드는 크게 세 부분으로 구성됩니다. 파일을 열고(open), 쓰기(write) 또는 읽기(read) 연산을 한 후, 파일을 닫습니다(close).

파일을 이용한 통신은 부모-자식 관계의 프로세스 간 통신에 많이 사용되며 운영체제가 프로세스 동기화를 제공하지 않습니다. 그래서 프로세스가 알아서 동기화해야 하는데 주로 부모 프로세스가 wait() 함수를 이용하여 자식 프로세스의 작업이 끝날 때까지 기다렸다가 작업을 시작합니다.

파이프를 이용한 통신

파이프를 이용한 통신은 운영체제가 제공하는 동기화 통신 방식으로, 파일 입출력과 같이 open() 함수로 기술자를 얻어 작업한 후 close() 함수로 마무리합니다. 파이프를 이용한 통신은 공유 메모리나 공유 파일을 이용한 통신과 마찬가지로 단방향 통신입니다.

파이프는 이름 없는 파이프(anonymous pipe)와 이름 있는 파이프(named pipe)로 나뉩니다.

이름 없는 파이프

일반적으로 파이프라고 하면 이름 없는 파이프를 가리킵니다. 부모와 자식 프로세스 혹은 같은 부모를 가진 자식 프로세스와 같이 서로 관련 있는 프로세스 간 통신에 사용됩니다.

이름 있는 파이프

FIFO라 불리는 특수 파일을 이용하며 서로 관련 없는 프로세스 간 통신에 사용됩니다.

소켓을 이용한 통신

지금까지는 하나의 컴퓨터에 있는 프로세스 간 통신을 살펴보았는데 여러 컴퓨터에 있는 프로세스끼리도 통신을 할 수 있습니다. 이러한 통신을 네트워킹이라 하는데 이때는 소켓을 이용한 통신이 사용됩니다.

인터넷을 사용하려면 TCP/IP, 즉 IP(Internet Protocol)와 TCP(Transmission Control Protocol)가 있어야 합니다. 인터넷에서 IP의 역할은 목적지까지 데이터를 전송하는 것이며 이때 사용되는 주소가 IP 주소입니다.

하나의 컴퓨터 내에서 네트워크를 사용하는 각 프로세스를 구분할 주소가 필요한데 이것이 포트(Port) 번호입니다.

포트 번호는 TCP가 네트워크를 사용하는 프로세스를 구분하기 위해 사용하는 주소입니다. IP 주소가 아파트의 동 번호라면, 포트 번호는 해당 동의 호수로 보면 됩니다. 네트워크를 사용하는 프로세스가 생성되면 운영체제로부터 0에서 65535 사이의 포트 번호를 부여받습니다.

서버용 데몬에 임의의 포트 번호를 부여하면 클라이언트가 해당 번호를 매번 찾아야 합니다. 그래서 중요한 서버용 프로세스(데몬)에는 미리 정해진 포트 번호를 할당합니다.

하나의 포트에 여러 클라이언트를 연결하려면 소켓(socket)이 필요합니다. 결국 네트워크에서 데이터를 보낸다는 것은 클라이언트 소켓이 서버 소켓에 데이터를 보내는 것입니다.

앞에서 언급했듯이 파이프를 이용한 통신에서는 양방향 통신에 파이프가 2개 필요하지만 소켓을 이용한 통신에서는 소켓을 1개만 사용해도 양방향 통신이 가능합니다.

📌 공유 자원과 임계구역

1. 공유 자원에 대한 접근

공유 자원(shared resource)은 여러 프로세스가 공동으로 이용하는 변수, 메모리, 파일 등을 말합니다. 공유 자원은 공동으로 이용되기 때문에 누가 언제 데이터를 읽거나 쓰느냐에 따라 그 결과가 달라질 수 있습니다. 따라서 프로세스들의 공유 자원 접근 순서를 정하여 예상치 못한 문제가 발생하지 않도록 해야 합니다.

2개 이상의 프로세스가 공유 자원을 병행해서 읽거나 쓰는 상황을 경쟁 조건(race condition)이 발생했다고 합니다. 경쟁 조건이 발생하면 공유 자원 접근 순서에 따라 결과가 달라질 수 있습니다.

2. 임계구역

임계구역(critical section) : 공유 자원 접근 순서에 따라 실행 결과가 달라지는 프로그램의 영역

임계구역에서는 프로세스들이 동시에 작업하면 안 됩니다. 한 프로세스가 임계구역에 들어가면 다른 프로세스는 임계구엽 밖에서 기다려야 하며, 임계구역에 들어간 프로세스가 나와야 밖에 있던 프로세스가 들어갈 수 있습니다.

3. 생산자-소비자 문제

임계구역과 관련된 전통적인 문제로 생산자-소비자 문제(producer-consumer problem)가 있습니다. 이 문제에서는 생산자 프로세스와 소비자 프로세스가 서로 독립적으로 작업합니다.

임계구역은 하드웨어 자원을 사용할 때도 적용되는 개념입니다. 예를 들어 프린터 한 대를 여러 명이 동시에 사용하는 경우에 프린터는 임계구역이 됩니다.

4. 임계구역 문제 해결 조건

임계구역 문제를 해결하는 방법은 다음 절에서 살펴볼 텐데, 어떤 방법이든 다음 세 가지 조건을 만족해야 합니다.

상호 배제(mutual exclusion)

한 프로세스가 임계구역에 들어가면 다른 프로세스는 임계구역에 들어갈 수 없습니다. 이것이 지켜지지 않으면 임계구역을 설정한 의미가 없습니다. 두 요리사가 한 믹서에 각자 사용할 재료를 한 번에 넣고 갈면 안 되는 것처럼 임계구역 내에는 한 번에 하나의 프로세스만 있어야 합니다.

한정 대기(bounded wating)

한 요리사가 믹서를 계속 사용하느라 다른 요리사가 믹서를 사용하지 못한 채 계속 기다리면 안 됩니다. 어떤 프로세스도 임계구역에 진입하지 못하여 무한 대기(infinite postpone) 하지 않아야 합니다.

진행의 융통성(progress flexibility)

두 요리사가 번갈아가며 믹서를 사용하기로 했다고 가정해봅시다. 그러면 요리사 B는 믹서를 사용한 후 요리사 A가 믹서를 사용할 일이 있든 없든 믹서를 사용할 때까지 기다려야 하는데 이는 매우 비효율적입니다. 요리사 B는 요리사 A의 작업 속도와 관계없이 믹서가 비어 있으면 언제든 사용할 수 있어야 합니다. 진행의 융통성은 한 프로세스가 다른 프로세스의 진행을 방해해서는 안 된다는 것을 의미합니다.

📌 임계구역 문제 해결 방법

임계구역 문제를 해결하는 단순한 방법은 잠근(lock)을 이용하는 것입니다. 예를 들어 임계구역이 화장실이라면 사용자는 화장실 문을 잠근 채 사용하고 나올 때 잠금을 해제합니다. 이때 기다리는 사람이 있다면 잠금 해제는 화장실을 사용해도 좋다는 신호가 됩니다. 잠금 해제와 동시에 동기화 신호를 보내는 것입니다.

1. 기본 코드 소개

#include <stdio.h>

typedef enum {false, true} boolean;
extern boolean lock=false;
extern int balance;

main() {
	while(lock==true);
    lock=true;
    balance=balance+10;	// 임계구역
    lock=false;
}

2. 임계구역 문제 해결 조건을 고려한 코드 설계

상호 배제 문제

한정 대기 문제

프로세스 P1과 P2 둘다 while 문을 빠져나오지 못하고 무한 루프에 빠져서 임계구역에 진입하지 못합니다. 이는 한정 대기 조건에 보장하지 못하는 상황으로 교착 상태(deadlock)라고 합니다. 교착 상태는 프로세스가 살아 있으나 작업이 진행되지 못하는 상태입니다.

한편 P2에는 확장성 문제도 있습니다. 프로세스 P1은 자신의 lock1을 잡그고 lock2를 검사합니다. 만약 프로세스가 3개라면 lock3를 만들고 다른 프로세스들이 lock3을 검사해야 합니다. 이처럼 프로세스가 늘어나면 검사해야 하는 lock의 개수도 늘어나 비효율적입니다.

진행의 융통성 문제

P2의 코드에서는 잠금을 확인하는 문장이 하나이므로 상호 배제와 한정 대기를 보장합니다.

프로세스 P1은 P2가 임계구역에 진입했다가 나온 다음에야 다시 진입할 수 있으므로 프로세스 P2가 프로세스 P1의 진행을 방해하는 구조입니다. 이렇게 프로세스의 진행이 다른 프로세스로 인해 방해받는 현상을 경직된 동기화(lockstep synchronization)이라고 합니다. 이 코드에서는 진행의 융통성 조건을 보장하지 못합니다.

하드웨어로 해결 방법

임계구역을 하드웨어로 해결하는 방법은 편리하지만 바쁜 대기를 사용하여 검사하기 때문에 자원 낭비가 발생합니다. 일부 하드웨어에서는 바쁜 대기 없이 잠금을 동기화해 주기도 하지만, 이는 성능 좋은 하드웨어에서나 가능한 일입니다.

3. 피터슨 알고리즘

피터슨 알고리즘은 입계구역 문제를 해결하기 위해 게리 피터슨(Gary Peterson)이 제안한 것입니다. turn이라는 공유 변수를 하나 더 사용합니다.

프로세스 P1은 임계구역에 진입하기 전에 먼저 잠금을 걸고(lock1=true) turn을 2로 설정합니다. 변수 turn은 두 프로세스가 동시에 lock을 설정하여 임계구역에 못들어가는 상황에 대비하기 위한 장치입니다. 두 프로세스가 동시에 lock을 설정했더라도 turn을 사용하여 다른 프로세스에 양보합니다.

피터슨 알고리즘은 임계구역 문제 해결의 세 가지 조건을 모두 만족하지만 2개의 프로세스만 사용 가능하다는 한계가 있습니다. 여러 프로세스가 하나의 임계구역을 사용하려면 공유 변수를 추가하고 코드를 변경해야 합니다.

4. 데커 알고리즘

데오도뤼스 데커(Tehodorus Dekker)가 제안한 데커 알고리즘도 임계구역 문제 해결의 세 가지 조건을 모두 만족합니다. 지금까지 소개한 알고리즘 중 피터슨 알고리즘을 제외하고는 모두 임계구역 문제를 해결하기 위해 검사와 지정 같은 하드웨어의 도움이 필요했습니다. 즉, 연산 중간에 타임아웃이 발생하지 않도록 하드웨어적으로 막아야 임계구역 문제가 해결되었습니다. 그러나 데커 알고리즘은 하드웨어의 도움 없이도 임계구역 문제를 해결할 수 있다는 것이 특징입니다.

  1. 프로세스 P1은 우선 잠금을 겁니다.(lock1=true)
  2. 프로세스 P2의 잠금이 걸렸는지 확인합니다[while(lock2==true)].
  3. 만약 프로세스 P2도 잠금으르 걸었다면 누가 먼저인지 확인합니다[if(turn==2)]. 만약 프로세스 P1의 차례라면(turn==1) 임계구역으로 진입하고 프로세스 P2의 차례라면(turn==2) 4로 이동합니다.
  4. 프로세스 P1은 잠금을 풀고(lock1=false) 프로세스 P2가 작업을 마칠 때까지 기다립니다.[while(turn==2)]. 프로세스 P2가 작업을 마치면 잠금을 걸고(lock1=true) 임계구역으로 진입합니다.

피터슨 알고리즘이나 데커 알고리즘은 임계구역 문제 해결의 세 가지 조건을 모두 만족하지만 매우 복잡합니다. 프로세스가 늘어나면 변수도 늘어나고 전체 알고리즘도 복잡해집니다. 임계구역을 보호하기 위해 복잡한 알고리즘을 구현하는 것은 바람직한 접근 방법이 아닙니다.

5. 세마포어

앞에서 살펴본 임계구역 문제 해결 알고리즘은 바쁜 대기를 사용하여 자원을 낭비하거나 알고리즘이 너무 복잡합니다. 이러한 단점을 해결하기 위해 에츠허르 데이크스탈(Edsger Dijkstra)는 세마포어(semaphore)라는 알고리즘을 제안했습니다.

세마포어 알고리즘에서는 프로세스가 임계구역에 진입하기 전에 스위치를 사용 중으로 놓고 임계구역으로 들어갑니다. 이후에 도착하는 프로세스는 앞의 프로세스가 작업을 마칠 때까지 기다립니다. 프로세스가 작업을 마치면 세마포어는 다음 프로세스에 임계구역을 사용하라는 동기화 신호를 보냅니다. 세마포어 알고리즘에서는 다른 알고리즘과 달리 임계구역이 잠겼는지 직접 점검하거나, 바쁜 대기를 하거나, 다른 프로세스에 동기화 메시지를 보낼 필요가 없습니다.

세마포어는 공유 자원이 여러 개일 떄도 사용할 수 있습니다. 예를 들어, 프린터 두 대를 3명이 동시에 사용하는 경우에 해당합니다. 공유 자원이 2개이므로 Semaphore(n)의 초깃값은 2입니다.

6. 모니터

세마포어의 가장 큰 문제는 잘못된 사용으로 인해 임계구역이 보호받지 못한다는 것입니다.

  1. 프로세스가 세마포어를 사용하지 않고 바로 임계구역에 들어간 경우로 임계구역을 보호할 수 없습니다.
  2. P()를 두 번 사용하여 wake_up() 신호가 발생하지 않은 경우입니다. 프로세스 간의 동기화가 이루어지지 않아 세마포어 큐에서 대기하고 있는 프로세스들이 무한 대기에 빠집니다.
  3. P()와 V()를 반대로 사용하여 상호 배제가 보장되지 않은 경우로 임계구역을 보호할 수 없습니다.

공유 자원을 사용할 때 모든 프로세스가 세마포어 알고리즘을 따른다면 굳이 P()와 V()를 사용할 필요 없이 자동으로 처리하면 됩니다. 이를 실제로 구현한 것이 모니터(monitor)입니다. 모니터는 공유 자원을 내부적으로 숨기고 공유 자원에 접근하기 위한 인터페이스만 제공함으로써 자원을 보호하고 프로세스 간에 동기화시킵니다.

모니터는 시스템 호출과 같은 개념입니다. 운영체제가 관리하는 자원을 사용자가 마음대로 사용하게 두면 실수나 악의적인 의도로 시스템 자원을 망가뜨릴 수 있습니다. 이러한 문제를 예방하기 위해 운영체제는 시스템 자원을 사용자로부터 숨기고 사용자 요구 사항을 처리할 수 있는 인터페이스만 제공하는데, 이것이 시스템 호출입니다.

시스템 호출과 같은 방법으로 모니터도 보호할 자원을 임계구역으로 숨기고 임계구역에서 작업할 수 있는 인터페이스만 제공하여 자원을 보호합니다.

모니터는 프로세스 P1과 P2의 요청을 모니터 큐에 넣은 후 순서대로 실행하고 그 결과만 해당 프로세스에 알려줍니다. 이 방법은 사용자 입장에서는 복잡한 코드를 실행하지 않아서 좋고, 시스템 입장에서는 임계구역을 보호할 수 있어서 좋습니다.

모니터를 사용하면 세마포어와 P()와 V()를 사용할 필요가 없습니다. 임계구역의 보호나 프로세스의 동기화가 모니터 내부에서 처리되므로 사용자는 increase()를 호출하기만 하면 됩니다.

📌 파일, 파이프, 소켓 프로그래밍

1. 파일

순차 파일

아무리 큰 파일이라도 파일 내의 데이터는 개념적으로 한 줄로 저장됩니다.

파일 내의 데이터는 한 줄로 길게 저장되는데 이러한 파일을 순차 파일(sequential file)이라 합니다.

2. 파이프

파이프 : 동기화를 지원하는 단방향 통신 시스템

  1. 이름 없는 파이프
  2. 이름 있는 파이프

파이프는 부모와 자식 프로세스 혹은 같은 부모와 자식 프로세스처럼 서로 관련 있는 프로세스 간 통신에 사용됩니다.

파일을 이용한 통신과 파이프를 이용한 통신의 차이를 살펴봅니다. 파일에서는 read()와 write()가 파일 기술자를 공유하지만 파이프에서는 read()와 write()의 기술자가 따로 존재합니다. 파일에서는 파일 기술자를 공유하기 때문에 read(0를 하건 write()를 하건 상관 없이 파일 기술자가 전진하므로 동기화를 할 수 없습니다. 그러나 파이프에서는 기술자가 따로 존재하기 때문에 동기화가 가능합니다. 파이프는 기술자를 fd[2]와 같이 2개의 원소를 가진 배열로 정의하는데, 원소 하나는 읽기용이고 다른 하나는 쓰기용입니다.

파일을 이용한 통신 코드와 달리 파이프의 코드에서는 부모 프로세스에 wait()가 없습니다. 이렇게 wait()가 없으면 부모 프로세스와 자식 프로세스 중 어떤 것이 먼저 실행되는지 보장할 수 없습니다. 부모 프로세스가 자식 프로세스보다 먼저 실행되면 부모 프로세스는 자식 프로세스가 보낸 데이터를 읽지 못하고 종료될 것입니다. 그러나 파이프는 대기가 있는 통신이기 때문에 wait()가 필요 없습니다.

3. 네트워킹

네트워킹 : 여러 컴퓨터에 있는 프로세스에 데이터를 전달하는 방법 중 가장 대중화 된 것

소켓을 이용한 네트워킹에서도 open(), read()/write(). close() 구조를 사용합니다.

클라이언트는 소켓을 생성한 후 connect()를 사용하여 서버와의 접속을 시도합니다. 서버와 접속되면 read() 혹은 write() 작업을 하며, 작업이 끝나면 사용한 소켓 기술자(socket descriptor)를 닫고 종료합니다. 클라이언트의 통신 절차는 파일이나 파이프를 이용한 통신 절차에 비해 특별히 추가되는 것이 없습니다. 하지만 서버 쪽 통신 절차는 좀 더 복잡합니다. 서버는 소켓을 생성한 후 bind()를 사용하여 생성한 소켓을 특정 포트에 등록합니다.

여ㅑ러 컴퓨터가 연결된 네트워크 환경에서는 각 컴퓨터를 IP 주소로 구분합니다. 그런데 한 컴퓨터 내에도 여러 프로세스가 존재하기 때문에 어떤 프로세스와 통신할지 구분해야 합니다. 이때 사용하는 구분 번호를 포트 번호(port number)라고 합니다. IP 주소가 아파트의 동 번호라면 포트 번호는 호수에 해당합니다.

profile
끄적끄적…

0개의 댓글