리눅스 환경에서 C언어를 사용하여 빠른 프로세스간 통신(IPC)를 구현해야 할 때에 공유메모리를 사용할 수 있다. 그러나 한 프로세스가 데이터를 읽는 도중에 다른 프로세스가 해당 공간에 대해 쓰기 작업을 한다면 데이터 부정합이 발생할 수 있다. POSIX 세마포어 중 이름 있는 세마포어(named semaphore)를 사용하여 공유자원에 대한 동시 접근 문제를 해결할 수 있다.


 공유메모리를 사용하기 위해 아래와 같은 함수들을 사용해야 한다.

shmget() 

임의의 키값과 메모리크기를 매개면수로 주어 공유메모리 생성을 커널에 요청하고, 정상적으로 생성되었을시 id를 받아온다. 


shmat()

공유메모리를 현재 프로세스에 붙인다(attach) 반환값으로는 공유메모리의 포인터가 나오고 이 포인터를 통해 메모리에 접근할 수 있다.


shmdt()

공유메모리를 현재 프로세스에서 분리한다(detach)


shmctl()

공유메모리에 대한 제어를 한다. 여기서는 삭제를 위해 사용.

생성된 공유메모리에 대한 정보는
ipcs -m
명령으로 확인할 수 있다.




POSIX named 세마포어는 아래 함수들을 통해 사용할 수 있다.


sem_open()

named 세마포어를 생성하고 세마포어 구조체 포인터(sem_t *)를 받아온다.


sem_wait()

공유자원에 접근하기 전에 세마포어를 잠근다. 만약 해당 자원에 접근할 수 있는 프로세스가 최대 1개이고, 잠겨있는 상태이면 block되어 풀릴 때까지 대기한다.


sem_trywait()

sem_wait()의 논블로킹 함수로, 자원이 잠겨 있어도 일단 -1을 반환하며 함수가 종료되고, errno를 통해 추가적인 처리를 할 수 있다. 


sem_post()

공유자원에 대한 접근이 끝났을 때 세마포어 잠금을 해제한다.


sem_close()

해당 프로세스에서 세마포어를 닫는다.


sem_unlink()

세마포어를 파괴한다.



예제코드는 아래와 같이 컴파일하였다.

clang provicer.c -o provider -std=gnu99 -lpthread


provider.c

#include <stdio.h>

#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

int main(void)
{
    int shmid;
    size_t shsize = 1024;
    const int key = 16000;
    char *shm;

    sem_t *mysem;
    sem_unlink("mysem");
    if((mysem = sem_open("mysem", O_CREAT, 0777, 1)) == NULL) {
        perror("Sem Open Error");
        exit(1);
    }

    if((shmid = shmget((size_t)key, shsize, IPC_CREAT|0666))<0) {
        perror("shmget");
        exit(1);
    }

    if((shm = (char*)shmat(shmid, NULL, 0)) == (char*)-1) {
        perror("shmat");
        exit(1);
    }

    for(int i=0; i<100; i++) {
        shm[i] = 0;
    }


    for(;;) {
        sem_wait(mysem);
        for(int i=0; i<100; i++) {
            shm[i] = (shm[i]+1)%10;
        }
        usleep(1000*1000);
        sem_post(mysem);
        usleep(1000*1000);
    }

    getchar();

    sem_close(mysem);

    sem_unlink("mysem");

    shmdt(shm);
    shmctl(shmid, IPC_RMID, 0);


    return 0;
}


consumer.c

#include <stdio.h>

#include <unistd.h>
#include <semaphore.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>

int main(void)
{
    int shmid;
    size_t shsize = 1024;
    const int key = 16000;
    char *shm;

    sem_t *mysem;
    if((mysem = sem_open("mysem", 0, 0777, 0)) == SEM_FAILED) {
        perror("Sem Open Error");
        exit(1);
    }

    if((shmid = shmget((key_t)key, shsize, IPC_CREAT|0666))<0) {
        perror("shmget");
        exit(1);
    }

    if((shm = (char*)shmat(shmid, NULL, 0)) == (char*)-1) {
        perror("shmat");
        exit(1);
    }

    for(;;) {
        if(sem_trywait(mysem) == 0) {
            for(int i=0; i<100; i++) {
                printf("%d", (shm[i]));
            }

            putchar('\n');
            sem_post(mysem);
        }
        else {
            switch(errno) {
                case EAGAIN:
                    printf("EAGAIN\n");
                    break;
                case EDEADLK:
                    printf("EDEADLK\n");
                    break;
                case EINTR:
                    printf("EINTR\n");
                    break;
            }
        }

        usleep(100*1000);
    }
    

    sem_close(mysem);
    shmdt(shm);

    return 0;
}



+ Recent posts