일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- 뷰포트
- 섭페
- exit()
- atexit()
- fork 시스템 콜
- bg 명령어
- 개체 외곽 하이라이트 기준선 없애는 법
- Standard Stream
- 3D 배경 그래픽
- houdini
- copy-on-write
- Redirection
- 3Ds max 기초
- 생활코딩 복습
- 추가 업로딩 중
- bash shell 조건문
- exec 시스템 콜
- background process
- 섭페에서 텍스처 추출
- msync
- 후디니
- wait 시스템 콜
- mmap
- bash shell 변수
- substance painter
- ps 명령어
- foreground process
- bash shell 반복문
- pane & desktop
- Symmetry
- Today
- Total
Researcher to Developer
IPC 기법 관련 시스템 콜 - pipe, 메세지 큐 본문
#IPC
Inter Process Communication
Process는 기본적으로 서로 통신할 수 있는 방법이 없다.
둘 다 저장 매체에 접근할 수 있기 때문에
파일을 통해 공유할 수 도 있지만 실시간 공유의 어려움이 있어 추천되지는 않는다.
그래서 등장하게 된 것이 IPC 기법이다.
기본 개념은 이러하다.
Process는 커널 공간을 공통적으로 가지고 있고 그렇기 때문에
물리 메모리의 특정한 주소를 공유할 수 있다.
이것이 가능하게 된 근거는
각 페이지 테이블의 커널 공간에 해당되는 주소가 둘 다 동일한 위치를 가리키도록 해놓으면
실제 메모리 상으로 공유가 가능하다.
즉, 커널 공간은 공유가 가능하다는 것을 기반으로 다양한 IPC 기법이 나오게 되었다.
IPC 기법 중 pipe와 message queue는 모두 커널 공간의 메모리를 사용(공유)합니다.
#1. pipe 파이프
파이프는 단방향 통신이다. (부모 → 자식)
fork () 로 자식 프로세스를 만들었을 때 부모로부터 자식에게로 통신하는 방법이다.
parent → fork () → child
fd[1] write → pipe → fd[0] read
파이프에 대한 포인터값을 가지고 fork할 때 반드시 부모에서 자식으로 보낼 수만 있다.
code 예제는 다음과 같다.
#include <stdio.h>
#inlcude <stdlib.h>
#include <unistd.h>
#define MSGSIZE 255
char* msg= "Hello Child Process!";
int main()
{
char buf[255]; //255개 배열
int fd[2], pid, nbytes; // fd[2] 는 fd[0], fd[1] 을 가질 수 있다는 의미
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); // 커널 영역 - 부모에서 write () 을 사용해서 fd[1]에 msg를 씁니다.
exit(0);
}
else { // 자식 프로세스에는 pid 값이 0이됨
printf("child PID:d\n", getpid());
nbytes = read(fd[0], buf, MSGSIZE); // 자식에서는 read를 하는데 fd[0]으로 읽음
printf("%d %s\n", nbytes, buf);
exit(0);
}
return 0;
}
#2. 메세지 큐(message queue)
큐니까, 기본은 FIFO 정책으로 데이터 전송
파이프 처럼 부모 자식 관계일 필요가 없다. → 양방향 통신이 가능하다.
msqid = msgget(key, msgflg) // key는 1234, msgflg는 옵션
// msgget 이라는 함수로 메세지 큐를 만들어야함.
// msgflg 에는 메세지 큐를 생성할 때 옵션을 줄 수 있는 항목이다.
#msgflg 설정
IPC_CREAT : 새로운 키라면 식별자를 새로 생성, IPC_CREATI 접근 권한 (대표적인 사용하는 옵션)
사용은 이렇게 한다.
IPC_CREAT|0655 → rw-r--r-- ( | : or 라는 의미, 생성하고 접근 권한을 줄 수 도 있다는 의미)
#메세지 큐를 전송할 때 사용하는 시스템 콜
msgsnd
msgsnd(msqud, &sbuf, buf_length, IPC_NOWAIT)
msgflg 설정 : 블록 모드 (0) / 비블록 모드(IPC_NOWAIT)
메세지 큐 전송 프로그램 일부 코드 예제는 다음과 같다.
msqid = msgget(1234, IPC_CREAT|0644) // key는 1234, msgflg는 옵션
msgsnd(msqid, &sbuf, buf_length, IPC_NOWAIT)
#메세지 큐를 수신할 때 사용하는 시스템 콜
ssize_t msgrcv(int msqid, void *msqp, size_t msgsz, ling msgtyp, int msgflg)
msgrcv(msqid, &rbuf, MSGSZ, 1, 0) //msgrcv 예
#메세지 타입 설정
msgtyp 설정 : 0이면 첫번째 메세지, 양수이면 타입이 일치하는 첫번째 메세지
msgflg 설정 : 블록 모드(0) / 비블록 모드(IPC_NOWAIT)
메시지 큐 수신 프로그램 일부 코드 예제는 다음과 같다.
msqid = msgget(1234, IPC_CREAT|0644) // key는 동일하게 1234로 해야 해당 큐의 msgid를 얻을 수 있다.
msgrcv(msqid, &rbuf, MSGSZ, 1, 0)
메세지 큐 사용 예를 code로 확인해보자.
먼저 vi msgqueuesnd.c 입력 후 다음의 코드를 작성했다.
#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;
ket_t key = 1234;
msgid = msgget(key, IPC_CREAT|0644);
if(msgid == -1) {
perror("msgget");
exit(1);
}msg.type = 1; // 타입을 1로 넣고
strcpy(msg.text, "Hello Message Queue\n");
if(msgsnd(msgid, (void*)&msg, 50, IPC_NOWAIT) == -1) { // 50 size 만큼만 데이터를 전송
perror("msgsnd");
exit(1);
}
return 0;}
그 다음 msgqueue 를 받는 코드를 입력해야 한다.
vi msgqueuercv.c 입력 후 다음의 코드를 입력해준다.
#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;
ket_t key = 1234;
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;
}
이렇게 코드를 작성해준 다음
msgqueuesnd.c
msgqueuercv.c
파일을 컴파일 해주고
msgqueuesnd 실행을 하면 화면에 아무것도 출력되지 않는다.
그 이유는 일단 1234 키에 해당하는 메세지 큐는 커널 영역에 만들어 졌고 출력할 내용이 없기 때문이다.
그 상태에서 msgqueuercv 를 실행하면
Received Message is [50] Hello Message Queue 라고 출력된다.
해당 커널 영역은 프로그램이 꺼져도 공유하기 때문에 물리 메모리에 존재한다.
그 커널 영역의 특정 메세지 큐에는 우리가 작성한 데이터가 있는 것
프로세스를 실행하고 메세지 큐의 아이디를 가지고 해당 주소로 접근하면
그곳에 있는 데이터를 읽을 수 있는 것
msgqueuesnd, msgqueuercv는 서로 다른 프로그램이지만 이렇게 통신이 가능하다.
msgctl
message를 control 하는 명령어
msgctl(msgid, IPC_RMID, 0);
해당 ID를 가진 메세지큐를 삭제하는 명령
#ftok()
키생성을 위한 함수
path 경로명의 inode 값과 숫자값(id)를 기반으로 키를 생성한다
경로 삭제 후 재생성시 inode 값이 달라지므로, 이전과는 다른 키값이 리턴된다.
#include <sys/ipc.h>
key_t ftok(const char *path, int id);
위 두 줄을 레퍼런스로 아래 코드를 작성할 수 있다.
key = ftok("keyfile", 1);
id = msgget(key, IPC_CREAT|0640);
// 해당 디렉토리와 , 숫자값으로 조합을 해서 유니크한 키값을 생성한다. 그 값을 가지고 메 세지 큐를 생성할 수 있다.