본문 바로가기

STUDY/linux

IPC in LINUX - Shared Memory

IPC 방법 중 shared memory를 사용하는 방법에 대해서 알아보자.

shared memory에 대한 것은 다음 포스팅을 참고하면 좋다.

 

process concept

process가 무엇인가? process는 프로그램 실행 흐름의 가장 기본적인 단위이다. 다들 이렇게 설명하는데 사실 뭔 소린지 이해가 안갈 수 있다. 그럴때는 프로그램 실행을 위한 작업의 대상, 즉 os가 sc

josushell.tistory.com

 

Shared Memory는 IPC 중 가장 빠른 방식이다. message passing 처럼 데이터를 kernel에 copy해야하는 일이 없기 때문이다.

하지만 운영체제가 IPC에 개입하지 않는 만큼, 동기화는 개발자가 직접 맞추어야 한다.

즉 데이터가 없을 때 데이터를 읽는 접근에 대한 것을 막아야 한다는 의미이다. 주로 semaphore를 사용해서 이를 처리한다.


Shared Memory 를 위한 syscall은 크게 4가지가 중요하다.

- shmget

: memory identifier를 가져온다. 이때 shared memory가 없으면 새로 생성한다.

이 syscall은 physical memory를 만드는 작업이다.

- shmat

:  virtual memory에 physical memory를 연결한다.

- shmdt

: virtual memory와 physical memory의 연결을 해제한다.

shmdt는 shared memory가 사라지게 하는 것이 아니라 단지 연결을 끊는 것이다. 따라서 필요없어진 shared memory는 꼭 제거해야 한다.

- shmctnl

: shared memory에 대한 다양한 control을 담당한다.

예를 들면 삭제, fetch, lock 등등이다.

 

 

shared memory는 virtual memory management에 대한 이해를 기반으로 한다. 따라서 다음 포스팅을 참고하면 좋다.

 

Virtual Memory Management

이전 포스트에서 운영체제의 핵심 기능 중 하나인 memory management를 살펴보았다. 하지만 memory management의 고질적인 문제 중 하나는 역시 "메모리 부족" 사태이다. 이를 해결하기 위한 것이 바로 가

josushell.tistory.com

 

page 단위로 구성한다면 다음과 같다.

이때 이 virtual address를 같은 physical address로 mapping하면 shared memory를 가진다.

shared library, 즉 dynamic linking도 이와 같은 원리이다.


System Call for Shared Memory

# include <sys/types.h>

# include <sys/ipc.h>

# include <sys/shm.h>

int shmget(key_t key, int size, int flag);

 

함수 명세

return 성공 시 shared memory ID, 실패 시 -1
description physical memory에 shared memory를 생성한다.

 

파라미터

key_t key shared memory를 생성할 때의 key
int size shared memory의 크기 (이미 있는 shared memory를 불러오는 경우 0)
int flag shared memory의 mode (option)

이렇게 생성한 shared memory를 process의 virtual memory에 붙여야 사용 가능하다.

 

 

# include <sys/types.h>

# include <sys/ipc.h>

# include <sys/shm.h>

void *shmat(int shmid, void *addr, int flag);

 

함수 명세

return 성공 시 shared memory segment의 virtual address에 대한 pointer, 실패 시 -1
description virtual address를 shared memory와 연결한다.

 

파라미터

int shmid shared memory ID (physical memory), shmget의 return 값
void *addr virtual address 시작 주소 설정 (0인 경우, kernel에 의해 첫번째로 가능한 주소가 연결된다.)
int flag shared memory mode (주로 0) - SHM_RND, SHM_RDONLY

 

두번째 파라미터와 세번째 파라미터는 함께 쓰는 경우가 많다.

- addr != 0 and not SHM_RND

addr에 전달된 주소와 연결된다.

 

- addr != 0 and SHM_RND

(addr - (addr % SHMLBA))에 해당하는 주소와 연결된다.

 

- SHM_RDONLY

read-only mode로 연결된다.


 

# include <sys/types.h>

# include <sys/ipc.h>

# include <sys/msg.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

 

함수 명세

return 성공 시 0, 실패 시 -1
description shared memory에 대한 여러 설정과 동작을 제어한다.

 

파라미터

int shmid shared memory ID (physical memory), shmget의 return 값
int cmd shared memory control operations
struct shmid_ds *buf shared memory에 대한 메타정보

 

다음은 두번째 인자의 값으로 가능하다.

 

- IPC_STAT: shared memory에 shmid_ds 구조체를 fetch한다.

- IPC_SET: shmid_ds 구조체를 설정한다.

- IPC_RMID: system에서 shared memory segment를 지운다.

- SHM_LOCK: memory의 shared memory에 lock을 건다.

- SHM_UNLOCK: shared memory lock을 해제한다.

 

 

# include <sys/types.h>

# include <sys/ipc.h>

# include <sys/shm.h>

int shmdt(void *addr);

 

함수 명세

return 성공 시 0, 실패 시 -1
description shared memory의 연결을 해제한다.

 

파라미터

void *addr 연결된 shared memory의 시작 주소

shmat을 해서 얻은 virtual address pointer를 전달한다.


Example

shared memory 를 생성하고 메모리 주소를 출력해보자.

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main(int argc, char *argv[])
{
	int	shmid;
	char	*ptr;
	char	*shmptr;

	char	Array[4000];

	if ((ptr = (char *)malloc(10000)) == NULL)
		exit(1);

	if ((shmid = shmget(IPC_PRIVATE, 10000, SHM_R|SHM_W)) < 0)
	{
		free(ptr);
		exit(1);
	}

	if ((shmptr = shmat(shmid, NULL, 0)) < 0)
	{
		free(ptr);
		exit(1);
	}

	printf("Array from %p ~ %p\n", Array, Array + 4000);
	printf("Stack: %p\n", &shmid);
	printf("Heap from %p ~ %p\n", ptr, ptr + 10000);
	printf("Shared Memory attached %p ~ %p\n", shmptr, shmptr + 4000);

	if (shmdt(shmptr) < 0)
	{
		free(ptr);
		exit(1);
	}

	if (shmctl(shmid, IPC_RMID, NULL) < 0)
	{
		free(ptr);
		exit(1);
	}
	
	free(ptr);
	return 0;	

}

 

결과는 다음과 같다.

 

 

즉 shared memory는 heap과 stack의 중간쯤에 생성된다.

 

Example2

실제로 shared memory를 사용하여 IPC를 할때를 알아보자.

 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_KEY		(0x900043)
#define SHM_SIZE	1024
#define SHM_MODE	(SHM_R |SHM_W |IPC_CREAT)

int main(int argc, char *argv[]){
	char	*ptr;
	char	*pData
	int	shmid;

	if((shmid = shmget(SHM_KEY, SHM_SIZE ,SHM_MODE))<0)
		exit(1);

	if((ptr = shmat(shmid, NULL, 0)) < 0)
		exit(1);

	pData = ptr;
	printf("Received request: %s.....",pData);
	sprintf(pData,"This is a reply from %d.",getpid());

	if(shmdt(ptr) < 0)
		exit(1);

	if(shmctl(shmid, IPC_RMID, NULL) < 0)
		exit(1);
	
	return 0;
}

 

결국 shared memory도 메모리이기 때문에 데이터를 읽는 것과 똑같이 사용하면 된다.

따라서 shared memory를 가리킬 포인터로 ptr을 둔 다음, pData는 이 포인터를 가리키게 하여 pData로 데이터를 읽는다.

이때 메모리에 데이터가 없어도 프로세스는 waiting 상태에 진입하여 대기하지 않는다. 왜냐하면 운영체제가 sync를 맞추어주지 않기 때문이다. 따라서 signal 또는 shared memory 상에 플래그를 세우는 방식 등등을 사용해서 동기화를 직접 처리해야 한다.

 

'STUDY > linux' 카테고리의 다른 글

Synchronization in LINUX - POSIX Semaphore로 producer-consumer 해결하기  (0) 2022.04.13
IPC in LINUX - Message Passing  (0) 2022.04.02
Files and Directories in LINUX  (0) 2022.03.31
SIGNAL in LINUX  (0) 2022.03.29
Process and Threads in LINUX  (0) 2022.03.28