이번 포스팅 그림 요약—

IMG_062287506EDD-1.jpeg

IMG_FDFCC6D1A31B-1.jpeg

IMG_5D623E19F993-1.jpeg


프로세스의 실행 - 독립적 실행 / 협력적 실행

—> 독립적 실행인경우 CPU의 타임 스케쥴링만 잘 해주면 문제 없음.

협력적 실행이 필요한 경우가 있음.

독립적 실행 → 공유하는 데이터도 없고 메세지 주고받을 사항이 없는 경우.

협력적 실행 → 영향을 주거나 받는 경우 ⇒ 데이터를 공유하거나 메세지를 주고 받는 경우

IPC (Inter_process Communication)

데이터를 주거나 받거나 하는 경우.

→ 공유 메모리를 써서 데이터를 주고 받는 방법

→메세지를 주고 받는 방법 중

한가지로 통신을 하면 된다.

스크린샷 2022-06-07 오후 6.06.01.png

공유 메모리 방법 : 예를 들어, Process A(아빠)와 Process B(아들) 사이에 shared memory(용돈함)이 있어서, 아빠가 용돈을 넣으면 아들이 가져가는 구조.

메세지 주고 받는 방법(메세지 파싱) : 운영체제에서 맡긴다.

아빠가 은행 계좌에 입금을 하면, 은행(운영체제)이 알아서 아들 계좌에다가 송금.

Producer-Consumer Problem (생산자 - 소비자 문제)

협력하는 프로세스들 간에 생기는 가장 기본적인 통신 문제.

생산자 - 정보를 생산

소비자 - 정보를 소비

하는 모델

ex > webserver - browser

브라우저가 리퀘스트하면 웹 서버가 html 파일을 전송

  1. 공유메모리를 사용한 솔루션

    생산자와 소비자는 concurrently하게 돌아간다.(각자 자기 할 일을 한다)

    버퍼를 만들어서, 생산자는 버퍼를 채우고, 소비자는 버퍼를 비운(소비한)다.

    이때, bounded buffer인 경우가 일반적인데, buffer가 가득차면 생산자는 wait 하고 있어야 한다. buffer가 비워져 있는 경우 소비자는 wait해야한다.

    이 버퍼를 shared memory(생산자와 소비자가 공유하고 있는 영역)로 만든다.

    각각 프로세스의 영역은 서로 침범할 수 없다.(침범시 문제가 됨)

    따라서 두 프로세스 모두 접근 가능한 shared memory는 OS가 특별히 관리해줘야 한다.

    스크린샷 2022-06-07 오후 6.25.06.png

    구조체를 만들어서 shared buffer를 하나 만들었다고 치면, 생산자는 in을 증가 시키고, 소비자는 out을 증가 시키면서 데이터를 생산하고 소비하는 식으로 구현 할 수 있다.

    스크린샷 2022-06-07 오후 6.27.02.png

    생산자는 item을 하나 만들어서 buffer에 채운다.

    in + 1 == out이면 (buffer가 가득차면) 대기한다.(do_nothing)

    비어있다면 버퍼를 채운다.

    스크린샷 2022-06-07 오후 6.28.32.png

    소비자는 버퍼에 있는 item가져와서 소비

    버퍼를 통해서 동기화가 잘 이루어 진다.

    → 문제점 : 메모리 영역을 공유하게 되면, 메모리 영역에 명시적으로 프로그래머가 짜야한다. 생산자와 소비자가 각 1개씩만 있을 때는 문제가 되지 않지만, 생산자가 n개가 되거나 소비자가 n개가 되었는데 shared memory는 1개인 경우 사람이 직접 다 프로그래밍 해주어야한다.

    스크린샷 2022-06-07 오후 6.38.02.png

  2. 메세지 파싱을 이용한 솔루션

    쉐어드 메모리에서 복잡해지는 문제점은 OS가 알아서 해결해준다.

    OS가 “서로 메세지를 주고받는 프로세스들”에게 api(수단)를 제공한다.—> 메세지를 주고 받는 것을 편하게 할 수 있게 한다.

    스크린샷 2022-06-07 오후 7.08.20.png

    생산자는 메세지를 보내기만 한다.

    스크린샷 2022-06-07 오후 7.08.47.png

    소비자는 메세지를 받기만 한다.

    나머지는 운영체제에서 다 알아서 해준다.

    ⇒ 메세지 패싱 방법에서는 수많은 prosumer들이 통신을 할 때 문제가 될 수 있어서 따로 shared memory가 필요해질 수도 있다. → 후에 다시 다룰 것.

    메세지 패싱 방법에서는 프로그래머 입장에서는 생산자가 소비자에게 다이렉트로 메세지를 보내는 것.(중간 모든 과정은 OS의 system call 안에서 이루어짐) 우리 입장에서는 생산자(P)와 소비자(Q)를 direct로 연결해주는 Communication Link만 만들어주면 된다.

    send() / receive() 두개의 시스템 콜만 적용을 하면 된다.

    communication link의 구현 방법도 나름 다양해 진다.

    →direct / indirect → 직접 / 간접

    →synchronous / asynchronous

    1만원 이하시 5만원 / 아무때나(기분 좋으면) 용돈 주는 모델

    →automatic / 명시적 버퍼링

    다이렉트하게 보낼 시 → 받는 사람을 명시적으로 정해서 주는 방식. ex > send(P, message) , receive(Q, message) → 어떤 프로세스가 받을 지를 명시해야한다.

    ⇒ 이경우 커뮤니케이션 링크는 자동적으로 생성된다.

    ⇒ 이 링크는 두 프로세스간에서는 단 하나만 존재한다.

    indirect하게 보낼시 →(ex 용돈함) 메세지는 메일박스(mailbox or ports)로 전송하고, 메일박스에서 수신 할 수 있다. ⇒ 운영체제에서 요즘은 port라고 많이 부른다. 이 포트를 사용함으로써, send(A, message) , receive(A, message) → 포트A에게 메세지를 주기만 하고, 포트 A에서부터 메세지를 수신한다(읽어온다).

    ⇒2개의 프로세스가 포트를 공유할 때 비로소 링크가 생성된다.

    ⇒하나의 링크가 여러개의 프로세스들이 커뮤니케이션 하는데 사용해도 문제 되지 않는다.

    ⇒여러개의 differnet link들이 존재할 수 있다. (복잡한 커뮤니케이션 링크도 만들 수 있다.)

    →OS 입장에서는 indrect한 통신이 가능하게 하려면 사용자가

    할 수 있게 해주면 된다.

    포트?(port? == mailbox(좀 옛날 용어 느낌)

    하나의 object인데,

    이 오브젝트에게 프로세스가 메세지를 보낼 수 있고,

    이 오브젝트로 부터 메세지가 삭제(소비, 수신) 될 수 있다.

단, 이런 프로세스간의 통신을 실제로 구현하기 시작하면,

좀 더 고려해야하는 사항들이 생긴다.

blocking send / receive를 쓴다 (synchronous io)

⇒ 동기화 된다.

센더가 상대(소비자 프로세스==리시버)가 전송을 다 받았다는 것을 확신 할 수 있으므로 요금 부과 시점이 명확하다.

but 센더가 모든 데이터를 다 보낼때까지 기다려야하므로 프로세스의 실행 속도가 느리다.

non-blocking send / receive를 쓴다 (asynchronous io)

⇒비 동기적 전송

센더가 상대(리시버)가 전송을 다 받았다는 것을 확신 할 수가 없다.(운영체제가 다 보냈겠거니 하고 센더는 다른일을 하기 때문) 요금 부과 시점이 불 명확하다.

but 센더는 운영체제에게 send 요청만 보내고 다른 일을 할 수 있기에, 센더나 리시버 프로세스 입장에서는 효율적으로, 빠른 속도로 실행 될 수 있다.