QNX RTOS: 5-10. Shared Memory
2025. 12. 15. 15:04ㆍ운영체제/QNX
1. Shared Memory(공유 메모리) 개념
- 공유 메모리(shared memory): 두 개 이상 프로세스(process)가 같은 물리 메모리(physical memory) 영역을 각자 가상주소(virtual address)로 매핑(map)하여 함께 접근하는 IPC 방식.
- 목적: 큰 데이터(large data)를 메시지 복사 없이 공유하여 성능 개선.
- 메모리는 연속(contiguous)일 수도 있고 아닐 수도 있음(대개 “같은 메모리를 본다”가 핵심이지, 연속성은 보장하지 않음).

2. POSIX 방식: shm_open() 기반 Named Shared Memory(이름 있는 공유 메모리)
2.1 이름 규칙(Name rule)
- POSIX 규칙:
- leading slash(/) 없으면 undefined behavior
- 중간 slash(embedded /)는 implementation-defined
- QNX 동작:
- leading slash 없으면 현재 작업 디렉토리(CWD)를 앞에 붙이는 방식으로 해석할 수 있음 → 원치 않는 동작 가능
- 결론:
- 항상 절대 경로 형태로 /name 사용 권장
- 필요하면 /group/sub/name처럼 하위 분류로 namespace 충돌 방지 가능
3. “생성자(creator)”가 하는 절차 (POSIX Named)
공유메모리를 “처음 만들고 초기화”하는 쪽 기준 절차:
Step A. 생성: shm_open()
- shm_open("/myname", O_RDWR | O_CREAT, 0600)
- O_CREAT: 없으면 생성
- O_EXCL 함께 사용 가능: O_CREAT | O_EXCL
- 누가 먼저 만들지 모르는 다중 프로세스 환경에서 “최초 1명만 생성자” 판별 용도
- 0600: 소유자만 읽기/쓰기 가능(권한 관리)
Step B. 크기 할당: ftruncate()
- 생성 직후 shared memory object는 크기 0 → 실제 메모리 할당 필요
- ftruncate(fd, size)
- 요청 크기는 페이지(page) 크기 단위로 올림(round up)
- QNX 일반 구현: 4KB(4K) 페이지
- 예: 21KB 요청 → 24KB 할당
Step C. 매핑: mmap()
- mmap(..., PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)
- MAP_SHARED: 진짜 공유(서로 같은 메모리 봄)
- MAP_PRIVATE: copy-on-write/사본 → IPC 공유 목적과 반대
- PROT_*는 MMU(Memory Management Unit) 보호 비트 설정
- mmap 권한은 shm_open의 open mode와 일치해야 함
(읽기 전용으로 열고 쓰기 매핑하면 실패)
Step D. 초기화(중요)
- 새로 할당된 메모리는 zero-fill(0으로 초기화됨)
- 그 다음에 사용자 정의 구조체/버퍼/동기화 객체 등을 초기화
Step E. FD 정리
- mmap 후에는 보통 fd는 더 이상 필요 없으므로 close(fd) 가능
(매핑은 유지됨)
4. “접근자(Accress)”가 하는 절차 (이미 만들어진 Named)
다른 프로세스가 이미 만들어 둔 공유메모리를 붙는 경우:
- shm_open("/myname", O_RDWR, 0)
- 생성이 아니므로 O_CREAT 없음
- mode 인자(세 번째)는 의미 없지만 함수 시그니처상 넣어야 하므로 0 넣는 관례
- mmap()
- 필요 시 read-only로 매핑 가능
- 전체가 아니라 부분 구간만 매핑도 가능
- close(fd)
- 매핑 이후 fd는 닫아도 됨
부분 매핑 주의
- offset/length로 일부만 매핑 가능하지만,
- 실제 매핑/보호 단위는 페이지 정렬(page-aligned)
- 예: offset 6KB로 매핑 요청 → 내부적으로 4KB 경계부터 매핑될 수 있음
- 반환 포인터는 요청 offset 기준으로 맞춰주지만, 페이지 단위로 더 넓게 매핑될 수 있음
5. 언제 메모리가 “진짜로” 해제되는가? Reference Count(참조 카운트)
QNX 프로세스 매니저(Process Manager)가 참조 카운트(reference count)로 수명 관리:
참조는 보통 3종류:
- 이름(name): pathname 공간에 남아있는 엔트리
- 파일 디스크립터(fd): shm_open()로 얻은 핸들
- 매핑(mapping): mmap()으로 프로세스 주소공간에 매핑된 상태
정리 포인트:
- 프로세스 종료 시:
- fd는 자동 close
- mapping은 자동 munmap
- 하지만 이름(name)은 자동으로 제거되지 않음
- 파일과 동일: 이름이 남아 있으면 객체도 남아 있음(단, 공유메모리는 “부팅 동안만” 유효)
이름 제거(삭제)
- 표준 POSIX: shm_unlink("/myname")
- QNX 디버그 편의:
- 이름 있는 공유메모리는 /dev/shm 아래 파일처럼 보임
- 예: shm_open("/myname") → /dev/shm/myname 같은 형태
- 따라서 쉘에서 ls /dev/shm, rm /dev/shm/myname로 정리 가능
이름을 먼저 지우면?
- shm_unlink()를 먼저 해도,
- 이미 매핑된 프로세스가 있으면 메모리는 계속 존재
- 단지 “이름으로 새로 찾는 것”만 불가능해짐
→ “익명화(anonymous-like)” 효과
6. Shared Memory의 대표 문제 2가지
(1) Access(접근) 문제
- 이름 충돌(namespace collision): 서로 다른 팀/회사 코드가 같은 /myname 사용
- 보안(security): 유닉스 권한(permissions)만으로 세밀한 접근 통제가 어렵다
(2) Synchronization(동기화) 문제
- 공유메모리는 결국 “멀티프로세스에서의 공유 데이터”
→ 단일 프로세스 내 스레드 동기화 문제가 그대로 확장됨 - 해결: 공유메모리 내부에 동기화 객체를 두고 사용
7. 공유메모리에서 동기화 객체 쓰는 법
7.1 세마포어(semaphore)
- unnamed semaphore를 sem_init()로 만들 때:
- pshared 인자를 0이 아닌 값으로 설정해야 프로세스 간 공유 가능
7.2 pthread 동기화(mutex/cond/rwlock 등)
- pthread_mutexattr_*, pthread_condattr_* 등 attribute(속성 구조체) 사용
- 반드시:
- PTHREAD_PROCESS_SHARED 설정 필요
(pthread_mutexattr_setpshared() 등)
- PTHREAD_PROCESS_SHARED 설정 필요
7.3 단순 플래그/카운터
- atomic(원자) 연산(atomic functions) 사용 가능
8. “프로세스가 죽으면 mutex가 잠긴 채로 남는” 문제와 Robust Mutex(강건 뮤텍스)
문제 상황
- 프로세스 A가 공유메모리 mutex를 lock한 상태에서 프로세스 A가 크래시
- 프로세스 B는 계속 살아 있고 그 mutex를 lock하려고 함
- mutex는 논리적으로 “잠긴 상태” → 시스템이 교착/정지 위험
해결: Robust Mutex
- mutex 생성 시 속성으로 robust 설정:
- pthread_mutexattr_setrobust()
- 공유메모리이면 추가로 pthread_mutexattr_setpshared()도 필요
→ process-shared + robust 조합
동작 핵심
- 프로세스가 죽은 상태에서 보호 데이터가 불완전할 수 있으므로,
- 다음 lock 시에:
- pthread_mutex_lock()가 EOWNERDEAD 반환
- 의미: “이 mutex 소유자가 죽었고, 보호 데이터는 unknown state(알 수 없는 상태)”
복구 절차
- 복구 가능하면:
- 데이터를 known-good 상태로 복원
- pthread_mutex_consistent() 호출 후 unlock
- 복구 불가능하면:
- pthread_mutex_consistent() 호출하지 않고 unlock
- 이후 lock은 ENOTRECOVERABLE (복구 불가) 반환
- 기다리던 다른 스레드들도 ENOTRECOVERABLE로 깨워질 수 있음
→ 상위 수준에서 재초기화/프로세스 재시작 같은 “메타 복구” 필요
9. 실무에서 자주 쓰는 패턴: “IPC로 제어, Shared Memory로 데이터”

- MsgSend()/MsgReceive()/MsgReply()는 자체적으로 동기화 성질이 있음
- client는 reply 받을 때까지 reply-blocked
- 서버가 receive~reply 구간에서만 shared memory를 읽는 규칙을 두면 동기화가 쉬움
- 그래서 흔한 설계:
- 제어/메타데이터(control/metadata): 메시지(IPC)로 전달
- 대용량 데이터(bulk data): shared memory로 전달
- 예: 그래픽 서버
- client가 공유메모리에 이미지 생성 → 메시지로 “image #5 보여줘” 전달 → 서버가 처리 후 reply
- 데이터 복사 오버헤드 감소 + 반복 재사용 가능
10. Anonymous Shared Memory(익명 공유메모리) + Handle(핸들) 방식
이름 기반(/myname)의 문제(충돌/보안)를 줄이기 위한 방식.
핵심 아이디어
- pathname으로 찾지 않고,
- 서버가 만든 익명 shared memory를 특정 client에게만 유효한 handle로 전달하여 사용하게 함.
생성
- shm_open(SHM_ANON, ...)
- 이름이 없음
- 각 SHM_ANON 호출은 서로 다른 객체(“같은 이름이면 같은 객체”가 아님)
handle 생성: shm_create_handle()
- 입력:
- 익명 shm의 fd
- 허용 대상 프로세스 pid(사용자 pid)
- client에게 허용할 최대 권한(permissions)
- 출력:
- handle 값(성공 시 갱신됨)
client에서 열기
- shm_open_handle(handle, flags)
- 또는 더 안전한:
- shm_open_handle_pid(handle, server_pid, flags)
- “이 handle이 내가 믿는 서버 pid에서 온 게 맞는지” 확인(중간자 공격 방지 개념)
'운영체제 > QNX' 카테고리의 다른 글
| QNX RTOS: 6-2. IPC 선택 기준 (0) | 2025.12.15 |
|---|---|
| QNX RTOS: 6-1. IPC Methods (0) | 2025.12.15 |
| QNX RTOS: 5-9. Event Delivery (0) | 2025.12.15 |
| QNX RTOS: 5-8. Deadlock Avoidance (0) | 2025.12.03 |
| QNX RTOS: 5-7. Server Designs (0) | 2025.12.02 |