리눅스 환경에서 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;
}





int *A;

A는 int형 포인터이다.


int B[5];

B는 크기가 5인 int형 배열이다.


int *C[5];

연산자우선순위에서 배열첨자인[]가 먼저이므로 C는 배열이다. 크기가 5인 배열.

즉 int형 포인터(int *)를 5개 담을 수 있는 배열이다.


int (*D)[5];

괄호 안을 먼저 처리하므로 D는 포인터이다.

크기가 5인 int형 배열(int [5])의 포인터를 할당할 수 있는 포인터인 것이다.


int *(*E)[5];

D와 같이 괄호 안을 먼저 처리하므로 E는 포인터이다.

이 것 역시 남은 부분을 보면 E가 무슨 포인터인지 알 수 있는데,

남은부분이 int *[5]이므로 E는 int *[5]의 포인터를 담을 수 있는 포인터가 된다.

이 int *[5]꼴은 C와 같은 형태이다. 이것을 참조해서 정리해 보면

E는'int형 포인터(int *)를 5개 담을 수 있는 배열'의 포인터을 할당할 수 있는 포인터이다.


int *F[5][4];

[]가 먼저 처리되므로 F는 이차원배열이다.

int * 형태의 데이터를 5x4로 담을 수 있는 크기가 20인 이차원배열. 


int (*G[5])[4];

역시 우선순위를 따져보면 G는 크기 5의 배열이다.

남은부분은 int (*)[4]로, 이는 D와 같은 꼴이다.

즉 G는, D와 같은 형태의 데이터를 5개 담을 수 있는 배열.


int *(*H[5])[4];

H 역시 배열이며, 남은부분은 int *(*)[4]이고,

이는 E와 같은 형태이다.

즉 H는, E와 같은 형태의 데이터를 5개 담을 수 있는 배열.

 



#include <stdio.h>

void main(void) {
 printf("%d",function(60));
}
int function(int n)
{
 int a=0, c=1;
 for(;c;) c<=100 && (a+=c++)<n-c || (c=0);
 return a;
}


냉무

+ Recent posts