공유 메모리란?
자료 조사한 곳 : https://www.joinc.co.kr/w/Site/system_programing/IPC/SharedMemory
공유 메모리란 무엇인가?
공유 메모리란 무엇인가?
모든 프로세스들은 자신이 업무를 수행하기 위해, 필요한 자료를 저장하기 위해 그에 맞는 메모리 공간을 가지게 된다. 그 메모리 공간 안에는 CPU가 처리하는 명령어들, 프로그램 시작시 정의되고 초기화된 데이터, 프로그램 시작 시 정의는 되었지만 초기화 되지 않은 데이터(BSS), 함수 호출에 필요한 정보, 동적 할당이 이루어지는 데이터 등이 들어가게 된다.
프로세스는 이러한 데이터를 저장하고 사용하기 위한 메모리 공간을 커널(하드웨어의 자원을 자원이 필요한 프로세스에 나눠주고, 프로세스 제어(task maneger), 메모리 제어, 프로그램이 운영체제 요구하는 시스템 콜 등을 수행함)에 요구해서 할당받아 사용하게 되는데. 이 메모리공간은 기본적으로 이 메모리를 요청한 프로세스만 접근 가능하도록 한다. 하지만 여러 개의 프로세스가 특정 메모리 공간을 동시에 접근해야 할 필요성을 가질 때가 있다. 공유 메모리는 이러한 작업을 위한 효율적인 방법을 제공한다.
* 공유메모리는 여러 IPC(프로세스들 사이에 서로 데이터를 주고받는 행위 또는 그에 대한 방법이나 경로) 중에서 가장 빠른 수행속도를 보여 준다고 한다. 그 이유는 하나의 메모리를 공유해서 접근하게 되므로, 데이터 복사 같은 불필요한 오버헤드가 발생하지 않기 때문에 빠른 데이터의 이용이 가능하다. 하지만 하나의 프로세스가 메모리에 접근 중에 있을 때, 다른 프로세스가 메모리에 접근하는 일이 발생할 경우에는 데이터가 훼손될 수 있으므로, 한 번에 하나의 프로세스가 메모리에 접근하고 있다는 것을 보증해 줄 수 있어야 한다. 이러한 작업을 위해 UNIX(:12) 에서는 Semaphore(:12) 라는 또 다른 공유자원을 제어할 수 있도록 해주는 도구를 제공해 준다.
다음은 공유메모리에 관련된 함수들의 모음이다.
#include <sys/types.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)
- 공유메모리가 어떻게 할당되고, 어떤 과정을 통해서 접근가능한지에 대해서 알아보자.
공유메모리는 최초로 공유메모리 영역을 만드는 프로세스가 커널에 공유메모리 공간의 할당을 요청함으로써 이루어지고, 만들어진 공유메모리는 커널에 의해서 관리된다.
따라서 한번 만들어진 공유메모리는 운영체제를 재부팅하거나, 직접 공유메모리 공간을 삭제시키지 않는 한 공유메모리를 사용하는 프로세스가 모두 없어졌다고 하더라도 계속 유지된다.
프로세스가 커널에게 공유메모리 공간을 요청하게 되면, 커널은 공유메모리 공간을 할당시켜주고 이 공유메모리 공간을 관리하기 위한 내부 자료구조를 통해 이 공유메모리를 관리하게 된다. 이 자료는 shmid_ds 라는 구조체에 의해서 관리되고 shm.h에 정의되어 있다.
struct shmid_ds
{
struct ipc_perm shm_perm; // 퍼미션
int shm_segsz; // 메모리 공간의 크기 (segment size)
time_t shm_atime; // 마지막 attach 시간 (attach time)
time_t shm_dtime; // 마지막 detach 시간 (detach time)
time_t shm_ctime; // 마지막 변경 시간 (change time)
unsigned short shm_cpid; // 생성프로세스의 pid (process id)
unsigned short shm_lpid; // 마지막으로 작동한 프로세스의 pid
short shm_nattch; // 현재 접근한 프로세스의 수
};
Unix 버젼에 따라서 멤버변수들이 약간씩 차이를 보일수 있다.
shm_perm
공유메모리는 여러개의 프로세스가 동시에 접근 가능하므로, 파일과 같이 그 접근권한을 분명히 명시해줘야 한다.
shm_segsz
할당된 메모리의 byte 크기이다
shm_atime
가장최근의 프로세스가 세그먼트를 attach한 시간
shm_dtime
가장최근의 프로세스가 세그먼트를 detach한 시간
shm_ctime
마지막으로 이 구조체가 변경된 시간
shm_cpid
이 구조체를 생성한 프로세스의 pid
shm_lpid
마지막으로 작동을 수행한 프로세스의 pid
shm_nattch
현재 접근중인 프로세스의 수 이러한 공유메모리에 접근을 하기 위해서는 고유의 공유메모리 key 를 통해서 접근가능해지며, 이 key값을 통해서 다른 여러개의 공유메모리들과 구분되어 질수 있다.
SYSTEM CALL: shmget();
PROTOTYPE: int shmget ( key_t key, int size, int shmflg );
RETURNS: shared memory segment identifier on success
-1 on error: errno = EINVAL (Invalid segment size specified)
EEXIST (Segment exists, cannot create)
EIDRM (Segment is marked for deletion, or was removed)
ENOENT (Segment does not exist)
EACCES (Permission denied)
ENOMEM (Not enough memory to create segment)
NOTES:
shmget 은 커널에 공유메모리 공간을 요청하기 위해 호출하는 시스템 호출 함수이다. key 는 바로 위에서 설명했듯이 공유메모리임을 알려주기 위해서 사용된다. shmget을 이용해서 새로운 공유메모리 영역을 생성하거나 기존에 만들어져 있었던 공유메모리 영역을 참조할 수 있다.
첫 번째 매개변수는 여러 개의 공유메모리 중 원하는 공유메모리에 접근하기 위한 key의 값이다(ftok() 호출에 의해 반환됨). 이 key 값은 커널에 의해서 관리되고, key 값을 통해서 선택적인 공유메모리로의 접근이 가능하다.
두 번째 매개변수는 공유메모리의 최소 크기이다. 새로운 공유메모리를 생성하고자 하면 크기를 명시해 주어야 하고, 존재하는 메모리를 참조하면 크기는 0으로 명시한다.
세 번째 매개변수는 공유메모리의 접근권한과(파일권한과 동일하게 유저, 그룹, Other에 대한 읽기/쓰기 권한을 지정할 수 있지만 실행권한은 줄 수 없다), 생성방식을 명시하기 위해서 사용한다. 매개변수의 생성방식을 지정하기 위해서 IPC_CREAT 와 IPC_EXCL을 사용할 수 있다.
IPC_CREAT
커널 안에 이미 존재하지 않는다면 세그먼트를 만든다. 만약 있다면 무시하며 생성을 위해 접근 권한을 지정해 주어야 한다.
IPC_EXCL
IPC_CREAT와 함께 사용될 때, 세그먼트가 이미 존재하면 실패하며 공유메모리에 접근하지 못한다. 이 옵션이 없어야 기존 공유 메모리에 접근할 수 있다.
IPC_CREAT가 혼자 사용되면, shmget()은 새롭게 만들어진 세그먼트의 세그먼트 확인자(공유 메모리 공간을 지시하는 공유메모리 공간 “식별자”)를 반환하거나 같은 키 값을 가진 세그먼트가 존재하면 그 세그먼트의 확인자(존재하는 공유메모리 공간의 “식별자”)를 반환한다.
IPC_EXCL이 IPC_CREAT와 함께 사용된다면, 새로운 세그먼트가 만들어 지거나 세그먼트가 존재하면, -1값을 가지며 호출은 실패한다. IPC_EXCL은 그 자체로는 쓸모가 없지만, IPC_CREAT와 조합될 때, 존재하지 않는 세그먼트가 접근을 위해 열리는 것을 막는데 사용된다.
* shmat(shm attach)
형태 : void *shmat(int shmid, const void *shmaddr, int shmflg);
우리가 shmget을 통해 공유메모리 공간을 생성했으면, 우리는 공유메모리에 접근할 수 있는 int 형의 식별자를 얻게 된다. 우리는 이 식별자를 shmat을 이용해서 지금 사용할 프로세스가 공유메모리를 사용가능하도록 “덧붙임” 작업을 해주어야 한다.
첫 번째 매개변수는 shmget을 이용해서 얻어낸 식별자 번호이며, 두 번째 매개변수는 메모리가 붙을 주소를 명시하기 위해 사용한다. 0을 사용할 경우 커널이 메모리가 붙을 주소를 명시하게 된다. 특별한 사항이 없다면 0을 사용하도록 한다. 세 번째 매개변수를 SHM_RDONLY를 지정할 경우 읽기 전용으로, 아무 값도 지정하지 않을 경우 “읽기/쓰기 가능” 모드로 열리게 된다.
shmat() 함수는 공유 메모리를 마치 프로세스의 몸 안으로 첨부한다. 공유 메모리를 이용하면 프로세스끼리 통신을 할 수 있으며, 같은 데이터를 공유할 수 있다. 이렇게 같은 메모리 영역을 공유하기 위해서는 공유 메모리를 생성한 후에 프로세스에 자신의 영역에 첨부를 한 후에 자신의 메모리를 사용하듯 사용한다. 즉, 공유 메모리를 사용하기 위해서는 공유 메모리를 생성한 후에, 이 메모리가 필요한 프로세스는 필요할 때 마다 자신의 프로세스에 첨부를 한 후에 다른 메모리를 사용하듯 사용한다고 생각하면 된다.
* shmdt (shm dettach)
형태 : int shmdt(const void *shmaddr);
프로세스가 더 이상 공유메모리를 사용할 필요가 없을 경우에 프로세스와 공유 메모리를 분리 시키기 위해서 사용한다. 이 함수는 현재 프로세스와 공유 메모리를 분리 시킬 뿐이지. 공유 메모리를 삭제하는 건 아니라는 점을 기억해야 한다. 공유 메모리를 삭제시키기 위해서는 shmctl과 같은 함수를 이용해야 한다.
shmdt 가 성공적으로 수행되면 커널은 shmid_ds 의 내용을 갱신하게 된다. 즉 shm_dtime, shmlpid, shm_nattach 등의 내용을 갱신하는데, shm_dtime 은 가장 최근에 shmdt를 사용한 시간, shm_lpid 는 호출한 프로세서의 pid, shm_nattch 는 현재 공유메모리를 사용하는 (shmat를 이용해서 공유메모리에 붙어있는) 프로세스의 수를 돌려준다. shmdt를 사용하게 되면 shm_nattach는 1 감소하게 되고, shm_nattch가 0이면 더 이상 붙어있는 프로세스가 없다는 뜻이 된다. shm_nattch 가 0이 되어있을 때 만약 이 공유메모리가 shm_ctl 등에 의해 삭제표시 되어 있다면, 이 공유메모리는 삭제된다.
* shmctl (shm control)
형태 : int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl은 공유메모리를 제어하기 위해서 사용한다. shmid_ds를 직접 제어함으로써, 해당 공유메모리에 대한 소유자, 그룹 등의 허가권을 변경하거나, 공유메모리를 삭제, 혹은 공유메모리의 잠금을 설정하거나 해제하는 등의 작업을 한다.
2번째 매개변수를 이용해서 shmid 가 가리키는 공유메모리를 제어하며, cmd를 이용해서 원하는 제어를 할 수 있다. cmd를 이용해 내릴 수 있는 명령에는 다음과 같은 것들이 있다.
IPC_STAT
공유메모리 공간에 관한 정보를 가져오기 위해서 사용된다. 정보는 buf에 저장된다.
IPC_SET
공유메모리 공간에 대한 사용자 권한 변경을 위해서 사용된다. 사용자 권한 변경을 위해서는 슈퍼유저 혹은 사용자 권한을 가지고 있어야 한다.
IPC_RMID
공유메모리 공간을 삭제하기 위해서 사용된다. 이 명령을 사용한다고 해서 곧바로 사용되는 건 아니며, 더 이상 공유메모리 공간을 사용하는 프로세스가 없을 때, 즉 shm_nattch가 0일 때 까지 기다렸다가 삭제된다. 즉, 해당 공유메모리 공간에 대해서 삭제표시를 하는 것이라고 생각하면 편하다.
* 공유 메모리 정보 확인
-l 옵션과 함께 ipcs를 실행하면 ipc(:12)자원 제한 정보를 확인 가능하다.
-m 옵션으로 실행하면 현재 사용중인 ipc 자원 정보를 확인 가능하다.
리눅스 운영체제(:12)는 /proc 파일 시스템으로 공유 메모리 자원 값을 변경할 수 있다.
/proc/sys/kernel/shmmax : 프로세스가 생성할 수 있는 공유 메모리의 최대 크기
/proc/sys/kernel/shmall : 현재 사용중인 공유 메모리 크기
댓글
댓글 쓰기