기여 목록 / 8402737
FSA: Restore mmap change notifications on Linux
Linux 파일 변경 감시에서 일반 쓰기의 조기 알림과 writable mmap 변경 알림을 함께 제공하려고 시도한 패치입니다. 두 이벤트를 함께 처리할 때 발생하는 중복 알림을 정확히 구분할 수 없어 중단했습니다.
문제 설명
CL 8345888은 IN_CLOSE_WRITE 대신
IN_MODIFY를 구독해 파일 디스크립터를 닫기 전에도 일반 쓰기를 알리도록
변경했습니다. 그러나 MAP_SHARED를 통한 변경에는 IN_MODIFY가 발생하지
않아, writable mmap의 마지막 참조를 해제할 때 받던 변경 알림이 사라졌습니다.
IN_CLOSE_WRITE를 다시 구독하면 mmap 변경을 감지할 수 있지만, 일반적인
쓰기 후 닫기에서는 IN_MODIFY와 IN_CLOSE_WRITE가 각각 수정 알림을
생성할 수 있습니다. 두 알림이 서로 다른 inotify 읽기 버퍼에 들어오면
같은 버퍼 안에서만 처리하는 병합 로직으로는 중복을 막을 수 없습니다.
해결 내용
최종 패치셋 11에서는 다음 접근을 시도했습니다.
IN_MODIFY와IN_CLOSE_WRITE를 함께 구독해 일반 쓰기의 조기 알림과 writable mmap의 닫기 시점 알림을 모두 전달합니다.- 같은 읽기 버퍼 안에서 동일 항목에 대한 연속된 수정 이벤트를 합칩니다. 서로 다른 버퍼의 이벤트는 합치지 않아 뒤늦은 mmap 변경 알림을 버리지 않도록 했습니다.
- inotify 이벤트 길이를 검사하고, 검사된
base::span으로 이름을 비교합니다. 잘못된 이벤트가 있으면 오류를 기록하고 현재 버퍼의 나머지 처리를 멈추되, 앞서 확인한 이벤트는 보존합니다. - mmap과 이벤트 경계에 대한 회귀 테스트를 추가하고, 일반 쓰기 테스트는 정확한 콜백 횟수 대신 각 쓰기 후 새 알림이 도착하는지 검사했습니다.
하지만 최종 리뷰에서 일반 쓰기 후 닫기 동작을 다시 재현한 결과,
IN_MODIFY를 처리한 뒤 IN_CLOSE_WRITE가 별도 수정 알림을 전달하는
현상이 제어된 실행 100회 모두에서 확인되었습니다. inotify 이벤트에는
수정과 닫기가 같은 쓰기 디스크립터에서 왔는지 식별할 정보가 없습니다.
경로만 기준으로 나중의 닫기 알림을 억제하면 다른 디스크립터에서 발생한
실제 mmap 변경까지 놓칠 수 있습니다.
따라서 이 CL은 병합하지 않고 중단했습니다. Gerrit의 중단 메시지는 CL 8467263에서 조기 알림 변경을 되돌려 기존의 닫기 기반 동작으로 돌아가는 방향을 명시합니다. 이는 열린 디스크립터를 통한 일반 쓰기가 닫기 전에는 보고되지 않는 제한도 다시 생기므로, 해당 경우는 별도로 다뤄야 합니다.
테스트 방법
- 기존 기록에 따르면 WSL Linux의 tmpfs와 ext4에서 일반 쓰기 및 mmap의 inotify 이벤트를 비교했습니다.
- 수정한
FilePathWatcher코드를 로컬 하네스에서 컴파일하고 테스트했으며, 기존 기록에는 watcher 테스트 208개와 mmap·버퍼 관련 테스트 24개가 통과했다고 적혀 있습니다. - 패치셋 4의 CQ 컴파일에서
memcmp의 안전하지 않은 버퍼 사용 오류가 발견되어, 길이가 확인된 span으로 비교하도록 수정했습니다. - Gerrit 기록상 최종 패치셋 11은 CQ dry run을 통과했습니다. 다만 이 결과가 서로 다른 읽기 버퍼에서 발생하는 중복 알림 문제를 해결했다는 뜻은 아닙니다.
- 최종 리뷰에서
IN_MODIFY와IN_CLOSE_WRITE의 중복 수정 콜백을 제어된 실행 100회 모두에서 재현했고,IN_MODIFY만으로는 writable mmap 변경을 감지하지 못함을 다시 확인했습니다.
배운 점
같은 파일에서 발생한 수정 이벤트라도 이전 알림을 근거로 뒤따르는 닫기 이벤트를 무조건 버릴 수는 없습니다. inotify가 쓰기 디스크립터의 식별 정보를 제공하지 않아, 중복 알림과 별개의 mmap 변경을 정확히 구분할 수 없기 때문입니다.
같은 읽기 버퍼 안의 이벤트만 합치는 방법은 알림 수를 줄일 수 있지만 버퍼를 가로지르는 중복까지 해결하지는 못했습니다. 회귀가 확인되었을 때는 시도한 패치를 중단하고 기존 동작으로 되돌리는 방향을 선택했습니다.