일기 대신 코드 슬쩍

[운영체제] Processes(2) 본문

OS

[운영체제] Processes(2)

코코자 2024. 4. 3. 22:54

 

프로세스 두번째 시간이다.

저번에 프로세스가 뭔지부터 어떻게 생성되는지에 대해 알아보았다.

이번에는 프로세스끼리 어떻게 통신하는지 알아보겠다.

가보자고

3.4 Interprocess Communication(IPC)

프로세스들은 서로 데이터를 공유하냐에 따라 independent인지 coorperating인지 나뉜다.

 

📢 IPC는 coorperating인 프로세스들이 데이터를 공유할 때 사용하는 메커니즘이다.

 

IPC에는 2가지 방법이 존재한다.

첫번째, shared memory 방식 (왼쪽, (a))

두번째, message passing 방식이다. (오른쪽, (b))

이 2가지 방법에 대해 자세히 알아보도록 하자!!!!


3.5 IPC in Shared-Memory Systems

Shared memory를 먼저 알아보자.

  • Shared memory를 구성하기 위해서는 communicating process가 필요하다.
  • Share 할 부분에 대해 각 process는 process의 protection을 제거해야 한다. (이때, system call이 한 번 필요하다)
  • 이렇게 protection을 제거하고 나면, OS의 control없이(system call 안 하고) 프로세스간 read & write가 가능해진다.

결론적으로 성능이 더 좋아진다!!

[Example]

shared-memory의 대표적인 예시로 producer-consumer problem을 해결했다는 것이 있다. 먼저 producer-consumer problem에 대해 설명하자면,

Producer-Consumer problem

  • producer: data를 생산함
  • consumer: data를 소비함

이 2개가 별도의 process이다. 즉 process간 통신을 필요로 한다.

그래서! 여기에 shared-memory를 적용한다면~?

shared-memory에 data를 저장할 수 있는 buffer를 만들어놓고, producer는 buffer를 채우고, consumer는 buffer를 비우면 된다.

이 동작이 동기화되어 사용되면 shared-memory system!

그러면 이걸 코드로 구현해보자!

#define BUFFER_SIZE 10 
//버퍼 크기 10으로 정의

typedef struct{ 
	...
} item; //item이라는 구조체 타입을 정의

item buffer[BUFFER_SIZE]; //circular array로 구
int in = 0; /* producer가 data를 삽입할 위치
producer가 data 삽입할때마다 증가 */
int out = 0; /* consumer가 data를 꺼낼 위치
consumer가 data 꺼낼때마다 증가 */

circular array를 정의하고, 2개의 pointer가 있다.

다음으로 producer와 consumer process의 코드도 살펴볼까?

item next_produced;

while(true){
	/* produce an item in next_produced */
    
    while(((in+1) % BUFFER_SIZE) == out) //buffer가 full이 아닌동안
    {
    	/* do nothing*/
    }
    buffer[in] = next_produced; //data 삽입
    in = (in + 1) % BUFFER_SIZE; //in 증가시키기
}

in = (in+1) % BUFFER_SIZE 는 circular array이기 때문에 in 인덱스를 증가하고, buffer 크기를 넘어서면 0으로 돌아가도록 해준다.

item next_consumed;

while(true){
    while(in==out)
    {
    	/* do nothing*/
    }
    
    next_consumed = buffer[out];
    out = (out+1) % BUFFER_SIZE;
    
    /

앞서 producer와 비슷한 방식으로 작동한다.


3.6 IPC in Message-Passing Systems

이번에는 message-passing 방법에 대해서도 알아보겠따.

Shared-memory 방법은 address space 공유가 필요했던 반면에, message-passing 방법은 address space를 공유하지 않고도 통신이 가능하다.

message-passing은 2가지 operation이 있다. 바로 send(message)와 receive(message)이다.

만약 두 개의 process P와 Q가 있다고 하고, 이 process들이 통신한다면 producer는 send(message), receiver는 receive(message) 를 이용하면 된다.

그러면 두 개의 process간에 communication link가 필요하다! communication link를 구현하는 방법은 다양하다..

  • direct communication or indirect communication
  • Synchronous communication or asynchronous communication
  • automatic buffering or explicit buffering

각각의 방법에 대해서 소개해보겠다.

Direct communication

direct방식은 또 symmetryasymmetry로 나뉜다. (하 그만좀 나눠)

direct 방식의 특징은 process간의 communication link가 자동으로 생성되고, process pair 당 오직 1개의 link만 존재한다는 것이다.

단점으로는 send, receive process를 지정해야하므로 process를 변경해야 할 때 다 찾아서 변경해야하는 번거로움이 있다. 즉, modularity에 한계가 있다.

 

📌

[Symmetry direct communication]

먼저 direct symmetry는 send process와 receive process 모두 명시해야 한다. 이렇게 명시하고, 각 process에 따른 동작을 수행한다.

 

send(P, message) : process P에게 message를 전송

receive(Q, message): process Q로부터 message를 수신

 

[Asymmetry direct communication]

asymmetry는 send process에 대해서는 명시해야 하지만, receive에 대해서는 process를 지정하지 않는다. 즉, 어떤 process에서도 다 receive할 수 있다.

 

send(P, message) : process P에게 message를 전송

receive(id, message): 모든 process로부터 message를 수신

 

 

Indirect communication

process 사이의 통신이 아닌, mailbox 또는 port를 이용해 message가 전달되는 방법이다.

*mailbox(port) : message를 넣을 수도, 가져갈 수도 있는 일종의 buffer

한 개의 communication link가 2개의 process에서 존재하는 것이 아니라, 여러개의 process에서 존재할 수 있음,

또한 2개의 process에 여러 link가 존재할 수도 있다!

 

send(A, message) : mailbox A에 message를 전송

receive(A, message) : mailbox A 로부터 message를 수신

Synchronization

blocking이 sychronous, non-blocking이 asychronous방식이라고 할 수 있다.

  • blocking send: sender는 message가 수신될때까지 sending이 막힘
  • non-blocking send: sender는 message를 보내고 계속해서 다른일 수행
  • blocking receive: receiver는 message가 수신 가능할 때 까지 대기
  • non-blocking receive: receiver는 valid한 message 혹은 null message를 받음

아까 언급했던 producer-cosumer problem을 message-passing에서는 blocking send, blocking receive인 경우에 해결할 수 있다.

Buffering

IPC에서의 일시적인 queue라고 할 수 있겠다.! 3가지 방식이 있다.

  • Zero capacity: buffering을 하지 않는것, message를 저장하지 않으므로 waiting message라는 건 존재하지 않고, blocking도 허용하지 않음
  • Bounded capacity: 유한한 길이의 message를 가지게 된다. 최대 n개의 message를 가지고, link가 full이 되면 sender 막힌다.
  • Unbounded capacity: 무한한 길이의 buffer를 가진다. 절대 blocked 되지 않고, 이론적으로 어렵다. 근데 어느정도 충분한 buffer를 확보하고 일정간격으로 message를 receive한다면 현실적으로 구현할 수 있다!

3.7 Examples of IPC Systems

  • Shared-memory: POSIX shared memory
  • Message-passing: Pipes

이게 각각의 예시이다 . 조금더 자세히 살펴보자.

Shared-memory: POSIX

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>

int main()
{
    const int SIZE = 4096;                      // shared memory의 크기
    const char* name = "OS";                    // shared memory의 이름
    const char* message_0 = "Hello, ";
    const char* message_1 = "Shared Memory!\\n";
    
    int shm_fd; // shared memory의 file 설명자
    char* ptr;  // shared memory의 pointer

    // shared memory 객체 생성
    shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
    
    // shared memory의 크기 설정
    ftruncate(shm_fd, SIZE);

    // shared memory 객체에 매핑
    ptr = (char*) mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);

    // shared memory에 메시지 작성
    sprintf(ptr, "%s", message_0);
    ptr += strlen(message_0);
    sprintf(ptr, "%s", message_1);
    ptr += strlen(message_1);

    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>

int main()
{
    const int SIZE = 4096;                      // shared memory의 크기
    const char* name = "OS";                    // shared memory의 이름
    
    int shm_fd; // shared memory의 file 설명자
    char* ptr;  // shared memory의 pointer

    // shared memory 객체 생성
    shm_fd = shm_open(name, O_RDONLY, 0666);
    
    // shared memory 객체에 매핑
    ptr = (char*) mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);

    // shared memory 객체 읽기
    printf("%s", (char*)ptr);

    // shared memory 제거
    shm_unlink(name);

    return 0;
}

consumer니까 readonly임.

Message-passing: Pipes

pipes는 단순한 방법이다. pipe는 두 process가 통신할 수 있는 통로의 역할을 수행한다.

다만 pipes를 구현할 때 고려해야 할 것들이 있다.

  • Bidirectional or Undirectional?
  • Bidriectional인 경우에만, half duplex or full duplex?
    • half duplex: 한쪽 방향으로만 통신
    • full duplex: pipe 2개를 사용하여 송수신
  • parent-child같은 relationship이 필요한가?
  • pipe가 network를 통해 통신하는가?

Pipes는 두가지가 있다. ordinary pipes와 Named pipes이다. 각각에 대해 알아보자!!

[Ordinary pipes]

  • Undirectional 통신, 만약 Bidirectional로 통신하고 싶다면, 2개의 pipe를 사용해야함
  • 두 process가 producer-consumer 방식으로 통신함
  • 생성된 process의 외부에서는 접근할 수 없음
  • 일반적으로 parent-child process 간의 통신을 위해 사용

[Named pipes]

  • Bidirectional 통신, half-duplex
  • parent-child process 관계없이 접근이 가능함

3.8 Communication in Client-Server Systems

Socket

Socket: IP Adress와 Port number가 결합된 식별자, client-server architecture

ex) 146.86.5.20:1625 → IP adress는 146.86.5.20, Port number는 1625

socket도 pair를 이뤄야하고, 각 process마다 socket을 가지고 있어야 한다. 위의 그림에서 host x 가 web server와 통신하기 위해서는 web server의 IP Adress와 port number를 알아야 한다. 모든 connection은 unique해야해서 different port를 가진다.

RPC(Remote Procedure Call)

  • 원격 서비스의 가장 일반적인 형태 중 하나이다.
  • well structured messages: no longer just packets of data
  • function과 parameter를 실행할 수 있게 함
  • distributed file system 구현에 효과적임

별도의 원격 제어를 위한 코딩없이 다른 주소 공간에서 함수나 프로시저를 실행할 수 있게 하는 프로시저간의 통신 기술이다.

 

'OS' 카테고리의 다른 글

[운영체제] Threads & Concurrency (2)  (1) 2024.04.18
[운영체제] Threads & Concurrency  (0) 2024.04.11
[운영체제] Processes  (0) 2024.04.01
[운영체제] OS 구조  (0) 2024.04.01
[운영체제] OS 소개(2)  (0) 2024.03.31