본문 바로가기
C_C++ 프로그래밍/뇌를 자극하는 TCP_IP

뇌자극 TCP/IP 11강 요약

by RoJae 2019. 6. 2.




IPC = Inter process Communications

네트워크 프로그래밍이 아닌 시스템 프로그래밍에 가까운 부분이다.


소켓 함수는 20개 내외로 제한적이기 때문에, 응용 프로그램 개발을 할 시에는 시스템 프로그래밍 기술이 매우 중요하다.

네트워크 프로그램도 시스템 위에서 작동한다.



프로세스간의 통신을 위해서 사용한다.
안정적인 작동이 가능한다.
최근에는 쓰레드 기반을 사용한다. (IPC가 너무 난해하기 때문에)



System V IPC 계열과 POSIX IPC가 존재한다.



PIPE
단방향의 데이터 통신용으로 사용한다.
=> 양방향을 사용하기 위해서는 2개를 생성

부모 프로세스와 자식 프로세스간의 통신을 위해서 사용한다.

이름이 없는 파이프와 이름이 있는 파이프로 나뉨

#include<unistd.h>
int pipe(int filedes[2]);

Named PIPE (이름이 있는 파이프)
마치 파일처럼 사용할 수 있다. (이름이 존재)


Unix Domain Socket

유닉스 영역의 통신에서만 사용한다.
유닉스 영역에 서버/클라이언트 환경을 만들 수 있다.
소켓과 거의 동일하다.
중대형 어플리케이션 만들기 위해서 용이하다. (mysql)

struct sockaddr_un sockaddr;
socket(AF_UNIX, SOCK_STREAM, 0);
sprintf(sockaddr.sun_path, "filename.sock");


공유 메모리

PIPE와 Unix Domain Socket는 메시지 기반의 IPC로 공유 메모리를 사용할 수 있다.

메모리 공간을 공유하며, 빠르고 효율적이다. 

하지만, 접근제어가 필요하게 된다 => Mutex 혹은 Semaphores가 필요함




공유 메모리 생성

shmget 함수로 공유 메모리 생성이 가능하다.

shmid = shmget((key_t) key, int size, int shmflg);

1. key : 공유 메모리는 커널에서 관리를 한다. 각 프로세스는 이를 이용해 접근할 공유 메모리를 식별한다.

2. size : 커널에게 요청할 메모리의 크기

3. shmflg
IPC_CREAT : 공유 메모리를 새로 생성한다.
IPC_EXCL    : IPC_CREAT와 함께 사용하며, key 값을 가지는 공유 메모리 영역이 존재하면 에러가 난다.



공유 메모리 첨부와 분리하기

shmdt(const void *shmaddr)

shmaddr : 분리할 공유 메모리를 가리키는 포인터로 shmat에서 사용했던 shmaddr를 그대로 사용한다.


shmat(int shmid, const void *shmaddr, int shmflg)

shmid : shmget 함수로 만든 공유 메모리 식별자

shmaddr : 공유 메모리 영역을 가리키는 주소이다. NULL인 경우 운영체제가 알아서 찾아서 반환해준다.

shmflg : 쓰기 전용을 지정할 수는 없고, 값을 지정하게 되면 읽기 전용이 된다.






공유 메모리 사용 예제

produce.c

#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char **argv){
        int shmid;

        int *cal_num;
        void *share_mem = NULL;

        // 0666 => 110110110 rwxrwxrwx
        shmid = shmget((key_t)1234, sizeof(int), 0666|IPC_CREAT);
        if(shmid == -1){
                perror("shmget fail");
                return 1;
        }
        // *share_mem --> attached mem
        share_mem = shmat(shmid, NULL, 0);
        if(share_mem == (void *)-1){
                perror("shmat fail");
                return 1;
        }

        cal_num = (int *)share_mem;
        while(1){
                *cal_num = *cal_num + 2;
                sleep(1);
        }
        return 1;
}


consumer.c

#include<sys/ipc.h>
#include<sys/shm.h>
#include<string.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char **argv){
        int shmid;
        int *cal_num;
        void *share_mem = NULL;

        shmid = shmget((key_t)1234, sizeof(int), 0);
        if(shmid == -1){
                perror("shmget fail");
                return 1;
        }
        // *share_mem --> attached mem
        share_mem = shmat(shmid, NULL, 0);
        if(share_mem == (void *) -1){
                perror("shmat fail");
                return 1;
        }

        cal_num = (int *) share_mem;

        while(1){
                sleep(1);
                printf("Read Data : %d\n", *cal_num);
        }
        return 1;
}





하지만, 공유 데이터에 서로 접근하여 사용하게 되기 때문에

접근 제어가 필요하게 된다.

=> critical section (임계영역)이 필요


세마포어 (Semaphores) 생성

int semget(key_t key, int nsems, int semflg);

key : 공유 메모리와 같이 식별하여 생성할 수 있다.

nsems : 세마포어는 배열로 만들어진다. 이때 그 크기를 지정한다.

semflg : 세마포어의 생성 방식을 결정한다.
IPC_CREAT : 커널에 key 값의 세마포어가 없다면 새로 생성한다.
IPC_EXCL : IPC_CREAT와 함께 사용한다. key가 이미 존재하면 에러 발생


세마포어 임계영역 관리

int semop(int semid, struct sembuf *sops, unsigned nsops)

semid : semget 함수로 생성한 세마포어 식별자이다.

sops : 세마포어 제어를 위해 사용하는 세마포어 구조체이다.

nsops : 세마포어 집합의 개수이다. 하나만 접근이 가능할려면 1을 사용



간단한 세마포어 예제

producer.c

#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<string.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

union semun{
        int val;
};

int main(int argc, char **argv){
        int shmid;
        int semid;

        int *cal_num;
        void *share_mem = NULL;
        union semun sem_union;

        struct sembuf semopen = {0,-1,SEM_UNDO};
        struct sembuf semclose = {0,1,SEM_UNDO};

        // Get share memory
        shmid = shmget((key_t)1234, sizeof(int), 0666|IPC_CREAT);
        if(shmid == -1){
                return 1;
        }

        // let's make semaphores
        semid = semget((key_t)3477, 1, IPC_CREAT|0666);
        if(semid == -1){
                return 1;
        }

        // connect to share mem
        share_mem = shmat(shmid, NULL, 0);
        if(share_mem == (void *) -1){
                return 1;
        }

        // *cal_num --> share_memory
        cal_num = (int *)share_mem;


        sem_union.val = 1;
        // init semaphores
        if(-1 == semctl(semid, 0, SETVAL, sem_union)){
                return 1;
        }

        while(1){
                int local_var = 0;
                // critical section
                if(semop(semid, &semopen, 1) == -1){
                        return 1;
                }
                local_var = *cal_num + 1;
                sleep(1);
                *cal_num = local_var;
                printf("%d\n", *cal_num);
                semop(semid, &semclose, 1);
                // end
        }
        return 1;
}


consumer.c

#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/shm.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>

int main(int argc, char **argv){
        int shmid;
        int semid;

        int *cal_num;
        void *share_mem = NULL;

        // semaphores open ans close data
        struct sembuf semopen = {0, -1, SEM_UNDO};
        struct sembuf semclose = {0, 1, SEM_UNDO};

        // connect share mem
        shmid = shmget((key_t)1234, sizeof(int), 0666);
        if(shmid == -1){
                perror("shmget fail");
                return 1;
        }

        // acees key = 3477, access = 0, auth 666
        semid = semget((key_t)3477, 0, 0666);
        if(semid == -1)
        {
                perror("semget fail");
                return 1;
        }

        // connet share mem
        share_mem = shmat(shmid, NULL, 0);
        if(share_mem == (void *)-1){
                perror("shmat fail");
                return 1;
        }

        // cal_num ==> share memory
        cal_num = (int *) share_mem;

        while(1){
                // local int
                int local_val = 0;
                // critical section open
                if(semop(semid, &semopen, 1) == -1){
                        perror("semop error :");
                }
                local_val = *cal_num+1;
               sleep(2);
                *cal_num = local_val;
                printf("count %d\n", *cal_num);
                semop(semid, &semclose, 1);
                // close
        }
        return 1;
}



시그널 함수

시그널 함수를 사용하여 프로세스의 종료를 무시할 수 있는 등

다양한 처리가 가능하지만, 마지막 시그널만 기억한다. (덮어쓰기)

=> 리얼타임 시그널을 사용하면 시그널의 장점을 유지하면서 대기열까지 제공한다.

※ 본 글은 개인 포트폴리오 혹은 공부용으로 사용하기 때문에, 무단 복사 유포는 금지하지만, 개인 공부 용도로는 얼마든지 사용하셔도 좋습니다



댓글