본문 바로가기

UNIX_LINUX_C_C++

IPC 설비-메시지 큐

출처: http://blog.naver.com/endfirst?Redirect=Log&logNo=20007573126

IPC 설비-메시지 큐

UNIX는 프로세스 통신분야를 매우 풍부하게 지원하는 시스템이 되어, 서로 협력하는 태스크들로
구성된 시스템을 프로그램할 때 개발자들은 다양한 종류의 접근법을 사용할 수 있다.
고급 IPC 설비는 다음의 세 부류로 나뉜다.

1. 메시지 전달
2. 세마포
3 .공유 메모리

ipc 설비중 가장 중요한 것은 key 이다.
===key===
1. 여러 프로세스 사이에 IPC 자원을 쉽게 공유해준다.
2. 식별될 수 있는 객체로 메시지 큐, 세마포의 집합, 공유 메모리 세그먼트
3. 키는 화일이름이 아니다.


==================라이브러리 함수=================================
#include<sys/ipc.h>

key_t ftok(const char *path, int id);

설명-화일의 경로이름을 키로 변한

성공: 화일 path에 연관된 정보에 기반한 키 값을 돌려준다.
실패: (ket_t)-1의 값
====================================================

ipc 연산 종류

1. msgget : 새로운 메시지 큐 생성
2. semget : 새로운 세마포 생성
3. shmget : 새로운 공유메모리 생성
4. msgctl, semctl, shmctl : 상태 정보를 얻거나 제어값을 지정하는 제어 연산이다.
5. msgsnd : 메시지 큐에 한 메시지를 넣는다.
6. msgrcv : 메시지 큐로부터 한 메시를 읽는다.

상태 자료구조

구조체(ipc_perm)
uid_t cuid: /* ipc 객체 생성자의 사용자 식별번호 */
gid_t cgid; /* 생성자의 그룹 식별번호 */
uid_t uid; /* 유효 그룹 식별번호 */
gid_t gid; /* 생성자의 그룹 식별번호 */
mode_t umode; /* 허가 */


======메시지 큐=========
과정
1. 메시지 큐는 msgget 프리미티브에 의해 생성되고 접근
2. 일단 큐가 만들어지면 적당한 큐 허가를 가진 프로세스가 msgsnd를 사용하여 메시지를 큐에 넣을
수 있다.
3. 다른 프로세스는 msgrcv에 의해 이 메시지를 읽을 수 있으며, 이때 메시지는 큐로부터 제거된다.


=======================================================================
---------------
메시지 큐의 구조
---------------
struct msgbuf{
long mtype; /* 메시지 형식 */
char mtext[SIZE]; /* 메시지 데이터 */
};
=======================================================================
---------------
메시지 큐의 생성
---------------
---------------------------
#include<sys/msg.h>

int msgget(key_t key, int permflags);
---------------------------

설명: 새 큐를 생성 또는 기존 큐에 접근
성공 : 양의 정수
실패 : 음수

-key 인수: 메시지 큐를 시스템에 식별시키는 단순한 숫자
-permflags:
IPC_CREAT : key에 해당하는 메시지 큐가 존재하지 않는 겨우 msgget이 이를 생성
설정되지 않으면 msgget은 그 키를 가진 메시지 큐가 존재하는 경우에만 메시지 큐
식별자를 돌려준다.
IPC_EXCL : IPC_CREAT와 이것이 동시에 설정된 경우 호출은 단지 하나의 메시지 큐를 생성하기
위한 것
key에 대한 큐가 이미 존재하면 -1을 리턴

예 ) mqid = msgget((key_t) 0100, 0644 | IPC_CREAT | IPC_EXCL);
* key값이 100인 메시지 큐 생성
* 호출이 성공하는 경우 큐의 허가는 0644가 된다.
* 따라서 큐를 만든 프로세스만이 메시지를 큐에 넣을 수 있다.
* 필요한 경우 msgctl을 사용하여 큐에 연관된 허가와 소유권을 변경할 수 있다.
=================================================================================
---------------
메시지 큐의 삭제
--------------
----------------------------------------
#include<sys/msg.h>

int msgctl(int mqid, int cmd, struct mqid_ds *buf);
----------------------------------------
성공 : 0
실패 : -1

cmd : IPC_RMID를 사용
buf : NULL포인터 사용

----------------------------------------
------------------
메시지 전송
------------------
-----------------------------------------------------
#include<sys/msg.h>

int msgsnd(int mqid, const void *message, size_t size, int flags);
---------------------------------------------------
성공 : 0 반환
실패 : -1 반환
**인자 설명**
-mqid : 메시지를 넣을 메시지 큐의 인스턴스를 지정
-message: 큐에 넣고 싶은 메시지의 포인터
-size : 메시지 요소인 mtext[]의 바이트 크기
-flag : 옵션 플래그
0 옵션 플래그가 없는 경우
IPC_NOWAIT 현재 메시지 큐가 가득 차 있는 경우 하무가 에러를 반환
==================================================================================
------------
메시지 수신
------------
------------------------------------------------------
#include<sys/msg.h>

int msgrcv(int mqid, void *message, size_t size, long msg_type, int flags);
-----------------------------------------------------
-mqid : 메시지를 넣을 메시지 큐의 인스턴스를 지정
-message: 메시지를 수신하게 될 버퍼
-size : 메시지 요소인 mtext[]의 바이트 크기
-msg_type
------------------------------------------------------------------------
msgtype flags 설명
------------------------------------------------------------------------
> 0 0 msgrcv()함수는 msgtype 인자가 메시지
의 메시지 형식값과 일치하는 경우에만
메시지를 반환한다.

> 0 MSG_EXCEPT msgrcv()함수는 msgtype 인자가 메시지
형식과 일치하는 경우에만 메시지를 반환
한다.

msgrcv()함수는 큐에 넣어진 첫번째 메시지를
0 무시 반환한다.

< 0 무시 msgrcv()함수는 abs(msgtype)와 같거나 작은
가장 하위 메시지를 반환한다.
------------------------------------------------------------------------
-msgflg : 수신을 위한 옵션 플래그
IPC_NOWAIT : msgrcv()함수가 더 이상 수신할 메시지가 없는 경우 ENOMSG 에러 반환
MSG_EXCEPT : msgtyp 인자가 0보다 큰 값으로 사용되ㄹ 때 msgtype과 다른 첫번째 메시지를
수신
MSG_NOERROR : 수신쪽의 버퍼의 크기에 맞출 필요가 생기게 되는 경우 메시지를 축소시킨다.
만약 크기에 맞게 줄일 수 없는 경우 E2BIG 에러 반환

=====================================================================================
예제 프로그램
===================== q.h ===================================
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/ipc.h>
#include<string.h>
#include<sys/msg.h>
#include<sys/types.h>

#define QKEY (key_t)0105 /* 큐의 키를 식별한다. */
#define QPERM 0660 /* 큐의 허가 */
#define MAXOBN 50 /* 개체 이름의 최대길이 */
#define MAXPRIOR 10 /* 최대 우선 순위 수준 */

int init_queue(void);

struct q_entry{
long mtype;
char mtext[MAXOBN+1];
};

int init_queue(void)
{
int queue_id;

/* 메시지 큐를 생성하거나 개방하려고 시도한다. */
if((queue_id = msgget(QKEY, IPC_CREAT | QPERM)) == -1)
perror("msgget failed");


return(queue_id);
};

============================================================

====================== serv.c ================================
#include"q.h"

int proc_obj(struct q_entry *);
int serve(void);


main()
{
pid_t pid;

switch(pid = fork()){
case 0: /* 자식 */
serve();
break; /* 실제로는 서버는 결코 퇴장(exit)하지 않음 */
case -1:
perror("fork to start server failed");
break;
default:
printf("server process pid is %d\n", pid);
}
exit(pid != -1 ? 0 : 1);
}

int serve(void)
{
int mlen, r_qid;
struct q_entry r_entry;

/* 필요에 따라 메시지 큐를 초기화한다. */
if((r_qid = init_queue()) == -1)
return (-1);

/* 다음 메시지를 가져와 처리한다. 필요하면 기다린다. */

for(;;)
{
if((mlen = msgrcv(r_qid, &r_entry, MAXOBN, (-1 * MAXPRIOR), MSG_NOERROR)) == -1)
{
perror("msgrcv failed");
return(-1);
}
else
{
/* 우리의 문자열을 가지고 있는지 확인한다.*/
r_entry.mtext[mlen] = '\0';

/* 객체 이름을 처리한다. */
proc_obj(&r_entry);
}
}
}

int proc_obj(struct q_entry *msg)
{
printf("\n priority: %ld name: %s \n", msg->mtype, msg->mtext);
}

===============================================================
======================== etest.c ===============================
#include<stdlib.h>
#include"q.h"


int enter(char *, int );

main(int argc, char **argv)
{
int priority;


if(argc !=3)
{
fprintf(stderr, "usage: %s objname prioritey\n", argv[0]);
exit(1);
}

if((priority = atoi(argv[2])) <= 0 || priority > MAXPRIOR)
{
perror("invalid priority");
exit(2);
}

if(enter(argv[1], priority) < 0)
{
perror("enter failure");
exit(3);
}

exit(0);
}

int enter(char *objname, int priority)
{
int len, s_qid;
struct q_entry s_entry; /* 메시지를 저장할 구조 */

/* 이름의 길이, 우선순의 수준을 확인한다. */

if((len = strlen(objname)) > MAXOBN)
{
perror("name too long");
return(-1);
}


if(priority > MAXPRIOR || priority < 0)
{
perror("invalid priority level");
return(-1);
}

/* 필요에 따라 메시지 큐를 초기화 시킨다. */

if((s_qid = init_queue()) == -1)
return(-1);

/* s_entry를 초기화한다. */
s_entry.mtype = (long) priority;
strncpy(s_entry.mtext, objname, MAXOBN);

/* 메시지를 보내고, 필요할 경우 기다린다. */
if(msgsnd(s_qid, &s_entry, len, 0) == -1)
{
perror("msgsnd failed");
return(-1);
}
else
return(0);
}

================================================================
실행결과

[qtam@qtam bak]$ ./etest msgequeueex1 3
[qtam@qtam bak]$ ./etest msgequeueex2 7
[qtam@qtam bak]$ ./etest msgequeueex3 2
[qtam@qtam bak]$ ./etest msgequeueex4 4
[qtam@qtam bak]$ ./serv
server process pid is 1577

priority: 2 name: msgequeueex3

priority: 3 name: msgequeueex1

priority: 4 name: msgequeueex4

priority: 7 name: msgequeueex2
[qtam@qtam bak]$
================================================================