그림 1. 하나의 공유 메모리를 사용하는 Process 들
mmap을 사용한 메모리 공유 방법은 파일을 사용하여 메모리를 메핑한다. 하지만 이번에 소개할 Shared Memory는 파일을 사용하지 않고 직접적으로 메모리를 공유하는 방법에 대해서 기술한다.
Shared Memory 는 둘 이상의 프로세스 간에 공유하는 메모리 공간을 의미하며, unrelated process 간에 도 공유가 가능하다. 하지만 여러 프로세스가 하나의 메모리 공간을 사용함으로 향후에 작성할 동기화 메커니즘이 필수적으로 들어가야 한다.
Shared Memory 를 사용하는데 다음 두가지가 있다.
여기서 POSIX가 기능적으로 좋지만 옛날에 작성된 코드들은 대부분 Sysv로 되어있기 때문에 둘다 알아두어야 한다. SYSV와는 달리 POSIX는 다음과 같은 특징을 가지고 있다.
SYSV와 POSIX에서 사용하고 있는 API는 다음과 같다.
int shmget(key_t key, size_t size, int shmflg);
: shared memory id 값을 가져오는 함수. 때에 따라 생성도 가능하다.파라미터
key: shared memory key값 --> ftok() API를 통해서 생성 가능.
size: shared memory size in bytes ‐ 단, PAGE_SIZE의 배수이어야 함
shmflg: permission + mask
‐ IPC_CREAT: 생성 요청
‐ IPC_EXCL: 같은 key값을 갖는 shared memory 존재시 에러
‐ SHM_HUGETLB: hugepage로부터 생성하도록 설정
‐ SHM_HUGE_2MB, SHM_HUGE_1GB: hugepage size 지정
반환값
• 성공: shared memory id
• 실패: -1
void *shmat(int shmid, const void *shmaddr, int shmflg);
: shared memory에 접근한다.파라미터
shmid: shared memory id(i.e. from shmget())
shmaddr: mapping 할 address hint
‐ NULL: kernel이 알아서 지정하도록
‐ non-NULL: 반드시 이 주소로 mapping 하도록 함
▫ 단, PAGE_SIZE로 align되어 있거나
▫ shmflg에 SHM_RND를 명시
shmflg
‐ SHM_RND: shmaddr을 align하도록 지정
‐ SHM_EXEC: mapping 영역에 execution 가능하도록 설정
‐ SHM_RDONLY: read-only access
‐ SHM_REMAP: 기존에 mapping 된 영역에서 remap 하도록 설정
▫ 단, shmaddr은 반드시 non-NULL이어야 함
반환값
• 성공: attached shared memory address pointer
• 실패: (void *) -1
int shmdt(const void *shmaddr);
: shared memory를 detach 한다.파라미터
- shmaddr: detach 할 shared memory address
반환값
- 성공: 0
- 실패: -1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
: shared memory에 대한 제어 기능 수행파라미터
shmid: shared memory id • cmd
‐ IPC_STAT: 커널의 정보를 buf에 복사
‐ IPC_SET: buf의 내용을 커널에 설정
‐ IPC_RMID: shared memory id 제거. --> 해당 설정을 많이 사용.
‐ IPC_INFO: system-wide shared memory 관련 정보 요청(buf에 struct shminfo * )
‐ SHM_INFO: shared memory에 의해 사용된 메모리 정보 요청(buf에 struct shm_info *)
‐ SHM_STAT: shmid를 index로 하여 IPC_STAT 요청
buf
반환값
• IPC_INFO, SHM_INFO: 성공시 가장 많이 사용된 shared memory entry의 index를 리턴
• SHM_STAT: 성공시 해당 shared memory id 리턴
• 나머지 operation: 성공시 0 리턴
• 실패시 -1 리턴
struct shmid_ds
구조struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};:q
int shm_open(const char *name, int oflag, mode_t mode);
: shared memory 생성/열기파라미터
• name: SHM 이름(반드시 /로 시작해야 함. 예: “/testshm”)
• oflag
‐ mask of O_RDONLY, O_RDWR, O_CREAT, O_EXCL, O_TRUNC
• mode: permission
‐oflag에 O_CREAT가 없다면 mode는 반드시 0이어야 함
반환값
• 성공: shared memory descriptor 리턴(음수가 아닌 정수)
• 실패: -1
int shm_unlink(const char *name);
: shared memory 삭제파라미터
• name: shared memory 이름(반드시 /로 시작해야 함. 예: “/testshm”)
반환값
• 성공: 0
• 실패: -1
POSIX 를 사용하는 shared memory 프로그래밍 시퀀스는 어떠한 형식을 가지고 있다.
각 과정에 대한 시퀀스는 다음과 같다.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/mman.h>
#include<sys/stat.h>
#define KEY_PATH "/tmp"
#define PROJ_ID 123
#define PAGE_SIZE 4096
#define MONITORING_COUNT 5
#define ROUNDUP(x) (((x) + (PAGE_SIZE -1)) & ~(PAGE_SIZE - 1))
#define ERROR_CHECK(x, y, z) if((x) == (-1)) { perror(y); return z; }
#define SHM_FILE_PATH "/posix_test"
typedef struct data_info {
int size;
char message[PAGE_SIZE];
} DATA_INFO;
static void print_usage(const char *progname)
{
printf("Usage: %s (posix|sysv) (recv|send Message)\n", progname);
}
static int posix_init(void)
{
int fd;
fd = shm_open(SHM_FILE_PATH, O_CREAT | O_RDWR, 0644);
ERROR_CHECK(fd, "shm_open()", -1);
ERROR_CHECK(ftruncate(fd, sizeof(DATA_INFO)), "ftruncate()", -1);
return fd;
}
DATA_INFO *posix_gen_info(int fd)
{
DATA_INFO *info;
info = (DATA_INFO *)mmap(NULL, sizeof(DATA_INFO), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
ERROR_CHECK(info, "mmap()", MAP_FAILED);
return info;
}
static int posix_finish(int fd, DATA_INFO *info)
{
munmap(info, sizeof(DATA_INFO));
close(fd);
return 0;
}
static int posix_recv_message(void)
{
int fd;
int n;
DATA_INFO *info;
DATA_INFO local;
fd = posix_init();
info = posix_gen_info(fd);
memset(&local, 0, sizeof(local));
memset(info, 0, sizeof(DATA_INFO));
n = 0;
while(1){
if(memcmp((const void *)&local, (const void *)info, sizeof(local))){
/* diff */
printf("[Monitor - POSIX] size: %d, send Message: %s\n", info->size, info->message);
memcpy(&local, info, sizeof(DATA_INFO));
n ++;
if( n == MONITORING_COUNT) break;
}
sleep(1);
}
return posix_finish(fd, info);
}
static int posix_send_message(const char * message)
{
int fd;
DATA_INFO *info;
fd = shm_open(SHM_FILE_PATH, O_CREAT | O_RDWR, 0644);
ERROR_CHECK(fd, "shm_open()", -1);
info = (DATA_INFO *)mmap(NULL, sizeof(DATA_INFO), PROT_WRITE, MAP_SHARED, fd, 0);
info->size = sizeof(message);
strcpy(info->message, message);
munmap(info, sizeof(DATA_INFO));
return 0;
}
static int sysv_init(void)
{
key_t key;
int shm_id;
key = ftok(KEY_PATH, PROJ_ID);
ERROR_CHECK(key, "ftok()", -1);
shm_id = shmget(key, ROUNDUP(sizeof(DATA_INFO)), IPC_CREAT | 0644);
ERROR_CHECK(shm_id, "shmget()", -1);
return shm_id;
}
static DATA_INFO *sysv_gen_info(int shm_id)
{
DATA_INFO *info;
info = (DATA_INFO *)shmat(shm_id, NULL, 0);
ERROR_CHECK((void *)info, "shmat()", NULL);
memset((void *)info, 0, sizeof(DATA_INFO));
return info;
}
static int sysv_finish(const void *info)
{
ERROR_CHECK((const void *)info, "shmdt()", -1);
return 0;
}
static int sysv_recv_message(void)
{
int n;
int shm_id;
DATA_INFO *info;
DATA_INFO local;
shm_id = sysv_init();
ERROR_CHECK(shm_id, "sysv_init()", -1);
info = sysv_gen_info(shm_id);
if(info == NULL){
perror("sysv_gen_info()");
return -1;
}
memset(&local, 0, sizeof(local));
n = 0;
while(1){
if(memcmp((const void *)&local, (const void *)info, sizeof(local))){
/* diff */
printf("[Monitor - SYSV] size: %d, send Message: %s\n", info->size, info->message);
memcpy(&local, info, sizeof(DATA_INFO));
n ++;
if( n == MONITORING_COUNT) break;
}
sleep(1);
}
return sysv_finish((const void *)info);
}
static int sys_send_message(const char *message)
{
int shm_id;
DATA_INFO *info;
shm_id = sysv_init();
ERROR_CHECK(shm_id, "sysv_init()", -1);
info = sysv_gen_info(shm_id);
if(info == NULL){
perror("sysv_gen_info()");
return -1;
}
info->size = sizeof(message);
strcpy(info->message, message);
return sysv_finish(info);
}
int main(int argc, char **argv)
{
if(argc < 3){
print_usage(argv[0]);
return -1;
}
if(!strcmp((const char *)argv[1], "posix")) {
/* POSIX */
if(!strcmp((const char *)argv[2], "recv")){
/* recv Message */
posix_recv_message();
} else if(!strcmp((const char *)argv[2], "send")){
/* send Message */
if(argc < 4) { goto main_err; }
posix_send_message((const char *)argv[3]);
} else { goto main_err;}
} else if(!strcmp((const char *)argv[1], "sysv")){
/* SYSV */
if(!strcmp((const char *)argv[2], "recv")){
/* recv Message */
sysv_recv_message();
} else if(!strcmp((const char *)argv[2], "send")){
/* send Message */
if(argc < 4) { goto main_err; }
sys_send_message((const char *)argv[3]);
} else { goto main_err;}
} else { goto main_err; }
return 0;
main_err:
print_usage(argv[0]);
return -1;
}