부모 프로세스와 자식 프로세스
pid가 1인 프로세스(systemd)가 루트 부모 프로세스 역할을 하고, 새로 생성되는 프로세스들을 자식 프로세스라 한다.
이 자식 프로세스들은 또다시 부모가 될 수 있으며 그 결과 트리를 형성한다.
부모 프로세스가 자식 프로세스를 생성할 때,
1. 부모의 메모리 공간을 복사해 줄 수 있다. 전역변수, 힙, 스택 영역이 자식에게 그대로 전달된다. 하지만 복사된 후에는 부모와 자식이 독립적인 메모리 공간을 가진다.(변경해도 서로 영향 x)
2. 자식 프로세스는 부모가 갖고 있던 환경변수를 그대로 상속받는다.
3. file descriptor를 공유할 수 있다. 부모가 열린 파일을 자식도 동일하게 사용할 수 있다.
4. IPC(파이프, 메세지 큐, 공유 메모리, 소켓)를 사용하면 부모가 자식에게 직접 데이터를 전달할 수 있다.
부모가 자식을 생성하고, 두 프로세스를 실행시키는 데 두가지 가능한 방법이 존재한다.
부모 프로세스는 자식을 만든 후, 자신도 계속 실행을 이어간다. fork()로 자식 프로세스를 생성한 후, 부모와 자식이 각자 실행된다.
부모는 wait()(시스템 콜)을 통해, 자식이 끝날 때 까지 기다릴 수 있다.(멈춰있다)
또한 부모가 새로운 프로세스를 만들면 그 프로세스의 메모리가 어떻게 구성될지도 두가지 방식이 있다.
자식 프로세스가 부모 프로세스의 복사본을 가지거나, 자식 프로세스가 완전히 새로운 프로세스를 실행한다.
하지만 modern OS에서는 진짜로 메모리를 복사하지 않고 필요할 때만 복사하도록 최적화한다.
부모 프로세스는 "부모 프로세스 실행 중"을 출력하고 끝난다.
자식 프로세스는 부모의 복사본이 아니라 새로운 프로그램을 실행하게 된다.
운영체제는 이처럼 부모와 자식이라는 개념을 활용하여 멀티태스킹과 프로세스 관리를 수행하고, 데이터를 전달한다.
프로세스 간 통신
기본적으로 프로세스 간 통신은, 같은 공유 메모리 상에서 이루어진다. 크게 간접통신과 직접통신으로 나뉜다.
직접 통신은 프로세스들이 서로의 이름을 직접 알아야 하므로 유지보수가 어렵고 잘 사용되지 않는다.
송신자와 수신자의 id를 하드코딩해야 하기 때문에, 그 수가 늘어나면 복잡해진다.
때문에 운영체제가 관리하는 간접통신 방법이 일반적이다. 운영체제는 독립적으로 존재하며 어떠한 프로세스에 예속되지 않는 메세지 큐를 반드시 제공해야 한다.
간접 통신에서는 해당 메세지 큐나 포트를 통해 데이터를 주고받으며, 메세지 큐는 버퍼의 크기에 따라 무용량, 유한, 무한 버퍼로 나뉜다.
무용량 버퍼는 메세지를 저장할 곳이 없어 수신자가 즉시 받아야 하므로 블로킹 방식과 유사하다.
유한 버퍼는 저장할 수 있는 메세지 개수가 제한적이기 때문에, 버퍼가 가득 차면 송신자는 대기해야 하고 버퍼가 비어 있으면 수신자가 기다려야 하므로 블로킹과 논블로킹 방식이 상황에 따라 달라진다.
무한 버퍼는 송신자가 언제는 메세지를 보낼 수 있어 논블로킹 방식과 유사하다.
결국 대부분의 IPC는 간접 통신을 기반으로 이루어지며, 메세지 전달 방식은 버퍼 크기와 블로킹 여부에 따라 다르게 동작하게 된다.
무용량 버퍼 (Zero-Capacity) | 수신자가 없으면 대기 | 송신자가 있어야 메시지 받음 | 블로킹 |
유한 버퍼 (Finite-Capacity) | 버퍼가 가득 차면 대기 | 버퍼가 비어 있으면 대기 | 상황에 따라 블로킹/논블로킹 변함 |
무한 버퍼 (Infinite-Capacity) | 즉시 메시지 저장 가능 | 언제든지 메시지 받음 | 논블로킹 |
Inter-Process Communication, IPC
부모-자식 프로세스는 독립적인 메모리 공간을 가지므로, 그냥 변수 변경으로는 데이터를 공유할 수 없다. 따라서 별도의 데이터 전달 방식이 필요했고 IPC가 등장했다.
IPC는 같은 컴퓨터 내 프로세스 간, 혹은 네트워크를 통한 원격 프로세스 간 데이터를 주고받을 수 있도록 해주는 기술이다
속도, 데이터 구조, 동기화 필요여부에 따라 적절한 IPC방식을 선택해야 한다.
1. 파이프(Pipe)
한 프로세스가 데이터를 쓰고, 다른 프로세스가 읽는다. 부모-자식 관계에서만 사용 가능한 단방향 통신으로 구현이 간단하다.
2. 메세지 큐(Message Queue)
커널이 제공하는 메세지 버퍼를 통해 통신한다. 구조화된 메세지 전송이 가능하다.
다수의 프로세스 간 통신이 가능하며, 여러 개의 프로세스가 메세지를 공유할 수 있다.
하지만 메세지에 크기 제한이 있으며, 여러 프로세스 간 동기화가 필요하다.
(message queue의 발전형은 kafka라고 볼 수 있을 것 같다. os 메세지 큐보다 더 큰 규모의 분산 메세지 처리를 가능케 하니까..)
3. 공유 메모리(Shared Memory)
부모-자식 프로세스가 같은 메모리 영역을 공유하는 방법이다.
메모리에 직접 접근하므로 가장 빠르며, race condition을 방지하기 위해 동기화(뮤텍스, 세마포어)가 필요하다.
(Redis는 공유 메모리와 메세지 큐 개념을 결합-확장한 인메모리 데이터 저장소라고 볼 수 있을 것 같다.)
4. 소켓(Socket)
네트워크를 통한 원격 프로세스 간 통신이다. 같은 컴퓨터 내에서 프로세스 간 통신하는 로컬 소켓과, 네트워크를 통해 다른 컴퓨터와 통신하는 TCP/IP 소켓이 있다.
구현이 간단하지 않다.
(HTTP 기반으로 웹 클라이언트와 서버 간 실시간 양방향 통신을 가능케 한, websocket이 IPC socket의 발전형일것이다.)
5. FIFO(Named Pipe)
파이프와 비슷하지만, 부모-자식 관계가 아닌 프로세스 간에도 파일처럼 이용 가능하다. 하지만 이 또한 단방향(반이중 half duplex) 통신과 다름없다. 데이터가 양방향으로 전송될 필요가 있다면, 보통 2개의 FIFO가 사용된다.
(이 또한 kafka, redis pub/sub, rabbitMQ.. FIFO는 고수준 메세지 브로커 기술의 원시적인 형태인 듯.)
Quiz
Q3.4 일부 컴퓨터 시스템은 다수의 레지스터 집합을 제공한다. 새 컨텍스트가 레지스터 집합 중 하나에 이미 적재된 경우, 컨텍스트 스위칭 시 어떤 일이 발생하는지 설명하라. 새 컨텍스트가 레지스터 집합이 아닌 메모리에 있고 모든 레지스터 집합이 사용 중이면 어떤 일이 발생하는가?
A. 이미 적재되어 있는 상황이라면 그냥 컨텍스트 스위칭을 수행할 수 있다.
A. 우선순위가 낮은 PCB의 컨텍스트와 스위칭되거나 인터럽트가 발생되길 기다렸다가 스위칭 될것이다.
Q3.5 프로세스가 fork()연산을 사용하여 새로운 프로세스를 생성할 때 스택, 힙 중 어떤 상태가 부모 프로세스와 자식 프로세스 간에 공유되는가?
A. 힙, 스택, 전역변수 등 대부분 메모리가 복사된다. 처음에는 단순히 공유되었다가 자식에서 필요할 경우(수정하는경우)에만 복사된다.
(피드백: 이정도면 ok)
RPC는 살짝 머리아파서 넘겼다. 나중에 봐야지
'독서기록장' 카테고리의 다른 글
2. 운영체제 3장 프로세스 기초, 컨텍스트 스위칭 (1) | 2025.01.29 |
---|---|
1. 객체지향의 사실과 오해 (0) | 2024.12.23 |