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