공유 메모리는 프로세스 간에 서로 공유하는 메모리 공간이다.
프로세스는 시작 시 혹은 실행 중에 이러한 데이터를 저장하고 사용하기 위한 메모리 공간을 커널에 요구하여서 할당받아 사용하게 되는데, 이러한 메모리 공간은 기본적으로 메모리를 요청한 프로세스만이 접근 가능하도록 되어있다.
하지만 가끔은 여러 개의 프로세스가 특정 메모리 공간을 동시에 접근해야 할 필요성을 가질 때가 있다.
이러한 작업을 위한 효율적인 방법을 공유 메모리가 제공한다.
공유 메모리는 여러 IPC 중에서 가장 빠른 수행 속도를 보여준다.
이유는 하나의 메모리를 공유해서 접근하게 되므로, 데이터 복사와 같은 불필요한 오버헤드가 발생하지 않기 때문에, 빠른 데이터의 이용이 가능하다. 그러나 하나의 프로세스가 메모리에 접근 중에 있을 때, 또 다른 프로세스가 메모리에 접근하는 일이 발생하면 자칫 데이터가 훼손될 수 있으므로, 한 번에 하나의 프로세스가 메모리에 접근하고 있다는 걸 보증해줄 수 있어야 한다.
공유 메모리 관련된 함수
#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 *shaddr)
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; // 메모리 공간의 크기
time_t shm_atime; // 마지막 attach 시간
time_t shm_dtime; // 마지막 detach 시간
time_t shm_ctime; // 마지막 변경 시간
unsigned short shm_cpid; // 생성 프로세스의 pid
unsigned short shm_lpid; // 마지막으로 작동한 프로세스의 pid
short shm_nattch; // 현재 접근한 프로세스의 수
};
shm_perm : 공유 메모리는 여러 개의 프로세스가 동시에 접근 가능하므로, 파일과 같이 그 접근권한을 분명히 명시해줘 한다.
shm_segsz : 할당된 메모리의 byte 크기이다.
shm_atime : 가장 최근의 프로세스가 세그먼트를 attach 한 시간
shm_dtime : 가장 최근의 프로세스가 세그먼트를 detach 한 시간
shm_ctime : 마지막으로 이 구조체가 변경된 시간
shm_cpid : 이 구조체를 생성한 프로세스의 pid
shm_lpid : 마지막으로 작동을 수행한 프로세스의 pid
shm_nattch : 현재 접근 중인 프로세스의 수
공유 메모리에 접근을 하기 위해서는 공유의 공유 메모리 key를 통해서 접근 가능해지며, 이 key값을 통해서 다른 여러 개의 공유 메모리들과 구분될 수 있다.
shmget
커널에 공유 메모리 공간을 요청하기 위해 호출하는 시스템 호출 함수이다.
key는 바로 위에서 설명했듯이 고유의 공유 메모리임을 알려주기 위해서 사용된다.
shmget을 이용해서 새로운 공유 메모리 영역을 생성하거나 기존에 만들어져 있던 공유 메모리 영역을 참조할 수 있다.
첫 번째 argument는 여러 개의 공유 메모리 중 원하는 공유 메모리에 접근하기 위한 Key 값이다. 이 Key값은 커널에 의해서 관리되며, Key값을 통해서 선택적인 공유 메모리에 접근이 가능하다.
두 번째 argument는 공유 메모리의 최소 크기이다. 새로운 공유 메모리를 생성하고자 한다면 크기를 명시해주어야 한다.
존재하는 메모리를 참조한다면 크기를 0으로 명시한다.
세 번째 argument는 공유 메모리의 접근권한과 생성 방식을 명시하기 위해서 사용한다.
argument의 생성 방식을 지정하기 위해서 IPC_CREATE와 IPC_EXCL을 사용할 수 있다.
IPC_CREATE : key를 이용 새로운 공유 메모리 공간을 만든다.
IPC_EXCL : IPC_CREATE와 같이 사용되며, 공유 메모리 공간이 이미 존재할 경우 error를 되돌려준다.
만약 IPC_CREATE만 사용된다면 shmget()은 새로운 생성되는 공유 메모리 공간을 지시하는 공유 메모리 공간 "식별자" 되돌려준다. 만약 입력된 key값이 지시하는 공유 메모리 공간이 이미 존재하고 있다면 존재하는 공유메모리 공간의 "식별자"를 되돌려준다.
세 번째 argument는 이외에도 권한을 지정해줄 수도 있다. 권한은 파일 권한과 동일하게, 유저, 그룹, Other에 대한 읽기, 쓰기 권한을 지정할 수 있다. 단 실행 권한은 줄 수 없도록 되어 있다.
ex)
int shmid;
key_t key;
key = 1234;
shmid = shmget(key, 1024, IPC_CREATE | 0666);
if(shmid == -1)
return -1;
shmat
공유 메모리 공간을 생성했으면, 우리는 공유 메모리에 접근할 수 있는 int형의 "식별자"를 얻게 된다. 우리는 이 식별자를 shmat을 이용해서 지금의 프로세스가 공유 메모리를 사용 가능하도록 "덧붙임" wkrdjqdmf gowndjdigksek.
argument
1. shmget을 이용해서 얻어낸 식별자 번호
2. 메모리가 붙을 주소를 명시하기 위해 사용하는데, 0을 사용할 경우 커널이 메모리가 붙을 주소를 명시하게 된다. 특별한 사항이 없다면 0을 사용하도록 한다.
3. 우리는 해당 공유 메모리에 대한 "읽기 전용", "읽기/쓰기 가능" 모드로 열 수 있는데, SHM_RDONLY를 지정할 경우 읽기 전용으로, 아무 값도 지정하지 않을 경우 "읽기/쓰기 가능" 모드로 열리게 된다.
shmdt
프로세스가 더 이상 공유 메모리를 사용할 필요가 없을 경우 프로세스와 공유 메모리를 분리하기 위해서 사용한다.
이 함수를 호출할 경우 단지 현재 프로세스와 공유 메모리를 분리시킬 뿐이지, 공유 메모리 내용을 삭제하지는 않는다.
공유 메모리를 커널상에서 삭제시키길 원한다면 shmctl 같은 함수를 이용해야 한다.
shmdt가 성공적으로 수행되면 커널은 shmid_ds의 내용을 갱신한다.
즉, shm_dtime, shm_lpid, shm_nattch 등의 내용을 갱신하는데, shm_dtime는 가장 최근에 dettach (즉 shmdt를 사용한)된 시간, shm_lpid는 호출한 프로세서의 PID, shm_nattch는 현재 공유 메모리를 사용하는 (shmat를 이용해서 공유 메모리에 붙어있는) 프로세스의 수를 돌려준다. shmdt를 사용하게 되면 shm_nattch는 1 감소하게 될 것이며, shm_nattch가 0 즉 더 이상 붙어있는 프로세스가 없다는 뜻이 될 것이다. shm_nattch가 0이 되어있을 때 만약 이 공유 메모리가 shm_ctl등에 의해 삭제 표시가 되어 있다면, 이 공유 메모리는 삭제되게 된다.
shmctl
shmid_ds를 직접 제어함으로써, 해당 공유 메모리에 대한 소유자, 그룹 등의 허가권을 변경하거나, 공유 메모리를 삭제 혹은 공유 메모리의 잠금을 설정하거나 해제하는 등의 작업을 한다.
second argument를 이용해서 shmid가 가리키는 공유 메모리를 제어하며, cmd를 이용해서 원하는 제어를 할 수 있다.
cmd를 이용해 내릴 수 있는 명령
- IPC_STAT : 공유 메모리 공간에 관한 정보를 가져오기 위해서 사용된다. 정보는 buf에 저장된다.
- IPC_SET : 공유메모리 공간에 대한 사용자 권한 변경을 위해서 사용된다. 사용자 권한 변경을 위해서는 슈퍼유저 혹은 사용자 권한을 가지고 있어야 한다.
- IPC_RMID : 공유 메모리 공간을 삭제하기 위해서 사용된다. 이명령을 사용한다고 해서 곧바로 사용되는 건 아니며, 더 이상 공유 메모리 공간을 사용하는 프로세스가 없을 때, 즉 shm_nattch 가 0일 때까지 기다렸다가 삭제된다.
즉, 해당 공유 메모리 공간에 대해서 삭제 표시를 하는 거라고 생각하면 된다.
예제)
#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
#include<unistd.h>
int main()
{
int shmid;
int pid;
int *cal_num;
void *shared_memory = (void*)0;
// 공유 메모리 공간을 만든다.
shmid = shmget((key_t)1234, sizeof(int), 0666 | IPC_CREATE);
if(shmid == -1)
{
perror("shmget failed : ");
exit(0);
}
// 공유 메모리를 사용하기 위해 프로세스 메모리에 붙인다.
shared_memory = shmat(shmid, (void*)0, 0);
if(shared_memory == (void*)-1)
{
perror("shmat failed : ");
exit(0);
}
cal_num = (int *)shared_memory;
pid = fork();
if(pid == 0)
{
shmid = shmget((key_)1234, sizeof(int), 0);
if(shmid == -1)
{
perror("shmget failed : ");
exit(0);
}
// 공유 메모리를 사용하기 위해 프로세스 메모리에 붙인다.
shared_memory = shmat(shmid, (void*)0, 0);
if(shared_memory == (void*)-1)
{
perror("shmat failed : ");
exit(0);
}
cal_num = (int*)shared_memory;
*cal_num = 1;
while(1)
{
*cal_num = *cal_num +1;
printf("child %d\n", *cal_num);
sleep(1);
}
}
else if(pid >0)
{
while(1)
{
sleep(1);
printf("%d\n", *cal_num);
}
}
return 0;
}
예제 프로그램은 int형의 공유 메모리 공간을 할당한 다음. 자식 프로세스에서 여기에 1씩 더하고 부모 프로세스에서는 공유 메모리 내용을 출력하는 일을 한다.
공유 메모리 제어
쉘에서 공유 메모리의 상황을 보여주기 위해서 ipcs(1)란 도구를 제공한다.
ipcs를 사용하면 공유 메모리뿐만 아닌, Semaphore, Message Queue 등 소위 system V IPC 설비에 대한 내용을 보여준다. 그리고 ipcrm 도구를 이용해서 필요 없는 공유 메모리, Message Queue, Semaphore 등을 지워줄 수 있다.
공유메모리 정보 확인
-l 옵션과 함께 ipcs를 실행하면 ipc자원 제한 정보를 확인할 수 있다.
-m 옵션으로 실행하면 현재 사용 중인 ipc 자원 정보를 확인할 수 있다.
/proc 파일 시스템으로 제어하기
리눅스 운영체제는 /proc 파일 시스템으로 공유메모리 자원 값을 변경할 수 있다.
- /proc/sys/kernel/shmmax : 프로세스가 생성할 수 있는 공유 메모리의 최대 크기
- /proc/sys/kernel/shmall : 현재 사용 중인 공유 메모리 크기
'시스템 > 리눅스' 카테고리의 다른 글
우분투 한글 설정 (0) | 2020.12.30 |
---|---|
우분투 ssh 서버 설치 (0) | 2020.12.30 |