본문 바로가기

운영체제

프로세스 간 통신

IPC (InterProcess Communication)

운영체제 내에서 실행되는 병행 프로세스들은 독립적이거나 서로 밀접한 프로세스들이다. 

이러한 서로 밀접한 프로세스끼리, 서로 데이터를 보내거나 받을 수 있는 프로세스 간 통신을 IPC 기법이라고 한다.

 

 프로세스 모델

1. 공유 메모리 (shared memory)

 : 프로세스간 영역에 데이터를 읽고 쓰는 정보를 교환할 수 있다.

 : 시스템 콜을 사용하여 구현되므로 부가적인 시간 소비가 필요하며 커널 영역에서 구축된다.

2. 메시지 전달(message passing)

 : 통신이 주체가 되어 프로세스간 교환을 통해 통신이 이루어진다.

 : 적은 양의 데이터를 교환하는데 유용하다. 분산 시스템에서 공유 메모리보다 구축하기 쉽다.

 

(a) message passing / (b) shared memory

 

 

IPC in Shared-Memory Systems

 메모리 공유 영역은 세그먼트를 생성하는 프로세스의 주소 공간에 위치한다. 따라서 세그먼트를 이용하려고 하는 다른 프로세스는 해당 세그먼트의 자신의 주소 공간 값을 넣어야 한다.  

 공유 메모리 IPC의 흔한 패러다임에서는 생산자 - 소비자 패러다임을 사용한다. 마치 컴파일러에서 어셈블러리 코드를 생성하고, 어셈블러는 이것을 소비한다. 어셈블러는 Object 모듈을 생산하며 , 로더(적재기)는 이것을 다시 소비한다. 이런식으로, 생산자와 소비자는 모두 소모할 수 있는 데이터 값이 존재할 수 있어야 한다는 특징을 가지며, 생산자와 소비자는 반드시 동기화되어야 생산되지 않은 항목들을 사용하지 않을 수 있다.

 

#define BUFFER_SIZE 10

typedef struct
{
	...
}item;

item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;


// 공유 메모리 사용한 생산자 프로세스 

item next_produced;

while(true)
{
	while((in + 1) % BUFFER_SIZE) == out)
    ;
    buffer(in) = next_produced;
    in = (in +1 ) % BUFFER_SIZE;
    
}


// 공유 메모리를 사용한 소비자 프로세스
int next_produced;

while(true)
{
	while(in == out)
    next consumed = buffer[out];
    out = (out + 1 ) % BUFFER_SIZE;
}

 

 

 

IPC in Message-Passing Systems

  메시지 전달 방식은 통신하는 다른 컴퓨터들과 네트워크에 의하여 분산 시스템 환경에서 적합하다. 우리가 흔히 사용하는 메시지 시스템에서 두 가지 연산을 제공한다. send(message) 와 receive(message)이다.

 만약, A 와 B 가 통신을 원한다면, 통신 연결이 설정되어 있어야 하며, send() , receive() 행위에 초점을 두어 논리적으로 구현하는 것에 초점을 둔다.

 

 

Synchronization()

메시지 전달의 봉쇄형(blocking) 과 비봉쇄형(nonblocking) 두 가지 형식이 있다. 

생산자와 수신자 모두 send() 혹은 recevie() 되어질때까지 무한 while()문을 선회하며 메시지 전달을 체크한다.

 

 

생산자와 소비자를 위한 POSIX 공유 메모리 API를 보여주는 프로그램 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
 
int main()
{
    /* the size (in bytes) of shared memory object */
    const int SIZE = 4096;
 
    /* name of the shared memory object */
    const char* name = "OS";
 
    /* strings written to shared memory */
    const char* message_0 = "Hello";
    const char* message_1 = "World!";
 
    /* shared memory file descriptor */
    int shm_fd;
 
    /* pointer to shared memory object */
    void* ptr;
 
    /* create the shared memory object */
    shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
 
    /* configure the size of the shared memory object */
    ftruncate(shm_fd, SIZE);
 
    /* memory map the shared memory object */
    ptr = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);
 
    /* write to the shared memory object */
    sprintf(ptr, "%s", message_0);
 
    ptr += strlen(message_0);
    sprintf(ptr, "%s", message_1);
    ptr += strlen(message_1);
    return 0;
}

 < POSIX 공유 메모리 API 설명하는 생산자 프로세스>

 

 

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
 
int main()
{
    /* the size (in bytes) of shared memory object */
    const int SIZE = 4096;
 
    /* name of the shared memory object */
    const char* name = "OS";
 
    /* shared memory file descriptor */
    int shm_fd;
 
    /* pointer to shared memory object */
    void* ptr;
 
    /* open the shared memory object */
    shm_fd = shm_open(name, O_RDONLY, 0666);
 
    /* memory map the shared memory object */
    ptr = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
 
    /* read from the shared memory object */
    printf("%s", (char*)ptr);
 
    /* remove the shared memory object */
    shm_unlink(name);
    return 0;
}

<POSIX 공유 메모리 API를 전달하는 소비자 프로세스>

 

 

 

파이프

파이프는 두 프로세스가 통신할 수 있게 하는 전달자로서 동작한다.

일반 파이프와 지명 파이프의 두 가지 유형으로 존재한다.

 

일반 파이프

 생산자- 소비자 형태로 두 프로세스 간의 통신을 허용한다. 생산자는 (쓰기), 소비자는 (읽기)를 동작하며, 단방향 통신을 제공한다. 따라서 파이프는 일반적으로 읽기, 쓰기를 지원한다.

 일반적으로, 파이프는 생성한 프로세스 이외에는 접근이 불가하다. 하지만 이전에 다뤘던 부모 , 자식 프로세스가 생성하는  fork()에 의하면 특징을 물려받는다. 이는 자식 프로세스는 부모 프로세스로부터 열린 파일에 대한 것을 부모로부터 상속받기 때문에, 부모 프로세스에서 파이프 쓰기가 가능하며, 자식 프로세스에서는 데이터의 읽기 종단을 행할 수 있다.

 

지명 파이프

일반 파이프와는 다르게, 오로지 프로세스들이 서로 통신하는 동안에만 존재한다. UNIX 와 Windows 시스템에서 프로세스 통신이 끝나면 일반 파이프는 사라진다. 반면, 지명 파이프는 양방향으로 통신이 가능하며 부모- 자식 프로세스의 관계 또한 필요치 않다. 지명 파이프는 FIFO라고 부르며 mkfifo() 시스템 콜을 사용하여, open, write, read, close 시스템 콜로 이루어졌다. 양방향 통신을 지원하지만 반이중 전송을 사용하기 때문에 동시에 데이터를 전송하게 된다면 두 개의 지명 파이프가 필요하다. 이때, 같은 기기 내의 통신 아닌 다른 컴퓨터와의 통신이 필요하게 된다면, 소켓을 사용해야한다.

'운영체제' 카테고리의 다른 글

운영체제 구조  (0) 2024.09.14