프로세스는Concurrently하게 처리될 경우프로세스간Independent하거나,Cooperative하게 처리된다.
프로세스가Independent한 경우
。다른프로세스와 데이터를 공유하지 않는프로세스
▶ 스케듈링만 잘하면 문제가 발생되지 않는다.
프로세스가Cooperative한 경우
。다른프로세스와 데이터를 공유하는프로세스
▶ 다른프로세스에 영향을 주거나 받을 수 있으며 주로 문제가 발생하는 유형
。대표적으로부모 프로세스,자식 프로세스가 존재
프로세스 간 통신 (
IPC: Inter-Process Communication )
。OS상에서Concurrently하게 실행중인프로세스간 서로 데이터 및 정보를 공유하기 위한Mechanism
。프로세스가Cooperative한 경우 데이터를 주고받는IPC기능이 필요한 경우IPC Mechanism( ex.Shared MemoryorMessage Passing)으로IPC를 구현
。IPC를 구현한IPC System은 컴퓨터 내부 프로세스 간 통신( ex.Pipe) / 네트워크 상 통신( ex.Socket) 등이 존재.
IPC의 두가지 주요메카니즘
。아래 두가지 기법으로IPC를 구현
공유메모리( Shared Memory )
。프로세스간 특정메모리 공간( =Shared Memory)을 서로 공유하여 데이터를 주고받는 방식으로IPC를 구현
▶ 주로생산자 프로세스와소비자 프로세스간 데이터 교환 시버퍼로서 활용
。Shared Memory는메모리 영역으로서 범위가 제한되어있음.
▶bounded buffer
。모든프로세스에서 접근이 가능
。메모리 영역상에서 공유하는 경우 개발자가Shared Memory에 접근 및 상호작용하는 코드를 일일이 명시적으로 하드코딩하여 작성해야하는 단점 존재
- 버퍼 ( Buffer )
。처리속도가 다른프로세스간 상호작용하는 데이터를 저장하는 임시저장공간
▶ 두프로세스의 전송률을 비슷하게 조정하는버퍼링에 활용
메세지전달( Message Passing )
。송신자 프로세스와수신자 프로세스간메모리 영역상Message Queue를 통해 서로Message를 주고받는 방식으로IPC를 구현
。OS가IPC용도의API(send(),receive())를 기본 제공 및 관리하므로Shared Memory처럼 개발자가 직접 상호작용하는 코드를 하드코딩할 필요 없이 간편하게API만 활용
。프로세스간 통신을 구현하기위해 우선 두프로세스간Communication Link가 사전에 설정되어야하며 이후send()와receive()의 두API를 통해Message-Passing을 수행
통신링크(Comm.link: Communication Link )
。Message-Passing방식으로IPC를 수행하기 위해 사전에 설정되어야하는 링크
▶ 구현된 이후API를 통해 메시지를 주고받음
。Comm.link구현 옵션 :
1)Direct / Indirect Communication
2)Synchronous / Asynchronous Communication
3)Automatic or explicit Buffering
ex )Direct하고 ,Synchronous하며Automatic한Communication Link구현
- Direct or Indirect Communication
직접 통신 연결( Direct Communication )
。메세지를 주고받을 두개의수신자 프로세스와송신자 프로세스의 각각의 프로세스 이름을 명시적으로 설정하여 직접적인 통신을 수행
。통신을 수행할 2개의 프로세스 이름만 알면 프로세스간통신 링크를 자동으로 구축
▶ 구현된통신 링크는 오직 연결된 두프로세스와의 상호작용만 가능
。프로세스 이름이 변경될 경우 문제가 생기는 단점이 존재
▶Mailbox를 활용한간접 통신 연결을 활용
API종류
。send( P, 메시지 ):P 프로세스에게 메세지를 전송
。receive( Q, 메시지 ):Q 프로세스으로부터로부터 메세지를 수신
간접 통신 연결( Indirect Communication )
。임의의프로세스간 메세지 전달 시Mailbox( =port)를 통해 메세지를 송신 및 수신받는 구조
。통신을 수행할 임의의프로세스들이 서로 동일한Shared Mailbox로 연결되어 공유 시통신링크를 구축할 수 있다.
▶직접 통신 연결과 다르게 구현된통신링크는메일박스를 통해 2개 이상의프로세스간 상호작용이 가능
。각통신링크는 하나의메일박스에 대응하며 각프로세스사이에 여러개의통신링크를 구현할 수 있음
▶ 복잡한통신링크를 구현할 수 있다
。OS는프로세스에게 다음메카니즘을 기본적으로 제공.
메일박스 생성 및 삭제/메일박스를 통한 메시지 송수신
▶ 다음 메카니즘을 활용하여OS에 의해Port( = Mailbox )가 자동으로 관리되도록 설정
- Mailbox ( =
Port)
。복수의프로세스로 부터 메시지를 수신받아 저장하거나 송신하여 삭제하는공유 저장소용도의객체
API종류
。send( A, 메시지 ):A 메일박스에게 메세지를 전송
。receive( A, 메시지 ):A 메일박스으로부터로부터 메세지를 수신
- Synchronous or Asynchronous Communication
。Blocking : Synchronous,Non-blocking : Asynchronous
。다음 4가지 방식을 조합하여IPC의Message Passing방식을 구현
- Blocking I/O : 동기
。메세지 송수신 작업이 완료될때까지송/수신 프로세스를 대기해야하므로 비효율적이지만 데이터의 안정성이 높음
Blocking Send
。송신자 프로세스가 메세지 전송 시 해당 메세지가 수신할때까지Block되어대기큐에 전송되어Waiting상태로 대기
▶메세지가 수신될때까지 다른 프로세스가CPU Scheduler에 의해 선택되어디스패처에 의해CPU가 할당되어 실행
Blocking Receive :
。수신자 프로세스는 메세지를 수신할때까지대기큐에서Waiting상태로 대기
- Non-Blocking I/O : 비동기
。메세지 송수신 작업여부에 관계 없이송/수신 프로세스를 계속 실행할 수 있어 효율적이지만 중간에프로세스의 연결이 끊길 경우 전송중인 데이터가 불완전하게 전송되므로 불안정
Non-Blocking Send
。송신자 프로세스가 메세지 전송 시 메세지 수신여부에 관계없이 계속Running상태를 유지
Non-Blocking Receive
。수신자 프로세스는 메세지 수신여부에 관계없이Running상태로서 계속Valid Message또는Null Message를 내보냄.
▶ 내보내는 메세지에 따라 수신여부를 판단.
생산자-소비자 문제 ( Producer-Consumer Problem )
。OS상에서 여러프로세스가 공유자원( ex.버퍼)에 접근하여 상호작용 시 발생하는동기화( Syncronization ) 문제
▶Cooperative한프로세스간 주로 발생하는 문제
。생산자가 정보를 생성하면소비자가 해당 정보를 소비하는 형태에서 생산속도와 소비속도의 차이로 인해 발생하는 문제
ex1 )Compiler가Assembly Code를 생성 시Assembler가 해당 코드를 소비하여machine code를 생성 시 발생하는 문제
ex2 )웹 서버가HTML문서 생성 시웹 브러우저가 해당웹 주소로 접근하여 시각화하여 소비하는 경우 발생하는 문제
IPC:Shared-Memory기법으로 문제 해결 시
。생산자 프로세스와소비자 프로세스둘다Concurrently하게 실행할 경우메모리 영역상Shared-Memory를Shared Buffer로서 활용하여동기화를 원활하게하여 데이터를 공유하도록 설정
▶장치 컨트롤러의데이터 버퍼링과 유사
。Shared Buffer는원형버퍼로 구현
▶생산자 프로세스는Shared Buffer에데이터를 채우고,소비자 프로세스는Shared Buffer를 비우면서 상호작용
。이때Shared-Memory는메모리 영역으로서 범위가 제한된Bounded Buffer이므로Bounded Buffer Problem이 발생할 수 있음
▶Buffer가 가득찬 경우생산자 프로세스의 상태를Waiting변경 및Buffer가 빈 경우소비자 프로세스의 상태를Waiting으로 설정
。메모리 영역을 사용하는 방식이므로 개발자가 코드를 일일이 명시적으로 하드코딩하여 작성해야하는 단점 존재
▶메세지전달방식을 사용하는 이유 중 하나코드구현
。Shared Buffer초기화
▶전역변수로작성하여 각프로세스에서 접근하도록 설정#include <stdio.h> #include <stdbool.h> // 버퍼 초기화 #define BUFFER_SIZE 10 typedef struct { // 버퍼 요소를 구성할 item 구조체 선언 } item; // 버퍼 배열 초기화 item buffer[BUFFER_SIZE]; // 버퍼 인덱스 초기화 int in = 0; int out = 0;。
생산자 프로세스정의// 생산자 프로세스 정의 item next_produced; while(true){ // 버퍼가 가득찬 경우 아무 작업도 하지않기 while(( in + 1 ) % BUFFER_SIZE == out) ; // // 버퍼가 빈 경우 item input buffer[in] = next_produced; in = (in + 1) % BUFFER_SIZE; };。
소비자 프로세스정의// 소비자 프로세스 정의 item next_consumed; while (true) { // 버퍼가 비어있는경우 아무 작업하지 않기 while ( in == out ) ; next_consumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; }
IPC Mechanism으로 구현된 대표적인IPC System
。Shared Memory▶POSIX Shared Memory
。Message Passing▶Pipes
POSIX Shared Memory
。Shared Memory Mechanism으로 구현된IPC System
。memory-mapped files을 통해HDD에 저장된파일을메모리에 Mapping 후 해당 영역을Shared Memory로 활용
。Shared Memory방식이므로 개발자가shm_open(),shm_unlink()등 데이터 전달에 대해 일일이 하드코딩할 코드가 많다
POSIX( Portable Operating System Interface For Unix )
。서로 다른UNIX OS간 호환성을 위해 공통API를 책정한Application Interface규격
▶POSIX를 준수하는UNIX OS는 다른UNIX OS와 호환
MMF( Memory Mapped Files )
。OS에서HDD에 저장된파일을메모리에 Mapping하여파일을메모리의 일부로 간주하여 접근할 수 있도록 하는 기능
파일이 매핑된메모리주소공간에POSIX Shared Memory생성
1)POSIX Shared Memory객체 생성
fd=shm_open(name, O_CREAT | ORDWR, 0666);
2) 객체의byte기준 크기 설정
ftruncate(fd, 4096)
▶4096 Byte로 크기 설정
3)POSIX Shared Memory객체를파일의메모리상 주소공간으로 Mapping
mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
shm_open()
。POSIX Shared Memory객체를 생성하거나 기존 객체를 open하는 역할을 수행
。생성된 객체의 크기는0이므로 크기 설정이 필요.
ftruncate(fd, size)
。생성된 객체의 크기를Byte단위로 설정
mmap()
。POSIX Shared Memory객체를메모리 영역으로 Mapping
PROT( Protection Flags )
。메모리 보호목적으로Mapping된메모리 영역에 대해 어떠한 접근을 허용할 것인지 지정하는 인자
PROT종류
PROT_READ
。메모리 페이지를 읽기 가능하게 설정
PROT_WRITE
。메모리 페이지를 쓰기 가능하게 설정
PROT_EXEC
。메모리 페이지에서기계어실행이 가능하도록 허용
PROT_NONE
。메모리 페이지에 어떠한 접근을 모두 금지
flags
。Mapping된메모리 영역의 종류, 공유 방식, 주소지정방식을 정의하는 인자
MAP_SHARED
。프로세스가Shared Memory객체에 대해 해당메모리 영역에 대한WRITE작업을 허용
POSIX Shared-Memory API구현
- Producer Process
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/shm.h> #include <sys/stat.h> #include <sys/mman.h> int main(){ // 생성할 POSIX shared memory 객체의 크기 , 이름 설정 const int SIZE = 4096; const char *name = "OS"; // // 전달할 메시지 설정 const char *message_0 = "Hello, "; const char *message_1 = "Shared Memory!\n"; // int shm_fd; // shared memory의 file descriptor 정의 char *ptr; // shared memory 포인터 // // POSIX Shared Memory 객체 생성 shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666); // // 객체의 크기 설정 ftruncate(shm_fd, SIZE); // // 객체의 Mapping 수행 ptr = (char *)mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); // // POSIX Shared Memory 객체로 메세지 공유 sprintf(ptr, "%s" , message_0); ptr += strlen(message_0); sprintf(ptr, "%s", message_1); ptr += strlen(message_1); }。이후 터미널에
gcc 파일명.c -lrt로 컴파일
▶-lrt:librt라이브러리에 정의된 함수를 활용하기위해 해당 라이브러리를 링크
librt
。POSIX표준 中Real-time Extension을 제공하는 라이브러리
。POSIX Shared Memory를 구현하는shm_open()등의 함수는librt에 구현되어있어-lrt없이 컴파일 시Link Error발생.
- Consumer Process
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/shm.h> #include <sys/stat.h> #include <sys/mman.h> int main(){ const int SIZE = 4096; const char *name = "OS"; int shm_fd; char *ptr; // // 읽기 목적의 POSIX Shared Memory 객체 생성 shm_fd = shm_open(name, O_RDONLY,0666); // // POSIX Shared Memory 객체 Mapping ptr = (char *)mmap(0, SIZE, PROT_READ , MAP_SHARED, shm_fd, 0); // 객체로부터 읽기 printf("%s", (char *)ptr); // 객체 삭제하기 shm_unlink(name); return 0; }
。 메세지가POSIX Shared Memory에 송신 및 수신이 정상적으로 수행됨을 관측가능.
。소비자 프로세스에서shm_unlink()를 통해메모리 공간상에 Mapping된shared memory가 삭제됨.
▶ 한번 더 호출하는 경우 해당 객체를 찾을 수 없음
Pipes
。초창기UNIX System에서 활용된Message Passing으로 구현된IPC Mechanism
。2개의프로세스간 통신으로 데이터를 주고받는 통로
▶파이프는Message Box( =Port) 역할을 수행
。동일한HW Device내프로세스간 통신 시 사용.
▶Client-Server System상네트워크로 연결된 다른 컴퓨터의프로세스접근 시Pipe대신Socket사용
Pipe구현 시 고려사항
단방향( Unidirectional ) /양방향( Bidirectional )
양방향인 경우Half Duplex/Full Duplex
。반이중( Half Duplex ) :
한번에 한 방향으로만 데이터의 송수신이 가능한 것
。전이중( Full Duplex ) :
데이터를 동시에 송수신이 가능
- 통신을 수행하는
프로세스간Relationship존재 여부
。Named Pipe는Relationship없이 사용가능
ex )부모 / 자식 프로세스관계
네트워크상 통신 여부
。네트워크로 연결된 다른 컴퓨터의프로세스접근 시Pipe대신Socket사용
Pipe의 종류
。파이프는Message Box( =Port) 역할을 수행
일반 파이프( Ordinary Pipe ) =익명 파이프( Anonymous Pipe )
。생산자 소비자 형태에서 두프로세스간 통신을 수행
▶ 통신을 수행하는프로세스는 서로부모/자식 프로세스관계여야한다.
▶생산자 프로세스는부모 프로세스로서파이프좌측종단( =write end)에 데이터를 생성
▶소비자 프로세스는자식 프로세스로서파이프우측종단( =read end)에서 데이터를 읽음
。Ordinary Pipe는 2개의File Descriptor를 활용한 단방향 (Unidirectional) 통신을 수행
▶양방향통신 구현 시 2개의Ordinary Pipe를 구현
。일반 파이프구현 시 반드시부모 / 자식 프로세스관계로 정의하여부모 프로세스에서는Write End,자식 프로세스에서는Read End를 수행하며 두프로세스모두Concurrently하게 실행하여프로세스간 통신을 구현
▶파이프를 생성한프로세스이외는 접근 불가능
지명 파이프( Named Pipe )
。프로세스간 각파이프에 이름을 지정
。양방향 통신( Bidirectional )
。부모 자식 프로세스간Relationship이 없어도 사용 가능
UNIX System상Ordinary Pipe구현
。Ordinary Pipe생성 :pipe(int fd[])
。입력종단(write end) :fd[0]
。쓰기종단(read end) :fd[1]
。부모 프로세스는write end,자식 프로세스는read end를 수행하며 두프로세스모두Concurrently하게 동시처리.#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #define BUFFER_SIZE 25 #define READ_END 0 #define WRITE_END 1 int main(){ char write_msg[BUFFER_SIZE] = "Greetings"; char read_msg[BUFFER_SIZE]; int fd[2]; // file descriptor 초기화 // 파이프 생성 pipe(fd); // 자식 프로세스 생성 pid_t pid; pid = fork(); if (pid > 0){ // 부모프로세스 // wait()이 없으므로 자식프로세스와 함께 실행 close(fd[READ_END]); // 파이프 작성 write(fd[WRITE_END], write_msg, strlen(write_msg)+1); close(fd[WRITE_END]); } else if ( pid == 0 ){ // 자식프로세스 close(fd[WRITE_END]); // 파이프 읽기 read(fd[READ_END], read_msg, BUFFER_SIZE); printf("read %s\n", read_msg); close(fd[READ_END]); } return 0; }
식별자(fd: file descriptor )
。파일또는I/O Resource를 처리하는 용도로 활용되는Abstract Indicator
pipe(int배열)
。UNIX에서IPC를 위해Ordinary Pipe를 구현 시 사용하는System Call
▶WRITEorREAD를 수행하는 2개의식별자를 생성int fd[2]; // file descriptor 초기화 // 파이프 생성 pipe(fd);。
식별자역할의int fd[2]를 생성 및 매개변수로 전달시Write End과Read End를 수행하는 2개의식별자를 생성
▶Read End용도식별자:fd[0]
▶Write End용도식별자:fd[1]
File Descriptor닫기
。생성된Ordinary Pipe는단방향이므로식별자의write(fd)또는read(fd)작업 후 특정방향으로 설정된식별자를 닫는 과정이 필요.
▶단방향이므로 각 용도의 실행중인식별자를 종료해야 다른 용도의식별자를 실행가능
。close(fd[0]):Read End용도식별자종료
。close(fd[1]):Write End용도식별자종료
파이프에 메세지 작성
write(fd[0], 메세지, 메세지크기)
파이프에서 메세지 읽기
read(fd[1], 버퍼, 버퍼크기)
네크워크상에서의IPC System
。Web Server같은Client - Server System에는 2가지프로세스간 통신방법이 존재 (소켓,RPC)
소켓( Sockets ) 컴퓨터네트워크 - 소켓
。네트워크상에서 연결된 두 컴퓨터의프로세스간IPC를 위한Endpoint
▶IPC를 수행할네트워크상컴퓨터의프로세스는소켓번호로 식별
。Client Socket과Server Socket으로 구분되어소켓을 연결하여 두프로세스간 네트워크 통신을 가능하게함.
。프로토콜 계층상에서어플리케이션 계층의프로세스와트랜스포트 계층의프로토콜간Message를 전달하는Interface역할을 수행
。IPC 메카니즘으로Message-Passing을 활용
▶Pipes와 유사
。네트워크 통신을 수행할 각프로세스는 하나의소켓을 가진다
▶소켓을 생성하는 경우Port Number가 식별자로서 각각의소켓에 할당됨
。Socket=IP Address+Port
▶IP Address번호와 각Host의프로세스를 연결하는파이프용도의Port의 번호를 조합하여네트워크로 연결된Host를 식별 후IPC를 수행
▶Pipe=Message Box=Port
ex)www.google.com:8080의 경우도메인 주소로 변환한IP Addrss의서버컴퓨터에서Port를 통해 데이터를 요청
。도커 컨테이너에서Port는서비스의 단위로 해석할 수 있다
▶ 단일IP Address를 가지면서 각각 다른Port를 가지는 다수의도커 컨테이너를 가질 수 있으므로
Server Socket
。Server Socket이 바인딩한포트번호의포트에 연결가능한Client Socket수는 한정됨
▶Load Balancing을 수행
Client Socket
원격 프로시저 호출(RPC: Remote Procedure Calls )
。네트워크상에서 연결된 컴퓨터의프로세스간Procedure Call 메카니즘을 추상화하여 설계된IPC의 확장개념의 기술
。Client는 네트워크로 연결된 다른 컴퓨터를Remote Host로서Host의프로세스의 함수 or프로시저를 원격호출하여 지역함수처럼 실행
。Skeleton과Stub간 통신 시 데이터의Marshaling을 수행하여 전송에 적절한 형식으로 변환
。Java에서는RMI( Remote Method Invocation )로 구현됨
Stub( =Cilent Stub)
。Client Side에서 사용되는Proxy객체로서Client가 다른 컴퓨터의프로세스의Remote Method를 호출하는 주체로 활용
Skeleton( =Server Stub)
。Server-Side에서 실행되어Stub을 통한Client의 요청으로Procedure Call한 실제 Method에 전달 및 결과를Client에게 응답
Java에서의Socket구현
자바 Socket 관련 클래스
。Java의소켓은네트워크 통신을 위한API역할을 수행
。Socket클래스와ServerSocket클래스의 객체를 생성하여Server Socket과Client Socket로서 활용
- Server Socket
。Server-Side로서TCP 서버역할을 수행할 특정포트번호로 바인딩된ServerSocket Class의 객체 생성
▶서버소켓객체는 바인딩된포트번호로Client Socket부터의 연결요청을 상시대기
。Client Socket로부터 연결이 된 경우서버소켓객체.accept()를 통해Client Socket의Socket 객체를 반환
▶Client Socket의Socket Class객체가 반환된 경우 두프로세스간 연결이 정상적으로 되었으며 반환된Socket 객체를 통해 통신을 수행
。통신이 전부 수행된 경우서버소켓객체.close()를 통해프로세스간 연결종료import java.io.*; import java.net.*; public class DateServer { public static void main(String[] args) throws Exception { // 포트 8080의 TCP 서버소켓 생성 ServerSocket server = new ServerSocket(8080); while ( true ){ // 포트:8080으로 Client Socket으로부터의 연결요청 대기 System.out.println("연결대기중"); // 연결요청 수신 및 Client Socket의 Socket 객체 반환 Socket client = server.accept(); PrintWriter pout = new PrintWriter(client.getOutputStream(),true); System.out.println("새로운 클라이언트 소켓 연결됨"); pout.println(new java.util.Date().toString()); // 소켓 종료 및 연결요청 재대기 client.close(); } } }.
- Client Socket
。연결을 수행할 특정ServerSocket의 컴퓨터의IP주소,포트번호를 생성자로 전달하여 연결될Client Socket역할의Socket 객체를 생성
▶ 해당Socket 객체를 통해 데이터의 송수신을 수행import java.io.*; import java.net.*; public class DateClient { public static void main(String[] args) throws Exception { Socket socket = new Socket("127.0.0.1", 8080); InputStream in = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String line = null; // 소켓으로부터 Date 를 Read while ((line = br.readLine()) != null ) System.out.println(line); // 소켓 연결 종료 socket.close(); } }
출처
。운영체제 공룡책 강의