0. 시작하기전에
- 운영체제에서 배운 IPC 기법을 간단하게 복습해보자.
1. IPC 기법이란
- IPC 기법은 프로세스 간의 통신을 위한 기술이다.
- 프로세스 교체 등의 이유로 한 프로세스에서 다른 프로세스의 상태를 확인해야 하는데,
- 보안상의 이유로 프로세스는 다른 프로세스의 데이터에 접근할 수 없고, 프로세스 간의 통신은 불가능하다.
- 그러므로 일종의 우회 방식으로 프로세스간의 통신을 가능하게 하는 기술이 IPC 기법이다.
2. IPC 기법의 종류
a) File 기법
- 프로세스는 저장매체에 접근이 가능하므로, 각 프로세스의 상태를 기록하는 공유 파일을 통해 통신하는 방식이다.
- 저장매체에 접근하는데 시간이 오래걸리며, 공유 파일이 업데이트 되더라도 다른 파일은 이를 알 수 있는 방법이 없다.
- 그러므로 잘 사용되지 않는 IPC 기법이다.
b) 커널 공간을 사용하는 IPC 기법
- 프로세스의 커널 공간은 모두 공유된 영역이다. 즉, 커널영역은 동일한 물리메모리의 주소를 가리킨다.
- 이 커널공간을 활용하는 방식에 따라 IPC 기법이 다양하게 분류된다.
- 여기에는 message queue, pipe, shared memory, semaphore, signal, socket 등의 기법이 있다.
3. Pipe 기법
- 단방향 통신을 이용한 기법
- fork()로 자식 프로세스를 생성했을 때, 부모 프로세스의 데이터가 자식 프로세스에게 그대로 복사된다.
- 이 복사된 데이터를 이용한 단방향 통신방법을 pipe 기법이라 한다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MSGSIZE 255
char* msg = "Hello Child Process!";
int main()
{
char buf[255];
int fd[2], pid, nbytes;
if(pipe(fd) < 0) // pipe(fd)로 파이프 생성
exit(1);
pid = fork(); // 이 함수 실행 다음 코드부터 부모/자식 프로세스로 나뉨
if (pid > 0) { // 부모 프로세스에는 자식프로세스의 pid값이 들어감
printf("parent PID: %d, child PID: %d\n", getpid(), pid);
write(fd[1], msg, MSGSIZE); // fd[1]에 write한다.
exit(0);
}
else { // 자식프로세스에는 pid값이 0이 됨
printf("child PID: %d\n", getpid());
nbytes = read(fd[0], buf, MSGSIZE); // fd[0]을 읽음
printf("%d %s\n", nbytes, buf);
exit(0);
}
return 0;
}
▶ 실행결과
4. Message Queue
- Queue 자료구조를 사용하므로 FIFO 방식으로 프로세스의 우선순위를 지정하는 방식이다.
- 다음 예제 코드를 통해서 message queue가 어떻게 구현되는지 알아보자.
a) msgget()
- msgget() 함수를 통해서 message queue를 생성한다.
- key 값은 해당 메세지 큐를 구분할 수 있는 고유값을 입력한다.
- msgflg()에는 메세지 큐를 생성할 때 부여할 여러가지 옵션을 입력한다.
- 옵션에는 대표적으로 IPC_CREAT가 있으며, 이는 입력된 key값이 새로운 고유값인 경우, 메세지 큐를 새로 생성하는 기능이다.
- IPC_CREAT 옵션은 |(or) 기호를 사용하여 메세지큐의 권한까지 설정할 수 있다.
b) msgsnd()
- msgsnd() 함수는 메세지 큐를 전송할 때 사용하는 함수다.
- msqid는 위에서 생성된 메세지 큐의 id를 사용한다.
- 나머지 인자는 메세지큐에 담을 데이터를 의미한다.
- IPC_NOWAIT 옵션은 수신자가 메세지를 읽지 않더라도 기다리지 않고 다음 코드를 실행하는 기능이다.
- IPC_NOWAIT 옵션을 사용하지 않고 0을 입력하면 메세지를 읽을 때까지 대기상태가 된다.
▶ 메세지큐 전송코드 예시
c) msgrcv()
- 메세지 큐를 수신할 때 사용하는 함수
- msgtyp는 메세지의 타입을 설정하는 부분이다.
- 이 부분에 대해서는 아래의 실습예제에서 자세히 설명할 예정이다.
▶ 메세지큐 수신코드 예시
- 각 메세지 큐는 고유번호를 갖고 있다.
- 그러므로 어떤 메세지 큐의 메세지를 수신한다면, 해당 메세지 큐의 msqid를 입력해서 직접 수신 받아야 한다.
d) message queue 실습예제
- 아래의 코드로 message queue를 작성하여 실습해보자.
▶ 메세지 큐 전송 프로그램
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
// 메세지 타입을 설정하는 부분
typedef struct msgbuf {
long type;
char text[50];
} MsgBuf;
int main(void) {
int msgid, len;
MsgBuf msg;
key_t key = 1234;
msgid = msgget(key, IPC_CREAT|0644); // 메세지 큐 생성
if(msgid == -1) { // 메세지 큐가 생성이 안된 경우
perror("msgget");
exit(1);
}
msg.type = 1;
strcpy(msg.text, "Hello Message Queue!\n");
if(msgsnd(msgid, (void *)&msg, 50, IPC_NOWAIT) == -1) { // 메세지 큐 전송 실패
perror("msgsnd");
exit(1);
}
return 0;
}
▶ 메세지 큐 수신 프로그램
#include <sys/msg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct msgbuf { // 메세지 큐 전송 프로그램과 동일한 메세지 타입을 생성
long type;
char text[50];
} MsgBuf;
int main(void) {
MsgBuf msg;
int msgid, len;
key_t key = 1234; // 메세지 큐 전송 프로그램과 동일한 메세지 id 사용
if((msgid = msgget(key, IPC_CREAT|0644)) < 0) { // 메세지 키도 동일하게 생성
perror("msgget");
exit(1);
}
len = msgrcv(msgid, &msg, 50, 0, 0); // 위에서 입력한 키값을 가진 메세지 큐를 수신
printf("Received Message is [%d] %s\n", len, msg.text);
return 0;
}
- msgrcv() 함수는 msgtyp을 인자로 받는다.
- 위의 예시를 보면, msgrcv()에서 msgtyp의 값으로 0을 입력받았다.
- 다음 그림을 보면서 이해해보자.
- 위의 그림과 같이 메세지 큐에는 메세지들이 들어있다.
- msgtyp를 0으로 입력하면 가장 나중에 입력된 메시지 A가 수신되는 것이다.
- msgtyp를 0이 아닌 다른 수를 입력하면, 해당 타입에 매칭되는 메세지가 수신된다.
- 예를 들어서, msgtyp를 1로 한다면 D가 가장 먼저 수신된다.
▶ 메세지 큐 전송/수신 결과
- message queue 전송 프로그램을 먼저 실행한 후에 수신 프로그램을 실행해야 올바르게 작동한다.
- message queue를 전송하는 프로그램을 실행하면 커널영역에 메세지큐가 생성되어 메모리 상에 존재하는 것이다.
- message queue를 수신하는 프로그램을 실행하면 커널영역에 존재하는 메세지큐를 읽어오는 것이다.
e) 결론
- 이와 같이 메세지 큐 함수를 이용하여 메세지를 보낼 수 있다.
- 이를 응용하면 하나의 프로세스 내부에서 스스로에게 메세지를 전송할 수도 있다.
- 메세지 큐는 전송부와 수신부로 나뉘기에, 양방향 통신이 가능한 것이 장점이다.
5. 참고자료
- 키를 생성하는 방법 중 한가지를 소개한다.
- ftok()는 어떤 파일 또는 디렉토리의 inode값을 이용하여 고유한(unique) 키를 생성하는 함수다.
'컴퓨터공학기초 개념 > 시스템 프로그래밍' 카테고리의 다른 글
28. 시그널 - 사용법 이해 (0) | 2021.09.21 |
---|---|
27. 프로세스 - IPC 기법(shared memory) (0) | 2021.09.21 |
25. 프로세스 - 우선순위 스케줄링 시스템 콜 (0) | 2021.09.17 |
24. 프로세스 - 프로세스 생성과 종료 총정리 (0) | 2021.09.17 |
23. 프로세스 - 프로세스 종료(exit) (0) | 2021.09.17 |
댓글