멀티 쓰레드 환경에서 fork는 조심해야 한다.
리눅스나 유닉스 같은 POSIX 시스템에서는 fork
를 이용해서 자신과 똑같은 프로세스를 만들 수 있다. 이때 fork
를 호출한 프로세스를 부모 프로세스라고 하고, 새로 생성된 프로세스를 자식 프로세스라고 부르는데, 자식 프로세스는 부모 프로세스의 모든 메모리를 복사한다.
fork
는 그 뒤 exec
을 해서 다른 바이너리를 실행시키는 fork-exec이 일반적인 사용법이다. 하지만 자식 프로세스는 부모 프로세스와 완전히 같은 메모리를 가지기 때문에, 스레드가 존재하지 않던 시절에는 exec
을 하지 않고 병렬 처리를 하기 위해서도 자주 사용되었다. 지금은 스레드를 사용하는 것이 더 사용하기 쉽고 가벼운 방식이기에 스레드를 병렬처리를 위해 스레드를 주로 사용하지만, 스레드보다 서로 간에 독립적이기 때문에 일을 분리하기 위해서 사용하기도 한다.
분명히 fork
는 없어서는 안 될 기능이지만 fork
에는 태생적 한계가 있다. 애초에 fork
는 스레드라는 개념이 존재하지 않던 시절에 만들어졌기 때문에 thread-safe 하지 않다. 따라서 멀티스레드 환경에서 사용하려면 조심해서 사용해야 한다.
fork
가 멀티스레드 환경에서 문제를 일으키는 이유는 fork
가 부모 프로세스의 메모리를 전부 복사하지만, fork
를 호출한 스레드를 제외한 나머지 스레드들은 죽어버리기 때문이다. 따라서 fork
를 호출한 스레드 이외의 스레드에서 획득한 자원은 아무것도 해제되지 않는다. 메모리 릭도 충분히 문제지만, 이는 단순한 메모리 릭만을 의미하지 않는다.
fork
가 복사한 메모리에는 힙이나 스택 이외에 mutex나 condition variable 들도 포함된다. 만약 mutex나 condition variable이 다른 스레드에서 사용된 채로 fork
된다면 해당 변수로 보호되는 critical section에는 다시는 진입할 수 없게 된다. 특히나 malloc
같은 thread-safe 한 함수는 대부분 내부적으로 글로벌한 mutex를 사용하기 때문에 내 코드에 mutex가 없어도 안심할 수 없다. 따라서 멀티스레드 환경에서는 fork
하기 전에 다른 스레드가 전부 멈추는 것을 확인하는 것이 좋다.
하지만 fork
후 바로 exec
하면 이런 것을 신경 쓸 필요 없다. exec
의 경우 메모리를 완전히 새로 만들기 때문에 이런 문제가 발생하지 않는다.
도움이 되었습니다 🥰
답글삭제