Skip to content

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. 서버 실행 (터미널 1)
    ./svmsg_file_server
    
  2. 클라이언트 실행 (터미널 2)
    ./svmsg_file_client /etc/hostname
    
  3. Memory queue 확인
    ipcs -q
    
  4. Memory queue에서 삭제
    ipcrm -q <msqid>
    

코드 요약#

  1. 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);
    }
    
  2. 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

실습 순서#

  1. 공유 메모리 생성 및 쓰기
    ./svshm_create -c -p 4096
    
  2. 다른 프로세스에서 attach 및 읽기
    ./svshm_attach <PID>:0x0
    
  3. 상태 확인
    ./svshm_mon <PID>
    

코드#

  1. 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);
    }
    

  2. 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);
    }
    

  3. 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 값을 변경하여 실행 횟수 변경 가능