본문 바로가기

리눅스 커널/기타

[C++] 리눅스 System V 공유메모리

[1. 개요]

리눅스 공유메모리의 한 종류인 System V 공유메모리의 간단한 사용 방법 및

유의 사항을 정리하도록 한다.


[2. 생성]

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main()
{
        const int id = shmget(0x12345678, 4096, IPC_CREAT | IPC_EXCL | 0666);
        // 할당할 메모리 공간의 크기
        // 생성, 이미 존재하면 실패, 접근 권한
        if (id == -1) {
                perror("shmget fail");
                return 1;
        }
        
        // 공유메모리 세그먼트에 attach
        void* ptr = shmat(id, NULL, 0);
        if (ptr == (void*)(-1)) {
                perror("shmat fail");
                return 2;
        }

        strcpy((char*)(ptr), "hello");

#ifdef DEL
        if (shmctl(id, IPC_RMID, NULL) == -1) {
                perror("shmctl fail");
                return 3;
        }
#endif

        // 공유메모리 세그먼트를 detach
        shmdt(ptr);

        return 0;
}

 

[3. 접근]

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main()
{
        const int id = shmget(0x12345678, 4096, 0444);
        if (id == -1) {
                perror("shmget fail");
                return 1;
        }

        void* ptr = shmat(id, NULL, 0);
        if (ptr == (void*)(-1)) {
                perror("shmat fail");
                return 2;
        }

        const char* str = (const char*)(ptr);
        std::cout << str << std::endl;

        shmdt(ptr);

        return 0;
}

[4. 삭제]

공유메모리 세그먼트의 삭제는 shmctl 을 통해서 이루어 진다.

shmctl 과 여러 옵션을 통해 공유메모리에 대한 상세한 컨트롤이 가능한데,

여기서 IPC_RMID 는 공유메모리 세그먼트를 삭제하는 것이 아니라, 삭제 해줄 것을 (커널에?) 요청하는 것이다.

정리하면, 

  • shmctl 이 정상 호출 된 경우, 공유메모리 세그먼트가 삭제되는 것이 아니라,
  • 더 이상 참조 되지 않을 때 삭제 할 것을 요청하는 것이다.
  • 즉, shmctl 을 통하여 삭제를 요청하지 않는다면, attach 된 프로세스가 없어도 공유메모리는 남아 있게 된다.

이러한 특성을 통해, 프로세스가 종료되어도 공유메모리는 남아 있을 수 있다.

그러나, 프로세스를 재시작 해야하는 경우 그리고 이때 IPC_EXCL 옵션으로 공유메모리를 생성하는 경우,

정상 실행이 되지 않을 수 있다.

 

이 경우에는, shmget 으로 공유메모리 세그먼트를 생성하고, detach 하기 전에,

shmctl 을 먼저 호출 하는 방식을 고려할 수 있다.

물론, shmctl 을 호출하는 쪽은 공유메모리를 생성하는 프로세스에서 하도록 한다.

 

공유메모리 세그먼트 삭제를 요청한 경우, 더 이상의 새로운 attach 는 허용되지 않는다.


[5. 기타 참고 사항]

  • ftok()
  • ipcs
  • ipcmk
  • ipcrm
  • /proc/sysvipc/msg
  • /proc/sysvipc/sem
  • /proc/sysvipc/shm

윈도우 API 와 달리, 이미 할당된 공유메모리 세그먼트의 크기를 알 수 있다.

  • shmctl 과 IPC_STAT 을 이용하여...

POSIX 방식의 공유메모리도 있음. tmpfs 를 이용하여 /dev/shm 에 생성 됨.

  • shm_open
  • shm_unlink
  • mmap
  • munmap
  • ftruncate

[6. 중요]

리눅스 공유메모리는 커널에 의해 관리가 된다.

유저 프로세스가 공유메모리를 read 가능하게 open 하고, read only 로 attach 하면,

해당 영역은 page table 상에서 read-only 로 설정되고, 쓰기 연산이 발생하는 순간 sigsegv 로 처리하게 된다.

 

즉 커널이 read-only 대상이 된 공유메모리를 오염되지 않게 보호하는 것이 보장하고 있다.

 

문제는, read-only 로 attach 해야한다는 것이다.

  • shmat 시, SHM_RDONLY 를 사용해야 함

0 을 사용하면, write 가 가능해져서, 스택 오버플로우 등으로 인해 의도하지 않았어도 공유메모리가 오염 될 수 있다.

  • 해당 현상이 매번 발생하진 않지만, 이것이 추후 디버깅을 어렵게 한다.

또, shmget 의 접근권한보다, shmat 시 접근 권한이 더 우선적으로 처리된다는 것이다.

  • shmget 시 0444 로 해도, shmat  시 SHM_RDONLY 를 하지 않으면 write 가 가능하다.
  • 반드시, shmat 시 SHM_RDONLY 를 사용하도록 한다. (READ-ONLY 인 경우.)

따라서, 3의 예제를 read only 로 하려면 아래와 같이 해야 한다.

#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

int main()
{
        const int id = shmget(0x12345678, 4096, 0444);
        if (id == -1) {
                perror("shmget fail");
                return 1;
        }

        void* ptr = shmat(id, NULL, SHM_RDONLY);
        if (ptr == (void*)(-1)) {
                perror("shmat fail");
                return 2;
        }

        const char* str = (const char*)(ptr);
        std::cout << str << std::endl;

        shmdt(ptr);

        return 0;
}

'리눅스 커널 > 기타' 카테고리의 다른 글

리눅스 프린터 사용하기  (0) 2021.10.28
정적, 동적 라이브러리 만들기  (0) 2021.10.28
아파치, php, mariadb 설치  (0) 2021.10.28
Linux latop touchpad on/off  (0) 2021.10.28
kali linux unlock session  (0) 2021.10.28