IPC(Inter Process Communication)

1. 메모리를 활용한 방법

1-1. shared memory

  • 할당된 공유 메모리의 크기는 고정됩니다.
  • 다른 두 프로세스의 VMS 가 RAM 의 같은 물리적 위치를 공유합니다.
  • 생성된 공유 메모리는 커널에 의해 관리됩니다.
  • 공유 메모리 영역을 사용하는 모든 프로세스가 종료 되어도 재부팅 하거나 직접 해제하지 않는 이상 유지됩니다.
    shmctl() 시스템 콜을 통해 공유 메모리를 제거할 수 있습니다.
  • 커널에 의해 공유 메모리 영역이 생성되면 shmat() 시스템 콜을 통해
    각 프로세스 메모리의 데이터 영역에 attach 됩니다.
// writer.c
#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define KEY_NUM 3306
#define MEM_SIZE 1024

int main(void) 
{
  int shm_id;
  void *shm_addr;
  int count;

  /**
  int shmget(key_t key, int size, int shmflg)

  create shared memory by kernel but not yet available by process
  @param key    : identifier for shared memory
  @param size   : size of memory
  @param shmflg : options
                  IPC_CREAT - if hared mem not exists, create
                  IPC_EXCL  - if already exists, failed
  @return       : failed - -1 or identifier
  */

  shm_id = shmget((key_t)KEY_NUM, MEM_SIZE, IPC_CREAT | 0666);

  if (shm_id == -1) 
  {
    perror("shmget error : ");
    return 0;
  }

  /**
  void* shmat(int shmid, const void* shmaddr, int shmflg)
  
  After create the shared memory by kernel,
  need to attach this memory to the process to use this shared meemory
  @param shmid    : identifier for shared memoery
  @param shmaddr  : address for this memory segment
                    if 0, kernel will do it
  @param shmflg   : options 
                    SHM_RONLY - read only
  @return         : failed -1 or the address of shared memory attached to process
  */
  shm_addr = shmat(shm_id, (void *)0, 0);

  if ((void *)-1 == shm_addr)
  {
    perror("shmat error : ");
    return 0;
  }

  printf("Shared memory address : %p\n", shm_addr);

  count = 0;
  while (1) 
  {
    sprintf((char *)shm_addr, "%d", count++);
    sleep(1);
  }

  return 0;
}
// reader.c
#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

#define KEY_NUM 3306
#define MEM_SIZE 1024

int main(void) 
{
  int shm_id;
  void *shm_addr;

  if (-1 == (shm_id = shmget((key_t)KEY_NUM, MEM_SIZE, IPC_CREAT | 0666)))
  {
    printf("공유 메모리 생성 실패\n");
    return -1;
  }

  if((void *) -1  == (shm_addr = shmat(shm_id, (void *)0, 0)))
  {
    printf("공유 메모리 첨부 실패\n");
    return -1;
  }

  printf("start address of shared memory : %p\n", shm_addr);

  while(1)
  {
    printf("Data read from shared mem : %s\n", (char *)shm_addr);
    sleep(1);
  }

  return 0;
}

위 예제 코드는
writer.c 에서 공유 메모리를 1024 바이트 크기로 생성하고 프로세스에 attach 한 후 1초 마다 공유 메모리에 count++ 값을 씁니다.
reader.c 는 writer.c 에서 생성한 공유 메모리에 접근해 프로세스에 attach 한 후 1초 마다 공유 메모리 값을 읽습니다.

writer.c 와 reader.c 의 shm_addr 은 각 프로세스의 가상 메모리 값이기 때문에 다를 수 있습니다.
하지만 메모리의 물리적 주소는 같습니다.

각 프로세스에 할당된 공유 메모리 정보는 ipcs -m 명령어를 통해 확인할 수 있습니다.

1-2. pipe(file)

  • 2차 메모리(HDD, SDD 등)에 저장되고 여러 프로세스가 공통으로 사용하는 임시 파일 입니다.
  • shared memory 와 다르게 할당된 크기가 변할 수 있고 스트림을 통해 읽고 쓸 수 있습니다.
  • pipe 는 한 방향으로만 데이터 전송이 가능합니다.
  • pipe 는 파일이므로 파일 디스크립터를 통해서 접근 가능합니다.

1-2-1. unnamed pipe

  • 이름이 없는 pipe 로서 부모, 자식 프로세스 사이에서만 사용 가능합니다.
  • pipe 의 파일 디스크립터를 부모, 자식 프로세스 사이에서만 공유합니다.
// unnamed-pipe.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>

#define BUFFER_SIZE 512

int main() 
{
  const char *msg[] = {"apple is red", "banana is yellow", "cherry is red"};
  char buffer[BUFFER_SIZE];
  int filedes[2], nRead, i;
  pid_t pid;

  if (pipe(filedes) == -1)
  {
    printf("fail to call fork()\n");
    exit(1);
  }

  if ((pid = fork()) == -1)
  {
    printf("fail to call fork()\n");
    exit(1);
  }

  // parent
  if (pid > 0) 
  {
    // close input file
    close(filedes[0]);
    for (int i = 0; i < 3; i++) 
    {
      strcpy(buffer, msg[i]);
      write(filedes[1], buffer, BUFFER_SIZE);
    }
  }
  // child
  else if (pid == 0) 
  {
    // close output file
    close(filedes[1]);
    for (int i = 0; i < 3; i++) 
    {
      nRead = read(filedes[0], buffer, BUFFER_SIZE);
      printf("[child] %s\n", buffer);
    }
  }

  return 0;
}

위 코드를 실행하면
부모 프로세스가 msg 배열에 저장된 문자열을 차례대로 unnamed pipe 에 쓰고
자식 프로세스는 msg 배열에 저장된 문자열을 차례대로 unnamed pipe 에서 읽어옵니다.

pipe() 함수는 읽기, 쓰기 한 쌍의 파이프를 만들고 각 파이프의 파일 디스크립터를 전달합니다.
filedes[0] 은 읽기, filedes[1] 은 쓰기 파일 디스크립터를 저장하고 있습니다.

1-2-2. named pipe(FIFO)

  • 이름이 있는 파이프로서 이름을 통해 파일 디스크립터를 얻을 수 있어, 다른 프로세스 간에 사용할 수 있습니다.
// receiver.c
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define MSG_SIZE 80

int main() 
{
  char msg[MSG_SIZE];
  int filedes;
  int nread, cnt;

  if (mkfifo("./fifo", 0666) == -1) 
  {
    printf("fail to call fifo()\n");
    exit(1);
  }

  if ((filedes = open("./fifo", O_RDWR)) < 0)
  {
    printf("fail to call fifo()\n");
    exit(1);
  }

  for (int cnt = 0; cnt < 3; cnt++) 
  {
    if ((nread = read(filedes, msg, MSG_SIZE)) < 0)
    {
      printf("fail to call read()\n");
      exit(1);
    }

    printf("recv: %s\n", msg);
  }

  unlink("./fifo");
  return 0;
}
// sender.c
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define MSG_SIZE 80

int main() 
{
  char msg[MSG_SIZE];
  int filedes;
  int cnt;

  if ((filedes = open("./fifo", O_WRONLY)) < 0) 
  {
    printf("fail to call open()\n");
    exit(1);
  }

  for (int cnt = 0; cnt < 3; cnt++) 
  {
    printf("input a message : ");
    fgets(msg, MSG_SIZE, stdin);
    msg[MSG_SIZE - 1] = '\0';

    if (write(filedes,msg,MSG_SIZE) == -1) 
    {
      printf("fail to call wirte()\n");
      exit(1);
    }

    sleep(1);
  }

  return 0;
}

위 코드를 실행하면
sender 프로세스에서 사용자가 콘솔에 입력한 내용을 fifo 라는 이름의 FIFO 에 쓰고
receiver 프로세스에서 fifo 를 읽어 콘솔에 출력합니다. 이 과정을 3번 반복합니다.

receiver.c 에서 mkfifo() 함수를 통해 FIFO 를 생성합니다.
sender.c 에서 FIFO 파일을 여는 open(“./fifo”, O_WRONLY) 함수는
다른 프로세스가 READ 모드로 FIFO 파일을 열기 전 까지 블로킹 됩니다.
receiver.c 의 read() 함수는 fifo 에 읽을 데이터가 생길 때 까지 블로킹 됩니다.

2. 네트워크를 활용한 방법

2-1. socket

2-2. rpc

3. 그 외

3-1. data_seg pragma

  • 후깅 목적의 DLL 이 타겟 프로세스에 인젝션

3-2. registry(윈도우 한정)

출처
  • https://www.youtube.com/watch?v=eLCTRdSj7o&t=609
  • https://m.blog.naver.com/demonic3540/221671407431
  • https://blog.naver.com/PostView.naver?blogId=akj&logNo=&parentCategoryNo=&categoryNo=31&viewDate=&isShowPopularPosts=false&from=postView