Operating System 3 - Process Communication, IPC
Interprocess communication(IPC - 프로세스 간 통신)
일반적으로 동시에 실행되는 프로세스라 해도 상관 관계가 없는 독립적인 작업을 할 경우 통신할 필요가 없다. (서로 영향을 주고 받을 필요가 없고 공유되는 데이터도 없다.)
그러나 cooperating 하는 프로세스의 경우 정보를 주고 받으며 서로 영향을 줄 수 있기 때문에 통신이 필요하다.
- Cooperating Process
information sharing, Computation speed up, modularity의 장점을 가지기 때문에 협력 프로세스를 사용한다.
방법은 shared memory, message passing 기법의 두 가지가 있다.
1. Shared memory (user mode)
일반적으로 여러 프로세스는 부모-자식 관계여도 메모리를 공유하지 않는다. 일반적으로 여러 프로세스는 부모-자식 관계여도 메모리를 공유하지 않는다. (왜냐하면 각 프로세스가 달라지면 메모리를 읽었을 때 서로 다른 값이 될 수 있기 때문)
그러나 이 방법은 OS가 메모리의 특정 영역을 shared로 정하여 해당 부분을 여러 프로세스가 동시에 접근할 수 있게 된다. 각 프로세스는 자신의 메모리 영역을 그대로 가지고 있고, 공유해야 될 정보는 해당 부분에 놓는 것이다.
이 때 주의할 점은 이 communication은 user process의 control 하에서 이루어진다. (소비 정책 등) 그래서 누가 정보를 생산하고 누가 소비할 것인지에 대한 동기화 문제들이 발생한다.
* Producer - Consumer Problem : 어떤 것은 무언가를 생산하고, 다른 것은 그것을 소비하는 식으로 작동한다.
이런 데이터를 넣어 놓는 곳은 buffer인데, 만약 무한히 들어가는 unbounded-buffer를 사용하면 생산자가 무한히 데이터를 넣어 영원히 block되지 않고 메모리를 낭비하게 된다. 따라서 일반적으로 원형큐를 사용한 bounded-buffer를 사용한다.
이는 %를 이용한 원형 큐에 소비 포인터(in)과 생산 포인터(out)의 위치를 옮겨가며 빈 곳이 있을 때만 생산하고, 값이 있을 때만 소비하는 식으로 작동하게 된다.
13p 코드는 in이 out 한 칸 뒤에 있을 때 공간이 비었을 때도 쓸 수 없다는 문제점이 있다.
2. Message passing (kernel mode - system)
메시지 패싱은 각 프로세스가 다른 프로세스에게 메시지를 날리고, OS가 해당 메시지들을 큐에 넣어놓았다가 전달하는 방식이다.
위의 공유 메모리 방법은 각 프로세스를 만드는 프로그램 소스코드 내에 상호 관리를 모두 고려해여 명시해야 한다. 그래서 다소 복잡하다. (user controlled) 또한 shared memory는 같은 메모리 공간을 공유해야 하기 때문에 같은 컴퓨터에서만 가능하다.
그러나 Message passing 방법은 OS에 의해 관리되기 때문에 send / receive 만 하면 되고, 소스코드를 복잡하게 쓰지 않고 메서드를 사용하여 편리하게 가능하다.
그러나 두 프로세스가 서로 통신하기 위해서는 communication link가 필요하다.
- Direct communication (naming, addressing)
받는 프로세스와 보내느 프로세스를 지정해 놓음. 이 경우, send(P, message) / receive(Q, message)와 같은 방법으로 실현되며 1:1 커뮤니케이션이 발생한다. (P, Q는 프로세스)
- Indirect communication (mailbox, port)
약속된 오브젝트(지정된 포트번호 등)에 원하는 메시지를 놓거나 받을 수 있다. 각 포트는 unique id가 있으므로 허용된 프로세스만 접근할 수 있도록 관리한다. 이 경우 이 포트를 공유하는 프로세스들 끼리만 통신이 가능하다. send(A, message) / receive(A, message) - A는 port id.
이 경우 1:1이 아니라 여러 link, 가능한 여러 조합이 만들어질 수 있으며 컴퓨터 내부 뿐 아니라 외부까지 통신할 수 있다. (네트워크까지 접근 가능)
간접 방식은 OS가 관리하므로 새 port를 만들고, 삭제하고, send/receive 할 수 있어야 한다. 예를 들어 메일박스가 프로세스에 의해 만들어졌다면 프로세스가 끝날 때 해당 포트도 같이 삭제 시켜줘야 한다. 그러나 OS에 의해 생성된 메일 박스는 영구적으로 OS가 관리하고 프로세스들이 사용할 수 있게 된다.
보내는 방식 뿐 아니라 프로세스 간의 동기화 문제도 존재한다.
- Synchronization (Blocking)
blocking send/ receive : sender가 보낸 메시지가 receive될 때까지 기다린다. 마찬가지로 receive도 메시지가 available 할 때까지 blocked 된다.
- Asynchronous (non - blocking)
non-blocking send/ receive : sender가 메시지를 보내고, 다른 작업을 계속한다. receiver 역시 작업을 하며 메시지를 수시로 확인하여 null 메시지를 받거나, 원하는 메시지를 받는다.
이런 방법은 서로 혼합되어 사용될 수 있다. 예를 들어 non-blocking send & blocking receive 간의 조합이 가능하다. (PC - Printer 등) 필요한 서비스에 따라 알맞은 상태를 선택하여 사용하면 된다.
그리고 blocking send/receive를 동시에 사용하는 경우는 rendezvous라 한다. (랑데뷰) 이 경우는 어떤 사용량에 대한 금액을 청구할 때 사용하게 된다.
* POSIX(Portable Operating System Interface) Produce & Consume - UNIX 규격
UNIX는 모두 (명령어 등) 파일로 처리한다. 따라서 shared memory도 memory-mapped file로 처리를 한다. 이는 어떤 파일이 만들어질 떄 secondary storage에 보내지 않고 계속 메인 메모리에 올려 놓겠다는 것이다.
- create : fd = shm_open(name, O_CREAT | O_RDWR, 0666); //shared memory open, 0666은 파일 권한 설정
# LINUX의 파일 권한 설정.
Directory, User, Group, Other - Read, Write, eXecute : 각 자리에 알맞은 값 0/1에 따라 파일의 종류와 권한을 모두 설정할 수 있다.
- size : ftruncate(shm_fd, 4096); // 일반적으로 unix는 통신단위로 4k를 사용
- establish : mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //만든 오브젝트 메모리 반환
이렇게 할당받은 오브젝트 크기를 포인터를 옮겨가며 원하는 값을 Produce 할 수 있다. 이후 consumer는 전체 오브젝트의 시작 주소 포인터를 받아 전체를 출력할 수 있다. 그리고 shm.unlink()를 통해 해당 공간에서 나올 수 있다.
Pipes - Message passing conduit
두 프로세스가 통신할 때 양방향/단방향인 지, half duplex/full duplex (한 번에 한 방향으로만 가능, 동시에 양방향으로 가능) 인 지를 결정하게 된다. 그러나 같은 컴퓨터 내부에서만 사용 가능하다.
- Ordinary pipes : 바깥에서 들어올 수 없고 parent - child process 끼리만 통신 가능한 경우.
이 경우는 producer는 생성만, consumer는 소비만 하여 단방향 통신이 된다. 따라서 producer는 write-end를 사용하고 consumer는 read-end만 쓸 수 있다. (방향성이 존재)
만약 반대 방향의 통신이 필요한 경우, 따로 파이프를 하나 더 만들어서 역방향으로 입구를 연결해야 한다.
fd를 이용하여 각 프로세스가 사용할 read_end/write_end만 사용하도록 사용하지 않는 것은 닫아야 한다. 만약 닫지 않고 계속 생성한다면 오류가 발생한다.
- Named pipes : 부모자식 관계가 아니더라도 통신할 수 있는 것.
위와 달리 파이프의 이름을 지정하여 각 프로세스에게 사용할 프로세스를 지정해준다. 이를 통해 여러 프로세스가 원하는 파이프를 사용할 수 있게 된다.
Communication in Client-server System
PC간 통신을 위해 포트를 사용하는 것
- Sockets
OSI 4계층의 transfer 단에서 작동한다. server와 client end 단, 프로세스와 프로세스 간의 통신을 하게 해줌. 이 때 IP address를 통해 서로를 찾고 port를 통해 연결된다. 따라서 ip주소와 port를 합친 것을 socket이라 한다. 사용하고 싶은 포트와 연결하려면, 나의 소켓도 열어줘야 접근 가능하다.
1024 포트번호 이하는 well-known이라 따로 번호를 적어주지 않아도 접속 가능하다. (기본적으로 약속된 포트번호들)
또한 127.0.0.1은 자기 자신으로 돌아오는 loopback이라 한다. 따라서 개발 시에 원하는 프로세스를 실행할 때 자기자신을 찾을 수 있다.
* TCP(connection-oriented) : 보냈으면 받았다고 응답을 함. 신뢰성이 높다.
UDP(connectionless) : 받은 것의 응답을 확인하지 않음. 속도가 빠르다.
서버는 포트를 열고, 새 접속이 있을 때 마다 새 소켓을 생성하여 클라이언트와 연결을 할당하게 된다. 그리고 클라이언트는 서버가 연 포트에 접속하여 소켓을 할당받고, 해당 소켓과 통신하여 원하는 데이터를 받아오게 된다.
- Remote Procedure Calls (RPC)
원격 컴퓨터의 프로시저를 실행하도록 요구하는 것. OSI 7계층의 application 단에서 작동한다.
socket을 연결하여 통신이 될 때 서로 다른 OS일 때도 원하는 명령어를 내릴 수 있게 해야 된다. 이는 stub이라는 것을 통해 가능하다.
예를 들어 big-endian을 사용하는 아키텍처와 little-endian을 사용하는 아키텍처를 서로 통신하게 하려면 변환이 필요하다. 이는 stub 내부의 marshalls 작업이라 하며, 어떤 프로시저/기능에 따라 서로 다른 stub이 번역하여 제공한다.
댓글
댓글 쓰기