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() 해서, 여러 프로세스를 동시에 실행시킬 수 있음

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
    1. file 사용
    2. Message Queue
    3. Shared Memory
    4. Pipe
    5. Signal
    6. Semaphore
    7. Socket ...

2번부터는 모두 커널 공간을 사용하는 것임 - 이것이 핵심


정리

  • 여러 프로세스 동시 실행을 통한 성능 개선, 복잡한 프로그램을 위해 프로세스간 통신 필요
  • 프로세스간 공간이 완전 분리
  • 프로세스간 통신을 위한 특별한 기법 필요
    • IPC (InterProcess Communication)
  • 대부분의 IPC 기법은 결국 커널 공간을 활용하는 것임
    • 이유: 커널 공간은 공유하기 때문

다양한 IPC 기법

  • IPC: InterProcess Communcation
    1. file 사용
    2. Message Queue
    3. Shared Memory
    4. Pipe
    5. Signal
    6. Semaphore
    7. 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)


메시지 큐 코드 예제

  • 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를 가지고, 여러 프로세스가 접근 가능

공유 메모리 코드 예제

  1. 공유 메모리 생성
    shmid = shmget((key_t)1234, SIZE, IPC_CREAT|0666))
    shmaddr = shmat(shmid, (void *)0, 0)
    
  2. 공유 메모리에 쓰기
    strcpy((char *)shmaddr, "Linux Programming")
    
  3. 공유 메모리에서 읽기
    printf("%s\n", (char *)shmaddr)
    

정리

  1. 주요 IPC 기법
    • pipe
    • message queue
    • shared memory
  2. 모두 커널 공간을 활용해서 프로세스간 데이터 공유

이상은 기본적인 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) 동작

  • 프로그램에서 특정 시그널의 기본 동작 대신 다른 동작을 하도록 구현 가능
  • 각 프로세스에서 시그널 처리에 대해 다음과 같은 동작 설정 가능
    1. 시그널 무시
    2. 시그널 블록(블록을 푸는 순간, 해당 프로세스에서 시그널 처리)
    3. 프로그램 안에 등록된 시그널 핸들러로 재정의한 특정 동작 수행
    4. 등록된 시그널 핸들러가 없다면, 커널에서 기본 동작 수행

시그널 관련 코드 예제

  • 시그널 핸들러 등록 및 핸들러 구현 예 ```c 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;

}

---
## 시그널 관련 코드 예제
* 시그널 핸들러 무시
```c
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)

실제 관련 코드에 대해서는 시스템 프로그래밍을 통해 실습 과정을 함께 진행해보겠습니다.