페이지 캐시 페이지가 수정되면 더티로 표시되고 다시 쓰기가 필요하다는 것을 알고 있습니다.
시나리오 :
실행 파일 인 / apps / EXE 파일은 페이지 캐시에 완전히 페이징되고 (모든 페이지는 캐시 / 메모리에 있음) 프로세스 P에 의해 실행됩니다.
연속 릴리스는 / apps / EXE를 새로운 실행 파일로 대체합니다.
가정 1 :
프로세스 P (및 이전 실행 파일을 참조하는 파일 디스크립터가있는 다른 사람)는 문제없이 메모리 / apps / EXE에서 이전을 계속 사용하고 해당 경로를 실행하려고하는 새로운 프로세스는 새로운 실행 파일
가정 2 :
파일의 모든 페이지가 메모리에 매핑되지 않는 경우 교체 된 파일의 페이지를 요구하는 페이지 오류가 발생할 때까지 문제가 없으며 아마도 segfault가 발생한다고 가정합니다.
질문 1 :
파일의 모든 페이지를 vmtouch와 같은 것으로 막 으면 시나리오가 전혀 바뀌지 않습니까?
질문 2 :
/ apps / EXE가 원격 NFS에 있다면 차이가 있습니까? (그렇지 않다고 가정)
두 가지 가정을 수정하거나 확인하고 두 가지 질문에 대답하십시오.
이것이 일종의 3.10.0-957.el7 커널을 가진 CentOS 7.6 박스라고 가정하자
업데이트 : 더 생각해 보면이 시나리오가 다른 더티 페이지 시나리오와 다르지 않은지 궁금합니다.
새 바이너리를 작성하는 프로세스는 모두 페이지로 묶여 있기 때문에 모든 캐시 페이지를 읽고 가져오고 모든 페이지가 더티로 표시된다고 가정합니다. 그들이 잠겨 있으면 참조 카운트가 0이 된 후 코어 메모리를 차지하는 쓸모없는 페이지 일뿐입니다.
현재 실행중인 프로그램이 끝나면 다른 모든 것이 새로운 바이너리를 사용할 것이라고 생각합니다. 그것이 모두 맞다고 가정하면 파일의 일부만 페이징 될 때만 흥미로울 것 같습니다.
답변
연속 릴리스는 / apps / EXE를 새로운 실행 파일로 대체합니다.
이것이 중요한 부분입니다.
새 파일을 해제하는 방법은 새 파일 (예를 만드는 것입니다 /apps/EXE.tmp.20190907080000
,) 내용을 작성, 권한과 소유권을 설정하고 마지막으로 최종 이름을 보내고 (2)의 이름을 /apps/EXE
이전 파일을 교체.
결과적으로 새 파일은 새로운 inode 번호를 갖게됩니다 (즉, 다른 파일임을 의미합니다).
그리고 이전 파일에는 고유 한 inode 번호가있었습니다. 실제로 는 파일 이름이 더 이상 가리 키지 않더라도 실제로 는 주변에 있습니다 (또는 더 이상 해당 inode를 가리키는 파일 이름이 없습니다).
따라서 여기서 핵심은 Linux에서 “파일”에 대해 이야기 할 때 파일이 열리면 inode가 파일에 대한 참조이므로 “inodes”에 대해 가장 자주 이야기하는 것입니다.
가정 1 : 프로세스 P (및 이전 실행 파일을 참조하는 파일 설명자가있는 다른 사람)는 문제없이 메모리 / apps / EXE에서 이전을 계속 사용하고 해당 경로를 실행하려고하는 새로운 프로세스는 새로운 실행 파일
옳은.
가정 2 : 파일의 모든 페이지가 메모리에 매핑되지 않는 경우 교체 된 파일의 페이지를 요구하는 페이지 오류가 발생할 때까지 문제가 없으며 아마도 segfault가 발생한다고 가정합니다.
맞지 않습니다. 이전 inode는 여전히 존재하므로 이전 바이너리를 사용하는 프로세스의 페이지 결함은 여전히 디스크에서 해당 페이지를 찾을 수 있습니다.
이전 바이너리를 실행하는 프로세스 에 대한 /proc/${pid}/exe
심볼릭 링크 (또는 동등한 lsof
출력)를 보면이 효과 /app/EXE (deleted)
가 나타납니다.이 이름은 더 이상 존재하지 않지만 inode가 여전히 있음 을 나타냅니다.
바이너리가 사용하는 디스크 공간은 프로세스가 종료 된 후에 만 해제된다는 것을 알 수 있습니다 (아이 노드가 열려있는 유일한 프로세스라고 가정합니다). df
프로세스 종료 전과 후에 출력을 확인 하면 크기가 줄어 듭니다 당신이 더 이상 주변에 없다고 생각했던 오래된 바이너리의.
BTW, 이것은 바이너리뿐만 아니라 열린 파일을 가진 것입니다. 프로세스에서 파일을 열고 파일을 제거하면 해당 프로세스가 파일을 닫을 때까지 파일이 디스크에 보관됩니다 (또는 죽습니다). 하드 링크가 디스크의 inode를 가리키는 이름 수에 대한 카운터를 유지하는 방법과 마찬가지로, Linux 커널에서 파일 시스템 드라이버 는 메모리 에있는 해당 inode 에 대한 참조 수를 카운터로 유지 하고 실행중인 시스템의 모든 참조가 릴리스 된 후에 만 디스크에서 inode를 해제합니다.
질문 1 : 파일의 모든 페이지를 vmtouch와 같은 것으로 막 으면 시나리오가 변경됩니다.
이 질문은 페이지를 잠그지 않으면 segfault가 발생한다는 잘못된 가정 2에 근거합니다. 그렇지 않습니다.
질문 2 : / apps / EXE가 원격 NFS에 있다면 차이가 있습니까? (그렇지 않다고 가정)
그것은 똑같은 방식으로 대부분의 시간 동안 작동하도록 의도 되었지만 NFS에는 “gotchas”가 있습니다.
때로는 NFS에서 여전히 열려있는 파일을 삭제하는 결과물을 볼 수 있습니다 (해당 디렉토리에 숨겨진 파일로 표시됨).
또한 NFS 서버를 재부팅 할 때 장치 번호가 “재편성”되지 않도록 NFS 내보내기에 장치 번호를 할당 할 수있는 방법이 있습니다.
그러나 주요 아이디어는 동일합니다. NFS 클라이언트 드라이버는 여전히 inode를 사용하며 inode가 계속 참조되는 동안 서버에 파일을 보관하려고합니다.
답변
가정 2 : 파일의 모든 페이지가 메모리에 매핑되지 않는 경우 교체 된 파일의 페이지를 요구하는 페이지 오류가 발생할 때까지 문제가 없으며 아마도 segfault가 발생한다고 가정합니다.
커널이 현재 실행중인 파일 내부의 대체물을 쓰기 위해 열 수 없기 때문에 그렇지 않습니다. 이러한 조치는 ETXTBSY
[1] 과 함께 실패합니다 .
cp /bin/sleep sleep; ./sleep 3600 & echo none > ./sleep
[9] 5332
bash: ./sleep: Text file busy
dpkg 등이 바이너리를 업데이트 할 때는 덮어 쓰지는 않지만 rename(2)
단순히 디렉토리 항목을 완전히 다른 파일을 가리 키도록 사용 하며 이전 파일에 대한 핸들 또는 열린 핸들이 여전히있는 프로세스는 문제없이 계속 사용합니다. .
[1] 이러한 보호는 “텍스트”(라이브 코드 / 실행 파일) : 공유 라이브러리, 자바 클래스 등으로 간주 될 수있는 다른 파일로 확장되지 않습니다. 다른 프로세스에 의해 매핑하는 동안 이러한 파일을 수정하는 것입니다 그것은 충돌이 발생합니다. 리눅스에서 동적 링커는 MAP_DENYWRITE
플래그를 충실하게 전달 mmap(2)
하지만 실수하지는 않습니다. 아무것도 영향을 미치지 않습니다.
답변
연속 릴리스 프로세스가을 통해 파일의 적절한 원자 교체를 수행한다고 가정하면 filbranden의 대답은 정확합니다 rename
. 그렇지 않은 경우 파일을 제자리에서 수정하지만 상황이 다릅니다. 그러나 당신의 정신 모델은 여전히 잘못입니다.
페이지 캐시는 정식 버전 이고 수정 된 버전 이기 때문에 디스크에서 항목이 수정되고 페이지 캐시와 일치하지 않을 가능성이 없습니다 . 파일에 대한 모든 쓰기는 페이지 캐시를 통해 이루어집니다. 이미 존재하는 경우 기존 페이지가 수정됩니다. 아직 없으면 부분 페이지를 수정하려고하면 전체 페이지가 캐시되고 이미 캐시 된 것처럼 수정됩니다. 전체 페이지 또는 그 이상에 걸친 쓰기는 페이징하는 읽기 단계를 최적화 할 수 있습니다 (그리고 거의 확실하게 할 수 있습니다). .
(*) 나는 약간 거짓말을했다. NFS 및 기타 원격 파일 시스템의 경우 둘 이상이있을 수 있으며 일반적으로 사용되는 마운트 및 서버 측 옵션에 따라 원 자성 및 쓰기 시맨틱 순서를 올바르게 구현하지 않습니다. 그렇기 때문에 우리 중 많은 사람들이 기본적으로 깨진 것으로 생각하고 사용과 동시에 쓰기가 필요한 상황에서는 사용을 거부합니다.