지난번 인스타그램이 서버를 최적화한 여러 포스트를 작성하면서 ‘Copy on Write’ 라는 키워드를 많이 접하게 되었습니다. 관련해서 찾아보았던 내용 아래에 정리해 보았습니다.


1. Virtual Memory

운영체제는 프로세스 실행 시에 고유한 메모리를 할당하게 되는데, 실제 컴퓨터의 물리 메모리가 아니라 추상화된 자원을 할당합니다. 이러한 추상화된 메모리 공간을 가상 메모리(Virtual Memory)라고 합니다.

가상 메모리는 컴퓨터의 메모리보다 큰 프로세스를 실행하기 위해 고안된 기술입니다. 여러 프로세스가 동시에 실행되는 멀티프로세싱 환경에서는 프로세스가 점유하는 메모리가 컴퓨터의 실제 물리 메모리를 초과하는 경우가 발생할 수 있는데, 이러한 상황에서 자원을 효율적으로 관리하기 위해 가상 메모리라는 개념이 도입되었습니다.

운영체제는 가상 메모리 주소(virtual address) 공간을 할당하고, 필요할 때마다 실제 물리 주소(physical address)에 매핑합니다. 이러한 작업은 운영체제에서 MMU(Memory Management Unit)라고 불리는 메모리 관리 유닛에 의해 처리됩니다.

이렇게 가상 메모리를 도입하면서 여러 이점이 있는데, 우선 응용 프로그램이 공유 메모리 공간을 직접 관리할 필요가 없어졌습니다. 또한 프로세스 간의 공유 메모리를 활용하여 공통으로 사용하는 라이브러리를 효율적으로 관리할 수 있습니다. 그리고 페이징이나 세그먼트 기술로 컴퓨터가 보유한 메모리보다 더 많은 메모리를 할당하여 프로그램을 실행할 수 있습니다.

1.1 Page

가상 메모리 구현체는 메모리 공간을 연속적인 가상 메모리 주소 블록으로 나누어 저장하는데, 이러한 블록을 페이지(Page)라고 합니다. 하나의 페이지는 최소 4KB의 크기를 가집니다.

페이지 테이블(Page Table)은 메모리의 가상 주소와 물리 주소를 연결하는 자료 구조입니다. 응용 프로그램은 가상 주소 공간에서 작동하지만 페이지 테이블에 따라 물리 주소로 변환되어 메모리에 접근하게 됩니다. 이러한 변환을 처리하는 하드웨어가 바로 위에서 설명한 MMU입니다.

각 페이지 테이블 엔트리에는 해당 페이지가 실제 물리 메모리에 있는지 여부를 추적하고 있습니다. 해당 페이지가 메모리에 존재하는 경우에는 물리 주소를 포함하고, 존재하지 않는 경우에는 디스크로 이동해야 함을 알리는 플래그를 포함하고 있습니다.

가상 메모리의 모든 데이터가 물리 메모리에 저장되는 것은 아닙니다. 메모리가 부족해짐에 따라 일부 데이터가 물리 메모리에서 디스크로 옮겨질 수 있습니다. 이때 물리 메모리에 존재하지 않는 페이지에 접근하는 경우 페이지 폴트(Page Fault)를 발생시키고 해당 페이지를 디스크에서 메모리로 옮겨오거나 다른 필요한 조치를 취하게 됩니다.


2. Copy on Write

Copy on Write(COW)는 여러 프로세스가 물리적인 메모리 공간을 공유하는 경우에 이를 최적화 하기 위한 작업입니다. 메모리 페이지에 대해서 복제본이 필요한 경우에만 복사를 수행하여 공유 메모리를 효율적으로 관리합니다.

COW는 다음 방식으로 작동합니다.

  1. 먼저 메모리의 특정 페이지를 읽기 전용으로 표시합니다.
  2. 읽기 전용 페이지에 대한 변경 요청이 들어오면 커널이 새로운 물리 공간을 할당합니다. 만일 해당 페이지에 대한 참조 개수가 하나뿐이라면 새로운 할당 없이 기존 페이지에 직접 데이터를 변경합니다.
  3. 페이지 테이블의 참조를 업데이트하고 변경할 데이터를 입력합니다.

이런 방식으로 원본 데이터를 보호하면서 이루어진 변경이 다른 프로세스에는 영향이 가지 않도록 할 수 있습니다.

COW는 보통 fork() 시스템 호출 작업을 수행하면서 많이 발생합니다.

기본적으로 fork() 시스템 호출로 자식 프로세스를 생성하면 부모 프로세스의 메모리가 그대로 복제됩니다. 이때 각 프로세스는 독립된 가상 주소 공간을 가지지만 동일한 물리 메모리를 바라보게 됩니다.

이때 부모 프로세스의 모든 메모리를 복제하게 된다면 비효율적이기 때문에 COW 메커니즘을 통해 데이터가 수정되는 경우에만 완전한 복제를 수행합니다.

Copy-on-Write는 프로세스 생성 초기에 메모리 복사와 관련된 오버헤드를 줄여줌으로써 성능을 향상할 수 있습니다. 특히 메모리 자원을 공유하는 프로세스가 많을수록 더 효율적입니다. 다만 메모리의 수정이 자주 발생하는 경우엔 이러한 이점이 감소할 수 있으며, 특히 메모리 페이지에 대한 복사본이 필요한 순간까지 메모리를 공유하고 있어야 하므로 추가적인 메모리 사용이 발생할 수 있습니다.


References