일반적으로 프로세스 끼리는 서로 독립된 메모리 공간을 사용한다. 따라서 프로세스끼리는 서로의 영역을 침범할 수 없다.
ㅤㅤ
프로그램을 설계하다보면 여러 프로세스가 서로에게 정보나 신호를 주고받아야하는 상황이 온다. A 에서 처리한 결과가 B 에게 전달되어야 한다거나, 혹은 A와 B가 동시에 작업을 시작하거나 작업의 우선순위가 있을 수 있다. 이러한 상황에서 프로세스 간의 통신 (IPC, Inter-Process Communication)이 필요하다.
가장 기본이 되는 파일을 이용한 IPC(파이프, FIFO, Socket)가 궁금하다면 이 글을 읽어보자.
또, 그 다음 제시된 산업적인 니즈 충족을 위한 System V의 IPC(메시지 큐, 세마포어, 공유 메모리)가 궁금하다면 이 글을 읽어보자.
POSIX의 IPC
기존 IPC 방법이 UNIX 시스템 간에 호환성을 확실히 제공해주기는 했지만, 아무래도 오래된 API라 사용 방법이 꽤나 복잡하다는 단점이 있었다. 그래도 POSIX IPC가 더욱 사용하기 편리한 + 개선된 내부 구조를 가지고 제공되기 시작했다.
ㅤㅤ
3-1 메시지 큐
POSIX 메시지 큐는 System V와 동일하게 커널이 관리하는 메시지 저장소이다.하지만 파일 기반 API를 사용하여 더 직관적이고 현대적인 인터페이스를 제공한다.
ㅤㅤ
특징
- 이름 기반 식별:
/로 시작하는 이름 사용 (예:/my_queue) - 우선순위 지원: 0~31 우선순위, 높은 값이 먼저 수신됨 (System V 는 Type 기준이였음)
- 비동기 알림:
mq_notify()로 메시지 도착 시 시그널/스레드 알림 - 파일 디스크립터 반환:
select(),poll(),epoll()과 함께 사용 가능 - 가상 파일시스템:
/dev/mqueue/에서 큐 확인 가능
ㅤㅤ
장점
- 직관적인 API: 파일 open/close와 유사한 패턴
- 우선순위 기반 수신: 긴급 메시지 우선 처리 가능
- 비동기 알림: 폴링 없이 메시지 도착 감지
- 이식성: POSIX 표준으로 다양한 Unix 계열에서 동작
- 이벤트 루프 통합: fd 기반이라
epoll등과 함께 사용 가능
ㅤㅤ
단점
- 타입별 선택 불가: System V처럼 특정 타입만 수신하는 기능 없음
- 별도 라이브러리 링크:
lrt필요 - 이름 제약: 반드시
/로 시작, 그 외/불가 - 시스템별 제한 차이: 최대 메시지 크기, 큐 개수 등이 시스템마다 다름
ㅤㅤㅤㅤ
어디에 쓰나?
- 우선순위 기반 메시지 처리가 필요할 때
select()/epoll()기반 이벤트 루프에 통합할 때- 비동기 알림이 필요할 때
- 쉬운 API로 메시지큐를 쓸 때
ㅤㅤ
System V의 메시지큐와 다른 점
[ System V: 타입 기반 선택적 수신 ]
- 송신: type=1, type=2, type=1
- 수신: msgrcv(..., type=2) → type=2만 꺼냄
[ POSIX: 우선순위 기반 자동 정렬 ]
- 송신: prio=1, prio=5, prio=3
- 수신: mq_receive() → 항상 가장 높은 우선순위(5)부터
ㅤㅤ
메시지큐 실습
핵심적인 API 들은 아래와 같다.
#include <mqueue.h>
#include <fcntl.h> /* O_CREAT, O_RDONLY 등 */
#include <sys/stat.h> /* mode_t */
mqd_t mq_open(const char *name, int oflag, ...); /* 큐 열기/생성 */
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
int mq_close(mqd_t mqdes); /* 닫기 (삭제 아님) */
int mq_unlink(const char *name); /* 큐 삭제 */
int mq_getattr(mqd_t mqdes, struct mq_attr *attr); /* 속성 조회 */
int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
ㅤㅤ
메시지큐는 아래의 속성값들을 가진다.
struct mq_attr {
long mq_flags; /* 플래그: 0 또는 O_NONBLOCK */
long mq_maxmsg; /* 큐의 최대 메시지 개수 */
long mq_msgsize; /* 메시지 최대 크기 (바이트) */
long mq_curmsgs; /* 현재 큐에 있는 메시지 개수 */
};
ㅤㅤ
실습을 위해, Sender와 Receiver 가 하나의 메시지큐를 이용해 데이터를 주고 받는 코드로 실습을 진행하였다.
- sender는 메시지 큐를 만들고 메시지를 3개 넣는다. 이때 세 메시지는 서로 다른 우선순위를 가진다.
- receiver는 메시지 큐에 메시지가 있으면 받아와 출력한다. 이때 우선순위가 높은 메시지부터 먼저 출력한다. 모든 메시지를 출력하고 나면 메시지 큐를 해제한다. 만약 메시지가 없으면 에러를 출력한다.
ㅤㅤ
아래에 있는 실습 코드를 실행해보면, 아래와 같은 결과를 확인할 수 있다.

ㅤㅤ
이때 코드를 살펴보면 파일을 열고 닫거나, 파일 입출력을 수행하는 것처럼 API를 구성해두었다. 그래서 System V 의 메시지큐보다 조금 더 정돈된 + 마음이 편안한 코드이다.
/* 큐 생성/열기 */
mq = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr);
/* 우선순위 1 메시지 전송 (낮은 우선순위) */
strcpy(msg, "Normal message (priority 1)");
mq_send(mq, msg, strlen(msg) + 1, 1)
ㅤㅤ
실습 코드
// sender.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#define QUEUE_NAME "/test_queue"
#define MAX_MSG_SIZE 256
#define MAX_MSGS 10
int main(void)
{
mqd_t mq;
struct mq_attr attr;
char msg[MAX_MSG_SIZE];
/* 큐 속성 설정 */
attr.mq_flags = 0;
attr.mq_maxmsg = MAX_MSGS;
attr.mq_msgsize = MAX_MSG_SIZE;
attr.mq_curmsgs = 0;
/* 큐 생성/열기 */
mq = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr);
if (mq == (mqd_t)-1) {
perror("mq_open");
exit(1);
}
printf("Queue opened: %s\n", QUEUE_NAME);
/* 우선순위 1 메시지 전송 (낮은 우선순위) */
strcpy(msg, "Normal message (priority 1)");
if (mq_send(mq, msg, strlen(msg) + 1, 1) == -1) {
perror("mq_send");
exit(1);
}
printf("Sent [prio=1]: %s\n", msg);
/* 우선순위 5 메시지 전송 (높은 우선순위) */
strcpy(msg, "Urgent message (priority 5)");
if (mq_send(mq, msg, strlen(msg) + 1, 5) == -1) {
perror("mq_send");
exit(1);
}
printf("Sent [prio=5]: %s\n", msg);
/* 우선순위 3 메시지 전송 (중간 우선순위) */
strcpy(msg, "Medium message (priority 3)");
if (mq_send(mq, msg, strlen(msg) + 1, 3) == -1) {
perror("mq_send");
exit(1);
}
printf("Sent [prio=3]: %s\n", msg);
mq_close(mq);
printf("Messages sent. Run receiver to read.\n");
return 0;
}
// receiver.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#define QUEUE_NAME "/test_queue"
#define MAX_MSG_SIZE 256
int main(void)
{
mqd_t mq;
struct mq_attr attr;
char msg[MAX_MSG_SIZE];
unsigned int prio;
ssize_t bytes_read;
int i;
/* 큐 열기 (읽기 전용) */
mq = mq_open(QUEUE_NAME, O_RDONLY);
if (mq == (mqd_t)-1) {
perror("mq_open");
exit(1);
}
/* 큐 속성 조회 */
if (mq_getattr(mq, &attr) == -1) {
perror("mq_getattr");
exit(1);
}
printf("Queue opened: %s\n", QUEUE_NAME);
printf(" Max messages: %ld\n", attr.mq_maxmsg);
printf(" Max msg size: %ld\n", attr.mq_msgsize);
printf(" Current msgs: %ld\n", attr.mq_curmsgs);
printf("\n");
/* 모든 메시지 수신 (우선순위 높은 것부터) */
for (i = 0; i < attr.mq_curmsgs; i++) {
bytes_read = mq_receive(mq, msg, MAX_MSG_SIZE, &prio);
if (bytes_read == -1) {
perror("mq_receive");
exit(1);
}
printf("Received [prio=%u]: %s\n", prio, msg);
}
mq_close(mq);
/* 큐 삭제 */
if (mq_unlink(QUEUE_NAME) == -1) {
perror("mq_unlink");
exit(1);
}
printf("\nQueue removed.\n");
return 0;
}
유용한 명령어
# POSIX 메시지 큐 목록 확인
ls -la /dev/mqueue/
# 특정 큐 삭제
rm /dev/mqueue/test_queue
# 시스템 제한 확인
cat /proc/sys/fs/mqueue/msg_max # 큐당 최대 메시지 수
cat /proc/sys/fs/mqueue/msgsize_max # 메시지 최대 크기
cat /proc/sys/fs/mqueue/queues_max # 시스템 전체 최대 큐 수
ㅤㅤ
3-2 네임드 세마포어
POSIX 세마포어는 System V와 동일하게 정수 카운터 기반 동기화 도구이다. 서로 다른 프로세스간에 신호를 공유하기 위해 사용한다.
ㅤㅤ
특징
- 집합 개념 없이 하나씩 독립적으로 관리
- 간단한 API:
sem_wait()(P),sem_post()(V)로 직관적 - 두 가지 종류: Named(프로세스 간), Unnamed(스레드/fork 간) → 지금은 Named만 설명
- 타임아웃 지원:
sem_timedwait()으로 대기 시간 제한 가능
ㅤㅤ
장점
- 단순한 API: sem_wait/sem_post 두 함수로 충분
- 집합 오버헤드 없음: 단일 세마포어를 직접 다룸
- 유연한 선택: Named/Unnamed 중 용도에 맞게 선택
- 타임아웃 지원
ㅤㅤ
단점
- SEM_UNDO 없음: 프로세스 비정상 종료 시 자동 복구 불가
- 다중 세마포어 원자 연산 불가: 여러 세마포어 동시 획득 시 데드락 위험
ㅤㅤ
어디에 쓰나?
- 단순한 상호 배제가 필요할 때
- 프로세스 간 동기화 (Named) / 스레드 간 동기화 (Unnamed)
- 타임아웃이 필요한 동기화
ㅤㅤ
세마포어 실습
named 세마포어의 핵심적인 API 들은 아래와 같다.
#include <semaphore.h>
#include <fcntl.h>
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
int sem_close(sem_t *sem); /* 닫기 (삭제 아님) */
int sem_unlink(const char *name); /* 삭제 */
int sem_wait(sem_t *sem); /* P 연산 (블로킹) */
int sem_trywait(sem_t *sem); /* P 연산 (논블로킹) */
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_post(sem_t *sem); /* V 연산 */
int sem_getvalue(sem_t *sem, int *sval); /* 현재 값 조회 */
ㅤㅤ
System V의 실습과 동일하게, 특정한 공유자원이 있다고 가정하고 A와 B에서 critical section에 세마포어를 이용해 Lock 을 걸고 진입하는 과정을 실습 코드로 구성하였다.
ㅤㅤ
과연 언급했던 것처럼, POSIX의 세마포어는 매우 간단한 API를 가지고 있다. 단순히 세마포어를 하나 생성해준 다음 sem_wait, sem_post 함수로 이를 처리해주고 있다. System V와 비교해 확실히 사용성이 좋아보인다!
sem_t *sem = NULL;
if (sem_wait(sem) == -1) {
perror("sem_wait");
exit(1);
}
// 여기가 Critical Section 내부
if (sem_post(sem) == -1) {
perror("sem_post");
exit(1);
}
ㅤㅤ
그 결과는 정상적으로 잘 동작하는 것을 볼 수 있다.
pi07@pi07:~/Documents/TC1127/Psema $ ./writer A 1 &
pi07@pi07:~/Documents/TC1127/Psema $ ./writer B 1
[1] 4007
[A] Semaphore: ENABLED
[A] Semaphore opened: /test_sem
[A] Waiting for semaphore...
[A] === ENTERED critical section ===
[A] === LEAVING critical section ===
[A] Waiting for semaphore...
[A] === ENTERED critical section ===
[B] Semaphore: ENABLED
[B] Semaphore opened: /test_sem
[B] Waiting for semaphore...
[A] === LEAVING critical section ===
[B] === ENTERED critical section ===
[A] Waiting for semaphore...
[B] === LEAVING critical section ===
[A] === ENTERED critical section ===
[B] Waiting for semaphore...
[A] === LEAVING critical section ===
[B] === ENTERED critical section ===
[A] Waiting for semaphore...
[B] === LEAVING critical section ===
[A] === ENTERED critical section ===
[B] Waiting for semaphore...
[A] === LEAVING critical section ===
[B] === ENTERED critical section ===
[A] Waiting for semaphore...
[B] === LEAVING critical section ===
[A] === ENTERED critical section ===
[B] Waiting for semaphore...
[A] === LEAVING critical section ===
[B] === ENTERED critical section ===
[A] Done.
[B] === LEAVING critical section ===
[B] Waiting for semaphore...
[B] === ENTERED critical section ===
[B] === LEAVING critical section ===
[B] Done.
ㅤㅤ
실습 코드
실습을 위한 코드는 아래처럼 구성해주었다. 인자로 Semaphore를 이용하거나 하지 않도록 하여, 결과의 차이를 확인할 수 있도록 하였다.
// writer.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
#define SEM_NAME "/test_sem"
#define SHARED_FILE "shared_data.txt"
int main(int argc, char *argv[])
{
sem_t *sem = NULL;
FILE *fp;
char id;
int use_sem;
int i;
if (argc < 3) {
fprintf(stderr, "Usage: %s <writer_id (A, B, ...)> <use_semaphore (0 or 1)>\n", argv[0]);
fprintf(stderr, " 0: No semaphore (race condition)\n");
fprintf(stderr, " 1: Use semaphore (mutual exclusion)\n");
exit(1);
}
id = argv[1][0];
use_sem = atoi(argv[2]);
printf("[%c] Semaphore: %s\n", id, use_sem ? "ENABLED" : "DISABLED");
if (use_sem) {
/* Named Semaphore 열기/생성 (초기값 1 = binary semaphore) */
sem = sem_open(SEM_NAME, O_CREAT, 0644, 1);
if (sem == SEM_FAILED) {
perror("sem_open");
exit(1);
}
printf("[%c] Semaphore opened: %s\n", id, SEM_NAME);
}
/* 5회 반복해서 파일에 쓰기 */
for (i = 0; i < 5; i++) {
if (use_sem) {
printf("[%c] Waiting for semaphore...\n", id);
if (sem_wait(sem) == -1) {
perror("sem_wait");
exit(1);
}
printf("[%c] === ENTERED critical section ===\n", id);
} else {
printf("[%c] Entering WITHOUT semaphore...\n", id);
}
/* 임계 영역: 파일 쓰기 */
fp = fopen(SHARED_FILE, "a");
if (fp == NULL) {
perror("fopen");
if (use_sem) {
sem_post(sem);
}
exit(1);
}
fprintf(fp, "[%c] Line %d - start ... ", id, i + 1);
fflush(fp);
usleep(50000);
fprintf(fp, "middle ... ");
fflush(fp);
usleep(50000);
fprintf(fp, "end\n");
fflush(fp);
fclose(fp);
if (use_sem) {
printf("[%c] === LEAVING critical section ===\n", id);
if (sem_post(sem) == -1) {
perror("sem_post");
exit(1);
}
} else {
printf("[%c] Exiting WITHOUT semaphore...\n", id);
}
usleep(10000);
}
if (use_sem) {
sem_close(sem);
}
printf("[%c] Done.\n", id);
return 0;
}
// clean.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#define SEM_NAME "/test_sem"
#define SHARED_FILE "shared_data.txt"
int main(void)
{
/* 세마포어 삭제 */
if (sem_unlink(SEM_NAME) == 0) {
printf("Semaphore %s removed.\n", SEM_NAME);
} else {
perror("sem_unlink (may not exist)");
}
/* 공유 파일 삭제 */
if (unlink(SHARED_FILE) == 0) {
printf("Shared file removed.\n");
}
return 0;
}
ㅤㅤ
유용한 명령어
# Named Semaphore 목록 확인
ls -la /dev/shm/sem.*
# 특정 세마포어 삭제
rm /dev/shm/sem.test_sem
# 또는 프로그램에서
sem_unlink("/test_sem");
ㅤㅤ
3-3 mmap
POSIX 공유 메모리는 shm_open()으로 공유 메모리 객체를 생성하고, mmap()으로 프로세스 주소 공간에 매핑하는 방식이다. 파일 기반 API를 사용하여 System V보다 직관적이다.
ㅤㅤ
특징
- 파일 기반 API: fd를 반환하여 파일처럼 다룸
- mmap() 사용: 범용 메모리 매핑 함수와 통합
- 크기 조정 가능:
ftruncate()로 생성 후에도 크기 변경 - tmpfs 기반:
/dev/shm/에 존재 (RAM 기반 파일시스템) - 권한 관리: 파일 권한과 동일한 방식
ㅤㅤ
장점
- 직관적인 API: 파일 open/close 패턴과 유사
- mmap() 통합: 파일 매핑과 동일한 방식으로 사용
- 크기 유연성: 생성 후 크기 조정 가능
- 파일시스템 가시성:
/dev/shm/에서 확인 가능 - fd 기반:
select(),poll()등과 함께 사용 가능
ㅤㅤ
단점
- 2단계 과정:
shm_open()+mmap()필요 (System V는shmat()하나) - ftruncate() 필수: 생성 후 크기 설정 필요
- 동기화 미제공: 별도 세마포어 필요 (System V와 동일)
ㅤㅤ
언제 쓰냐면?
- 현대적인 POSIX 표준 API를 선호할 때 (간단한 코드가 좋다면!)
- 파일 기반 권한 관리가 필요할 때
- mmap()을 이미 사용하는 코드와 통합할 때
- 공유 메모리 크기를 동적으로 조정해야 할 때
ㅤㅤ
공유 메모리 실습
주요 API는 다음과 같다.
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* 공유 메모리 객체 생성/열기 */
int shm_open(const char *name, int oflag, mode_t mode);
/* 크기 설정 (생성 직후 필수) */
int ftruncate(int fd, off_t length);
/* 메모리 매핑 */
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
prot: PROT_READ | PROT_WRITE (읽기/쓰기 권한)
flags: MAP_SHARED (다른 프로세스와 공유)
/* 매핑 해제 */
int munmap(void *addr, size_t length);
/* 공유 메모리 객체 삭제 */
int shm_unlink(const char *name);
ㅤㅤ
기존 방식과 동일하게, 공유 메모리를 생성해서 해당 메모리 영역에 값을 작성하고, 읽어오는 방식으로 실습 시나리오를 구성하였다. 여기에서는 공유자원에 값을 30회 +1 을 수행하는 작업을 작성하였다.
pi07@pi07:~/Documents/TC1127/Pshared $ ./writer A 1 &
pi07@pi07:~/Documents/TC1127/Pshared $ ./writer B 1
[1] 4712
[A] Semaphore: ENABLED
[A] Shared memory mapped at: 0x7fb0ba8000
[A] Semaphore opened: /test_sem
[A] Read counter: 320
[A] Wrote counter: 321
...
[B] Semaphore: ENABLED
[B] Shared memory mapped at: 0x7f82543000
[A] Wrote counter: 339
[B] Semaphore opened: /test_sem
[B] Read counter: 339
[B] Wrote counter: 340
[A] Read counter: 340
[A] Wrote counter: 341
[B] Read counter: 341
[B] Wrote counter: 342
[A] Read counter: 342
[A] Wrote counter: 343
[B] Read counter: 343
[B] Wrote counter: 344
...
[B] Read counter: 379
[B] Wrote counter: 380
[B] Final counter: 380
[B] Final message: Last writer: B, count: 380
[B] Done.
ㅤㅤ
shm_writer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <semaphore.h>
#define SHM_NAME "/test_shm"
#define SEM_NAME "/test_sem"
#define SHM_SIZE 1024
struct shared_data {
int counter;
char message[256];
};
int main(int argc, char *argv[])
{
int fd;
struct shared_data *shared;
sem_t *sem = NULL;
char id;
int use_sem;
int i;
if (argc < 3) {
fprintf(stderr, "Usage: %s <writer_id (A, B, ...)> <use_semaphore (0 or 1)>\n", argv[0]);
exit(1);
}
id = argv[1][0];
use_sem = atoi(argv[2]);
printf("[%c] Semaphore: %s\n", id, use_sem ? "ENABLED" : "DISABLED");
/* 공유 메모리 열기/생성 */
fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0644);
if (fd == -1) {
perror("shm_open");
exit(1);
}
/* 크기 설정 (이미 설정되어 있어도 무해) */
if (ftruncate(fd, SHM_SIZE) == -1) {
perror("ftruncate");
exit(1);
}
/* 메모리 매핑 */
shared = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shared == MAP_FAILED) {
perror("mmap");
exit(1);
}
printf("[%c] Shared memory mapped at: %p\n", id, (void *)shared);
/* fd는 매핑 후 닫아도 됨 */
close(fd);
/* 세마포어 설정 */
if (use_sem) {
sem = sem_open(SEM_NAME, O_CREAT, 0644, 1);
if (sem == SEM_FAILED) {
perror("sem_open");
exit(1);
}
printf("[%c] Semaphore opened: %s\n", id, SEM_NAME);
}
/* 카운터 30회 증가 */
for (i = 0; i < 30; i++) {
int temp;
if (use_sem) {
sem_wait(sem);
}
/* 임계 영역: read-modify-write */
temp = shared->counter;
printf("[%c] Read counter: %d\n", id, temp);
usleep(10000);
temp++;
shared->counter = temp;
snprintf(shared->message, sizeof(shared->message),
"Last writer: %c, count: %d", id, temp);
printf("[%c] Wrote counter: %d\n", id, temp);
if (use_sem) {
sem_post(sem);
}
usleep(5000);
}
printf("[%c] Final counter: %d\n", id, shared->counter);
printf("[%c] Final message: %s\n", id, shared->message);
/* 정리 */
if (use_sem) {
sem_close(sem);
}
munmap(shared, SHM_SIZE);
printf("[%c] Done.\n", id);
return 0;
}
shm_reader.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <semaphore.h>
#define SHM_NAME "/test_shm"
#define SEM_NAME "/test_sem"
#define SHM_SIZE 1024
struct shared_data {
int counter;
char message[256];
};
int main(void)
{
int fd;
struct shared_data *shared;
/* 공유 메모리 열기 */
fd = shm_open(SHM_NAME, O_RDONLY, 0644);
if (fd == -1) {
perror("shm_open - shared memory not found");
exit(1);
}
/* 메모리 매핑 (읽기 전용) */
shared = mmap(NULL, SHM_SIZE, PROT_READ, MAP_SHARED, fd, 0);
if (shared == MAP_FAILED) {
perror("mmap");
exit(1);
}
close(fd);
/* 결과 출력 */
printf("=== POSIX Shared Memory Contents ===\n");
printf("Counter: %d\n", shared->counter);
printf("Message: %s\n", shared->message);
printf("====================================\n");
/* 정리 */
munmap(shared, SHM_SIZE);
/* 공유 메모리 삭제 */
if (shm_unlink(SHM_NAME) == -1) {
perror("shm_unlink");
} else {
printf("Shared memory removed.\n");
}
/* 세마포어 삭제 */
if (sem_unlink(SEM_NAME) == 0) {
printf("Semaphore removed.\n");
}
return 0;
}
shm_init.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#define SHM_NAME "/test_shm"
#define SHM_SIZE 1024
struct shared_data {
int counter;
char message[256];
};
int main(void)
{
int fd;
struct shared_data *shared;
/* 공유 메모리 생성 */
fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0644);
if (fd == -1) {
perror("shm_open");
exit(1);
}
/* 크기 설정 */
if (ftruncate(fd, SHM_SIZE) == -1) {
perror("ftruncate");
exit(1);
}
/* 매핑 */
shared = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shared == MAP_FAILED) {
perror("mmap");
exit(1);
}
close(fd);
/* 초기화 */
shared->counter = 0;
strcpy(shared->message, "Initialized");
printf("POSIX shared memory initialized.\n");
printf(" Name: %s\n", SHM_NAME);
printf(" Counter: %d\n", shared->counter);
munmap(shared, SHM_SIZE);
return 0;
}
ㅤㅤ
공유 메모리의 시스템콜
이 실습에서 시스템 콜을 출력해봤을 때, 눈여겨 볼 점은 POSIX에서 세마포어를 기다리면서 한 프로세스가 다른 프로세스를 기다릴 때 사용하는 시스템콜이 futex 인데, 이게 경쟁상태일때에만 동작하고, 경쟁 없이 혼자서 자원을 사용할 때에는 호출되지 않는 시스템콜이라는 것이다!
ㅤㅤ
POSIX의 내부 구현이 좀 더 최적화가 잘 되어있어, 실제 실행 속도가 더 빠른 것이라고 기대된다.
| 상황 | System V | POSIX |
|---|---|---|
| 경쟁 없음 | semtimedop 호출 (항상 커널 진입) |
시스템 콜 없음 (유저 공간에서 처리) |
| 경쟁 있음 | semtimedop 호출 |
futex 호출 |
경쟁 상태 → futex가 22번 호출됨
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 1 ftruncate
0.00 0.000000 0 1 1 faccessat
0.00 0.000000 0 4 openat
0.00 0.000000 0 4 close
0.00 0.000000 0 1 read
0.00 0.000000 0 66 write
0.00 0.000000 0 4 fstat
0.00 0.000000 0 1 set_tid_address
**0.00 0.000000 0 22 futex**
0.00 0.000000 0 1 set_robust_list
0.00 0.000000 0 60 clock_nanosleep
0.00 0.000000 0 3 brk
0.00 0.000000 0 5 munmap
0.00 0.000000 0 1 execve
0.00 0.000000 0 8 mmap
0.00 0.000000 0 4 mprotect
0.00 0.000000 0 1 prlimit64
0.00 0.000000 0 1 getrandom
0.00 0.000000 0 1 rseq
------ ----------- ----------- --------- --------- ----------------
100.00 0.000000 0 189 1 total
ㅤㅤ
경쟁 없이 혼자 자원을 독식 → futex 호출 없음
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 1 unlinkat
0.00 0.000000 0 1 linkat
0.00 0.000000 0 1 ftruncate
0.00 0.000000 0 1 1 faccessat
0.00 0.000000 0 5 1 openat
0.00 0.000000 0 4 close
0.00 0.000000 0 1 read
0.00 0.000000 0 27 write
0.00 0.000000 0 1 1 newfstatat
0.00 0.000000 0 4 fstat
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 1 set_robust_list
0.00 0.000000 0 20 clock_nanosleep
0.00 0.000000 0 2 rt_sigprocmask
0.00 0.000000 0 3 brk
0.00 0.000000 0 5 munmap
0.00 0.000000 0 1 execve
0.00 0.000000 0 10 mmap
0.00 0.000000 0 4 mprotect
0.00 0.000000 0 1 prlimit64
0.00 0.000000 0 2 getrandom
0.00 0.000000 0 1 rseq
------ ----------- ----------- --------- --------- ----------------
100.00 0.000000 0 97 3 total'Embedded System > Embedded Linux' 카테고리의 다른 글
| [Embedded Linux] 디바이스 드라이버 작성해보기 (1) | 2025.11.29 |
|---|---|
| [Embedded Linux] Linux의 디바이스 트리 (0) | 2025.11.29 |
| [Embedded Linux] 프로세스간 통신 IPC - System V 기반의 IPC (0) | 2025.11.29 |
| [Embedded Linux] 프로세스간 통신 IPC - 파일 기반의 IPC (0) | 2025.11.29 |
| [Embedded Linux] Linux 시그널 (0) | 2025.11.29 |