IPC 실습 가이드 (System V 기반)#
본 문서는 TLPI 예제 코드를 기반으로 System V IPC 메커니즘인 Message Queue, Semaphore, Shared Memory를 실습할 수 있도록 안내합니다. 각 섹션에는 실습 순서 및 코드가 포함되어 있습니다.
1. Message Queue 예제#
예제 파일#
svmsg_file_server.c
svmsg_file_client.c
실습 준비#
gcc -o svmsg_file_server svmsg_file_server.c
gcc -o svmsg_file_client svmsg_file_client.c
실습 순서#
- 서버 실행 (터미널 1)
./svmsg_file_server
- 클라이언트 실행 (터미널 2)
./svmsg_file_client /etc/hostname
- Memory queue 확인
ipcs -q
- Memory queue에서 삭제
ipcrm -q <msqid>
코드 요약#
- svmsg_file_client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/msg.h> #include <sys/types.h> #include <errno.h> #include <sys/stat.h> #define SERVER_KEY 0x1234 #define RESP_MT_FAILURE 1 #define RESP_MT_DATA 2 #define PATHNAME_SIZE 512 #define RESP_DATA_SIZE 4096 struct requestMsg { long mtype; int clientId; char pathname[PATHNAME_SIZE]; }; struct responseMsg { long mtype; char data[RESP_DATA_SIZE]; }; #define REQ_MSG_SIZE (sizeof(struct requestMsg) - sizeof(long)) #define RESP_MSG_SIZE (sizeof(struct responseMsg) - sizeof(long)) void errExit(const char *msg) { perror(msg); exit(EXIT_FAILURE); } void usageErr(const char *fmt, const char *prog) { fprintf(stderr, fmt, prog); exit(EXIT_FAILURE); } void cmdLineErr(const char *fmt, long val) { fprintf(stderr, fmt, val); exit(EXIT_FAILURE); } static int clientId; static void removeQueue(void) { if (msgctl(clientId, IPC_RMID, NULL) == -1) errExit("msgctl"); } int main(int argc, char *argv[]) { struct requestMsg req; struct responseMsg resp; int serverId, numMsgs; ssize_t msgLen, totBytes; if (argc != 2 || strcmp(argv[1], "--help") == 0) usageErr("%s pathname\n", argv[0]); if (strlen(argv[1]) > sizeof(req.pathname) - 1) cmdLineErr("pathname too long (max: %ld bytes)\n", (long) sizeof(req.pathname) - 1); serverId = msgget(SERVER_KEY, S_IWUSR); if (serverId == -1) errExit("msgget - server message queue"); clientId = msgget(IPC_PRIVATE, S_IRUSR | S_IWUSR | S_IWGRP); if (clientId == -1) errExit("msgget - client message queue"); if (atexit(removeQueue) != 0) errExit("atexit"); req.mtype = 1; req.clientId = clientId; strncpy(req.pathname, argv[1], sizeof(req.pathname) - 1); req.pathname[sizeof(req.pathname) - 1] = '\0'; if (msgsnd(serverId, &req, REQ_MSG_SIZE, 0) == -1) errExit("msgsnd"); msgLen = msgrcv(clientId, &resp, RESP_MSG_SIZE, 0, 0); if (msgLen == -1) errExit("msgrcv"); if (resp.mtype == RESP_MT_FAILURE) { printf("%s\n", resp.data); exit(EXIT_FAILURE); } totBytes = msgLen; for (numMsgs = 1; resp.mtype == RESP_MT_DATA; numMsgs++) { msgLen = msgrcv(clientId, &resp, RESP_MSG_SIZE, 0, 0); if (msgLen == -1) errExit("msgrcv"); totBytes += msgLen; } printf("Received %ld bytes (%d messages)\n", (long) totBytes, numMsgs); exit(EXIT_SUCCESS); }
- svmsg_file_server.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <errno.h> #include <sys/msg.h> #include <sys/types.h> #include <sys/wait.h> #define SERVER_KEY 0x1234 #define RESP_MT_FAILURE 1 #define RESP_MT_DATA 2 #define RESP_MT_END 3 #define PATHNAME_SIZE 512 #define RESP_DATA_SIZE 4096 struct requestMsg { long mtype; int clientId; char pathname[PATHNAME_SIZE]; }; struct responseMsg { long mtype; char data[RESP_DATA_SIZE]; }; #define REQ_MSG_SIZE (sizeof(struct requestMsg) - sizeof(long)) #define RESP_MSG_SIZE (sizeof(struct responseMsg) - sizeof(long)) void errExit(const char *msg) { perror(msg); exit(EXIT_FAILURE); } void errMsg(const char *msg) { perror(msg); } static void grimReaper(int sig) { int savedErrno = errno; while (waitpid(-1, NULL, WNOHANG) > 0) continue; errno = savedErrno; } static void serveRequest(const struct requestMsg *req) { int fd; ssize_t numRead; struct responseMsg resp; fd = open(req->pathname, O_RDONLY); if (fd == -1) { resp.mtype = RESP_MT_FAILURE; snprintf(resp.data, sizeof(resp.data), "%s", "Couldn't open"); msgsnd(req->clientId, &resp, strlen(resp.data) + 1, 0); exit(EXIT_FAILURE); } resp.mtype = RESP_MT_DATA; while ((numRead = read(fd, resp.data, RESP_MSG_SIZE)) > 0) if (msgsnd(req->clientId, &resp, numRead, 0) == -1) break; resp.mtype = RESP_MT_END; msgsnd(req->clientId, &resp, 0, 0); } int main(int argc, char *argv[]) { struct requestMsg req; pid_t pid; ssize_t msgLen; int serverId; struct sigaction sa; serverId = msgget(SERVER_KEY, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR | S_IWGRP); if (serverId == -1) errExit("msgget"); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = grimReaper; if (sigaction(SIGCHLD, &sa, NULL) == -1) errExit("sigaction"); for (;;) { msgLen = msgrcv(serverId, &req, REQ_MSG_SIZE, 0, 0); if (msgLen == -1) { if (errno == EINTR) continue; errMsg("msgrcv"); break; } pid = fork(); if (pid == -1) { errMsg("fork"); break; } if (pid == 0) { serveRequest(&req); _exit(EXIT_SUCCESS); } } if (msgctl(serverId, IPC_RMID, NULL) == -1) errExit("msgctl"); exit(EXIT_SUCCESS); }
2. Semaphore 예제#
예제 파일#
binary_sems.c
실습 준비#
gcc -o binary_sems binary_sems.c
실습#
세마포어 동작
./binary_sems
코드#
- binary_sems.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> // === Binary Semaphore API === union semun { int val; struct semid_ds *buf; unsigned short *array; }; typedef enum { FALSE = 0, TRUE = 1 } Boolean; Boolean bsUseSemUndo = FALSE; Boolean bsRetryOnEintr = TRUE; int initSemAvailable(int semId, int semNum) { union semun arg; arg.val = 1; return semctl(semId, semNum, SETVAL, arg); } int initSemInUse(int semId, int semNum) { union semun arg; arg.val = 0; return semctl(semId, semNum, SETVAL, arg); } int reserveSem(int semId, int semNum) { struct sembuf sops; sops.sem_num = semNum; sops.sem_op = -1; sops.sem_flg = bsUseSemUndo ? SEM_UNDO : 0; while (semop(semId, &sops, 1) == -1) if (errno != EINTR || !bsRetryOnEintr) return -1; return 0; } int releaseSem(int semId, int semNum) { struct sembuf sops; sops.sem_num = semNum; sops.sem_op = 1; sops.sem_flg = bsUseSemUndo ? SEM_UNDO : 0; return semop(semId, &sops, 1); } #define NUM_ITER 5 int semId; void* threadFunc(void* arg) { int id = *(int*)arg; int my_sem = id; int other_sem = 1 - id; for (int i = 0; i < NUM_ITER; i++) { reserveSem(semId, my_sem); printf("Thread %d entered critical section [%d]\n", id, i + 1); system("ipcs -s | grep -w $(whoami)"); sleep(1); releaseSem(semId, other_sem); usleep(100000); } return NULL; } int main() { pthread_t t1, t2; int t1_id = 0, t2_id = 1; semId = semget(IPC_PRIVATE, 2, IPC_CREAT | 0600); if (semId == -1) { perror("semget"); exit(EXIT_FAILURE); } if (initSemAvailable(semId, 0) == -1 || initSemInUse(semId, 1) == -1) { perror("initSem"); exit(EXIT_FAILURE); } printf(" Semaphore ID = %d (check with: ipcs -s)\n", semId); printf(" Starting test...\n"); pthread_create(&t1, NULL, threadFunc, &t1_id); pthread_create(&t2, NULL, threadFunc, &t2_id); pthread_join(t1, NULL); pthread_join(t2, NULL); semctl(semId, 0, IPC_RMID); printf(" Done. Semaphore removed.\n"); return 0; }
3. Shared Memory 예제#
예제 파일#
svshm_create.c
svshm_attach.c
svshm_mon.c
실습 준비#
gcc -o svshm_create svshm_create.c
gcc -o svshm_attach svshm_attach.c
gcc -o svshm_mon svshm_mon.c
실습 순서#
- 공유 메모리 생성 및 쓰기
./svshm_create -c -p 4096
- 다른 프로세스에서 attach 및 읽기
./svshm_attach <PID>:0x0
- 상태 확인
./svshm_mon <PID>
코드#
-
svshm_create.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/stat.h> #include <errno.h> /* === 유틸리티 함수 대체 === */ void errExit(const char *msg) { perror(msg); exit(EXIT_FAILURE); } void cmdLineErr(const char *msg) { fprintf(stderr, "%s", msg); exit(EXIT_FAILURE); } long getLong(const char *str, int base, const char *name) { char *endptr; long val = strtol(str, &endptr, base); if (*endptr != '\0') cmdLineErr("Invalid numeric input\n"); return val; } int getInt(const char *str, int base, const char *name) { return (int)getLong(str, base, name); } /* === 사용법 출력 === */ void usageError(const char *progName, const char *msg) { if (msg != NULL) fprintf(stderr, "%s", msg); fprintf(stderr, "Usage: %s [-cx] {-f pathname | -k key | -p} seg-size [octal-perms]\n", progName); fprintf(stderr, " -c Use IPC_CREAT flag\n"); fprintf(stderr, " -x Use IPC_EXCL flag\n"); fprintf(stderr, " -f pathname Generate key using ftok()\n"); fprintf(stderr, " -k key Use 'key' as key\n"); fprintf(stderr, " -p Use IPC_PRIVATE key\n"); exit(EXIT_FAILURE); } /* === 메인 시작 === */ int main(int argc, char *argv[]) { int numKeyFlags = 0; int flags = 0; long lkey; key_t key; int opt; while ((opt = getopt(argc, argv, "cf:k:px")) != -1) { switch (opt) { case 'c': flags |= IPC_CREAT; break; case 'f': key = ftok(optarg, 1); if (key == -1) errExit("ftok"); numKeyFlags++; break; case 'k': if (sscanf(optarg, "%li", &lkey) != 1) cmdLineErr("-k option requires numeric argument\n"); key = lkey; numKeyFlags++; break; case 'p': key = IPC_PRIVATE; numKeyFlags++; break; case 'x': flags |= IPC_EXCL; break; default: usageError(argv[0], NULL); } } if (numKeyFlags != 1) usageError(argv[0], "Exactly one of -f, -k, or -p must be supplied\n"); if (optind >= argc) usageError(argv[0], "Segment size must be specified\n"); int segSize = getLong(argv[optind], 0, "seg-size"); unsigned int perms = (argc <= optind + 1) ? (S_IRUSR | S_IWUSR) : getInt(argv[optind + 1], 8, "octal-perms"); int shmid = shmget(key, segSize, flags | perms); if (shmid == -1) errExit("shmget"); printf("Created shared memory segment: shmid = %d\n", shmid); exit(EXIT_SUCCESS); }
-
svshm_attach.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/shm.h> #include <sys/ipc.h> #include <sys/param.h> // SHMLBA void errExit(const char *fmt, const char *arg) { fprintf(stderr, "ERROR: "); if (arg != NULL) fprintf(stderr, fmt, arg); else perror(fmt); perror(" "); exit(EXIT_FAILURE); } void usageError(char *progName) { fprintf(stderr, "Usage: %s [shmid:address[rR]]...\n", progName); fprintf(stderr, " r=SHM_RND; R=SHM_RDONLY\n"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { printf("SHMLBA = %ld (%#lx), PID = %ld\n", (long) SHMLBA, (unsigned long) SHMLBA, (long) getpid()); for (int j = 1; j < argc; j++) { char *p; int shmid = strtol(argv[j], &p, 0); if (*p != ':') usageError(argv[0]); void *addr = (void *) strtol(p + 1, NULL, 0); int flags = 0; if (strchr(p + 1, 'r') != NULL) flags |= SHM_RND; if (strchr(p + 1, 'R') != NULL) flags |= SHM_RDONLY; void *retAddr = shmat(shmid, addr, flags); if (retAddr == (void *) -1) errExit("shmat: %s", argv[j]); printf("%d: %s ==> attached at %p\n", j, argv[j], retAddr); } printf("Sleeping 5 seconds...\n"); sleep(5); exit(EXIT_SUCCESS); }
-
svshm_mon.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/shm.h> #include <time.h> #include <unistd.h> void errExit(const char *msg) { perror(msg); exit(EXIT_FAILURE); } void usageError(const char *progName) { fprintf(stderr, "Usage: %s shmid\n", progName); exit(EXIT_FAILURE); } long getLong(const char *str) { char *endptr; long val = strtol(str, &endptr, 0); if (*endptr != '\0') { fprintf(stderr, "Invalid numeric input: %s\n", str); exit(EXIT_FAILURE); } return val; } void printShmDS(const struct shmid_ds *ds) { printf("Size: %ld\n", (long) ds->shm_segsz); printf("# of attached processes: %ld\n", (long) ds->shm_nattch); printf("Mode: %lo", (unsigned long) ds->shm_perm.mode); #ifdef SHM_DEST printf("%s", (ds->shm_perm.mode & SHM_DEST) ? " [DEST]" : ""); #endif #ifdef SHM_LOCKED printf("%s", (ds->shm_perm.mode & SHM_LOCKED) ? " [LOCKED]" : ""); #endif printf("\n"); printf("Last shmat(): %s", ctime(&ds->shm_atime)); printf("Last shmdt(): %s", ctime(&ds->shm_dtime)); printf("Last change: %s", ctime(&ds->shm_ctime)); printf("Creator PID: %ld\n", (long) ds->shm_cpid); printf("PID of last attach/detach: %ld\n", (long) ds->shm_lpid); } int main(int argc, char *argv[]) { if (argc != 2 || strcmp(argv[1], "--help") == 0) usageError(argv[0]); int shmid = (int) getLong(argv[1]); struct shmid_ds ds; if (shmctl(shmid, IPC_STAT, &ds) == -1) errExit("shmctl"); printShmDS(&ds); return 0; }
리소스 정리#
- 메시지 큐 목록 확인:
ipcs -q
- 세마포어 목록 확인:
ipcs -s
- 공유 메모리 목록 확인:
ipcs -m
- 제거 명령:
ipcrm -q [msqid] ipcrm -s [semid] ipcrm -m [shmid]
참고 사항#
- message queue는 실행후 꼭 삭제하기기
- semaphore 예제는
NUM_ITER
값을 변경하여 실행 횟수 변경 가능