스택 오버플로 질문은 처음에는 충분 해 보였지만, 귀하의 의견으로는 왜 여전히 이것에 대해 의문을 가질 수 있는지 이해합니다. 나에게 이것은 정확히 두 가지 UNIX 하위 시스템 (프로세스와 파일)이 통신 할 때 관련된 중요한 상황 입니다.
아시다시피 UNIX 시스템은 일반적으로 파일 하위 시스템과 프로세스 하위 시스템의 두 가지 하위 시스템으로 나뉩니다. 이제 시스템 호출을 통해 달리 지시되지 않는 한 커널은이 두 하위 시스템이 서로 상호 작용하지 않아야합니다. 그러나 한 가지 예외가 있습니다. 실행 파일을 프로세스의 텍스트 영역 으로로드하는 것입니다 . 물론, 하나는이 작업은 또한 시스템 호출 (트리거되어 있다고 주장 할 수있다 execve
), 그러나 이것은 일반적으로 알려진 하나의 프로세스 서브 시스템은 파일 서브 시스템에 대한 암시 적 요청을 할 경우.
프로세스 서브 시스템은 당연히 파일을 처리 할 수있는 방법이 없기 때문에 (그렇지 않으면 전체를 둘로 나누는 데 아무런 의미가 없습니다), 파일 서브 시스템이 파일에 액세스하기 위해 제공하는 모든 것을 사용해야합니다. 이는 프로세스 서브 시스템이 파일 편집 / 삭제와 관련하여 파일 서브 시스템이 수행하는 모든 측정에 제출됨을 의미합니다. 이 시점에서 나는 이 U & L 질문에 대한 Gilles의 답변 을 읽는 것이 좋습니다 . 내 대답의 나머지 부분은 Gilles의 이보다 일반적인 답변을 기반으로합니다.
가장 먼저 알아야 할 것은 내부적으로 파일은 inode를 통해서만 액세스 할 수 있다는 것입니다 . 커널에 경로가 주어지면 첫 번째 단계는 다른 모든 작업에 사용하기 위해 커널을 inode로 변환하는 것입니다. 프로세스가 실행 파일을 메모리에로드 할 때 경로 변환 후 파일 하위 시스템에서 제공 한 inode를 통해 실행 파일을 처리합니다. Inode는 여러 경로 (링크)와 연결될 수 있으며 프로그램은 링크 만 삭제할 수 있습니다. 파일 및 해당 inode를 삭제하려면 userland는 해당 inode에 대한 기존의 모든 링크를 제거하고 완전히 사용되지 않는지 확인해야합니다. 이러한 조건이 충족되면 커널은 디스크에서 파일을 자동으로 삭제합니다.
당신이 보면 Gilles의 답변 중 실행 파일 바꾸기 부분을 파일 을 편집 / 삭제 하는 방법 에 따라 커널이 항상 파일 하위 시스템 내에 구현 된 메커니즘을 통해 다르게 반응 / 적응한다는 것을 알 수 있습니다.
- 전략 1을 시도하면 ( open / truncate to zero / write 또는 open / write / truncate to new size) ) 커널이 요청을 처리하지 않아도됩니다. 오류 26 : 텍스트 파일 사용 중 (
ETXTBSY
)이 표시됩니다. 어떤 결과도 없습니다.
- 전략 2를 시도하면 첫 번째 단계는 실행 파일을 삭제하는 것입니다. 그러나 프로세스에서 사용하고 있기 때문에 파일 하위 시스템은 파일 (및 해당 inode) 이 파일 (모든 경로가 제거됨 )이되는 것을 방지 하고 프로세스는 마치 마치 아무 것도하지 않은 것처럼 계속 사용할 수 있습니다 . 이전 경로로 새 파일을 생성해도 아무런 변화가 없습니다. 새 파일에는 완전히 새로운 inode가 주어지며 실행중인 프로세스는 알지 못합니다. 디스크에서 실제로 삭제 . 이 시점에서 이전 파일의 내용에 액세스하는 유일한 방법은 inode를 통해 파일을 처리하는 것입니다. 즉, 프로세스 하위 시스템이 텍스트 섹션에 새 데이터를로드해야 할 때마다 프로세스 하위 시스템이 수행하는 작업입니다 (내부적으로 경로를 사용하는 데는 아무런 의미가 없습니다) inode로 변환 할 때). 찾았지만 했음연결을 해제
전략 2와 3은 실행 파일에도 안전합니다. 실행중인 실행 파일 (및 동적으로로드 된 라이브러리)은 파일 디스크립터가 있다는 의미에서 파일을 열지는 않지만 매우 유사한 방식으로 동작합니다. 일부 프로그램이 코드를 실행하는 한 파일은 디렉토리 항목이 없어도 디스크에 남아 있습니다.
- 전략 3은
mv
작업이 원자적인 것이기 때문에 상당히 유사합니다 . 아마도 rename
시스템 호출을 사용해야 할 것 입니다. 커널 모드에서 프로세스를 중단 할 수 없기 때문에이 작업이 완료 될 때까지 (성공적이든 아니든) 아무것도 방해하지 않습니다. 다시 말하지만, 이전 파일의 inode는 변경되지 않습니다. 새로운 파일이 만들어지고, 이미 실행중인 프로세스는 이전 inode의 링크 중 하나와 연결되어 있어도이를 인식하지 못합니다.
전략 3을 사용하면 새 파일을 기존 이름으로 이동하는 단계에서 이전 컨텐츠로 이어지는 디렉토리 항목이 제거되고 새 컨텐츠로 이어지는 디렉토리 항목이 작성됩니다. 이것은 하나의 원 자성 작업으로 이루어 지므로이 전략에는 큰 이점이 있습니다. 프로세스가 언제든지 파일을 열면 이전 내용이나 새 내용을 볼 수 있습니다. 혼합 된 내용이나 파일이 아닌 위험이 없습니다. 기존.
파일 재 컴파일 : 사용할 때 gcc
(그리고 다른 컴파일러에서도 동작이 유사 할 수 있음) 전략 2를 사용하고 strace
있습니다. 컴파일러 프로세스 중 하나 를 실행하여 확인할 수 있습니다 .
stat("a.out", {st_mode=S_IFREG|0750, st_size=8511, ...}) = 0
unlink("a.out") = 0
open("a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
chmod("a.out", 0750) = 0
- 컴파일러는
stat
및 lstat
시스템 호출을 통해 파일이 이미 존재 함을 감지 합니다.
- 파일은 연결되어 . 여기서는 더 이상 name을 통해 액세스 할 수 없지만
a.out
이미 실행중인 프로세스에서 사용되는 한 해당 inode 및 내용은 디스크에 남아 있습니다.
- 새로운 파일이 생성되고 이름으로 실행 가능하게됩니다
a.out
. 이것은 새로운 inode와 이미 실행중인 프로세스가 신경 쓰지 않는 새로운 내용입니다.
이제 공유 라이브러리와 관련하여 동일한 동작이 적용됩니다. 프로세스에서 라이브러리 객체를 사용하는 한 링크 변경 방법에 관계없이 디스크에서 삭제되지 않습니다. 무언가를 메모리에로드해야 할 때마다 커널은 파일의 inode를 통해이를 수행하므로 링크에 대한 변경 사항 (예 : 새 파일과 연결)을 무시합니다.