- 메시지큐란 IPC기법중 하나이다. Queue를 사용하여 데이터를 선입선출하며, "msgtype"에 따라서 특정 메시지 중 가장 먼저 들어온 메시지를 따로 받아올 수 도 있다. 이 메시지는 kernel에서 보관하기 때문에 프로세스가 종료되어도 사라지지 않는다. 메시지는 큐의 용량이 허용하는 한, 큐에 계속 쌓이며 읽은 메시지는 큐에서 삭제한다. LINUX(UNIX)에서는 POSIX 메시지큐를 사용한다(=System V 메시지큐의 최신버전). 본 글에서는 System V 방식의 메시지 큐를 사용할 것이다.
※ 메시지큐는 아래와 같은 협의된 데이터 구조를 활용한다.
struct msgbuf{
/* 메시지 타입은 반드시 long이면서 0이상의 값 */
long mType;
/* 아래의 데이터의 형식과 크기는 변경 가능 */
char mText[255];
}
- 메시지큐 관련 시스템 콜 (System V)
- System V 방식의 메시지큐를 사용하기 위해서는 아래 3개의 헤더파일이 필요하다.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
1. msgget : key를 번호로 가지는 큐의 id를 리턴한다.
int msgget(key_t key, int msgflg);
/*
key : 메시지큐를 얻어올 때 사용하는 고유 key값
msgflg :
IPC_CREATE : key에 해당하는 메시지큐가 없으면 새로 생성 후 msqid, 있으면 이미 존재하는 msqid 반환
IPC_EXCL :
1) IPC_EXCL만 사용 : key에 해당하는 메시지큐가 없으면 -1, 있으면 msqid 반환
2) IPC_CREATE|IPC_EXCL : key에 해당하는 메시지큐가 없으면 새로 생성 후 msqid, 있으면 -1 반환
=> 중복된 메시지큐를 방지, 가장 많이쓰며 보통 "IPC_CREATE|IPC_EXCL|0666" 처럼 권한과 같이 사용 (타프로그램 접근 위함)
*/
- 성공하면 msqid ,실패하면 -1을 반환하고 errno을 설정한다.
- ERRORS :
EACCES (Permission denied): 메시지 큐에 대한 읽기 또는 쓰기 권한이 없는 경우 발생합니다.
EEXIST (File exists): IPC_CREAT 플래그와 함께 사용했는데 이미 해당 키에 대한 메시지 큐가 존재하는 경우 발생합니다.
EINVAL (Invalid argument): 잘못된 인자가 전달되었을 때 발생합니다. 예를 들어, IPC_CREATE 플래그가 없는데 식별자가 존재하지 않는 경우 등.
ENOENT (No such file or directory): 주어진 키에 해당하는 메시지 큐가 존재하지 않는 경우 발생합니다.
ENOMEM (Not enough memory): 커널 메모리 부족으로 메시지 큐를 생성할 수 없는 경우 발생합니다.
2. msgsnd : 큐의 id(msqid)를 인자로 받아서 큐로 메시지를 보낸다.
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
/*
msqid : msgget으로 얻어온 메시지 큐의 id
msgp : 전송할 자료 의미, 메시지큐 구조체(msgbuf) 사용
msgsz : 전송할 자료의 크기, msgbuf의 mtype를 제외한 실 데이터(mText)의 크기
msgflg:
IPC_NOWAIT : blocking 하지않고 실패처리(-1 반환)
=> msgsnd는 default로 큐에 공간이 없을 때 block 상태가 되어 큐에 공간이 생길 때 까지 기다림
MSG_NOERROR : 메시지의 크기가 지정된 크기보다 클 경우, 지정된 크기(msgsz)만큼만 메시지를 전송
=> 만약 이를 사용하지 않고 메시지의 크기가 지정된 크기보다 클 경우 -1 반환
*/
- 성공하면 0, 실패하면 -1을 반환하고 errno을 설정한다.
- ERRORS :
EACCES (Permission denied): 메시지 큐에 대한 쓰기 권한이 없는 경우 발생합니다.
EAGAIN (Try again): 메시지 큐가 가득 차서 더 이상 메시지를 수용할 수 없는 경우 발생합니다. 비동기(non-blocking) 모드에서 사용됩니다.
EIDRM (Identifier removed): 메시지 큐가 삭제되었지만 해당 식별자로 메시지를 보내려고 시도한 경우 발생합니다.
EINTR (Interrupted system call): 시그널이 도착하여 시스템 호출이 중단된 경우 발생합니다.
EINVAL (Invalid argument): msqid가 유효한 메시지 큐 식별자가 아닌 경우, msgsz가 0인 경우 등 여러 가지 경우에 발생합니다.
ENOMEM (Not enough memory): 커널 메모리 부족 또는 메시지 큐를 위한 자원이 부족한 경우 발생합니다.
3. msgrcv : 큐의 id를 인자로 받아서 메시지를 읽고, 메시지를 큐에서 제거한다.
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
/*
msqid : msgget으로 얻어온 메시지 큐의 id
msgp : 수신할 자료 의미, 메시지큐 구조체(msgbuf) 사용
msgsz : 수신할 자료의 크기, msgbuf의 mtype를 제외한 실 데이터(mText)의 크기
msgtyp :
msgtyp > 0 : mType이 같은 첫번째 메시지 리턴
msgtyp == 0 : mType 상관없이 큐에 첫번째로 들어온 메시지 리턴
msgtyp < 0 : mType의 절댓값보다 작거나 같은 타입의 첫번째 메시지 리턴
msgflg :
IPC_NOWAIT : blocking 하지않고 실패처리(-1 반환)
=> msgrcv는 default로 메시지가 비어있을 때 block 상태가 되어 메시지가 올 때까지 기다림
MSG_NOERROR : 메시지의 크기가 지정된 크기보다 클 경우, 지정된 크기(msgsz)만큼만 메시지를 수신
=> 만약 이를 사용하지 않고 메시지의 크기가 지정된 크기보다 클 경우 -1 반환
*/
- 성공하면 0, 실패하면 -1을 반환하고 errno을 설정한다.
- ERRORS :
EACCES (Permission denied): 메시지 큐에 대한 읽기 권한이 없는 경우 발생합니다.
EIDRM (Identifier removed): 메시지 큐가 삭제되었지만 해당 식별자로 메시지를 받으려고 시도한 경우 발생합니다.
EINTR (Interrupted system call): 시그널이 도착하여 시스템 호출이 중단된 경우 발생합니다.
EINVAL (Invalid argument): msqid가 유효한 메시지 큐 식별자가 아닌 경우, msgsz가 음수인 경우 등 여러 가지 경우에 발생합니다.
ENOMSG (No message of the desired type): IPC_NOWAIT 플래그를 사용하였고, 큐에 기다리는 메시지가 없을 때 발생합니다.
4. msgctl : 큐의 id를 인자로 받아서 큐의 상태정보를 구하거나, 변경하거나, 아예 메시지큐를 삭제할 수 있다.
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
/*
msqid : msgget으로 얻어온 메시지큐의 id
cmd : 제어명령
IPC_STAT : 메시지큐의 현재상태를 buf에 저장
IPC_SET : 메시지큐의 상태를 buf값으로 변경(msg_perm, msg_qbytes만 변경 가능)
IPC_RMID : 메시지큐를 삭제, 이때는 buf가 필요없으므로 buf를 0으로 설정
*buf : 메시지큐 정보를 받을 버퍼
버퍼구조체 :
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (nonstandard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
*/
- 성공하면 0, 실패하면 -1을 반환하고 errno을 설정한다.
- ERRORS:
EINVAL (Invalid argument): msqid가 유효한 메시지 큐 식별자가 아닌 경우, 또는 cmd 매개변수가 잘못된 명령을 가리키는 경우에 발생합니다.
EACCES (Permission denied): 메시지 큐에 대한 쓰기 또는 읽기 권한이 없는 경우에 발생합니다.
EPERM (Operation not permitted): 호출자에게 필요한 권한이 없는 경우에 발생합니다.
- 오류코드들은 "<errno.h>" 헤더 파일에 정의되어 있다. 아래처럼 perror 함수나 strerror(errno)를 사용하여 실패한 이유를 출력할 수 있다.
/* error print */
perror("msgget");
fprintf(stderr, "Error: %s\n", strerror(errno));
- 예제 프로그램
/* msgcast : 메시지큐를 생성하고, 데이터를 큐에 쓰는 프로그램 */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
//구조체도 헤더파일에서 선언된 것과 겹치는 현상이 일어나서 컴파일이 되질 않습니다. 구조체 이름도 바꾸어야합니다.
struct msgbuf
{
long msgtype;
char mtext[256];
char myname[16];
int seq;
};
int main()
{
key_t key_id;
int i;
struct msgbuf mybuf, rcvbuf;
key_id = msgget((key_t)1234, IPC_CREATE|IPC_EXCL|0666);
if (key_id == -1)
{
perror("msgget error : ");
exit(0);
}
printf("Key is %d\n", key_id);
memset(mybuf.mtext, 0x00, 256);
memset(mybuf.myname, 0x00, 16);
memcpy(mybuf.mtext, "hello world 4", 13);
memcpy(mybuf.myname, "yundream", 8);
mybuf.seq = 0;
i = 0;
while(1)
{
// 짝수일경우 메시지 타입이 4
// 홀수일경우에는 메시지 타입이 3
if (i % 2 == 0)
mybuf.msgtype = 4;
else
mybuf.msgtype = 3;
mybuf.seq = i;
// 메시지를 전송한다.
if (msgsnd( key_id, (void *)&mybuf, sizeof(struct msgbuf), MSG_NOERROR|IPC_NOWAIT) == -1)
{
perror("msgsnd error : ");
exit(0);
}
printf("send %d\n", i);
i++;
sleep(1);
}
printf("%d \n", rcvbuf.msgtype);
printf("%s \n", rcvbuf.mtext);
printf("%s \n", rcvbuf.myname);
exit(0);
}
/* msgrecv : 메시지큐에 쓴 데이터를 읽어들이는 프로그램 */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
//구조체도 헤더파일에서 선언된 것과 겹치는 현상이 일어나서 컴파일이 되질 않습니다. 구조체 이름도 바꾸어야합니다.
struct msgbuf
{
long msgtype;
char mtext[256];
char myname[16];
int seq;
};
int main(int argc, char **argv)
{
key_t key_id;
struct msgbuf mybuf;
int msgtype;//전역변수로 빼야 됩니다.
// 아규먼트가 있을경우 msgtype 가 3인 메시지를 받아오고(홀수)
// 아규먼트가 없을경우 msgtype 가 4인 메시지를 받아온다(짝수)
if (argc == 2)
msgtype = 3;
else
msgtype = 4;
key_id = msgget(1234, IPC_CREATE|IPC_EXCL|0666);
if (key_id < 0)
{
perror("msgget error : ");
exit(0);
}
while(1)
{
if (msgrcv( key_id, (void *)&mybuf, sizeof(struct msgbuf), msgtype, 0) == -1)
{
perror("msgrcv error : ");
exit(0);
}
printf("%d\n", mybuf.seq);
}
exit(0);
}
ref)
'C' 카테고리의 다른 글
시스템 프로그래밍[C] #1 파일 입출력 (0) | 2023.07.31 |
---|