컴퓨터/운영체제

[OS] Inter Process Communication (IPC)

xeskin 2020. 4. 15. 17:16
반응형

유저가 실행시키는 프로세스들은 OS에서 동시적으로 돌아간다. 우리가 돌리는 프로세스들이 항상 독립적으로 돌아가는 것보다는 이들이 공유할 정보가 있다면 공유하고 협력한다면 효율을 더 높일 수 있는데, 이를 프로세스간의 커뮤니케이션을 통해 해결할 수 있다. 이를 'Inter Process Communication' (IPC) 라고 부른다. 프로세스가 협력을 하면 몇가지 장점이 있는데, 이는 다음과 같다.

 

프로세스 협력의 이점

Information sharing

- 몇몇 응용 프로그램들은 비슷한 정보를 갖고 돌아간다. (복사하고 붙여넣는 경우를 생각해보면 된다.)

Computation speedup

- 어떤 태스크를 빠르게 수행하고 싶을 때, 이걸 여러 서브 태스로 나눠서 병렬적으로 실행할 수 있다.

Moularity

- Microkernel을 생각해보면 이해하기 쉽다. 프로그램의 각 기능들을 나눠서 설계할 수 있다.

 

그럼 프로세스 간의 커뮤니케이션은 어떻게 할 수 있을까? 두가지 방법이 있다. 하나는 'Shared memory', 다른 하나는 'Message passing'이다.

먼저 'shared memory'는 두 사람이 화이트 보드를 공유하는 것을 생각해보면 이해하기 쉬운데, 프로세스1을 대표하는 사람과 프로세스2를 대표하는 사람 두명이 화이트보드라는 메모리 공간을 공유하고, 서로가 쓰는 것을 보면서 커뮤니케이션 하는 것이다. 그런데 서로 다른 프로세스간의 메모리 접근은 운영체제가 금지하는 것으로 알고 있다. 이때 운영체제가 메모리를 공유할 수 있도록 도와준다. 일단 메모리 공간이 공유되면 운영체제의 다른 커널의 도움없이, 프로세스가 메모리를 읽고 쓸 수 있고 메모리가 업데이트 되는 것은 모든 프로세스가 바로 사용 가능하다. 이 방법에서 중요한 것은 OS의 도움을 단 한번 받는다는 것이다.

이는 producer-consumer model로 설계할 수 있는데 프로세서가 공유하는 메모리에 버퍼를 준비하면, producer는 버퍼에 데이터를 저장하고, consumer는 버퍼를 읽어가는 식이다. 이때 producer와 consumer는 동기화가 되어야한다. 왜냐하면, producer가 어떤 정보를 채우지 않았는데 consumer가 읽어가려면 문제가 생기고 consumer가 정보를 비우지 않았거나 버퍼가 차 있는 상태에서 어떤 정보를 채우려고 해도 문제가 생기기 때문이다.

 

'Message passing'은 커널이라는 매개체를 통해 두 사람이 메시지를 주고 받는 것을 생각해보면 된다. 데이터는 메시지를 통해 주고 받고, 이는 데이터가 적을 때 유용하다. 왜냐하면 우리가 conflict를 고려할 필요가 없기 때문이다. 그리고 'shared memory'보다는 분산 시스템에 좀 더 적합하다는 장점이 있다. 분산 시스템에서는 CPU들이 네트워크로 연결되어 프로세스들이 서로 다른 CPU에서 돌아가기 때문이다.

 

우선, shared memory는 message passing보다 빠를 수 있는데 왜 그럴까? 왜냐하면 shared memory는 커널의 도움을 메모리 셋업할 때 '한번' 받는데 비해, message passing은 정보를 전달할 때 마다 커널을 호출해야 하기 때문이다. 이부분이 오버헤드가 되어 shared memory가 message passing보다 빠를 수 있다.

 

하지만, many core system에서는 message passing이 shared memory보다 빠를 수 있다. many core system에서 shared memory를 통해 ipc하는 경우를 생각해보자. shared memory에 있는 공유변수를 각 프로세서가 읽어가서 연산은 하는데 한 프로세서가 공유변수의 값을 바꾸면 어떻게 될까? 그러면 다른 프로세서가 그 공유변수를 사용할 수 없다. 공유변수는 모든 코어에서 일관성을 유지해야 하기 때문이다. 그러면 코어는 공유변수를 사용할 때마다 다른 코어에게 값을 바꿨는지 물어봐야하고 써야하는데, 이렇게 되면 문제가 굉장이 복잡해진다. 사실, 코어의 private cache의 일관성을 유지하는 것이 중요한 주제인데, 이를 'cache coherence protocol'이라고 부른다. 그래서 이런 경우에는 message passing이 shared memory보다 빠를 수 있는 것이다.

 

message passing을 좀 더 자세히 알아보자. 메시지를 주고 받는다고 했는데, 이 표현에서 내포된 두가지 연산이 있다. 'send message'와 'receive message'. 그러면 누가 보내고, 누구한테 보낼 것인가 'Naiming'을 해줘야할텐데, 이 방법에 따라서 'Direct communcation'과 'Indirect communication' 두가지로 나뉜다.

 

우선, 'Direct communication'은 누가 보내고 받는지 명시적인 방법이다. 그래서 send와 receive를 send(P, message), receive(Q, message)와 같이 사용한다. 그러면 보냈는지 받았는지 서로 확인이 되어야하기 때문에 동기화가 필요하다. 그런데 이 방법은 몇가지 단점이 있다. 첫 번째로 송/수신자를 정확하게 알아야 사용할 수 있다. 그리고 오직 두 프로세스 간에만 데이터를 주고 받을 수 있고, 명시적이기 때문에 하드 코딩이 되어 있어 주고 받는 걸 바꾸고 싶으면 코드 자체를 바꿔야한다.

 

*send(P, message)는 P에게 message를 보내는 것, receive(Q, message)는 Q로부터 message를 받는 것이다.

 

반면, 'Indirect communication'은 비명시적인 방법이다. mailbox A를 하나 만들어서 거기로 메시지를 주고 받는 것으로 생각하면 이해하기 쉽다. 그래서 send와 receive를 send(A, message), receive(A, message)와 같이 사용한다. 그러면 어떤 프로세스던간에 mailbox를 통해 정보를 넣고 뺄 수 있게되어 direct communication에서 문제가 된 1) 두 프로세스간에만 정보를 주고받는 것, 2) 하드 코딩 되어있는 것 을 해결할 수 있다.

 

위에서 동기화 이야기를 했는데, 'Blocking', 'Non-Blocking'인 경우에 send, receive에 대해서 알아보자. 

- Blocking send: 받는 프로세스가 메시지를 읽기 전까지 보내는 프로세스를 막아두는 것. 즉, 리시버가 읽어가기 전까지 센더가 기다리는 것을 말한다.

- Non-blocking send: 보낸 다음에 다른 일을 할 수 있는 것. 리시버가 읽지 않더라도 센더는 기다리지 않는다.

- Blocking receive: 받는 쪽에서 데이터가 올 때까지 기다리는 것을 말한다.

- Non-blocking reveive: 받는 쪽에서 데이터가 오지 않더라도 다른 일을 할 수 있는 것을 말한다.

 

이제 IPC의 예시중 하나인, 'Ordinary Pipe'에 대해서 알아보자. Ordinary pipe은 두 프로세스가 producer-consumer model로 정보를 주고 받는데, 이때 커뮤니케이션은 단방향이다. 위의 그림에서 fd(0)은 read-end를 fd(1)은 write-end를 의미한다. 커뮤니케이션을 위한 순서는 다음과 같다. parent process가 pipe()으로 pipe를 생성하면, fork()로 child process를 생성하고 pipe를 통해서 parent와 child가 데이터를 주고받을 수 있는 것이다. 그러면 이 방식은 shared memory일까 message passing일까? 답은 'Message Passing'이다. 왜냐하면 두 프로세스가 fd라는 커널 펑션을 통해서 데이터를 주고 받기 때문이다. 그리고 pipe는 단방향 커뮤니케이션이라고 했는데, 두 프로세스가 양방향 커뮤니케이션을 하려면 어떻게 해야할까? pipe를 두개 만들면 된다.

Ordinary pipe는 두 프로세스가 parent-child 관계를 갖고 있을 때 프로세스가 데이터를 주고 받는 방법에 대한 것이다. 그리고 커뮤니케이션이 단방향이기 때문에 양방향 커뮤니케이션을 위해서는 pipe를 두개 설치해야 했는데, parent-child 관계가 필요하지 않고, 양방향 커뮤니케이션을 할 수 있는 방법은 없을까? 'Named Pipes' (FIFO)라는 방법이 이를 도와준다. 이는 양방향 커뮤니케이션이 가능하고, parent-child 관계가 없어도 된다. 그리고 일단 생성되면 여러 프로세스가 접근 가능하고 파일 형태로 보인다는 것이 특징이다. 이는 프로세스의 라이프 사이클에 디펜드하지 않는데, 프로세스가 끝나더라도 남아있다. 그리고 open(), read(), write(), close()와 같은 시스템 콜을 통해 조작이 가능하다. 단점으로는 같은 컴퓨터를 사용하는 프로세스의 경우에만 이를 사용할 수 있는데, 이것이 파일의 형태로 보인다는 것을 생각하면 이해할 수 있다. 그러면 다른 컴퓨터를 사용하는 프로세스의 경우에는 어떻게 정보를 주고 받을 수 있을까?

우리는 이를 소켓을 통해 해결할 수 있다. 소켓은 다른 컴퓨터에서 실행되고 있는 프로세스간 정보를 주고받을 때 메시지 패싱 방법 중 하나이다. 이것도 메시지 패싱 방법이니 네이밍 방법이 필요한데, 어떻게 다른 컴퓨터와 프로세스를 표현할 수 있을까? 우리는 ip와 port로 네이밍을 구현할 수 있다. ip는 각 컴퓨터에 할당된 네트워크 주소이고, port는 하나의 컴퓨터에 실행되고 있는 어떤 프로세스의 할당된 번호이다.

반응형

'컴퓨터 > 운영체제' 카테고리의 다른 글

[OS] Thread (2)  (0) 2020.04.16
[OS] Thread (1)  (0) 2020.04.16
[OS] Context Switch와 레지스터 셋의 관계  (0) 2020.04.15
[OS] Interrupt  (0) 2020.04.03
[OS] Operating System Structures (part2)  (0) 2020.03.25