Interview/OS

Multiprocess 간 IPC 중 Message Queue MQ 통신

김 정출 2024. 9. 27. 17:59

Multiprocess 간의 통신(IPC, Inter-Process Communication)은 서로 다른 프로세스가 데이터를 주고받거나 리소스를 공유하는 방법입니다. 운영체제에서 프로세스는 독립된 메모리 공간을 가지기 때문에 직접적으로 데이터를 공유할 수 없습니다. 따라서 IPC 기법을 사용하여 프로세스 간의 데이터를 전달하거나 동기화해야 합니다.

LinuxUnix에서 사용하는 다양한 IPC 방법들을 아래에 설명합니다.

2. 메시지 큐 (Message Queue)

  • 메시지 큐커널이 관리하는 큐에 데이터를 메시지 단위로 보내고 받을 수 있는 방법입니다. FIFO 방식으로 동작하며, 각 메시지에는 타입이 지정되므로, 수신자는 메시지 타입에 따라 특정 메시지만 받을 수 있습니다.
  • 단방향 통신이나 양방향 통신 모두 가능하며, 동기화와 순서 보장이 가능합니다.
  • 커널에 의해 큐가 관리되므로, 중앙 집중식 데이터 전달이 가능하다.

메시지 큐 동작

  • msgget(): 메시지 큐를 생성하거나 가져옵니다.
  • msgsnd(): 큐에 메시지를 보냅니다.
  • msgrcv(): 큐에서 메시지를 읽습니다.
  • msgctl(): 큐를 삭제하거나 속성을 제어합니다.

메시지 큐는 직렬화가 필요한 데이터를 처리할 때 유용합니다.

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#define KEY 1234  // 메시지 큐를 구분하는 키값

struct msgbuf {
    long mtype;       // 메시지 타입
    char mtext[100];  // 메시지 내용
};

int main() {
    int msgid;
    struct msgbuf message;

    // 1. 메시지 큐 생성 또는 가져오기
    msgid = msgget(KEY, IPC_CREAT | 0666);  // 메시지 큐 생성 및 읽기/쓰기 권한
    if (msgid == -1) {
        perror("msgget failed");
        return 1;
    }

    // 2. 메시지 전송
    message.mtype = 1;  // 메시지 타입 지정
    strcpy(message.mtext, "Hello from sender!");

    if (msgsnd(msgid, &message, sizeof(message.mtext), 0) == -1) {
        perror("msgsnd failed");
        return 1;
    }

    printf("Message sent: %s\n", message.mtext);

    // 2. 메시지 큐 삭제
    if (msgctl(msgid, IPC_RMID, NULL) == -1) {
        perror("msgctl failed");
        return 1;
    }

    printf("Message queue deleted successfully.\n");

    return 0;
}

// 다른 프로세스
int main() {
    int msgid;
    struct msgbuf message;

    // 1. 메시지 큐 가져오기
    msgid = msgget(KEY, 0666);  // 메시지 큐 가져오기 (생성하지 않고 기존 큐 사용)
    if (msgid == -1) {
        perror("msgget failed");
        return 1;
    }

    // 2. 메시지 수신
    if (msgrcv(msgid, &message, sizeof(message.mtext), 1, 0) == -1) {
        perror("msgrcv failed");
        return 1;
    }

    printf("Message received: %s\n", message.mtext);

    return 0;
}

장점

  1. 비동기 통신

비동기적으로 메시지를 주고받을 수 있습니다. 즉, 송신자는 메시지를 큐에 넣고 즉시 다른 작업을 계속할 수 있으며, 수신자는 필요할 때 메시지를 읽으면 됩니다. 이 덕분에 송신자와 수신자가 동시에 실행될 필요가 없습니다.

  1. 영속성
    메시지 큐는 커널에서 관리되기 때문에 송신자가 메시지를 큐에 넣은 후, 수신자가 그 메시지를 처리하기 전까지 메시지가 큐에 유지됩니다. 따라서 송신자와 수신자가 동시에 실행되지 않아도 안전하게 메시지를 전달할 수 있습니다.
  2. 우선순위 처리 가능
    메시지에 우선순위를 부여할 수 있어, 수신자가 메시지를 읽을 때 우선순위에 따라 처리 순서를 조정할 수 있습니다. 이를 통해 중요한 메시지를 먼저 처리할 수 있습니다.
  3. 다수 프로세스 간 통신
    하나의 메시지 큐를 여러 프로세스가 공유할 수 있기 때문에, 여러 프로세스 간에 메시지 큐를 이용하여 데이터를 주고받는 다대다 통신이 가능합니다. 이는 단방향 통신을 제공하는 pipe()와 차별되는 중요한 장점입니다.
  4. 메모리 공유가 아닌 안전한 통신
    메시지 큐는 공유 메모리를 사용하지 않기 때문에 메모리 충돌이나 동기화 문제를 신경 쓸 필요가 없습니다. 데이터는 메시지 큐를 통해 복사되어 전달되므로 데이터 충돌 문제가 발생하지 않습니다.
  5. 유연성
    메시지 큐는 임의의 길이의 데이터를 주고받을 수 있으므로, 고정된 길이를 가진 데이터를 처리해야 하는 pipe나 다른 IPC 기법보다 더 유연한 데이터 전송이 가능합니다.
  6. 제한된 자원 관리
    커널에서 메시지 큐에 대한 자원을 관리하므로, 시스템 리소스 사용이 명확하게 제한되고 관리됩니다. 이는 직접적인 메모리 접근이나 파일 기반 통신보다 안전합니다.
  7. 프로세스 간의 독립성 보장
    메시지 큐를 사용하면 프로세스들이 서로의 존재를 몰라도 통신이 가능합니다. 송신자와 수신자는 서로의 프로세스 ID나 메모리 주소 등을 알 필요가 없으며, 메시지를 큐에 넣고 큐에서 읽기만 하면 됩니다. 이는 모듈화된 설계에 도움이 됩니다.