페이지 캐시로 100% 페이징된 파일이 다른 프로세스에 의해 수정되면 어떻게 되나요?

페이지 캐시로 100% 페이징된 파일이 다른 프로세스에 의해 수정되면 어떻게 되나요?

페이지 캐시 페이지가 수정되면 더티로 표시되어 다시 작성되어야 한다는 것을 알고 있지만 다음과 같은 경우에는 어떻게 되나요?

상상하다: /apps/EXE 파일은 페이지 캐시에 완전히 페이징되어(모든 페이지가 캐시/메모리에 있음) 프로세스 P에 의해 실행되는 실행 파일입니다.

그런 다음 연속 릴리스는 /apps/EXE를 새로운 실행 파일로 대체합니다.

가정 1: 나는 프로세스 P(및 이전 실행 파일을 참조하는 파일 설명자를 가진 다른 모든 사람)가 문제 없이 기존 메모리 /apps/EXE를 계속 사용할 것이며 해당 경로를 실행하려는 모든 새 프로세스가 새 실행 파일을 얻게 될 것이라고 가정합니다.

가정 2: 파일의 모든 페이지가 메모리에 매핑되지 않으면 페이지 오류가 발생할 때까지 모든 것이 괜찮을 것이라고 가정합니다. 교체된 파일의 페이지와 세그폴트가 필요할 수 있습니까?

질문 1: vmtouch와 같은 것을 사용하여 파일의 모든 페이지를 잠그면 상황이 바뀌나요?

질문 2: /apps/EXE가 원격 NFS에 있으면 차이가 있습니까? (아닌 것 같아요)

내 가정 중 두 가지를 수정하거나 확인하고 두 가지 질문에 답하십시오.

이것이 일종의 3.10.0-957.el7 커널을 갖춘 CentOS 7.6 시스템이라고 가정합니다.

업데이트: 좀 더 생각해 보면 이 상황이 다른 더티 페이지 상황과 다르지 않은지 궁금합니다.

나는 새 바이너리를 작성하는 프로세스가 읽기를 수행하고 모든 캐시된 페이지를 가져올 것이라고 추측합니다. 왜냐하면 모든 페이지가 페이지 아웃되었기 때문입니다. 그런 다음 해당 페이지는 모두 더티로 표시됩니다. mlocked되면 참조 카운트가 0이 된 후 코어 메모리를 차지하는 쓸모 없는 페이지가 됩니다.

현재 실행 중인 프로그램이 종료되면 다른 프로그램이 새 바이너리를 사용할 것이라고 생각됩니다. 이것이 모두 정확하다고 가정하면 파일의 일부만 페이징된 경우에만 의미가 있다고 생각합니다.

답변1

그런 다음 연속 릴리스는 /apps/EXE를 새로운 실행 파일로 대체합니다.

이것이 중요한 부분입니다.

새 파일을 게시하는 방법은 새 파일(예: )을 만들고 /apps/EXE.tmp.20190907080000, 콘텐츠를 작성하고, 권한과 소유권을 설정하고,마침내(2) 최종 이름으로 이름을 바꾸고 /apps/EXE이전 파일을 바꿉니다.

결과는 새 inode 번호를 가진 새 파일입니다. 즉, 사실상 다른 파일입니다.

그리고 이전 파일에는 자체 inode 번호가 있는데, 이는 실제로아직 거기 있어파일 이름이 더 이상 해당 항목을 가리키지 않더라도(또는 해당 inode를 가리키는 파일 이름이 더 이상 없습니다.)

따라서 여기서 핵심은 Linux에서 "파일"에 대해 이야기할 때 일반적으로 실제로 말하는 것은 "inodes"라는 것입니다. 왜냐하면 일단 파일이 열리면 inode는 우리가 보관하는 파일에 대한 참조이기 때문입니다.

가설 1:프로세스 P(및 이전 실행 파일을 참조하는 파일 설명자를 가진 다른 모든 사람)는 문제 없이 이전 메모리 /apps/EXE를 계속 사용할 것이며 해당 경로를 실행하려는 모든 새 프로세스는 새로운 실행 파일을 얻게 될 것이라고 가정합니다.

옳은.

가설 2:파일의 모든 페이지가 메모리에 매핑되지 않으면 페이지 오류가 발생할 때까지 모든 것이 괜찮을 것이라고 가정합니다. 파일에서 교체된 페이지가 필요하며 세그폴트도 발생할 수 있습니까?

잘못된. 이전 inode가 여전히 존재하므로 이전 바이너리를 사용하는 프로세스의 페이지 오류가 디스크에서 해당 페이지를 계속 찾을 수 있습니다.

이전 바이너리를 실행하는 프로세스의 /proc/${pid}/exe심볼릭 링크(또는 이에 상응하는 출력)를 보면 이에 따른 일부 효과를 확인할 수 있습니다. 그러면 이름은 더 이상 존재하지 않지만 inode는 여전히 존재한다는 것을 알 수 있습니다.lsof/app/EXE (deleted)

또한 바이너리에서 사용하는 디스크 공간은 프로세스가 종료된 후에만 해제된다는 것을 알 수 있습니다(해당 inode가 열려 있는 유일한 프로세스라고 가정). df프로세스 종료 전후의 출력을 확인 하면 더 이상 존재하지 않는다고 생각했던 이전 바이너리만큼 크기가 줄어든 것을 확인할 수 있습니다.

그런데 이는 바이너리 파일에만 적용되는 것이 아니라 열려 있는 모든 파일에 적용됩니다. 프로세스에서 파일을 열고 파일을 삭제하면 프로세스가 파일을 닫거나 종료할 때까지 해당 파일은 디스크에 남아 있습니다. 하드 링크가 디스크의 inode 이름을 가리키는 카운터를 유지하는 것과 유사하게 파일 시스템 드라이버(Linux 커널)는 해당 inode에 대한 참조 수에 대한 카운터를 유지합니다.기억 속에, 실행 중인 시스템에서 해당 inode에 대한 모든 참조가 해제된 후에만 inode가 디스크에서 해제됩니다.

질문 1: vmtouch와 같은 도구를 사용하여 파일의 모든 페이지를 잠그면 상황이 바뀌나요?

이 질문은 페이지를 잠그지 않으면 세그폴트가 발생한다는 잘못된 가정 2를 기반으로 합니다. 하지 않을 것이다.

질문 2: /apps/EXE가 원격 NFS에 있으면 차이가 있습니까? (아닌 것 같아요)

그것은의미는대부분의 경우 동일한 방식으로 작동하지만 NFS에는 몇 가지 "문제"가 있습니다.

때때로 NFS에 아직 열려 있는 삭제된 파일을 볼 수 있습니다(해당 디렉터리에 숨겨진 파일로 표시됨).

NFS 서버가 다시 시작될 때 장치 번호가 "셔플"되지 않도록 NFS 내보내기에 장치 번호를 할당할 수도 있습니다.

그러나 주요 아이디어는 동일합니다. NFS 클라이언트 드라이버는 여전히 inode를 사용하고 inode가 계속 참조되는 동안 (서버에서) 파일을 보존하려고 시도합니다.

답변2

가정 2: 파일의 모든 페이지가 메모리에 매핑되지 않으면 페이지 오류가 발생할 때까지 모든 것이 괜찮을 것이라고 가정합니다. 파일에서 교체된 페이지가 필요하며 세그폴트도 발생할 수 있습니까?

아니요, 커널에서는 현재 실행 중인 파일의 어떤 항목도 열고 교체하는 것을 허용하지 않기 때문에 이런 일이 발생하지 않습니다. 그러한 작업은 실패합니다ETXTBSY[1]:

cp /bin/sleep sleep; ./sleep 3600 & echo none > ./sleep
[9] 5332
bash: ./sleep: Text file busy

dpkg 등이 바이너리를 업데이트할 때 이를 덮어쓰지 않고 대신 rename(2)이를 사용하여 단순히 디렉터리 항목을 완전히 다른 파일로 가리키며 이전 파일에 대한 매핑 또는 열린 핸들이 여전히 있는 모든 프로세스는 계속해서 이를 사용합니다. 질문이 나타나지 않고.

[1]보호는 ETXBUSY"텍스트"(= 라이브 코드/실행 파일)로 간주될 수 있는 다른 파일로 확장되지 않습니다. 공유 라이브러리, Java 클래스 등은 다른 프로세스에 의해 매핑되는 동안 수정됩니다.~ 할 것이다프로세스가 충돌하게 됩니다. Linux에서 동적 링커는 충실하게 MAP_DENYWRITE플래그를 에 전달 mmap(2)하지만 실수하지 마십시오. 효과가 없습니다. 예:

$ cc -xc - <<<'void lib(){}' -shared -o lib.so
$ cc -Wl,-rpath=. lib.so -include unistd.h -xc - <<<'
   extern void lib();
   int main(){ truncate("lib.so", 0); lib(); }
'
./a.out
Bus error

답변3

지속적인 릴리스 프로세스가 통과한다고 가정하면 filbranden의 대답은 정확합니다 rename. 그렇지 않지만 파일이 내부에서 수정되면 상황이 달라집니다. 그러나 당신의 사고방식은 여전히 ​​​​잘못되어 있습니다.

디스크의 내용을 수정하거나 페이지 캐시와 일치하지 않게 하는 것은 불가능합니다.페이지 캐시는 표준 버전입니다.그리고 수정된 것. 파일에 대한 모든 쓰기는 페이지 캐시를 통해 이루어집니다. 이미 존재하는 경우 기존 페이지가 수정됩니다. 아직 존재하지 않는 경우 페이지의 일부를 수정하려고 하면 전체 페이지가 캐시된 다음 이미 캐시된 것처럼 수정됩니다. 전체 페이지 이상에 걸쳐 있는 쓰기는 페이징 읽기 단계를 최적화할 수 있으며 거의 ​​확실히 최적화할 수 있습니다. 어떤 경우든 존재하는 파일의 수정 가능한 표준 버전(*)은 단 하나뿐입니다. 즉, 페이지 캐시에 있는 버전입니다.

(*) 나는 조금 거짓말을했습니다. NFS 및 기타 원격 파일 시스템의 경우 둘 이상이 있을 수 있으며, 사용되는 파일과 마운트 및 서버 측 옵션에 따라 쓰기의 원자성 및 순서 지정 의미를 올바르게 구현하지 않는 경우가 많습니다. 이것이 바로 우리 중 많은 사람들이 그것들이 근본적으로 깨졌다고 생각하고 동시에 작성되면 사용을 거부하는 이유입니다.

관련 정보