System V MQ 시나리오
[생산자 프로세스] -> [OS 커널 속 메시지 큐] -> [소비자 프로세스]
여기서 메시지큐(Message Queue)는 커널이 관리하는 메모리 공간이고, 양쪽 프로세스는 시스템 콜(system call)을 통해서만 접근
1. 생산자가 하는 일
1) msgsnd() 호출
-> 유저 프로세스 -> 커널 진입 (시스템콜)
2) 커널이 메시지큐(커널 메모리)에 공간이 있는지 확인
- 큐가 가득 차면(blocking 모드) -> 생산자 프로세스는 슬립(block)
- 공간이 있으면 메시지를 복사(copy) 해서 커널 큐에 저장
3) 메시지 큐에 들어오면
- 그 큐를 기다리던 소비자 프로세스가 있다면, 커널이 깨워줌(wake up)
- 커널 입장에서 이건 이벤트 트리거에 가깝지만 사용자에게 직접 이벤트가 전달되진 않음
즉, 생산자는 데이터를 OS에게 “여기 넣어줘요” 하고 맡기고, 커널이 큐 상태를 관리하며, 대기 중인 소비자를 깨워줌.
2. 소비자가 하는 일
1) msgcrv() 호출
- 큐가 비어 있다면, 소비자는 커널에서 block 상태로 대기
- 메시지가 들어올 때까지 CPU 자원을 거의 안 씀 (sleep 상태)
2) 생산자가 메시지를 보내면
- 커널이 그 소비자를 깨워서(run queue에 올림)
- 소비자가 깨어나면 커널이 메시지를 유저 프로세스 메모리로 복사
3) 소비자는 받은 데이터를 처리하고 다시 msgrcv() 호출해 다음 메시지를 기다림
즉, 이벤트처럼 보이지만 실제로는 “슬립 & 웨이크업”으로 구현된 이벤트 흐름
3. OS 내부에서 일어나는 일 (커널 시점)
msgsnd() 호출 : 생산자의 데이터를 커널 큐에 복사
큐가 가득 차면 : 생산자 프로세스 block 상태로 큐 공간이 날 때까지 대기
msgrcv() 호출 : 큐가 비면 소비자 프로세스 block
메시지 도착 : 커널이 block 상태의 소비자를 깨움(wakeup)
메시지 전달 : 커널이 데이터 복사 후 소비자에게 리턴
4. '이벤트'의 본질
생산자가 메시지를 보낼 때 커널이 내부적으로 대기 중인 소비자에게 wake-up 신호를 줌
즉, 진짜 “이벤트 객체”가 아니라 스케줄러 레벨에서 깨어나는 일
커널 입장에서 보면
-> 이벤트를 명시적으로 push하는 게 아니라, “데이터가 큐에 들어옴 → wait queue에 있던 프로세스를 깨움” 이 전부
5. POSIX MQ(mq_*)
POSIX 메시지큐(mq_send, mq_receive)는 System V보다 한 단계 발전
- 메시지큐 자체가 파일 디스크립터로 표현됨 (→ epoll로 감시 가능)
- mq_notify() 로 커널이 시그널이나 스레드 콜백으로 이벤트 통지 가능
즉, 진짜 “이벤트 발생”을 하고 싶다면, POSIX 메시지큐 쪽이 적합
struct sigevent sev;
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGUSR1;
mq_notify(mq, &sev);
-> 메시지 도착 시 커널이 SIGUSR1 신호 발생
System V 메시지큐 느림
- 매번 유저 공간 ↔ 커널 공간 복사(copy) 발생
- 시스템콜 오버헤드 있음
- 스케줄러 wake-up 지연 존재
참고 (커널 안거침)
- Shared Memory Ring Buffer (Lock-Free Queue)
- mmap + spin/poll 기반 이벤트링
- user-space networking (DPDK, io_uring)
System V MQ 와 POSIX MQ 모두 '메시지 1건 도착' 단위로 깨워짐
따라서 소비자는 보통 하나만 읽고 다시 블록(block) 하거나 루프를 돌며 계속 읽는 구조로 작성됨
큐가 완전히 빌 때 까지 계속 읽지 않음.