프로세스간 커뮤니케이션 (IPC)
응원해주시면, 곧 좋은 컨텐츠를 만들어서 공유하겠습니다
7. 프로세스간 커뮤니케이션 (IPC)¶
프로세스간 커뮤니케이션¶
프로세스들이 서로의 공간을 쉽게 접근할 수 있다면? -- 프로세스 데이터/코드가 바뀔 수 있으니 얼마나 위험할까?
- 프로세스는 다른 프로세스의 공간을 접근할 수 없다.
출처: http://www.drdobbs.com/security/anatomy-of-a-stack-smashing-attack-and-h/240001832
IPC(InterProcess Communication)¶
프로세스간에 커뮤니케이션을 해야한다면 어떻게 해야할까?
- 프로세스간 통신 방법을 제공함
- IPC: InterProcess Communcation
OS.xlsx -> IPC
프로세스간 통신이 필요한가요?¶
- 성능을 높이기 위해 여러 프로세스를 만들어서 동시 실행
- 이 때 프로세스간 상태 확인 및 데이터 송수신이 필요
가볍게 생각해보기¶
- fork() 시스템콜
- fork() 함수로 프로세스 자신을 복사해서 새로운 프로세스로 만들 수 있음
- 부모 프로세스, 자식 프로세스
- fork() 함수로 프로세스 자신을 복사해서 새로운 프로세스로 만들 수 있음
가볍게 생각해보기¶
- 프로세스를 fork() 해서, 여러 프로세스를 동시에 실행시킬 수 있음
CPU가 한 개일때만 생각하지만, 최근에는 CPU 안에 코어가 8개 되는 경우도 많고, 각 프로세스를 각 코어에 동시 실행 가능(병렬 처리)
가볍게 생각해보기¶
여러 프로세스 동시 실행하기 예
1 ~ 10000까지 더하기
- fork() 함수로 10개 프로세스 만들어서, 각각 1 ~ 1000, 1001 ~ 2000, .... 더하기
- 각각 더한 값을 모두 합하면, 더 빠르게 동작 가능
단, 이 때 각 프로세스가 더한 값을 수집해야 하므로, 프로세스간 통신 필요
가볍게 생각해보기¶
- 웹서버 예
- 클라이언트와 서버
가볍게 생각해보기¶
- 웹서버 예
- 웹서버 만들기
- 웹서버란? 요청이 오면, HTML 파일을 클라이언트에 제공하는 프로그램
- 새로운 사용자 요청이 올 때마다, fork() 함수로 새로운 프로세스 만들고, 각 사용자 요청에 즉시 대응
CPU 병렬 처리가 가능하다면, 더 빠른 대응이 가능
단, 이 때 각 프로세스 제어 및 상태 정보 교환을 위해 프로세스간 통신 필요
파일을 사용한 커뮤니케이션¶
프로세스간에 커뮤니케이션을 해야한다면 어떻게 해야할까?
- 프로세스간 통신 방법을 제공함
- IPC: InterProcess Communcation
- 그 중의 한가지 방법: file 사용
간단히 다른 프로세스에 전달할 내용을 파일에 쓰고, 다른 프로세스가 해당 파일을 읽으면 됨
OS.xlsx -> IPC - file
프로세스간 커뮤니케이션¶
- file을 사용하면, 실시간으로 직접 원하는 프로세스에 데이터 전달이 어려움
- 왜? 해당 프로세스가 파일을 읽어야 하는데... 계속 실시간으로 읽고만 있을 수는 없으니..
그래서 보다 다양한 IPC 기법이 있음
실제 프로세스: 리눅스 예¶
- 프로세스간 공간은 완전히 분리되어 있다.
사용자 모드에서는 커널 공간 접근 불가
실제 프로세스: 리눅스 예¶
- 커널 공간은 공유한다.
보다 구체적인 내용은 가상 메모리에서 다룸
다양한 IPC 기법¶
- IPC: InterProcess Communcation
- file 사용
- Message Queue
- Shared Memory
- Pipe
- Signal
- Semaphore
- Socket
...
2번부터는 모두 커널 공간을 사용하는 것임 - 이것이 핵심
정리¶
- 여러 프로세스 동시 실행을 통한 성능 개선, 복잡한 프로그램을 위해 프로세스간 통신 필요
- 프로세스간 공간이 완전 분리
- 프로세스간 통신을 위한 특별한 기법 필요
- IPC (InterProcess Communication)
- 대부분의 IPC 기법은 결국 커널 공간을 활용하는 것임
- 이유: 커널 공간은 공유하기 때문
다양한 IPC 기법¶
- IPC: InterProcess Communcation
- file 사용
- Message Queue
- Shared Memory
- Pipe
- Signal
- Semaphore
- Socket
...
파이프¶
- pipe (파이프)
- 기본 파이프는 단방향 통신
- fork() 로 자식 프로세스 만들었을 때, 부모와 자식간의 통신
파이프 코드 예제¶
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가 0이 들어감
write(fd[1], msg, MSGSIZE); //fd[1]에 씁니다.
exit(0);
}
else { // 자식 프로세스에는 pid가 실제 프로세스 ID가 들어감
nbytes = read(fd[0], buf, MSGSIZE); // fd[0]으로 읽음
printf("%d %s\n", nbytes, buf);
exit(0);
}
return 0;
}
메시지 큐(message queue)¶
- 큐니까, 기본은 FIFO 정책으로 데이터 전송
메시지 큐 코드 예제¶
- A 프로세스
msqid = msgget(key, msgflg) // key는 1234, msgflg는 옵션
msgsnd(msqid, &sbuf, buf_length, IPC_NOWAIT)
- B 프로세스
msqid = msgget(key, msgflg) // key는 동일하게 1234로 해야 해당 큐의 msgid를 얻을 수 있음
msgrcv(msqid, &rbuf, MSGSZ, 1, 0)
파이프와 메시지 큐¶
- message queue는 부모/자식이 아니라, 어느 프로세스간에라도 데이터 송수신이 가능
- 먼저 넣은 데이터가, 먼저 읽혀진다.
pipe VS message queue¶
- 부모/자식 프로세스간 only or not
- 단방향만 가능 or 양방향 가능
IPC 기법과 커널 모드¶
pipe, message queue 는 모두 kernel 공간의 메모리를 사용합니다.
메모리 공간도 kernel/user 로 구분됩니다. 이 부분은 가상 메모리와 함께 다음에 이해해보겠습니다.
공유 메모리 (shared memory)¶
- 노골적으로 kernel space에 메모리 공간을 만들고, 해당 공간을 변수처럼 쓰는 방식
- message queue 처럼 FIFO 방식이 아니라, 해당 메모리 주소를 마치 변수처럼 접근하는 방식
- 공유메모리 key를 가지고, 여러 프로세스가 접근 가능
공유 메모리 코드 예제¶
- 공유 메모리 생성
shmid = shmget((key_t)1234, SIZE, IPC_CREAT|0666))
shmaddr = shmat(shmid, (void *)0, 0)
- 공유 메모리에 쓰기
strcpy((char *)shmaddr, "Linux Programming")
- 공유 메모리에서 읽기
printf("%s\n", (char *)shmaddr)
정리¶
- 주요 IPC 기법
- pipe
- message queue
- shared memory
- 모두 커널 공간을 활용해서 프로세스간 데이터 공유
이상은 기본적인 IPC
각 IPC 기법 개념 이해하기 (signal과 socket)¶
시그널과 IPC¶
- 본래 다른 용도이지만, IPC로도 사용가능한 기법
- 시그널 (signal)
- 소켓 (socket)
각각은 알면 알 수록, 깊은 내용 간단하게만 짚고 넘어가기
시그널 (signal)¶
- 유닉스에서 30년 이상 사용된 전통적인 기법
- 커널 또는 프로세스에서 다른 프로세스에 어떤 이벤트가 발생되었는지를 알려주는 기법
Ctrl+C 를 눌러서 프로세스 종료시키기 (시그널 사용 예)
주요 시그널¶
시그널 종류와 각 시그널에 따른 기본 동작이 미리 정해져 있음
- SIGKILL: 프로세스를 죽여라(슈퍼관리자가 사용하는 시그널로, 프로세스는 어떤 경우든 죽도록 되어 있음)
- SIGALARM: 알람을 발생한다
- SIGSTP: 프로세스를 멈춰라(Ctrl + z)
- SIGCONT: 멈춰진 프로세스를 실행해라
- SIGINT: 프로세스에 인터럽트를 보내서 프로세스를 죽여라(Ctrl + c)
- SIGSEGV: 프로세스가 다른 메모리영역을 침범했다.
시그널 종류: kill -l
시그널 (signal) 동작¶
- 프로그램에서 특정 시그널의 기본 동작 대신 다른 동작을 하도록 구현 가능
- 각 프로세스에서 시그널 처리에 대해 다음과 같은 동작 설정 가능
- 시그널 무시
- 시그널 블록(블록을 푸는 순간, 해당 프로세스에서 시그널 처리)
- 프로그램 안에 등록된 시그널 핸들러로 재정의한 특정 동작 수행
- 등록된 시그널 핸들러가 없다면, 커널에서 기본 동작 수행
시그널 관련 코드 예제¶
- 시그널 핸들러 등록 및 핸들러 구현 예
static void signal_handler (int signo) {
printf("Catch SIGINT!\n");
exit (EXIT_SUCCESS);
}
int main (void) {
if (signal (SIGINT, signal_handler) == SIG_ERR) {
printf("Can't catch SIGINT!\n");
exit (EXIT_FAILURE);
}
for (;;)
pause();
return 0;
}
시그널 관련 코드 예제¶
- 시그널 핸들러 무시
int main (void) {
if (signal (SIGINT, SIG_IGN) == SIG_ERR) {
printf("Can't catch SIGINT!\n");
exit (EXIT_FAILURE);
}
for (;;)
pause();
return 0;
}
시그널과 프로세스 (참고)¶
- PCB에 해당 프로세스가 블록 또는 처리해야하는 시그널 관련 정보 관리
소켓(socket)¶
- 소켓은 네트워크 통신을 위한 기술
- 기본적으로는 클라이언트와 서버등 두 개의 다른 컴퓨터간의 네트워크 기반 통신을 위한 기술
소켓(socket)과 IPC¶
소켓을 하나의 컴퓨터 안에서, 두 개의 프로세스간에 통신 기법으로 사용 가능
리눅스 커널 내부 네트워크 스택 예
정리¶
- 다양한 IPC 기법을 활용해서, 프로세스간 통신이 가능하다.
- IPC 기법 이외에도 사용할 수 있는 다음 두 가지 기술 개념도 가볍게 알아둡니다.
- 시그널(signal)
- 소켓(socket)
실제 관련 코드에 대해서는 시스템 프로그래밍을 통해 실습 과정을 함께 진행해보겠습니다.