Linux System Programming Lecture #9 – 메시지 큐(Message Queue)
동일한 플랫폼 상에서 유사하게 구현되어 ICP 도구로 별도 분류하기도 함 IPC(Inter-Process Communication) (1) 프로세스 통신 도구(IPC Facilites) : 비동기적으로 동작하는 프로세스간의 통신을 위하여 운영체제가 제공하는 도구 종류: Signal(신호) Pipe(파이프) Message Queue(메시지 큐) Semaphore(세마포어) Shared Memory(공유 메모리) Socket(소켓) 동일한 플랫폼 상에서 유사하게 구현되어 ICP 도구로 별도 분류하기도 함 Linux System Programming
IPC(Inter-Process Communication) (2) 프로세스 통신 도구 (IPC Facilites) : 세마포어(Semaphore) 프로세스 동기화와 자원 관리에 사용 프로세스간의 상호배제 문제 해결에 사용 공유 메모리(Shared Memory) 가장 빠른 프로세스 통신 도구 하나 이상의 프로세스에 부착하여 서로 공유하여 접근 가능 프로세스간의 공유 메모리 접근을 위해 동기화가 필요 메시지 큐(Message Queue) 프로세스들간에 이산적인 양의 데이터 송수신을 위해 사용 전송할 데이터를 메시지 형태로 생성하여 전달하고 메시지 수신을 통해 데이터 수신이 가능 인터넷 메일 서비스와 개념이 유사 Linux System Programming
IPC(Inter-Process Communication) (3) get ctl op 세마포어 semget semctl semop 공유 메모리 shmget shmctl shmop(shmat, shmdt) 메시지큐 msgget msgctl msgop(msgsnd, msgrcv) 기 능 IPC 도구의 생성 및 참조 기능 제공 소유자 및 접근 허가 변경 등의 기능 제공 IPC 도구의 연산(송수신 제어 등) 기능 제공 Linux System Programming
IPC(Inter-Process Communication) (4) get 시스템 호출 파일 열기 시스템 호출 open과 유사 새롭게 통신 도구를 생성하거나 이미 생성된 통신 도구에 대한 id를 반환하여 접근 가능하게 함 반환된 id는 파일 디스크립터와 비슷하게 동작 접근 허가 또한 파일 접근 허가와 비슷하게 규정 ctl 시스템 호출 통신 도구에 관한 상태 정보 읽기 몇몇 상태 정보(소유자, 그룹, 접근 허가 등)에 대한 변경 통신 도구의 제거 기능 op 시스템 호출 프로세스간의 데이터 전송을 실행 파일 시스템 호출 read/write와 유사 Shmat는 파일의 open과 유사하고, shmdt는 파일의 close와 유사 Linux System Programming
IPC(Inter-Process Communication) (5) IPC 도구 식별자 – IPC key 파일을 식별하기 위해 파일 이름을 사용하듯이 IPC 객체를 식별하기 위해 사용하는 정수형 숫자 프로세스들은 IPC key를 이용하여 IPC 자원을 공유 자료형: key_t (<sys/types.h>에 정의) IPC key 변환 시스템 호출 – ftok(file_to_key) IPC 도구의 공유를 편리하게 지원하기 위해 파일 경로명과 식별자를 IPC 도구에 부여하고, 이를 IPC key 값으로 변환하는 시스템 호출 char device Inode # of file key_t Linux System Programming
IPC(Inter-Process Communication) (6) IPC key 변환 시스템 호출 – ftok(file_to_key) Linux System Programming
IPC(Inter-Process Communication) (7) IPC 도구의 접근 허가 구조: struct ipc_perm 파일의 접근 허가와 유사한 기능을 지원 접근 모드에서 실행 모드는 무의미함 Linux System Programming
IPC(Inter-Process Communication) (8) IPC 도구 유형별로 생성된 IPC 객체를 관리하기 위해 운영체제가 유지하는 테이블 IPC 도구 유형별로 각각의 테이블을 유지 파일 테이블과 유사한 개념 Linux System Programming
IPC(Inter-Process Communication) (9) ipcs 명령 현재 사용중인 IPC 도구들의 상태(status)를 보여주는 명령 IPC 타입, 사용자 ID, key 값 그리고 접근 허가 등을 볼 수 있음 옵션을 사용하여 지정된 유형의 IPC 도구 상태 정보만을 접근 가능 -q : 메시지 큐, -m : 공유 메모리, -s : 세마포어 ipcrm 명령 IPC 객체를 제거하는 명령 IPC 도구 유형 및 key 값을 지정하는 원하는 IPC 객체를 제거 Linux System Programming
메시지 큐(Message Queue) (1) 메시지 큐: 프로세스간에 이산적인 데이터를 메시지 형태로 전송하는 통신 도구 파이프와 같이 FIFO 타입의 데이터 전송을 지원 메시지는 메시지와 바이트(byte)들의 모임으로 구성 메시지 유형은 정수값으로 여러가지 메시지 형태에 id를 부여하여 구별하기 위해 사용 바이트 모임은 전송하는 데이터를 의미하는 것으로 문자, 그림 또는 일련의 구조체 데이터 등을 전송하기 위햇 사용 관련 시스템 호출 함수 msgget() – 메시지 큐를 생성 msgsnd()/msgrcv() – 메시지 전송 및 수신 msgctl() – 메시지 큐 제어 Linux System Programming
메시지 큐(Message Queue) (2) 메시지 큐의 구조: 메시지 큐의 상태 구조체 : msgid_ds 메시지 큐 객체의 상태 정보를 저장하는 구조체 하나의 메시지 큐가 생성되면 하나의 msgid_ds 구조체가 생성 Linux System Programming
메시지 큐(Message Queue) (3) 메시지 큐의 동작: 전송 프로세스가 전송할 데이터를 메시지 형태로 만들어 메시지를 전송하면 전송된 메시지는 메시지 큐의 마지막에 연결된다 수신 프로세스는 메시지 큐에 존재하는 메시지를 선택적으로 수신할 수 있다. 수신 프로세스에서는 메시지를 수신할 때에 다음의 수신 정책 중에 하나를 지정하여야 한다. 메시지 큐에서 첫번째 메시지 메시지 큐에서 지정된 타입의 첫번째 메시지 메시지 큐에 있는 타입들의 범위들로 부터 첫번째 메시지 Linux System Programming
메시지 큐(Message Queue) (4) 메시지 큐의 생성: msgget msgget – 메시지 큐 생성 시스템 호출 주어진 key를 갖는 메시지 큐가 존재하면 메시지 큐의 id를 반환하고, 그렇지 않으면 새롭게 메시지 큐를 생성하고 id를 반환 Linux System Programming
메시지 큐(Message Queue) (5) 메시지 큐의 생성: msgget 메시지 큐 구조체: msqid_ds Linux System Programming
메시지 큐(Message Queue) (6) 메시지 큐의 생성: msgget 메시지 큐 구조체의 초기값 Linux System Programming
메시지 큐(Message Queue) (7) 메시지 큐의 생성: msgget msgget 시스템 호출의 flag 옵션 Linux System Programming
메시지 큐(Message Queue) (8) 메시지 큐의 제어: msgctl msgctl - 메시지 큐 제어 시스템 호출 메시지 큐의 상태를 질의하거나 상태 정보를 변경, 또는 메시지 큐를 제거하는 기능을 지원 Linux System Programming
메시지 큐(Message Queue) (9) 메시지 큐의 제어: msgctl msgctl 시스템 호출의 명령어 Linux System Programming
메시지 큐(Message Queue) (10) msgctl 시스템 호출의 사용 예 메시지 큐 관련 자료 구조 선언 메시지 큐의 제거 메시지 큐의 소유자 변경 #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int msqid; struct msqid_ds ds; msgctl(msqid, IPC_RMID, (struct msqid_ds *)0); msgctl(msqid, IPC_STAT, &ds); ds.msg_perm.uid = 51; msgctl(msqid, IPC_SET, &ds); Linux System Programming
메시지 큐(Message Queue) (11) msgctl 시스템 호출의 사용 예 메시지 큐의 접근 허가 변경 메시지 큐의 메시지 크기 변경 msgctl(msqid, IPC_STAT, &ds); ds.msg_perm.mode = 0660; msgctl(msqid, IPC_SET, &ds); msgctl(msqid, IPC_STAT, &ds); ds.msg_qbytes = 5000; msgctl(msqid, IPC_SET, &ds); Linux System Programming
메시지 큐(Message Queue) (12) 메시지 큐의 연산: msgsnd, msgrcv 메시지 구조체 : 메시지 유형은 전송되는 여러가지 유형의 메시지 형태를 분류하여 지정 메시지 텍스트는 전송하는 데이터를 저장 메시지 텍스트의 크기는 최소 0에서 최대 64 KB 이내이며, 묵시적으로 2048 바이트로 사용 #include <sys/msg.h> struct msgbuf { long mtype; /* 메시지 유형 */ char mtext[1]; /* 메지시 텍스트 */ } Linux System Programming
메시지 큐(Message Queue) (13) 메시지 큐의 연산: msgsnd, msgrcv 메시지 송신 시스템 호출 : msgsnd Linux System Programming
메시지 큐(Message Queue) (13) 메시지 큐의 연산: msgsnd, msgrcv msgsnd 시스템 호출에 의한 메시지 큐 구조체 변경 Linux System Programming
메시지 큐(Message Queue) (14) 메시지 큐의 연산: msgsnd, msgrcv 메시지 수신 시스템 호출 : msgrcv Linux System Programming
메시지 큐(Message Queue) (15) 메시지 큐의 연산: msgsnd, msgrcv msgrcv 시스템 호출에서의 메시지 타입 지정 msgrcv 시스템 호출에 의한 메시지 큐 구조체 변경 Linux System Programming
예제 프로그램 (1) 예제 9-1 하나의 메시지를 전송하고 수신하는 프로그램을 작성하여라 // file name : ex9-1a.c // #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> struct { long t; char a[60]; } x = {11L,"hello"}; main() { int mid; mid = msgget(11L,IPC_CREAT| 0666); msgsnd(mid,&x,strlen(x.a)+1,0); sleep(60); msgctl(mid,IPC_RMID,0); } Linux System Programming
예제 프로그램 (2) // file name : ex9-1b.c // #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> struct { long t; char a[60]; } x ; main() { int mid,rtn; mid = msgget(11L,0); rtn=msgrcv(mid,&x,60,0L,0); printf("rtn=%d type=%d text=%s\n",rtn,x.t,x.a); } Linux System Programming
예제 프로그램 (3) 예제 9-2 예제 9-1을 개선하여 여러 개의 메시지를 송수신하는 프로그램을 작성하여라. // file name : ex9-2a.c // #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #define MAX_SEND_SZ 30 #define DONE 99L main() { void perror(); key_t key; int mid; static struct { long mtype; char mtext[MAX_SEND_SZ]; } buf; Linux System Programming
예제 프로그램 (4) static char *string[3] = { "hello", "how are you", "good-bye"}; int i,rtn; if((key= ftok(".",'a'))==-1) { perror("Can\'t form key"); exit(1); } mid = msgget(key,IPC_CREAT| 0660); if(mid == -1) { perror("Sender can not make msg queue!"); exit(2); Linux System Programming
예제 프로그램 (5) buf.mtype = 1L; for(i=0;i<3;i++) { strcpy(buf.mtext,string[i]); if(msgsnd(mid,&buf,strlen(buf.mtext)+1,0)==-1) { perror("Sender can not msgsnd!"); exit(3); } rtn = msgrcv(mid,&buf,MAX_SEND_SZ,DONE,0); if(rtn == -1) { perror("Sender can not msgrcv"); exit(4); msgctl(mid,IPC_RMID,0); Linux System Programming
예제 프로그램 (6) // file name : ex9-2b.c // #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #define MAX_RECV_SZ 60 #define FIFO 0L #define ZERO_LEN 0 #define DONE 99L static struct { long mtype; char mtext[MAX_RECV_SZ]; } buf; key_t key; int mid; int rtn; Linux System Programming
예제 프로그램 (7) main(int argv,char *argc[]) { void perror(); if((key= ftok(".",'a'))==-1) { perror("Can\'t form key"); exit(1); } mid = msgget(key,0); if(mid == -1) { perror("Receiver can not access msg queue!"); exit(2); while(1) { rtn = msgrcv(mid,&buf,MAX_RECV_SZ,FIFO,0); printf("rtn=%d buf.mtype=%ld buf.mtext=%s\n", rtn,buf.mtype,buf.mtext); if(!strcmp(buf.mtext,"good-bye")) break; buf.mtype = DONE; msgsnd(mid,&buf,ZERO_LEN,0); Linux System Programming
예제 프로그램 (8) 예제 9-3 메시지 송신 프로그램가 키보드 입력을 메시지로 만들어 여러 개의 수신 프로그램에 전달하는 프로그램을 작성하여라. 송신 프로그램은 fork()/exec() 시스템 호출을 통하여 수신 프로세스를 생성하고, 수신 프로세스는 수신된 메시지를 표준 출력으로 출력하도록 한다. // file name : ex9-3a.c // #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #define MAX_SZ 80 #define NCHILD 3 Linux System Programming
예제 프로그램 (9) #define WAIT_SOLE_USE(MSGQID) { \ int still_active = NCHILD; \ int i; \ long s_pid; \ s_pid = getpid(); \ while(still_active) { for(i=0;i<NCHILD;i++) \ if(msgrcv(MSGQID,&buf,MAX_SZ,s_pid,IPC_NOWAIT) != -1) \ if(!strncmp(buf.mtext,"DONE",4)) still_active--; \ sleep(2); \ } \ } main() { void perror(); key_t ftok(); int msgqid; struct { long mtype; char mtext[MAX_SZ]; } buf; Linux System Programming
예제 프로그램 (10) int msg_length; static char qid_evar[40]; static char *envp[2] = { qid_evar }; int this_fork, child; long r_pid[NCHILD]; char child_name[20]; int eof = 0; if((msgqid = msgget(IPC_PRIVATE,IPC_CREAT | 0660)) == -1) { perror("Sender can't make message queue!"); exit(1); } sprintf(qid_evar,"MQID=%d",msgqid); for(child=0;child<NCHILD;child++) { this_fork = fork(); if(this_fork == -1) { perror("fork failed"); exit(2); else if(this_fork == 0) { sprintf(child_name,"mwall_r%d",child); execle("ex9-3b",child_name,0,envp); perror("exec failed"); exit(3); else r_pid[child] = (long) this_fork; Linux System Programming
예제 프로그램 (11) setbuf(stdout,(char *) 0); while(1) { fprintf(stdout, "Enter message to be sent to all receivers:"); if(gets(buf.mtext) == (char *) 0) { fprintf(stdout,"\n"); eof++; strcpy(buf.mtext,"EOF"); } msg_length = strlen(buf.mtext) +1; for(child=0;child<NCHILD;child++) { buf.mtype = r_pid[child]; if(msgsnd(msgqid,&buf,msg_length,0) == -1) { perror("Producer msgsnd error"); exit(4); if(eof) break; sleep(1); WAIT_SOLE_USE(msgqid); msgctl(msgqid,IPC_RMID,0); Linux System Programming
예제 프로그램 (12) // file name : ex9-3a.c // #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #define MAX_SZ 80 main(int argc, char *argv[]) { void perror(); char *getenv(); long pid; char *valuep; int msgqid; struct { long mtype; char mtext[MAX_SZ]; } buf; Linux System Programming
예제 프로그램 (13) pid = (long) getpid(); if((valuep = getenv("MQID")) == NULL) { fprintf(stderr,"%s:can't get env MQID\n",argv[0]); exit(1); } else sscanf(valuep,"%d",&msgqid); while(1) { msgrcv(msgqid,&buf,MAX_SZ,pid,MSG_NOERROR); printf("%s received '%s'\n",argv[0],buf.mtext); if(!strcmp(buf.mtext,"EOF")) break; buf.mtype = (long) getppid(); strcpy(buf.mtext,"DONE"); if(msgsnd(msgqid,&buf,strlen(buf.mtext),0) == -1) { fprintf(stderr,"%s:msgsnd error\n",argv[0]); exit(2); exit(0); Linux System Programming