프로세스 간 통신 (Inter-Process Communication, IPC)

date
Apr 17, 2024
slug
ipc
status
Published
tags
Computer Science
summary
type
Post
쓰레드는 프로세스 안에서 데이터가 공유 되었지만, 프로세스들이 정보를 교환하는 방법은 fork , exec 를 통해 열린 파일들을 전달하거나 파일 시스템을 거쳐야 했다.
APUE에서는 전통적인 프로세스 간 통신 방법인 파이프와 FIFO, 메시지 대기열, 세마포어, 공유 메모리를 설명한다.

파이프 (Pipe)

시험 범위 X

XSI IPC

메시지 대기열, 세마포어, 공유 메모리를 이용하는 형태의 IPC들을 XSI IPC라고 부르며, 세 종류의 IPC에는 비슷한 점이 존재한다.

식별자와 키를 사용

IPC 구조체 또는 IPC 객체를 생성할 때 정수 식별자를 통해서 참조한다. → 메시지를 대기열에 보내거나 대기열에서 메시지를 가져올 때 대기열의 식별자만 알면 된다.
그러나 식별자는 IPC 객체의 내부적인 이름이며, 프로세스들이 동일한 IPC 객체에 접근하기 위해서 각 IPC 객체마다 외부 이름으로 쓰이는 를 부여할 수 있다.
또한 FLAG 값을 통해 키를 어떻게 관리할 것인지 정의할 수 있다.

IPC_PRIVATE

IPC 객체가 항상 새로 생성되게 하는 상수 값이다. 이 기법의 단점은 서버가 정수 식별자파일에 기록하고 이후 클라이언트가 조회하기 위해서 파일 시스템 연산들이 필요하다고 한다. → 일반적으로 부모-자식 프로세스 관계에서 사용된다.

IPC_CREAT

IPC 객체를 생성할 때 사용하는 플래그이며, IPC 객체가 존재하지 않으면 새로 생성, 이미 존재하는 경우 해당 IPC 객체의 식별자를 반환한다.

IPC_EXCL

새로운 IPC 객체를 생성할 때 기존 객체가 참조되는 일을 없게 하려면 FLAG 값을 IPC_CREATIPC_EXCL의 조합으로 사용하면 된다.
만약 IPC 객체가 이미 존재하는 경우 EEXIST(-1) 오류가 반환 된다.

구조체

IPC 객체를 생성하면 접근 권한 구조체인 ipc_perm 가 정의된다.
IPC 종류에 따라 다른 구조체를 제공하며, 해당 구조체 내부에 ipc_perm 구조체가 공통으로 사용된다. → 메세지 큐 구조체인 msqid_ds, 세마포어 구조체인 semid_ds, 공유 메모리 구조체인 shmid_ds 가 있다.

장점과 단점

프로세스 간 효율적인 통신과 동기화를 위해서 XSI IPC는 많은 이점이 있지만, 단점도 존재한다.
IPC 객체는 시스템 전역 객체이며 참조 횟수가 관리되지 않는다는 것이다. 메시지를 넣고 나서 종료했을 때 msgrcvmsgctl을 호출하거나 ipcrm 명령을 실행하는 등 명시적으로 읽거나 삭제하기 전까지, 시스템이 재시동되기 전까지는 시스템에 그대로 유지된다.

메시지 대기열

시험 범위 X

세마포어 (Semaphore)

  1. 자원을 제어하는 세마포어의 값을 점검한다.
  1. 값이 양수라면 프로세스는 자원을 사용할 수 있으며, 사용한다면 세마포어 값을 1 감소한다. → 자원을 사용했음을 뜻한다.
  1. 세마포어 값이 0이면 0보다 커질 때까지 프로세스는 sleep 상태를 유지하며, 깨어나면 다시 1단계로 돌아간다.

예제 코드

공유 메모리 (Shared Memory)

notion image
둘 이상의 프로세스가 메모리의 한 영역을 공유하게 하는 기능
클라이언트와 서버 사이에서 자료를 복사하거나 넘기는 작업이 없기 때문에 가장 빠른 형태의 IPC
공유 메모리를 사용할 때 신경 써야 하는 부분은 여러 프로세스들의 접근을 동기화하는 것이다. → 익명의 메모리 구역을 공유하여 사용하기 때문이다. - 쓰레드에서 lock, unlock을 사용하는 이유와 같다. → 이때 레코드 잠금, 뮤텍스를 사용할 수도 있으나 세마포어를 흔히 사용한다.
공유 메모리 식별자를 얻기 위해 shmget 함수를 사용한다. 반환 값을 통해 이미 생성되어 있는지, 생성해야 하는지 등을 판단할 수 있다.
다양한 공유 메모리 연산을 수행하는 범용 함수이며, cmd 인자에 들어오는 값에 따라 다양한 동작을 수행한다.

IPC_STAT

이 구역에 대한 shmid_ds 구조체를 buf가 가리키는 곳에 저장한다.

IPC_SET

buf가 가리키는 구조체의 shm_perm.uid, shm_perm.gid, shm_perm.mode 필드를 구역의 shmid_ds 구조체에 복사한다. superuser 특권을 가진 프로세스만 실행 가능하다고 한다.

IPC_RMID

공유 메모리 구역을 제거하는 기능이며, 제거된 이후에는 shmat로 부착할 수 없다.
공유 메모리 구역의 식별자를 얻은 후에는 shmat 함수를 호출해서 그 구역을 프로세스의 주소 공간에 부착한다.
공유 메모리 구역을 다 사용했다면 shmdt 를 호출하여 구역에서 떼어내야 한다. → 그 구역의 식별자, 관련 자료구조를 시스템에서 제거한다는 뜻은 아니라고 한다. shmctlIPC_RMID 명령을 사용해야 실제로 제거된다. → 더 이상 사용하지 않겠다는 것이지 제거하는 개념이 아님

예제 코드

POSIX 세마포어

XSI 세마포어의 단점을 해결하기 위해 POSIX 세마포어 인터페이스가 고안되었다. XSI 세마포어보다 높은 성능을 내도록 구현할 수 있고, 사용 방법도 간단하고 익숙하며, 우아하게 동작한다고 한다. → XSI 세마포어가 제거된 경우 errno를 EIDRM으로 설정하고 오류를 돌려줬지만, POSIX 세마포어는 세마포어의 마지막 참조를 해제할 때까지 정상적으로 동작 된다고 한다.
명명된 세마포어를 새로 생성하거나 기존의 것을 참조할 때 sem_open을 사용한다.
oflag는 공유 메모리의 flag와 같은 역할이며, 예를 들어 O_CREAT | O_EXCEL 플래그를 설정하면 항상 새 세마포어가 생성된다.
세마포어를 다 사용한 이후에는 sem_close를 호출해서 연관된 모든 자원을 해제한다. 그러나 shmdt에서 설명했듯이 sem_close 도 호출한다고 세마포어의 값에 영향을 주지는 않는다.
명명된 세마포어를 파괴하고 싶다면 sem_unlink 를 사용한다. 그러나 세마포어를 참조하는 프로세스가 남아있지 않을 때 제거가 되며, 그런 프로세스가 남아 있다면 참조되고 있는 모든 세마포어가 sem_close 등에 의해 종료될 때까지 연기된다.
세마포어의 값을 감소할 때는 sem_wait 또는 sem_trywait을 사용한다.
만약 세마포어 값이 0인 상태에서 sem_wait을 호출하면 호출이 차단된다. → 세마포어 값을 감소하는 동작을 성공하거나, signal(신호)에 의해 가로채여야 호출이 반환된다.
차단을 피하고 싶다면 sem_trywait을 사용하면 된다. - errno를 EAGAIN으로 설정한 후 차단 없이 -1을 돌려준다.
세마포어 값을 감소하면서 호출이 차단될 시간을 지정할 수도 있다. - sem_timedwait 사용 → 어떤 시간을 지정해도 세마포어 값이 감소되는 동작은 무조건 수행되며, 감소하지 못한 채로 시간이 만료되면 errno를 ETIMEDOUT으로 설정하고 -1을 돌려준다.
차단된 프로세스에 sem_post를 호출하면 깨어나며, sem_post에 의해 증가된 세마포어 값이 sem_wait 또는 sem_timedwait에 의해 감소된다.
무명 세마포어를 생성할 때 sem_init 를 사용한다. - 하나의 프로세스에서 POSIX 세마포어를 사용할 때 무명 세마포어가 더 사용하기 쉽다고 한다. 무명과 명명된 세마포어의 차이는 세마포어를 생성하고 파괴하는 방법의 차이이다.
sem_open 은 반환 값으로 세마포어의 포인터를 돌려줬지만, sem_init는 첫 번째 인자인 sem초기화된 세마포어를 저장한다.
무명 세마포어를 다 사용한 후에는 sem_destroy 를 호출해서 폐기한다. → 호출한 이후에는 sem이 가리키는 세마포어는 사용할 수 없다. sem_init를 다시 호출해서 세마포어를 재초기화해야 한다.
세마포어의 현재 값을 조회할 때 사용하는 함수이다.
atomic하게 동작하지 않기 때문에 함수로 세마포어 값을 읽은 시점값을 사용하려는 시점 사이에 세마포어 값이 변할 수도 있음을 주의해야 한다. - sem_getvalue 는 디버깅용으로나 유용하다고 한다.
notion image

예제 코드

쓰레드와 세마포어를 사용하는 예제

notion image
notion image
 
세마포어를 사용한 예제는 여러 쓰레드가 동시에 접근하지 않기 때문에 하나씩 출력되고 있으며, count 결과도 정상적으로 출력된다.
세마포어를 사용하지 않은 예제는 두 쓰레드가 같은 결과를 출력하는데, 이는 count 값이 증가하기 전에 가져온 것임을 확인할 수 있다.
sem_post를 4번 호출하여 세마포어 값은 4로 시작되었으며, wait_fun 쓰레드에서는 sem_wait 을 호출할 때마다 1씩 줄어들고, 카운트가 0일 때 차단된다.
notion image

공유 메모리와 세마포어를 사용하는 예제

세마포어를 2개 사용하여 child process 입장에서는 parent process에서 semP 의 잠금을 풀을 때까지 대기하며, parent process 입장에서는 child process에서 semC의 잠금을 풀을 때까지 대기하기 때문에 서로 번갈아 가면서 출력 된다.
notion image

참고 자료


© hyuunnn 2024