거의 한 달만에 쓰는 CS 글이네요!
이번 주 CS스터디 주제는프로세스
였습니다.
공룡책을 열심히 읽어보다가 IPC / RPC 부분이 흥미로워서 발표를 준비했습니다.
쉽게 표현하면
프로그램
은 코드 덩어리,프로세스
는 실행 중인 프로그램이라고 표현할 수 있습니다. 프로세스는 OS로부터 독립적인 자원을 할당받게 되고, 외부의 접근을 OS가 차단해줍니다. 하지만 프로세스끼리 통신을 하거나 자원을 공유해야 하는 상황에는 어떻게 할까요?
프로세스 간의 통신 방법은 대략 2가지로 나눌 수 있습니다.
1. 같은 운영체제에 있는 경우 (IPC)
2. 서로 다른 운영체제에 있는 경우
같은 운영체제에 있는 경우 Pipe, Shared Memory, Message queue, Socket(UDS) 방식을 사용할 수 있습니다.
서로 다른 운영체제의 경우 Socket, RPC와 같은 방식을 사용해야 합니다.
IPC의 방식의 경우 Message Passing과 Shared Memory 두 가지로 나뉘게 됩니다.
Message Passing는 송신자와 수신자가 있는 방식을 의미합니다. 시스템콜을 사용하므로 기본적으로 동기화를 OS가 처리해준다는 장점이 있고, 데이터는 커널 메모리에 저장됩니다. 다만 시스템콜을 사용하기에 좀 느리다는 단점도 존재합니다.
우선 Pipe 방식에 대해 알아보겠습니다.
Pipe는 익명 Pipe와 Named Pipe 두 가지 방식이 있습니다.
익명 Pipe는 기본적으로 단방향 통신 채널이고, 양방향 통신을 위해선 파이프를 두 개 뚫어야 합니다. 또한 데이터를 따로 저장하지 않고 커널의 메모리 버퍼를 사용해 데이터를 전송합니다. fork()로 생성된 부모-자식 관계인 프로세스끼리의 통신에 많이 사용됩니다.
Named Pipe의 경우 양방향 통신을 지원합니다. 하지만 반이중 전송만 허용되기 때문에 일반적으로 양방향 통신을 할 경우 두 개의 파이프를 이용합니다. mkfifo()로 파일을 생성하여 통신 채널을 만들고 여러 프로세스들이 통신하게 합니다. 하지만 익명 파이프와 마찬가지로 데이터를 파일에 저장하지 않고 커널의 메모리 버퍼를 사용합니다.
아까 프로세스는 독립적인 공간을 가진다고 했었죠!
Shared Memory 방식은 다른 프로세스 간의 공유하는 메모리 공간을 마련하여 데이터를 읽고 쓰는 방식입니다. 시스템콜을 사용하지 않으므로 가장 빠른 방식입니다. 다만 같은 메모리 공간을 사용하게 될 경우 동기화 문제가 발생하고 mutex, semaphore 등과 같은 방식으로 해결해야 합니다.
RPC의 경우 최근 MSA 방식이 유행하면서 많이 쓰이는 추세입니다.
쉽게 표현하면 서로 다른 프로세스 간에 함수 호출을 가능하게 해주는 기술입니다. 언어 독립적이어서 서로 다른 프로그래밍 언어로 구현된 서버-클라이언트 관계에서도 사용 가능합니다. 또한 프로토콜 버퍼를 사용해서 바이너리로 인코딩하여 작은 크기로 통신할 수 있게 해줍니다.
RPC에서 중요한 개념은 Stub인데요, 클라이언트가 서버의 함수를 호출할 경우 이를 서버에 전송하고 서버가 결과를 반환해주면 클라이언트에게 전달하는 역할을 하게 됩니다. 따라서 서버와 클라이언트는 각자 다른 주소 공간에서 실행되기 때문에 공유하는 메모리 공간이 없습니다. 이를 해결하기 위해 Stub이 클라이언트와 서버 사이에서 함수 호출을 네트워크 요청으로 변환해줍니다.
좌측에 보이는 사진은 .proto 파일의 예시입니다. 프로토콜 버퍼에서 메세지 구조를 어떤 방식으로
Request / Response
를 할지 정의하는 파일입니다. 데이터를 어떻게 주고 받을지 계약한다고 이해하시면 됩니다. 또한 위의service
에 어떤 함수를 사용할지 또한 명시됩니다.
프로토콜 버퍼는 RPC를 이용한 통신에서 전송되는 데이터 양식인데요, 데이터를 직렬화하여 바이너리로 인코딩합니다. 일반적으로 REST api에 사용되는 JSON.XML과 같은 텍스트 기반 포멧보다 훨씬 효율적이고 작은 크기로 데이터를 전송하게 해줍니다.
gRPC에 대한 내용은 따로 다루지 못했는데 한번 공부해보시고 면접에서 많이 물어보는 질문에 답변을 준비해보는 것도 좋을 거 같습니다!