Copy-on-Write의 정의
Copy-on-Write(COW, 쓰기 시 복사)는 리소스를 복사해야 할 때 실제로 즉시 복사하지 않고, 원본 데이터를 공유하다가 누군가 ‘쓰기(write)’ 작업을 시도하는 순간에만 복사를 수행하는 최적화 기법입니다. 즉, 읽기만 하는 동안에는 굳이 데이터를 두 벌로 만들 필요가 없다는 아이디어에서 출발합니다.
비유하자면 도서관에서 책을 빌려 여러 사람이 함께 ‘보는’ 상황과 비슷합니다. 다들 그냥 읽기만 한다면 원본 책 한 권으로 충분하죠. 하지만 누군가 책에 밑줄을 긋고 싶다면, 그 사람만을 위해 복사본을 만들어주고 그 복사본에만 낙서를 하게 하는 겁니다. 나머지 사람들은 여전히 원본을 그대로 봅니다.
fork()와 COW의 동작 원리
리눅스에서 fork()는 부모 프로세스를 복제해 자식 프로세스를 만드는 시스템 콜입니다. 예전 방식이라면 부모의 메모리 전체(코드, 데이터, 힙, 스택)를 그대로 복사해야 하는데, 프로세스 메모리가 수백 MB만 되어도 이 복사 작업은 매우 무겁고 느립니다.
그래서 현대 운영체제는 COW를 적용합니다. fork() 호출 시 실제 메모리 데이터를 복사하는 대신, 부모와 자식의 페이지 테이블이 동일한 물리 메모리 페이지를 가리키도록 설정만 해줍니다. 이때 해당 페이지들은 ‘읽기 전용’으로 표시됩니다.
- 부모와 자식이 그냈 읽기만 한다면 → 물리 메모리는 계속 공유됨
- 부모나 자식 중 한쪽이 데이터를 수정하려 하면 → CPU가 페이지 폴트를 발생시킴
- 커널이 이 폴트를 감지하고, 그 순간에만 해당 페이지를 실제로 복사해 각자 독립된 페이지를 갖게 함
결국 수정이 일어난 페이지만 복사되고, 나머지 페이지는 끝까지 공유된 채로 남을 수도 있습니다.
실무에서 COW가 중요한 이유
대표적인 활용 사례가 서버 프로그램에서 자식 프로세스를 생성해 요청을 처리하는 구조입니다. 예를 들어 Redis는 BGSAVE로 데이터를 디스크에 저장할 때 fork()로 자식 프로세스를 만들어 스냅샷 작업을 맡깁니다. COW 덕분에 수십 GB짜리 데이터셋이라도 즉시 자식 프로세스가 생성되며, 저장 도중 실제로 변경되는 데이터만 그때그때 복사되므로 메모리와 시간을 크게 절약합니다.
만약 COW가 없었다면 fork()를 호출할 때마다 전체 메모리를 복사해야 하니, 대용량 프로세스에서는 그 자체로 심각한 병목이 되었을 것입니다. COW는 ‘필요한 순간에만 비용을 지불한다’는 지연 평가(lazy evaluation) 철학을 메모리 관리에 적용한 대표적인 예이며, 프로세스 생성 비용을 획기적으로 낮춰 유닉스 계열 운영체제의 성능을 지탱하는 핵심 기법 중 하나입니다.