[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 |