Time does not change us. It just unfolds us.

Etc

[IPC]Shared Memory

소젬 2021. 10. 22. 14:33

공유 메모리(Shared Memory)는 IPC(Inter-Process Communication)의 한 가지 방법으로 프로세스간 통신이 필요할 경우 사용한다.

공유 메모리를 통한 통신은 둘 이상의 프로세스가 공통 메모리에 액세스 할 수 있는 개념으로 

한 프로세스에서 변경한 메모리 공간의 내용을 다른 프로세스에서 접근할 수 있다.

 

공유 메모리의 생성 요청은 최초 공유 메모리 영역을 만드는 프로세스가 커널에 공유 메모리 공간의 할당을 요청함으로써

이루어지며 만들어진 공유 메모리는 커널에 의해서 관리된다.

만들어진 공유 메모리는 시스템을 리부팅하거나 직접 공유 메모리의 공간을 삭제시키지 않는 한, 

공유 메모리를 사용하는 모든 프로세스가 없어졌다고 하더라도 공유 메모리의 공간은 계속적으로 유지되게 된다.

 

프로세스가 커널에게 공유 메모리 공간을 요청하게 되면,

커널은 공유 메모리 공간을 할당시켜주고 공유 메모리 공간을 관리하기 위해 내부 자료구조를 사용합니다.

공유 메모리 정보들은 shmid_ds라는 구조체에 의해 관리되며  shm.h에 정의되어 있다.

 

 

공유메모리에는 두가지 방식이 존재한다.

System V 방식POSIX 방식

shmget → shmat → shmdt shm_open → ftruncate → mmap → munmap
shm segment 를 만들고 해당 id 를 통해 segment 들을 구분 관리
segment 개수가 32K로 제한
shm segment 에 대응하는 file 을 통해 memory 까지 관리
 shm  영역을 하나 잡고 파일을 하나 생성하여 해당 shm memory 영역을 파일에 mapping 하여 사용하는 구조

공유 메모리를 사용하기 위해서는 아래의 헤더파일을 include 해주어야 한다.

#include <sys/types.h>
#include <sys/shm.h>

 

■ shmget : 공유메모리 생성
함수 원형: int shmget(key_t key, int size, int shmflg)
- 커널에 공유 메모리 공간을 요청하기 위해 사용하는 시스템 호출 함수
- KEY값은 고유의 공유 메모리임을 나타낸다
- Argument (KEY, MEMORY_MAX_SIZE, 접근권한 | 생성방식)


■ shmmat : 프로세스에 메모리 세그먼트를 붙임
함수 원형: void *shmat( int shmid, const void *shmaddr, int shmflg )
- 공유 메모리 공간을 생성한 이후, 공유메모리에 접근할 수 있는 int형의 "식별자"를 얻는다
- 공유 메모리를 사용하기 위해 얻은 식별자를 이용하여 현재 프로세스가 공유 메모리에 접근할 수 있도록 연결하는 작업
- Argument (식별자, 메모리가 붙을 주소 (0을 사용할 경우 커널이 메모리가 붙을 주소를 명시), 읽기/쓰기 모드)


■ shmdt
함수 원형: int shmdt( const void *shmaddr)
- 프로세스가 더이상 공유 메모리를 사용하지 않을 경우 프로세스와 공유 메모리를 분리시키는 작업
- 해당 시스템 호출 함수는 현재 프로세스와 공유 메모리를 분리시킬 뿐, 공유 메모리의 공간을 삭제하지 않는다
- shmdt 가 성공적으로 수행되면 커널은 shmid_ds 의 내용을 갱신, 즉 shm_dtime, shm_lpid, shm_nattch 등의 내용을 갱신 하는데,

  shm_dtime 는 가장 최근에 dettach (즉 shmdt 를 사용한)된 시간, shm_lpid 는 호출한 프로세세의 PID, shm_nattch 는 현재 공유 메모리를

  사용하는 (shmat 를 이용해서 공유 메모리에 붙어있는) 프로세스의 수를 돌려준다.


■ shmctl 
함수 원형: int shmctl(int shmid, int cmd, struct shmid_ds *buf)

- 공유 메모리를 제어하기 위해 사용
- shmid_ds 구조체를 직접 제어함으로써, 해당 공유 메모리에 대한 소유자, 그룹 등의 허가권을 변경하거나, 공유 메모리 삭제,

  공유 메모리의 잠금을 설정하거나 헤제하는 작업

- Option

  • IPC_STAT : 공유 메모리 공간에 관한 정보를 가져오기 위해서 사용된다. 정보는 buf 에 저장된다.
  • IPC_SET    : 공유 메모리 공간에 대한 사용자권한 변경을 위해서 사용된다.사용자 권한 변경을 위해서는 슈퍼유저 혹은 사용자 권한을 가지고 있어야 한다.
  • IPC_RMID : 공유 메모리 공간을 삭제하기 위해서 사용된다. 이 명령을 사용한다고 해서 곧바로 사용되는건 아니며,더이상 공유 메모리 공간을 사용하는 프로세스가 없을 때, 즉 shm_nattch 가 0일때 까지 기다렸다가 삭제된다.

 


 

ipcs를 이용하여 세마포어, 공유메모리, 메시지큐 등 IPC를 확인할 수 있다.

Usage:
 ipcs [resource-option...] [output-option]
 ipcs -m|-q|-s -i <id>

Show information on IPC facilities.

Options:
 -i, --id <id>  print details on resource identified by <id>
 -h, --help     display this help
 -V, --version  display version

Resource options:
 -m, --shmems      shared memory segments
 -q, --queues      message queues
 -s, --semaphores  semaphores
 -a, --all         all (default)

Output options:
 -t, --time        show attach, detach and change times
 -p, --pid         show PIDs of creator and last operator
 -c, --creator     show creator and owner
 -l, --limits      show resource limits
 -u, --summary     show status summary
     --human       show sizes in human-readable format
 -b, --bytes       show sizes in bytes

For more details see ipcs(1).

이 중 IPC에서 가장 빠른 수행속도를 보여주는 공유메모리와 관련된 주 명령어는 다음과 같다.

/*공유 메모리 (Shared Memory) 확인*/
ipcs -m
ipcs

/*묶여있는 프로세스의 PID 값 확인*/
ipcs -mp

/*공유 메모리 (Shared Memory) 삭제*/
ipcrm -m [shmid]

/*공유 메모리 - dest status 제거 (destoryed된 값이기 때문에 삭제 불가*/
kill -15 [PID]

Shared Memory 예제

아래의 코드는 두 프로세스 사이에서 공유 메모리를 통해 데이터를 전달하는 코드의 예

해당 코드는 First Process가 공유메모리를 만들고 5초 후에 공유메모리에 1의 값을 써주는 작업을하고,

Second Process가 First Process가 만든 공유 메모리를 자신의 프로세스에 연결하고 값을 계속해서 읽다가 1의 값이

읽히는 순간 종료되는 프로그램이다.

 

 First 프로세스 - 공유 메모리 Create 및 Write

////////////////////////////////////
//     First Process Shared Memory   
////////////////////////////////////
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <unistd.h>
  
#define  KEY_NUM   1234
#define  MEM_SIZE  4096
  
using namespace std;
  
int shmid;
static int SharedMemoryCreate();
static int SharedMemoryWrite(char *shareddata, int size);
static int SharedMemoryRead(char *sMemory);
static int SharedMemoryFree(void);
  
int main(int argc, char *argv[])
{
    char buffer[MEM_SIZE] = {1,};
     
    SharedMemoryCreate();
    sleep(5);
     
    SharedMemoryWrite(buffer, sizeof(buffer));
    return 0;
}
  
static int SharedMemoryCreate()
{
    //shmget : 공유메모리를 구별하는 key값과 크기, 옵션을 부여하고 공유메모리를 생성
    if((shmid = shmget((key_t)KEY_NUM, MEM_SIZE, IPC_CREAT| IPC_EXCL | 0666)) == -1) {
        printf("There was shared memory.");
         
        shmid = shmget((key_t)KEY_NUM, MEM_SIZE, IPC_CREAT| 0666);
         
        if(shmid == -1)
        {
            perror("Shared memory create fail");
            return 1;
        }
        else
        {
            SharedMemoryFree();
            shmid = shmget((key_t)KEY_NUM, MEM_SIZE, IPC_CREAT| 0666);
             
            if(shmid == -1)
            {
                perror("Shared memory create fail");
                return 1;
            }
        }
    }
     
    return 0;
}
  
static int SharedMemoryWrite(char *shareddata, int size)
{
    void *shmaddr;
    if(size > MEM_SIZE)
    {
        printf("Shared memory size over");
        return 1;
    }
     
    if((shmaddr = shmat(shmid, (void *)0, 0)) == (void *)-1)
    {
        perror("Shmat failed");
        return 1;
    }
     
    memcpy((char *)shmaddr, shareddata, size);
     
    if(shmdt(shmaddr) == -1)
    {
        perror("Shmdt failed");
        return 1;
    }
    return 0;
}
  
static int SharedMemoryRead(char *sMemory)
{
    void *shmaddr;
    char mess[MEM_SIZE] = {0};
     
    if((shmaddr = shmat(shmid, (void *)0, 0)) == (void *)-1)
    {
        perror("Shmat failed");
        return 1;
    }
     
    memcpy(sMemory, (char *)shmaddr, sizeof(mess));
     
    if(shmdt(shmaddr) == -1)
    {
        perror("Shmdt failed");
        return 1;
    }
    return 0;
}
  
static int SharedMemoryFree(void)
{
    if(shmctl(shmid, IPC_RMID, 0) == -1)
    {
        perror("Shmctl failed");
        return 1;
    }
     
    printf("Shared memory end");
    return 0;
}

 

 

Second 프로세스 - 공유 메모리 Connect 및 Read

///////////////////////////////////////
//    Second Process Shared Memory   
////////////////////////////////////////
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define  KEY_NUM   1234
#define  MEM_SIZE  4096
using namespace std;
int shmid;
static int SharedMemoryInit();
static int SharedMemoryWrite(char *sMemory, int size);
static int SharedMemoryRead(char *sMemory);
int main(int argc, char *argv[])
{
    char buffer[MEM_SIZE] = {0,};
    SharedMemoryInit();
     
    while(1)
    {
        SharedMemoryRead(buffer);
        if(buffer[0] == 1)
        {
            cout << "Receive data from shared memory!" << endl;
            break;
        }   
    }
    return 0;
}
static int SharedMemoryInit()
{
    void *shmaddr;
     
    if((shmid = shmget((key_t)KEY_NUM, 0, 0)) == -1)
    {
        perror("Shmid failed");
    }
     
    return 0;
}
static int SharedMemoryWrite(char *sMemory, int size)
{
    void *shmaddr;
     
    if((shmaddr = shmat(shmid, (void *)0, 0)) == (void *)-1)
    {
        perror("Shmat failed");
    }
     
    memcpy((char *)shmaddr, sMemory, size);
     
    if(shmdt(shmaddr) == -1)
    {
        perror("Shmdt failed");
        exit(1);
    }
    return 0;
}
static int SharedMemoryRead(char *sMemory)
{
    void *shmaddr;
     
    if((shmaddr = shmat(shmid, (void *)0, 0)) == (void *)-1)
    {
        perror("Shmat failed");
    }
     
    memcpy(sMemory, (char *)shmaddr, sizeof(sMemory));
     
    if(shmdt(shmaddr) == -1)
    {
        perror("Shmdt failed");
    }
     
    return 0;
}

예제 프로그램 결과

 

 

'Etc' 카테고리의 다른 글

Database 용어 정리  (0) 2021.11.17
[IPC]Shared Memory 응용  (0) 2021.10.22
[IPC]Semaphore  (0) 2021.10.22
[IPC]Semaphore C 예제  (0) 2021.10.22
[IPC]Semaphore 함수  (0) 2021.10.22