Shared Memory
공유 메모리shared memory는 IPCinter-process communication를 시작하는 첫 걸음입니다. 일반적인 프로세스에서 사용되는 메모리 영역은 해당 프로세스만이 사용할 수 있는 특징이 있으나, 간혹 여러 개의 프로세스가 특정 메모리 영역을 사용을 하길 원하는 경우가 있을 수 있습니다. 그런 경우에 사용하는 것이 바로 공유 메모리입니다.
공유 메모리에 관련된 함수들로는 아래와 같습니다.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg)
void *shmat( int shmid, const void *shmaddr, int shmflg )
int shmdt( const void *shmaddr)
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
각각에 관해서 알아보도록 하겠습니다.
함수 | 내용 |
---|---|
shmget | key를 접근 번호로 하는 공유 메모리 공간 할당을 커널에 요청하도록 합니다. 커널이 성공적으로 공유 메모리 공간을 할당을 하면 공유 메모리를 가리키는 식별자를 반환하도록 합니다. 크게 생성에 관련된 플래그로는 IPC_CREAT, IPC_EXCL, mode_flags가 존재합니다. IPC_CREATE의 경우, 새로운 영역을 할당을 하는 것에 해당하며, IPC_EXCL는 만약 해당 공유 메모리 영역이 존재하면 -1(에러)을 반환하도록 합니다. 마지막으로 mode_flags는 0666과 같이 접근 권한을 지정하는 데 사용합니다. |
shmat | shmid 에 해당하는 공유 메모리 영역에 접근하여 프로세스에 해당 메모리를 사용할 수 있도록 가져오는 함수입니다. 그리고 적절한 공유 메모리 주소가 주어지지 않고 shmaddr로 (void *)0 가 넘어가게 된다면 시스템이 알아서 판단하여 적절한 위치로 설정하도록 합니다.그리고 shmflg는 가져오는 메모리 영역에 대한 접근 권한 설정입니다. 이를테면, SHM_RDONLY를 주게 되면 공유 메모리의 접근은 읽기 전용만 허가되게 됩니다. 만약 이 값이 0이라면 R/W가 다 허용됩니다. |
shmdt | 공유 메모리 영역에서 shmaddr 에 해당하는 주소를 공유 메모리에서 빼내는 것에 해당합니다. |
shmctl | 공유 메모리의 정보를 읽거나, 정보를 변경, 할당된 공유 메모리 영역을 삭제합니다. 플래그로는 아래와 같습니다. IPC_STAT은 공유 메모리의 정보를 조회하여 버퍼에 저장하는 플래그에 해당합니다. IPC_SET은 파일의 권한 및 사용자 변경과 관련된 플래그에 해당합니다. IPC_RMID는 생성된 공유 메모리의 키와 관련된 공유 메모리를 시스템에서 삭제합니다. 이 경우 버퍼는 사용되지 않으므로 NULL로 설정해주면 됩니다. |
단적으로, 아래의 코드는 공유 메모리를 사용하는 예제에 해당합니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SEGSIZE 100
void writeshm(int shmid, char *segptr, char *text)
{
// 현재 공유 메모리에 쓰도록 합니다.
strcpy(segptr, text);
printf("Done...\n");
}
void readshm(int shmid, char *segptr)
{
// 현재 공유 메모리를 읽도록 합니다.
printf("segptr: %s\n", segptr);
}
void removeshm(int shmid)
{
// 공유 메모리를 삭제하도록 합니다.
shmctl(shmid, IPC_RMID, 0);
printf("Shared memory segment marked for deletion\n");
}
void changemode(int shmid, char *mode)
{
struct shmid_ds myshmds;
// 현재 공유 영역의 정보를 받아오도록 합니다.
shmctl(shmid, IPC_STAT, &myshmds);
// 과거 권한을 조회해보도록 합니다.
printf("Old permissions were: %o\n", myshmds.shm_perm.mode);
// 공유 권한을 변경을 하도록 합니다.
sscanf(mode, "%ho", &myshmds.shm_perm.mode);
shmctl(shmid, IPC_SET, &myshmds);
// 갱신된 공유 권한을 확인합니다.
printf("New permissions are : %o\n", myshmds.shm_perm.mode);
}
void usage()
{
fprintf(stderr, "shmtool - A utility for tinkering with shared memory\n");
fprintf(stderr, "\nUSAGE: shmtool (w)rite \n");
fprintf(stderr, " (r)ead\n");
fprintf(stderr, " (d)elete\n");
fprintf(stderr, " (m)ode change \n");
exit(1);
}
int
main(int argc, char *argv[])
{
key_t key; // 공유 메모리 접근 번호입니다.
int shmid; // 공유 메모리를 가리키는 식별자를 가집니다.
char *segptr; // 공유 메모리를 받을 포인터에 해당합니다.
if(argc == 1)
usage();
key = ftok(".", 'S'); // 접근 번호를 생성하도록 합니다.
// 공유 메모리를 생성을 해보도록 합니다.
if((shmid = shmget(key, SEGSIZE, IPC_CREAT|IPC_EXCL|0666)) == -1)
{
printf("Shared memory segment exists - opening as client\n");
// 공유 메모리가 이미 존재한다면 공유 메모리를 생성하지 않고 읽도록 합니다.
if((shmid = shmget(key, SEGSIZE, 0)) == -1)
{
perror("shmget");
exit(1);
}
}
else
{
printf("Creating new shared memory segment\n");
}
// 공유 메모리를 로컬 포인터로 가져오도록 합니다.
if ((segptr = shmat(shmid, 0, 0)) == (char *)(-1)) {
perror("shmat"); // 공유 메모리를 받아오지 못하였습니다.
exit(-1);
}
switch(tolower(argv[1][0])) { // 명령어를 읽습니다.
case 'w': writeshm(shmid, segptr, argv[2]);
break;
case 'r': readshm(shmid, segptr);
break;
case 'd': removeshm(shmid);
break;
case 'm': changemode(shmid, argv[2]);
break;
default:
usage();
}
}
위와 같은 방식을 통해서 공유 메모리의 접근과 사용이 가능합니다. 이런 공유 메모리 영역을 만들 때에 유의 사항으로는 되도록 구조체나 배열을 공유 메모리 영역에 할당을 할 때에는 정적으로 만들어야 합니다. 무슨 말인가 하면 아래와 같이 만들어야 함을 의미합니다.
struct shm_info {
char str_ip[40]; // char *str_ip와 같이 하지 않도록 합니다.
int int_ip;
int int_id;
};
물론 자유자재로 공유 메모리를 사용하시는 분들은 동적 할당을 활용을 할 수도 있을 겁니다. 하지만 그런 사용은 필시 코드를 복잡하게 만들고 버그를 양산할 가능성이 높습니다. 따라서 최대한 정적 영역으로 사용하는 것을 추천 드립니다.
그리고 이러한 공유 메모리를 비롯한 IPC의 상태를 확인을 할 수 있는 명령으로 ipcs
라는 명령이 있습니다. 이런 ipcs
를 사용하면 아래와 같은 창이 나오게 됩니다.
IPC status from /dev/mem as of Mon Aug 14 15:03:46 1989
T ID KEY MODE OWNER GROUP
Message Queues:
q 0 0x00010381 -Rrw-rw-rw- root system
q 65537 0x00010307 -Rrw-rw-rw- root system
q 65538 0x00010311 -Rrw-rw-rw- root system
q 65539 0x0001032f -Rrw-rw-rw- root system
q 65540 0x0001031b -Rrw-rw-rw- root system
q 65541 0x00010339--rw-rw-rw- root system
q 6 0x0002fe03 -Rrw-rw-rw- root system
Shared Memory:
m 65537 0x00000000 DCrw------- root system
m 720898 0x00010300 -Crw-rw-rw- root system
m 65539 0x00000000 DCrw------- root system
Semaphores:
s 131072 0x4d02086a --ra-ra---- root system
s 65537 0x00000000 --ra------- root system
s 1310722 0x000133d0 --ra------- 7003 30720
출처: https://www.ibm.com/support/knowledgecenter/ko/ssw_aix_71/com.ibm.aix.cmds3/ipcs.htm
이를 통해, 현재 생성된 공유 메모리의 상태를 확인하고 관리할 수 있습니다. 만약에 프로세스의 비정상 종료로 인해서 적절하게 공유 메모리가 해제되지 않은 경우에는 ipcrm -m (shmid)
를 치면 삭제를 할 수 있습니다.