메시지 대기열

이미 공유 메모리가 있는데 메시지 큐가 필요한 이유는 무엇입니까? 여러 가지 이유가있을 것입니다. 단순화를 위해 이것을 여러 지점으로 나누도록하겠습니다.

  • 이해했듯이 프로세스에서 메시지를 수신하면 더 이상 다른 프로세스에서 사용할 수 없습니다. 공유 메모리에서는 여러 프로세스에서 데이터를 액세스 할 수 있습니다.

  • 작은 메시지 형식으로 통신하려는 경우.

  • 여러 프로세스가 동시에 통신 할 때 공유 메모리 데이터를 동기화하여 보호해야합니다.

  • 공유 메모리를 사용한 쓰기 및 읽기 빈도가 높기 때문에 기능을 구현하는 것이 매우 복잡합니다. 이런 종류의 경우 활용과 관련하여 가치가 없습니다.

  • 모든 프로세스가 공유 메모리에 액세스 할 필요가 없지만 필요한 프로세스가 거의없는 경우 메시지 큐로 구현하는 것이 좋습니다.

  • 다른 데이터 패킷과 통신하려는 경우 프로세스 A가 메시지 유형 1을 프로세스 B에, 메시지 유형 10을 프로세스 C에, 메시지 유형 20을 프로세스 D에 전송한다고 가정합니다.이 경우 메시지 큐로 구현하는 것이 더 간단합니다. 주어진 메시지 유형을 1, 10, 20으로 단순화하기 위해 아래에 설명 된대로 0 또는 + ve 또는 –ve가 될 수 있습니다.

  • 물론 메시지 큐의 순서는 FIFO (선입 선출)입니다. 큐에 삽입 된 첫 번째 메시지는 검색 할 첫 번째 메시지입니다.

공유 메모리 또는 메시지 대기열 사용은 응용 프로그램의 필요성과 응용 프로그램을 얼마나 효과적으로 활용할 수 있는지에 따라 다릅니다.

메시지 큐를 사용하는 통신은 다음과 같은 방법으로 발생할 수 있습니다.

  • 한 프로세스에서 공유 메모리에 쓰고 다른 프로세스에서 공유 메모리에서 읽습니다. 우리가 알고 있듯이 읽기는 여러 프로세스로도 수행 될 수 있습니다.

  • 다른 데이터 패킷을 사용하여 하나의 프로세스에서 공유 메모리에 쓰고 여러 프로세스에서 읽습니다. 즉, 메시지 유형에 따라 읽습니다.

메시지 대기열에 대한 특정 정보를 확인 했으므로 이제 메시지 대기열을 지원하는 시스템 호출 (System V)을 확인할 차례입니다.

메시지 대기열을 사용하여 통신을 수행하려면 다음 단계를 따르십시오.

Step 1 − 메시지 큐를 생성하거나 이미 존재하는 메시지 큐에 연결 (msgget ())

Step 2 − 메시지 큐에 쓰기 (msgsnd ())

Step 3 − 메시지 큐에서 읽기 (msgrcv ())

Step 4 − 메시지 큐 (msgctl ())에 대한 제어 작업 수행

이제 위의 호출에 대한 구문과 특정 정보를 확인하겠습니다.

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

int msgget(key_t key, int msgflg)

이 시스템 호출은 System V 메시지 큐를 생성하거나 할당합니다. 다음 인수를 전달해야합니다-

  • 첫 번째 인수 인 key는 메시지 큐를 인식합니다. 키는 임의의 값이거나 라이브러리 함수 ftok ()에서 파생 될 수있는 값일 수 있습니다.

  • 두 번째 인수 인 shmflg는 IPC_CREAT (존재하지 않는 경우 메시지 큐 생성) 또는 IPC_EXCL (메시지 큐를 생성하기 위해 IPC_CREAT와 함께 사용되며 메시지 큐가 이미있는 경우 호출이 실패 함)와 같은 필수 메시지 큐 플래그를 지정합니다. 권한도 전달해야합니다.

Note − 권한에 대한 자세한 내용은 이전 섹션을 참조하십시오.

이 호출은 성공시 유효한 메시지 큐 식별자 (메시지 큐의 추가 호출에 사용됨)를 반환하고 실패시 -1을 반환합니다. 실패의 원인을 알려면 errno 변수 또는 perror () 함수로 확인하십시오.

이 호출과 관련된 다양한 오류는 EACCESS (권한 거부 됨), EEXIST (큐가 이미 존재하여 생성 할 수 없음), ENOENT (큐가 존재하지 않음), ENOMEM (큐를 생성하기에 충분한 메모리가 없음) 등입니다.

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

int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)

이 시스템 호출은 메시지 큐 (System V)로 메시지를 보내거나 추가합니다. 다음 인수를 전달해야합니다-

  • 첫 번째 인수 인 msgid는 메시지 대기열 즉, 메시지 대기열 식별자를 인식합니다. msgget () 성공시 식별자 값을받습니다.

  • 두 번째 인수 인 msgp는 다음 형식의 구조로 정의 된 호출자에게 보낸 메시지에 대한 포인터입니다.

struct msgbuf {
   long mtype;
   char mtext[1];
};

mtype 변수는 msgrcv () 호출에 자세히 설명 된 다른 메시지 유형과 통신하는 데 사용됩니다. mtext 변수는 크기가 msgsz (양수 값)로 지정된 배열 또는 기타 구조입니다. mtext 필드가 언급되지 않은 경우 허용되는 크기가 0 인 메시지로 간주됩니다.

  • 세 번째 인수 인 msgsz는 메시지의 크기입니다 (메시지는 널 문자로 끝나야 함).

  • 네 번째 인수 인 msgflg는 IPC_NOWAIT와 같은 특정 플래그를 나타냅니다 (큐 또는 MSG_NOERROR에 메시지가없는 경우 즉시 반환 (msgsz 바이트 이상인 경우 메시지 텍스트를 자름))

이 호출은 성공하면 0을 반환하고 실패하면 -1을 반환합니다. 실패의 원인을 알려면 errno 변수 또는 perror () 함수로 확인하십시오.

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

int msgrcv(int msgid, const void *msgp, size_t msgsz, long msgtype, int msgflg)

이 시스템 호출은 메시지 큐 (System V)에서 메시지를 검색합니다. 다음 인수를 전달해야합니다-

  • 첫 번째 인수 인 msgid는 메시지 대기열 즉, 메시지 대기열 식별자를 인식합니다. msgget () 성공시 식별자 값을받습니다.

  • 두 번째 인수 인 msgp는 호출자로부터받은 메시지의 포인터입니다. 다음 형식의 구조로 정의됩니다-

struct msgbuf {
   long mtype;
   char mtext[1];
};

mtype 변수는 다른 메시지 유형과 통신하는 데 사용됩니다. mtext 변수는 크기가 msgsz (양수 값)로 지정된 배열 또는 기타 구조입니다. mtext 필드가 언급되지 않은 경우 허용되는 크기가 0 인 메시지로 간주됩니다.

  • 세 번째 인수 인 msgsz는 수신 된 메시지의 크기입니다 (메시지는 널 문자로 끝나야 함).

  • fouth 인수 인 msgtype은 메시지 유형을 나타냅니다.

    • If msgtype is 0 − 대기열에서 처음 수신 된 메시지를 읽습니다.

    • If msgtype is +ve − msgtype 유형의 대기열에서 첫 번째 메시지를 읽습니다 (msgtype이 10이면 다른 유형이 처음에 대기열에 있더라도 유형 10의 첫 번째 메시지 만 읽음).

    • If msgtype is –ve − 메시지 유형의 절대 값보다 작거나 같은 가장 낮은 유형의 첫 번째 메시지를 읽습니다 (예 : msgtype이 -5이면 유형이 5 미만인 첫 번째 메시지, 즉 1에서 5까지의 메시지 유형을 읽음).

  • 다섯 번째 인수 인 msgflg는 IPC_NOWAIT와 같은 특정 플래그를 나타냅니다 (큐 또는 MSG_NOERROR에 메시지가없는 경우 즉시 반환 (msgsz 바이트 이상인 경우 메시지 텍스트를 자름)).

이 호출은 성공시 mtext 배열에서 실제로 수신 된 바이트 수를 반환하고 실패시 -1을 반환합니다. 실패의 원인을 알려면 errno 변수 또는 perror () 함수로 확인하십시오.

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

int msgctl(int msgid, int cmd, struct msqid_ds *buf)

이 시스템 호출은 메시지 큐 (System V)의 제어 작업을 수행합니다. 다음 인수를 전달해야합니다-

  • 첫 번째 인수 인 msgid는 메시지 대기열 즉, 메시지 대기열 식별자를 인식합니다. msgget () 성공시 식별자 값을받습니다.

  • 두 번째 인수 cmd는 메시지 큐에서 필요한 제어 작업을 수행하는 명령입니다. cmd의 유효한 값은 다음과 같습니다.

IPC_STAT− struct msqid_ds의 각 멤버의 현재 값에 대한 정보를 buf가 가리키는 전달 된 구조에 복사합니다. 이 명령에는 메시지 큐에 대한 읽기 권한이 필요합니다.

IPC_SET − 구조 buf가 가리키는 사용자 ID, 소유자 그룹 ID, 권한 등을 설정합니다.

IPC_RMID − 메시지 대기열을 즉시 제거합니다.

IPC_INFO − struct msginfo 유형 인 buf가 가리키는 구조의 메시지 대기열 제한 및 매개 변수에 대한 정보를 반환합니다.

MSG_INFO − 메시지 대기열이 소비 한 시스템 자원에 대한 정보를 포함하는 msginfo 구조를 반환합니다.

  • 세 번째 인수 buf는 struct msqid_ds라는 메시지 큐 구조에 대한 포인터입니다. 이 구조의 값은 cmd에 따라 set 또는 get에 사용됩니다.

이 호출은 전달 된 명령에 따라 값을 반환합니다. IPC_INFO 및 MSG_INFO 또는 MSG_STAT의 성공은 메시지 큐의 인덱스 또는 식별자 또는 다른 작업의 경우 0을 반환하고 실패의 경우 -1을 반환합니다. 실패의 원인을 알려면 errno 변수 또는 perror () 함수로 확인하십시오.

메시지 큐에 대한 기본 정보와 시스템 호출을 확인 했으므로 이제 프로그램으로 확인할 차례입니다.

프로그램을보기 전에 설명을 보겠습니다.

Step 1 − 두 개의 프로세스를 생성합니다. 하나는 메시지 대기열 (msgq_send.c)로 전송하기위한 것이고 다른 하나는 메시지 대기열 (msgq_recv.c)에서 검색하기위한 것입니다.

Step 2− ftok () 함수를 사용하여 키 생성. 이를 위해 처음에는 고유 키를 얻기 위해 msgq.txt 파일이 작성됩니다.

Step 3 − 전송 프로세스는 다음을 수행합니다.

  • 사용자로부터 문자열 입력을 읽습니다.

  • 새 줄이 있으면 제거합니다.

  • 메시지 대기열로 전송

  • 입력이 끝날 때까지 프로세스를 반복합니다 (CTRL + D).

  • 입력의 끝이 수신되면 프로세스의 끝을 나타내는 "end"메시지를 보냅니다.

Step 4 − 수신 과정에서 다음을 수행합니다.

  • 큐에서 메시지를 읽습니다.
  • 출력을 표시합니다.
  • 수신 된 메시지가 "종료"이면 프로세스를 종료하고 종료합니다.

단순화하기 위해이 샘플에는 메시지 유형을 사용하지 않습니다. 또한 한 프로세스는 큐에 쓰고 다른 프로세스는 큐에서 읽는 중입니다. 이것은 필요에 따라 확장 될 수 있습니다. 즉, 이상적으로는 하나의 프로세스가 큐에 쓰고 여러 프로세스가 큐에서 읽 힙니다.

이제 프로세스 (메시지가 큐로 전송 됨)를 확인해 보겠습니다. – 파일 : msgq_send.c

/* Filename: msgq_send.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PERMS 0644
struct my_msgbuf {
   long mtype;
   char mtext[200];
};

int main(void) {
   struct my_msgbuf buf;
   int msqid;
   int len;
   key_t key;
   system("touch msgq.txt");
   
   if ((key = ftok("msgq.txt", 'B')) == -1) {
      perror("ftok");
      exit(1);
   }
   
   if ((msqid = msgget(key, PERMS | IPC_CREAT)) == -1) {
      perror("msgget");
      exit(1);
   }
   printf("message queue: ready to send messages.\n");
   printf("Enter lines of text, ^D to quit:\n");
   buf.mtype = 1; /* we don't really care in this case */
   
   while(fgets(buf.mtext, sizeof buf.mtext, stdin) != NULL) {
      len = strlen(buf.mtext);
      /* remove newline at end, if it exists */
      if (buf.mtext[len-1] == '\n') buf.mtext[len-1] = '\0';
      if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
      perror("msgsnd");
   }
   strcpy(buf.mtext, "end");
   len = strlen(buf.mtext);
   if (msgsnd(msqid, &buf, len+1, 0) == -1) /* +1 for '\0' */
   perror("msgsnd");
   
   if (msgctl(msqid, IPC_RMID, NULL) == -1) {
      perror("msgctl");
      exit(1);
   }
   printf("message queue: done sending messages.\n");
   return 0;
}

컴파일 및 실행 단계

message queue: ready to send messages.
Enter lines of text, ^D to quit:
this is line 1
this is line 2
message queue: done sending messages.

다음은 메시지 수신 프로세스의 코드입니다 (큐에서 메시지 검색) – 파일 : msgq_recv.c

/* Filename: msgq_recv.c */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PERMS 0644
struct my_msgbuf {
   long mtype;
   char mtext[200];
};

int main(void) {
   struct my_msgbuf buf;
   int msqid;
   int toend;
   key_t key;
   
   if ((key = ftok("msgq.txt", 'B')) == -1) {
      perror("ftok");
      exit(1);
   }
   
   if ((msqid = msgget(key, PERMS)) == -1) { /* connect to the queue */
      perror("msgget");
      exit(1);
   }
   printf("message queue: ready to receive messages.\n");
   
   for(;;) { /* normally receiving never ends but just to make conclusion 
             /* this program ends wuth string of end */
      if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {
         perror("msgrcv");
         exit(1);
      }
      printf("recvd: \"%s\"\n", buf.mtext);
      toend = strcmp(buf.mtext,"end");
      if (toend == 0)
      break;
   }
   printf("message queue: done receiving messages.\n");
   system("rm msgq.txt");
   return 0;
}

컴파일 및 실행 단계

message queue: ready to receive messages.
recvd: "this is line 1"
recvd: "this is line 2"
recvd: "end"
message queue: done receiving messages.