프로세스 간 통신(Inter Process Communication)의 종류

1. 전역 변수를 이용한 통신

  • 전역 변수를 이용한 통신은 공동으로 관리하는 메모리를 사용하여 데이터를 주고받는 것으로 송신측은 전역 변수에 데이터를 쓰고, 수신측은 전역 변수의 값을 읽어 통신이 이루어진다.
  • 주로 직접 관련이 있는 프로세스 간에 사용되는 통신 방법이다.
  • 예를 들어 부모 프로세스에 전역 변수를 선언한 후 자식 프로세스를 만들면 부모 프로세스와 자식 프로세스가 통신할 수 있다.
  • 하지만 굳이 직접적으로 관련이 있지 않아도, extern변수와 같이 전역 변수를 사용하여 통신할 수 있다.

2. 파이프를 이용한 통신

  • 파이프는 운영체제가 제공하는 동기화 통신 방식으로 open()함수로 파이프를 얻고 작업 후 close()합수로 파이프를 끝낸다.
    (동기화 : 프로세스 또는 스레드들이 수행되는 시점을 조절하여 서로가 알고 있는 정보가 일치하는 것을 의미한다.)
  • 파이프를 이용한 통신은 단방향 통신이므로 양방향을 원한다면 2개의 파이프를 사용하면 된다.

2_1 익명 파이프(Anonymous Pipe)

1) 일반적인 파이프이다.
2) 통신할 프로세스가 명확하게 알 수 있는 경우 사용한다.
3) 부모-자식 프로세스 or 형제 프로세스 간 통신에 사용한다.
4) 외부 프로세스에서는 사용할 수 없다.
5) FIFO(First In First Out)구조이다.
6) 두 개의 프로세스를 연결하고, 하나의 프로세스는 데이터를 쓰기만, 다른 하나는 데이터를 읽기만 할 수 있다. 단방향 통신이 가능한 파이프의 특징 때문에 반이중 통신이라고 부르기도 한다.
7) 송/수신이 모두 필요하다면 파이프를 2개를 만들면 된다.
8) 간단하게 사용할 수 있다.
9) PIPE 함수를 통해 생성한다.

  • 단점
    1) 반이중 통신이다 -> 프로세스가 읽기와 쓰기 통신을 모두 해야한다면, PIPE를 두 개를 만들어야 하므로 구현이 복잡해 질 수 있다.
    2) 전이중 통신을 고려해야될 상황이면 낭비가 심하다.

2_2 네임드 파이프(Named Pipe)

1) 전혀 모르는 상태의 프로세스들 사이의 통신에 사용한다.
2) 익명 파이프의 확장된 상태로 부모 프로세스와 무관한 다른 프로세스도 통신이 가능하다.
3) 이름을 가진 PIPE를 통해서 프로세스들 간의 단방향 통신을 지원한다.
4) 서로 다른 프로세스들이 PIPE의 이름만 알면 통신이 가능하다.
5) FIFO(First In First Out)구조이다.
6) 프로세스 통신을 위해 이름이 있는 파일을 사용하기 때문에 관련 없는 프로세스들 간의 통신이 가능하다.
7) mkfifo 또는 mknod 함수로 생성한다.

  • 단점
    1) 익명 파이프와 마찬가지로 반이중 통신이다. -> 프로세스가 읽기와 쓰기 통신을 모두 해야한다면, PIPE를 두 개를 만들어야 하므로 구현이 복잡해 질 수 있다.

2_3 메세지 큐(Message Queue)

  • 메모리를 이용한 PIPE의 일종이다.
  • 구조체 기반으로 통신한다.
  • FIFO(First In First Out)구조이다.
  • megtype에 따라 다른 구조체를 가져올 수 있다.
  • 프로세스 간 다양한 통신을 할 때 사용할 수 있다.

3. 파일을 이용한 통신

  • 파일은 열고(open), 쓰기(write), 읽기(read), 닫기(close)로 총 4가지로 이루어져 있다.
int main(){
    int fd;
    char buf[5];

    fd = open('com.txt', O_RDWR);
    write(fd, "test", 5);
    read(fd, buf, 5);
    close(fd);
}
  1. open하여 fd(파일 디스크립터)를 받아 이를 통해 파일을 제어한다. fd는 반드시 사용 후에 돌려주어야 하기 때문이 close를 호출해주어야 한다.
  2. 그 다음 쓰기, 읽기로 fd를 사용하여 파일의 정보를 쓰고, fd에 buf를 담아 정보를 읽어올 수도 있다.

입출력은 다음 그림처럼 입출력 프로세스가 따로 존재하여 관리한다. 즉, 운영체제의 입장에서 본다면, 저장장치의 데이터를 읽고 쓰는 것도 일반 프로세스와 입출력 프로세스 간의 통신이다.

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


4. 소켓을 이용한 통신

  • 소켓을 이용한 통신은 서로 다른 컴퓨터에 있는 프로세스끼리 통신하는데 사용한다.
  • 서로 다른 컴퓨터에 있는 프로세스 간의 통신은 네트워킹이라고 하며, 네트워킹 상황에서는 원격 프로시저 호출이나 소켓을 이용한다.
  • 통신하고자 하는 프로세스끼리 각자의 소켓을 연결하는 작업을 Binding(바인딩)이라고 한다.
  • 바인딩 후, 소켓이 write하면 데이터가 전송되고 read하면 데이터를 받아올 수 있다.
  • 클라이언트와 서버가 소켓을 통해 통신하는 구조로, 원격에서 프로세스 간 데이터를 공유할 때 사용한다.
  • 전이중 통신이 가능하다.
  • 서버/클라이언트 환경을 구축하는데 용이하다.
  • 중대형 애플리케이션에서 주로 사용한다.

5. 공유 메모리를 이용한 통신(Shared Memory)

  • 시스템 상의 공유 메모리를 통해 통신한다.
  • 일정한 크기의 메모리를 프로세스 간에 공유하는 구조이다.
  • 공유 메모리는 커널에서 관리된다.
  • 주로 프로세스 간 read와 write를 모두 필요롤 할 때 사용한다.
  • 프로세스간 사용하려면 메모리 크기가 동일 해야 한다.

공유자원과 임계구역

1. 공유자원(Shared Resource)

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

1_1 경쟁 조건(Race Condition)

  • 2개 이상의 프로세스가 공유 자원을 병렬적으로 읽거나 쓰는 상황을 의미한다.
  • 경쟁 조건이 발생하면, 공유 자원 접근 순서에 따라 실행 결과가 달라질 수 있다.

2. 임계 구역(Critical Section)

  • 공유 자원 접근 순서에 따라서 실행 결과가 달라지는 프로그램의 영역을 의미한다.
  • 임계 구역에서는 프로세스들이 동시에 작업하면 안된다. => 어떤 프로세스가 임계 구역에 들어가면 다른 프로세스는 기다려야하며, 임계 구역의 프로세스가 나와야 들어갈 수 있다.
  • 전역 변수 뿐만 아니라 하드웨어 자원을 사용할 때도 적용되는 개념이다.
    ex) 프린터 1대를 여러 사람들이 사용한다면, 프린터는 임계 구역이 된다.

2_1. 임계 구역 해결 조건

  1. 상호 배제(Mutual Exclusion) : 한 프로세스가 임계 구역에 들어가면 다른 프로세스는 임계 구역에 들어갈 수 없다.
  1. 한정 대기(Bounded Wating) : 어떤 프로세스도 무한대기를 하지 않아야 한다. 즉, 특정 프로세스가 임계 구역에 진입하지 못하면 안된다.
  1. 진행의 융통성(Progress Flexibility) : 한 프로세스가 다른 프로세스의 진행을 방해해선느 안된다.

2_1. 임계 구역 해결 방법

  • 가장 단순한 방법은 잠금(lock)을 이용하는 것이다. 프로세스가 임계 구역에 들어갈 때, 잠금을 걸고, 나올 때 잠금 해제와 동시에 동기화 신호를 보낸다.

2_1_1 피터슨 알고리즘

  • 게리 피터슨이 제안한 알고리즘
  • 변수 turn은 두 프로세스가 동시에 lock을 설정하여 임계 구역에 못들어가는 상황에 대비하는 장치 => 두 프로세스가 동시에 lock을 설정했더라도 turn을 사용하여 다른 프로세스에게 양보한다.
//공유 자원(변수)
boolean lock1 = false;
boolean lock2 = false;
int turn = 1;

// 프로세스 1
lock1 = true;
turn = 2;
while(lock2 == true && turn == 2);
/*** 임계구역 ***/
lock1 = false;

// 프로세스 2
lock2 = true;
turn = 1;
while(lock1 == true && turn == 1);
/*** 임계구역 ***/
lock2 = false;

한계

  • 임계 구역 해결의 세 가지 조건을 모두 만족하지만 프로세스 수가 늘어난다면 변수도 늘어나고 전체적인 알고리즘도 복잡해진다.
  • 바쁜 대기를 사용하여 자원을 낭비한다.(반복문(whlie)의 무한 loop)

2_1_2 데커 알고리즘

  • 테오도뤼스 데커가 제안한 알고리즘
  • 하드웨어의 도움 없이도 임계 구역 문제를 해결할 수 있다
// 공유 변수
boolean lock1 = false;
boolean lock2 = false;
int turn = 1;

// 프로세스 1
lock1 = true;
while(lock2 == true) {
	if(turn == 2) {
		lock1 = false;
		while(turn == 2);
		lock1 = true;
	}
}
/*** 임계구역 ***/
turn = 2;
lock1 = false;

// 프로세스 2
lock2 = true;
while(lock1 == true) {
	if(turn == 1) {
		lock2 = false;
		while(turn == 1);
		lock2 = true;
	}
}
/*** 임계구역 ***/
turn = 1;
lock2 = false;

한계

  • 임계구역 해결의 세가지 조건을 모두 만족하지만 프로세스수가 늘어나면 변수도 늘어나고 전체적인 알고리즘도 복잡해진다.
  • 바쁜 대기를 사용하여 자원을 낭비한다.(반복문(whlie)의 무한 loop)

2_1_3 세마포어 알고리즘

  • 에츠허르 데이크스트라(Edsger Dijkstra)가 제안한 알고리즘
  • 피터슨 알고리즘, 데커 알고리즘에 비에 비교적 간단하고 사용이 쉽다.
  • wakeup 신호를 사용하기 때문에 바쁜 대기를 하지 않아도 된다.
  • P()와 V()의 내부 코드는 검사와 지정을 사용하여 분리 실행되지 않고 완전히 실행되게 해야한다. => P()나 V()내부 코드가 실행되는 도중 다른 코드가 실행되면 상호 배제와 한정 대기 조건을 보장하지 못하기 때문에 완전 실행이 강요된다.
Semaphore(n); // RS = n;

P(); 
// if RS > 0 them RS = RS - 1
// else block()

/*** 임계구역 ***/

V();
// RS = RS + 1
// wake_up()

위 코드를 참고하여 순서를 정리해 보도록 하자

  1. 임계구역 사용전에 Semaphore(n) 로 초기화를 한다.
    1. n은 공유 가능한 자원의 수를 나타낸다. 전역변수 RS를 n으로 초기화 한다.
      ex) 프린터가 1대이면 1이고, 2대이면 2가 된다.
  1. 초기화가 끝난 후 임계구역에 들어가기 전 사용중이라고 표시를 한다.

    1) P(): 잠금을 수행하는 코드
    2) RS가 0보다 크면(사용 가능한 자원이 있으면) 1을 감소시키고 임계구역에 진입한다.
    3) RS가 0보다 작으면(사용 가능한 자원이 없으면) 0보다 커질 때 까지 기다린다.

  1. 임계구역을 나올 때 비었다고 표시한다.

    1) V(): 잠금 해제와 동기화를 같이 수행하는 코드를 의미한다.
    2) RS값을 1 증가시키고 세마포어에서 기다리는 프로세스에게 임계구역에 진입해도 좋다는 wake up 신호를 보낸다. => wake_up()

한계

  • 잘못된 사용으로 인해 임계구역이 보호받지 못할 수 있다.
    => 사용자가 고의로 세마포어를 사용하지 않거나 사용중에 실수를 한 경우(즉, 직접 변수에 접근하여 조작하기 때문에 위험하다.)

교착 상태와 해결 방법

1. 교착 상태

교착 상태란, 2개 이상의 프소세스가 서로 끝나기만을 기다리면서 더 이상 작업을 수행하지 않아서 아무것도 못하는 상태를 의미한다.

1_1. 교착 상태가 일어나는 이우

1) 상호 배제 => 여러 프로세스가 동시에 자원을 사용할 수 없는 상태
2) 비선점 => 한 프로세스가 다른 프로세스의 자원을 뺏는 상태
3) 점유와 대기 => 한 프로세스가 자원을 점유하고 있어 다른 프로세스가 대기하고 있는 상태
4) 원형 대기 => 무한적인 점유와 대기로 인해 프로세스가 서로 양보하지 않는 상태

1_2. 교착 상태를 해결하는 방법

1) 예방 : 교착 상태가 일어나는 이유 4가지를 모두 예방하는 즉, 모두 무력화하는 방법
2) 회피 : 교착 상태를 만들지 아니할 정도의 수준으로 프로세스에 자원을 할당하는 방법
3) 검출 : 자원 할당 그래프를 사용하여 교착 상태를 발견하고 처리하는 방법
4) 회복 : 교착 상태를 검출하여 이를 회복하는 방법

profile
개발을 꿈꾸는 초짜

0개의 댓글