운영체제에서 프로세스는 자신만의 가상 메모리를 할당 받아 이용하게 된다. 각 프로세스는 서로 다른 가상 메모리 영역으로 분리되어 있기때문에, 다른 프로세스의 가상 메모리에는 접근하지 못 한다. 그런데 프로세스 간 통신이 필요한 경우가 간혹 있다. 예를 들어, 포그라운드와 백그라운드가 분리되어 있는 경우에 그 둘은 데이터를 주고 받을 수 있어야 한다. 데이터베이스를 다루는 경우, 대개는 사용자로부터 SQL 쿼리를 입력받는 프론트엔드 프로세서와 실제 디스크 입출력으로 데이터를 처리하는 백엔드 프로세스가 분리되어 있다.
물론, 하나의 프로세스 내에서 스레드를 생성하여 활용할 수 있다. 스레드의 가장 큰 문제라고 하면, 한 스레드가 죽으면 프로세스 전체에 영향이 간다는 점이다. 아래 예시를 보자.
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
mutex my_mutex;
void my_thread(int id) {
my_mutex.lock();
cout << "my id is: " << id << endl;
if (id == 5) {
throw 1;
}
my_mutex.unlock();
}
int main() {
thread threads[10];
for (int i = 0; i < 10; i++) {
threads[i] = thread(my_thread, i);
}
for (int i = 0; i < 10; i++) {
threads[i].join();
}
return 0;
}
위 코드를 컴파일 하고 실행하면, i가 5일 때 생성된 스레드에서 예외가 발생하게 된다. 그럼 다른 스레드는 어떻게 될까?
$ ./threads_exception_test
my id is: 0
my id is: 1
my id is: 2
my id is: 3
my id is: 7
my id is: 5
libc++abi: terminating due to uncaught exception of type int
[1] 82759 abort ./threads_exception_test
프로세스 자체가 죽어버린다. 다른 모든 프로세스도 당연히 덩달아 죽어버리게 된다. 반면에, 아래 코드를 보자.
#include <unistd.h>
#include <iostream>
using namespace std;
int main() {
pid_t pid;
for (int i = 0; i < 10; i++) {
pid = fork();
if (pid > 0) {
break;
}
}
// 자식 프로세스일 때 예외 발생
if (pid > 0) {
throw 1;
}
cout << getpid() << endl;
return 0;
}
부모 프로세스에서 자식 프로세스를 10개 fork하고 전부 예외를 발생시킨다. 그러면 부모 프로세스는 어떻게 될까?
$ ./processes_exception_test
libc++abi: terminating due to uncaught exception of type int
libc++abi: terminating due to uncaught exception of type int
libc++abi: terminating due to uncaught exception of type int
libc++abi: terminating due to uncaught exception of type int
libc++abi: terminating due to uncaught exception of type int
libc++abi: terminating due to uncaught exception of type int
libc++abi: terminating due to uncaught exception of type int
libc++abi: terminating due to uncaught exception of type int
libc++abi: terminating due to uncaught exception of type int
83252
libc++abi: terminating due to uncaught exception of type int
부모 프로세스의 pid 값이 정상적으로 출력되는 것을 볼 수 있다. 즉, 스레드와 달리 자식 프로세스가 죽더라도 부모 프로세스에는 영향이 없다. 반대로 자식 프로세스는 그대로 두고 부모 프로세스를 죽였을 땐 어떻게 될까? 놀랍게도 자식 프로세스들은 잘 살아있는 모습을 볼 수 있다.
#include <unistd.h>
#include <iostream>
using namespace std;
int main() {
pid_t pid;
for (int i = 0; i < 10; i++) {
pid = fork();
if (pid == 0) {
break;
}
}
// 부모일 때만 예외를 발생
if (pid > 0) {
throw 1;
}
cout << getpid() << endl;
return 0;
}
$ ./processes_exception_test_2
83878
83879
83880
83881
83882
83883
83884
83885
83886
libc++abi: terminating due to uncaught exception of type int
83887
[1] 83877 abort ./processes_exception_test_2
여담이지만 데몬 프로세스가 이런 방식으로 생성이 된다.
이처럼 개별 작업이 다른 작업에 영향을 받지 않기 위해서라도 멀티 프로세스 방식을 적용하곤 한다. 스레드 간 통신은 전역 변수를 활용하고 mutex나 semaphorer를 활용하여 손쉽게 접근함으로써 구현할 수 있다. 스레드는 같은 가상 메모리 공간을 공유하기 때문이다. 반면에, fork를 통해 생성된 자식 프로세스는 가상 메모리 공간이 아에 새로 할당되기 때문에 그러한 방식은 활용할 수 없다.
IPC에는 다양한 방식이 있다. 파일 시스템, 소켓, 파이프, 세마포어, 공유 메모리 등등
1. 파일 시스템을 활용한 IPC
'개발 > CS' 카테고리의 다른 글
| Logistic Regression (1) | 2022.12.13 |
|---|---|
| Bayesian DecisionTheroy (1) | 2022.12.12 |