코학다식
[OS] 협력하는 프로세스들 본문
독립적인 프로세스들은 다른 프로세스의 실행에 영향을 주거나 받을 수 없다. 하지만 협력하는 프로세스들은 다른 프로세스의 실행에 영향을 주거나 받을 수 있다. 여러 개의 프로세스/스레드(thread)가 동시에 작업을 진행하는 것이다.
이는
- 정보 공유
- 계산 속도 증가
- 모듈성
- 편리함
등의 이점을 준다.
Inter-Process Communication(IPC)
IPC는 프로세스들이 통신하고 그들의 행위를 동기화하기 위한 매커니즘이다. 두 가지 방법이 존재한다.
- 공유된 변수(또는 메모리) 사용
- 메시지 전달 사용
POSIX Shared Memory(Producer)
#include <stdio.h>
#include <stlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main()
{
/* the size (in bytes) of shared memory object */
const int SIZE 4096;
/* name of the shared memory object */
const char *name = "OS";
/* strings written to shared memory */
const char *message_0 = "Hello";
const char *message_1 = "World!";
/* shared memory file descriptor */
int shm_fd;
/* pointer to shared memory object */
void *ptr;
/* Create the shared memory object */
shm_fd = shm_open(name, O_CREAT | O_RDRW, 0666);
/* configure the size of the shared memory object */
ftruncate(shm_fd, SIZE);
/* memory map the shared memory object */
ptr = mmap(0, SIZE, PROT_WRITE, MAP_SHARED, shm_fd, 0);
/* write to the shared memory object */
sprintf(ptr, "%s", message_0);
ptr += strlen(message_0);
sprintf(ptr, "%s", message_1);
ptr += strlen(message_1);
return 0;
}
POSIX Shared Memory(Consumer)
#include <stdio.h>
#include <stlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/stat.h>
int main()
{
/* the size (in bytes) of shared memory object */
const int SIZE 4096;
/* name of the shared memory object */
const char *name = "OS";
/* strings written to shared memory */
const char *message_0 = "Hello";
const char *message_1 = "World!";
/* shared memory file descriptor */
int shm_fd;
/* pointer to shared memory object */
void *ptr;
/* Create the shared memory object */
shm_fd = shm_open(name, O_RDONLY, 0666);
/* memory map the shared memory object */
ptr = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
/* read from the shared memory object */
printf("%s", (char*)ptr);
/* remove the shared memory object */
shm_unlink(name);
return 0;
}
위와 같은 방식으로 공유된 메모리를 통해 프로세스끼리 통신할 수 있다. 사용된 함수는 모두 System call 함수이다.
Message Passing System
메시지 전달 방식은 프로세스들이 같은 주소의 공간을 공유하지 않고 통신한다. IPD (message passing) 도구는 두 가지의 오퍼레이션을 제공한다
- send(message) - 고정된 크기의 메시지나 변수, 다양한 형태의 parameter
- receive(message)
만약 P와 Q가 통신하길 원한다면, 다음과 같은 것이 필요하다.
- 둘 사이의 통신 링크(논리적 링크)
- send/receive를 통한 메시지 교환
메시지 전달 시스템을 디자인하는 데에는 다음과 같은 이슈들이 존재한다. 차례대로 하나씩 살펴보도록 하자.
- 네이밍(Naming)
- 동기화(Synchronization)
- 버퍼링(Buffering)
Naming
- Direct Communication: 프로세스들은 서로를 명확히 지정해야 한다.
- send(P, message) - 프로세스 P로 메시지를 보낸다.
- receive(Q, message) - 프로세스 Q로부터 온 메시지를 받는다.
- Indirect Communication: 메시지는 특정 프로세스를 지정하지 않고 특정 사서함(포트)로 지정되고 전송된다.
- 각각의 사서함은 고유한 아이디를 가진다.
- 프로세스들은 그들의 사서함을 공유할 때만 통신할 수 있다.
- send(A, message) - 사서함 A로 메시지를 보낸다.
- receive(A, message) = 사서함 A로부터 메시지를 받는다.
Sychronization
메시지 전달은 blocking 또는 non-blocking일 수 있다.
Blocking은 동기적이다.
Non-blocking은 비동기적이다.
Blocking send: 메시지를 보내는 프로세스는 메시지가 메시지를 받는 프로세스나 사서함에 보내지기 전까지 동작하지 않는다.
Non-blokcing send: 메시지를 보내는 프로세스는 메시지를 보내고 오퍼레이션을 다시 시작한다.
Blocking receive*: 메시지가 유효하기 전까지 동작하지 않는다.
Non-blocking receive: 유효한 메시지든 아니든 계속 동작한다.
Buffering
메시지 큐가 링크에 붙어 있다. 일종의 데이터 보관함이다. 세 가지 방법 중 하나로 구현된다.
- Zero capacity: 0 messages, 보내는 프로세스는 받는 프로세스를 기다려야만 한다. (동기화) 이 경우 버퍼는 필요하지 않다.
- Bounded capacity: n messages, 보내는 프로세스는 링크가 꽉 찬 경우 기다려야 한다.
- Unbounded capacity: 무한한 길이, 보내는 프로세스는 기다리지 않는다. 하드웨어에 제한이 존재하므로 현실적으로는 불가능하다.
IPC in UNIX
Inter-Process Communication은 여러 프로세스들이 그들 사이에서 통신하기 위한 매커니즘이다. 다른 프로세스들은 다른 주소 공간에서 동작한다. 따라서 운영체제는 통신하기 위한 매커니즘을 제공해야 하는데, 이것이 IPC이다.
UNIX에서의 IPC 타입들은 다음과 같다. system V IPC는 전통적 IPC를 사용할 수 있다.
- 전통적 UNIX IPCs: signal, pipe, socket
- System V IPCs: message queue, semaphore, shared memory
Pipe
파이프(pipe)는 한 프로세스를 다른 프로세스로 연결하는 단방향의 byte 스트림이다.
- 한 통로의 끝에서 작성된 데이터는 다른 끝에서 읽힌다.
- 논리적 관점에서, 파이프는 글자들의 FIFO 큐와 유사하다.
- 구조화되지 않은 통신: 파이프에 있는 데이터의 송신자, 수신자나 크기는 알 수 없다.
- 파이프에 접근하는 것은 file descriptor에/로부터 작성함으로써/읽음으로써 가능하다.
Pipe Used by Commands
파이프 매커니즘의 현재 사용 중 하나는 커맨드들이 연결될 때 커맨드 라인 인터프리터의 수단으로써 수행되는 것이다.
Ordinary (Anonymous) pipe
프로세스에 의해 생성된, 그리고 연관된 descriptor 사이에서의 전달은 parend-child(조상과 자손) 사이에서만 가능하다.
혹은 파이프의 생성자를 공통된 조상으로 하는 프로세스들 사이에서의 통신만 제한적으로 가능하다.
anonymous pipe의 생성
int pipe(int filesdes[2]);
- filesdes[0]: read descriptor, filesdes[1]: write descriptor
- filesdes는 desriptor의 배열이다.
Use of Pipe - Example
#include <stdio.h>
#include <unistd.h>
int main(void){
/* fd[0] is for read, fd[1] is for write */
inr n, fd[2], pid; char line[100];
if(pipe(fd) < 0) exit(-1); // make pipe
if((pid == fork()) < 0) exit(-1);
else if(pid > 0) { /* parent */
close(fd[0]);
write(fd[1], "Hello, World\n", 12);
wait(NULL) //wait child
}
else { /* child */
close(fd[1]);
n = read(fd[0], line, MAXLINE);
write(STDOUT_FILENO, line, n);
}
}
Named Pipe(FIFO)
anonymous 파이프의 제약이 named 파이프에서는 없다. 왜냐하면 그들의 엔트리들은 파일 시스템에 존재하기 때문이다. 커널 안의 메모리에 파일을 만든 개념이라고 생각할 수 있다.
이름을 가지고 파일처럼 다루어진다. (e.g., open, close, read, write)
mkfifo
또는mknod
로 생성된다.- C언어 함수에 의해 생성될 수도 있다.
int mkfifo(const char *path, mode_t mode);
- path가 이름이 된다.
- named 파이프에서 읽어오거나 쓰는 건 standard
read()
그리고write()
시스템 콜을 사용하면 된다.
'Fundamentals > OS' 카테고리의 다른 글
[OS] CPU 스케줄링(CPU Scheduling) (0) | 2020.09.01 |
---|---|
[OS] 병행성(concurrency)과 스레드(thread) (0) | 2020.09.01 |
[OS] 프로세스 생성과 종료 (0) | 2019.09.26 |
[OS] 프로세스와 프로세스 스케줄링 (0) | 2019.09.24 |
[OS] Boot, 운영체제의 구조, VM (0) | 2019.09.19 |