[unix] 실행 중 이진 수정

개발할 때 종종 이진 파일을 실행하는 상황을 겪고 a.out백그라운드에서 긴 작업을 수행 한다고 말합니다 . 그렇게하는 동안 C 코드를 변경하고 다시 생성 a.out하고 컴파일 a.out합니다. 지금까지 나는 이것에 아무런 문제가 없었습니다. 실행중인 프로세스는 a.out정상적으로 계속 진행되며 충돌하지 않으며 항상 원래 시작된 이전 코드를 실행합니다.

그러나 a.outRAM의 크기와 비슷한 거대한 파일 이라고 할 수 있습니다. 이 경우 어떻게됩니까? 그리고 공유 객체 파일에 링크되었다고 말하면 런타임 중에 libblas.so수정하면 libblas.so어떻게됩니까? 무슨 일이 일어날 지?

내 주요 질문은 – 내가 실행할 때하는 OS 보증 않는 a.out당으로, 다음, 원래의 코드는 항상 정상적으로 실행됩니다 원래 바이너리 또는 크기에 관계없이, 진 .so그 경우에도, 그것은에 링크 된 파일 .o.so파일 동안 modfied된다 실행 시간?

비슷한 문제를 해결하는 다음과 같은 질문이 있음을 알고 있습니다 : https
:
//.com/questions/8506865/ -at-once 실행 중에 스크립트를 편집
하면 어떻게됩니까? 프로그램이 실행되는 동안 어떻게 실시간 업데이트를 수행 할 수 있습니까?

이것에 대해 조금 더 이해하는 데 도움이되었지만 그들이 내가 원하는 것을 정확하게 요구하고 있다고 생각하지 않습니다. 이것은 실행 중에 바이너리수정 한 결과의 일반적인 규칙입니다



답변

스택 오버플로 질문은 처음에는 충분 해 보였지만, 귀하의 의견으로는 왜 여전히 이것에 대해 의문을 가질 수 있는지 이해합니다. 나에게 이것은 정확히 두 가지 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
  • 컴파일러는 statlstat시스템 호출을 통해 파일이 이미 존재 함을 감지 합니다.
  • 파일은 연결되어 . 여기서는 더 이상 name을 통해 액세스 할 수 없지만 a.out이미 실행중인 프로세스에서 사용되는 한 해당 inode 및 내용은 디스크에 남아 있습니다.
  • 새로운 파일이 생성되고 이름으로 실행 가능하게됩니다 a.out . 이것은 새로운 inode와 이미 실행중인 프로세스가 신경 쓰지 않는 새로운 내용입니다.

이제 공유 라이브러리와 관련하여 동일한 동작이 적용됩니다. 프로세스에서 라이브러리 객체를 사용하는 한 링크 변경 방법에 관계없이 디스크에서 삭제되지 않습니다. 무언가를 메모리에로드해야 할 때마다 커널은 파일의 inode를 통해이를 수행하므로 링크에 대한 변경 사항 (예 : 새 파일과 연결)을 무시합니다.


답변

이해하는 것은 실행중인 프로세스의 메모리 매핑으로 인해 커널이 매핑 된 파일의 예약 된 부분을 업데이트 할 수 없다는 것입니다. 프로세스가 실행 중이면 모든 파일이 예약되어 있으므로 소스의 새 버전을 컴파일하여 실제로 새로운 inode 세트를 생성하기 때문에 업데이트합니다. 간단히 말해, 이전 버전의 실행 파일은 페이지 오류 이벤트를 통해 디스크에서 액세스 할 수 있습니다. 당신이 큰 파일을 업데이트 그래서하더라도, 그것은 해야 접근을 유지하고 커널은 해야 여전히 긴 프로세스가 실행 중일 손길이 닿지 않은 버전을 참조하십시오. 프로세스가 실행되는 동안 원본 파일 inode 재사용 해서는 안됩니다 .

물론 이것은 확인되어야합니다.


답변

.jar 파일을 교체 할 때 항상 그런 것은 아닙니다. 프로그램이 명시 적으로 정보를 요청할 때까지 Jar 자원 및 일부 런타임 리플렉션 클래스 로더를 디스크에서 읽지 않습니다.

jar는 메모리에 매핑되는 단일 실행 파일이 아니라 단순히 아카이브이기 때문에 문제가됩니다. 이것은 다소 논외 적이지만 여전히 귀하의 질문과 내가 발로 쏜 것의 파생물입니다.

따라서 실행 파일의 경우 : 그렇습니다. jar 파일의 경우 : (구현에 따라 다름)


답변