우분투에서 실행중인 프로그램을 이동할 수있는 이유는 무엇입니까?


24

방금 실행중인 활성 프로그램을 다른 디렉토리로 옮길 수 있다는 것을 깨달았습니다. 내 경험상 MacO 또는 Windows에서는 불가능했습니다. 우분투에서 어떻게 작동합니까?

편집 : Mac에서는 불가능하다고 생각했지만 주석이 확인하면 가능합니다. 아마도 Windows에서만 가능하지 않습니다. 모든 답변에 감사드립니다.


2
거의 크로스 사이트 속임수 : stackoverflow.com/a/196910/1394393 .
jpmc26

1
rename(2)OS X에서 실행 파일을 실행할 수 없습니까? 무슨 일이 생기 EBUSY나요? 왜 작동하지 않습니까? rename (2) 매뉴얼 페이지는 ETXTBUSY해당 시스템 호출에 대해 문서화하지 않으며 EBUSY디렉토리 이름 바꾸기가 가능하다는 것에 대해서만 이야기 하므로 POSIX 시스템은 실행 파일 이름 바꾸기를 허용하지 않을 수도 있습니다.
피터 코 데스

3
macOS 응용 프로그램은 실행 중 휴지통으로 이동하지 않고도 이동할 수 있습니다. NSBundle 등을 통해 URL을 생성하는 대신 파일 URL을 바이너리 또는 번들 리소스에 변수로 저장하는 경우 일부 앱이 그 이후에 오류가 발생할 수 있다고 가정합니다. 나는 그것이 macOS의 POSIX 준수라고 생각합니다.
Constantino Tsarouhas

1
실제로 Linux가 의도 한대로 작동하므로 수행중인 작업을 알아야합니다. : P
userDepth

2
그것에 대해 생각하는 또 다른 방법은 왜 가능 하지 않을까요? Windows가 당신을 허락하지 않는다고해서 반드시 프로세스가 작동하는 방식이나 그로 인해 근본적으로 불가능하다는 것을 의미하지는 않습니다.
토마스

답변:


32

그것을 분해하겠습니다.

당신이 실행 파일을 실행하면 시스템 호출의 순서는, 특히 실행 fork()execve():

  • fork()호출 프로세스의 하위 프로세스를 작성합니다.이 프로세스는 (대부분) 부모의 정확한 사본이며, 둘 다 여전히 동일한 실행 파일을 실행합니다 (기록 중 복사 메모리 페이지 사용). 두 번 리턴합니다. 상위에서 하위 PID를 리턴합니다. 자식에서는 0을 반환합니다. 일반적으로 자식 프로세스는 즉시 execve를 호출합니다.

  • execve()실행 파일의 전체 경로를 인수로 사용하고 호출 프로세스를 실행 파일로 바꿉니다. 이 시점에서 새로 생성 된 프로세스는 자체 가상 주소 공간, 즉 가상 메모리를 가져오고 실행은 시작점에서 시작합니다 (새 프로세스에 대한 플랫폼 ABI의 규칙에 의해 지정된 상태에서).

이 시점에서 커널의 ELF 로더는 실행 파일 의 텍스트 및 데이터 세그먼트 를 마치 마치mmap() 시스템 호출을 (공유 읽기 전용 및 개인 읽기-쓰기 매핑으로) 메모리에 매핑했습니다. BSS는 MAP_ANONYMOUS를 사용하는 것처럼 매핑됩니다. (BTW, 여기서는 단순성을 위해 동적 링크를 무시하고 있습니다. 동적 링커 는 주 실행 파일의 진입 점으로 점프하기 전에 모든 동적 라이브러리를 open()s 및 mmap()s합니다.)

새로 실행 된 () 사용자가 자체 코드를 실행하기 전에 실제로 디스크에서 메모리로 몇 페이지 만로드됩니다. 프로세스가 가상 주소 공간의 해당 부분에 닿으면 추가 페이지가 필요에 따라 페이징됩니다 . (사용자 공간 코드를 실행하기 전에 코드 또는 데이터 페이지를 미리로드하는 것은 성능 최적화 일뿐입니다.)


실행 파일은 하위 레벨의 inode로 식별됩니다. 파일이 실행되기 시작한 후에 커널은 열린 파일 디스크립터 나 파일 백업 메모리 매핑과 같이 파일 이름이 아닌 inode 참조에 의해 파일 내용을 그대로 유지합니다. 따라서 실행 파일을 파일 시스템의 다른 위치 나 다른 파일 시스템으로 쉽게 옮길 수 있습니다. 참고로 프로세스의 다양한 통계를 확인하려면 /proc/PID(PID는 주어진 프로세스의 프로세스 ID) 디렉토리를 들여다 볼 수 있습니다 . /proc/PID/exe디스크에서 연결이 해제되어 있어도 실행 파일을으로 열 수도 있습니다 .


이제 움직임을 파헤쳐 보자.

동일한 파일 시스템 내에서 파일을 이동할 때 실행되는 시스템 호출 rename()은 파일 이름을 다른 이름으로 바꾸는 것만으로 파일의 inode는 동일하게 유지됩니다.

두 개의 서로 다른 파일 시스템 사이에서 두 가지 일이 발생합니다.

  • 파일의 내용은 read()및로write()

  • 그 후, 파일을 사용하여 소스 디렉토리에서 파일의 연결이 해제되고 unlink()파일이 새 파일 시스템에서 새 inode를 얻습니다.

rm 실제로는 unlink() 디렉토리 트리에서 주어진 파일을 가져 오기 때문에 디렉토리에 대한 쓰기 권한이 있으면 해당 디렉토리에서 파일을 제거 할 수있는 충분한 권한을 얻게됩니다.

이제 재미있게, 두 파일 시스템간에 파일을 이동할 때 어떤 권한이 없는지 상상해보십시오. unlink() 하고 소스에서 파일 대한 .

파일은 처음에 대상 ( read(), write()) 에 복사 된 다음 unlink()권한이 충분하지 않아 실패합니다. 따라서 파일은 두 파일 시스템 모두에 남아 있습니다 !!


5
당신은 다소 혼란스러운 가상 및 물리적 메모리입니다. 프로그램이 실제 메모리에로드되는 방식에 대한 설명이 정확하지 않습니다. exec 시스템 호출은 실행 파일의 다양한 섹션을 실제 메모리에 복사하지 않고 프로세스를 시작하는 데 필요한 섹션 만로드합니다. 그 후 필요한 페이지가 필요할 때로드 될 수 있습니다. 실행 파일 바이트는 프로세스 가상 메모리의 일부이므로 프로세스의 전체 수명 동안 읽을 수 있으며 다시 읽을 수도 있습니다.
jlliagre 2016 년

@jlliagre 편집했습니다. 지금 명확 해지기를 바랍니다. 감사.
heemayl

6
"프로세스가 더 이상 파일 시스템을 사용하지 않습니다"라는 문장은 여전히 ​​의문입니다.
jlliagre

2
파일 시스템에서 주어진 파일이 파일 이름으로 직접 식별되지 않는다는 기본적인 이해는 훨씬 명확해야합니다.
Thorbjørn Ravn Andersen

2
업데이트에 여전히 부정확합니다. mmapunmap시스템 호출은 부하에 사용되는 OS가 RAM이 더 나은 다른 것에 사용된다 느끼는 경우 페이지가 메모리에서 언로드, 그들을 페이지 오류 발생에 액세스 할 때 페이지가 커널에 의해로드, 필요에 따라 페이지를 언로드되지 않습니다. 이러한로드 / 언로드 작업에는 시스템 호출이 없습니다.
jlliagre 2016 년

14

글쎄, 그것은 꽤 단호하다. / usr / local / bin / whoopdeedoo라는 실행 파일을 보자. 그것은 단지 소위 inode (유닉스 파일 시스템상의 파일의 기본 구조)에 대한 참조 일뿐 입니다. "사용 중"으로 표시되는 것은 inode입니다.

이제 / usr / local / whoopdeedoo 파일을 삭제하거나 이동할 때 inode에 대한 참조 만 이동되거나 지워집니다. 아이 노드 자체는 변하지 않고 유지된다. 기본적으로 그렇습니다.

나는 그것을 확인해야하지만 Mac OS X 파일 시스템 에서도이 작업을 수행 할 수 있다고 생각합니다.

Windows는 다른 접근 방식을 취합니다. 왜? 누가 알아...? NTFS의 내부에 익숙하지 않습니다. 이론적으로, 파일 이름의 내부 구조에 대한 참조를 사용하는 모든 파일 시스템이이를 수행 할 수 있어야합니다.

나는 지나치게 단순화했지만, Wikipedia에 대한 "시사점"섹션을 읽으십시오.


1
Windows에서 바로 가기를 사용하여 실행 파일을 시작하는 경우 바로 가기를 지울 수 있습니다. = 3
Ray

2
아니요, 그것은 심볼릭 링크를 지우는 것과 같습니다. 다른 의견에서는이 동작이 FAT 파일 시스템에 대한 레거시 지원 때문이라고 명시되어 있습니다. 그럴듯한 이유처럼 들립니다.
jawtheshark

1
이것은 inode와 특별히 관련 이 없습니다 . NTFS는 MFT 레코드를 사용하여 파일 상태를 추적하고 FAT는이를 위해 디렉토리 항목을 사용하지만 Linux는 여전히 사용자 관점에서 이러한 파일 시스템과 동일한 방식으로 작동합니다.
Ruslan

13

다른 모든 대답에서 누락 된 것으로 보이는 것은 파일을 열고 프로그램이 열린 파일 설명자를 보유하면 해당 파일 설명자가 닫힐 때까지 파일이 시스템에서 제거 되지 않는다는 것입니다.

참조 된 inode를 삭제하려는 시도는 파일이 닫힐 때까지 지연됩니다. 동일하거나 다른 파일 시스템에서 이름을 바꾸면 이름 바꾸기 동작과 상관없이 열린 파일에 영향을 줄 수 없으며 파일을 새 파일로 명시 적으로 삭제하거나 덮어 씁니다. 파일을 엉망으로 만들 수있는 유일한 방법은 파일 이름 바꾸기 / 삭제와 같은 디렉토리에서의 조작이 아니라 명시 적으로 inode를 열고 내용을 엉망으로 만드는 것입니다.

또한 커널이 파일을 실행할 때 실행 파일에 대한 참조를 유지하므로 실행 중에 파일이 수정되는 것을 다시 방지합니다.

따라서 결국 실행중인 프로그램을 구성하는 파일을 삭제 / 이동할 수있는 것처럼 보일 지라도 실제로 해당 파일의 내용은 프로그램이 끝날 때까지 메모리에 보관됩니다.


1
이것은 옳지 않다. execve()FD를 반환하지 않고 단순히 프로그램을 실행합니다. 당신이 실행하는 경우 그래서, 예를 들어, tail -f /foo.log다음 FD (자신이다 /proc/PID/fd/<fd_num>과 관련된) tail에 대한 foo.log아니라 실행 자체, tail아니라 부모에 대한뿐만 아니라이. 단일 실행 파일도 마찬가지입니다.
heemayl

@heemayl 언급하지 않았 execve으므로 이것이 어떻게 관련되는지 알 수 없습니다. 커널이 파일을 실행하기 시작하면 파일을 바꾸려고 시도해도 커널이 포인트 무트를 렌더링하는 프로그램을 수정하지 않습니다. 실행 파일이 실행되는 동안 실행 파일을 "업데이트"하려면 execve어느 시점에서 커널이 파일을 다시 읽도록 할 수 있지만 이것이 어떻게 중요한지 알 수 없습니다. 요점은 "실행 파일 실행"을 삭제하면 실행 파일이 중지 될 때까지 데이터 삭제를 트리거하지 않습니다.
Bakuriu

나는이 부분에 대해 이야기하고있는 프로그램이 단일 실행 파일로 구성되어있는 경우 에는 프로그램이 디렉토리에 변화의 미세 독립적으로 실행됩니다 실행을 시작하면 : 동일하거나 다른 파일 시스템의 이름을 변경하는 것은 영향을 미칠 수 없습니다 열린 핸들러를 , 당신은 반드시 이야기 execve()이 경우 FD가 포함되지 않은 경우 약 FD.
heemayl

2
파일을 참조하기 위해 파일 핸들이 필요하지 않습니다. 페이지를 매핑하는 것만으로도 충분합니다.
Simon Richter

1
유닉스에는 "파일 핸들"이 없습니다. heemayl이 여기서 이야기하고 open()있는 파일 기술자를 반환합니다 execve(). 예, 실행중인 프로세스에는 실행 파일에 대한 참조가 있지만 파일 디스크립터는 아닙니다. 아마도 munmap()실행 파일에 대한 모든 매핑을 수행 한 경우에도 여전히 inode가 해제되지 않도록하는 참조 (/ proc / self / exe에 반영)가있을 것입니다. (반환되지 않은 라이브러리 함수에서이 작업을 수행 한 경우 충돌없이 가능할 수 있습니다.) BTW, 사용중인 실행 파일을 자르거나 수정하면 ETXTBUSY작동하지만 작동 할 수 있습니다.
Peter Cordes

7

Linux 파일 시스템에서 파일을 이동할 때 파일 시스템 경계를 넘지 않는 한 (읽기 : 동일한 디스크 / 파티션에 유지) 변경하는 모든 것은 ..(부모 디렉토리)의 새 위치에 대한 inode입니다. . 실제 데이터는 디스크에서 전혀 이동하지 않고 포인터만으로 파일 시스템이 데이터를 찾을 수있는 위치를 알 수 있습니다.

그렇기 때문에 이동 작업이 너무 빠르며 실제로 프로그램 자체를 이동하지 않아도 실행중인 프로그램을 이동하는 데 문제가없는 것 같습니다.


귀하의 답변은 이진 실행 파일을 다른 파일 시스템으로 이동하면 해당 이진에서 시작된 프로세스 실행에 영향을 줄 것입니다.
jlliagre

6

프로그램 이동은 시작된 프로세스 실행에 영향을 미치지 않기 때문에 가능합니다.

프로그램이 시작되면 온 디스크 비트를 덮어 쓰지 못하도록 보호되지만 파일 이름을 바꾸거나 동일한 파일 시스템의 다른 위치로 이동하거나 파일 이름을 바꾸는 것과 같은 위치로 이동할 필요는 없습니다. 다른 파일 시스템으로 복사하십시오. 파일 시스템을 다른 곳에 복사 한 다음 제거하십시오.

프로세스에 파일 디스크립터가 열려 있거나 프로세스가 실행 중이기 때문에 사용중인 파일을 제거하면 파일 데이터가 제거되지 않고 파일 inode에서 참조 상태를 유지하지만 디렉토리 항목 만 제거합니다. 즉, inode에 도달 할 수있는 경로입니다.

프로그램을 시작해도 모든 물리적 메모리에 한 번에 모든 내용이로드되지는 않습니다. 반대로 프로세스를 시작하는 데 필요한 엄격한 최소값 만로드됩니다. 그런 다음 프로세스의 전체 수명 동안 필요한 페이지가 필요할 때로드됩니다. 이를 수요 페이징이라고합니다. RAM이 부족한 경우 OS에서 이러한 페이지를 보유한 RAM을 자유롭게 해제 할 수 있으므로 프로세스가 실행 가능한 inode에서 동일한 페이지를 여러 번로드 할 수 있습니다.

Windows에서 가능하지 않은 이유는 원래 기본 파일 시스템 (FAT)이 디렉토리 항목과 inode의 분할 개념을 지원하지 않았기 때문일 수 있습니다. 이 제한은 NTFS에 더 이상 존재하지 않았지만 OS 디자인은 오랫동안 유지되어 새로운 버전의 바이너리를 설치할 때 재부팅 해야하는 불쾌한 제약이 생겨났습니다. 이는 최신 버전의 Windows에는 해당되지 않습니다.


1
최신 버전의 Windows는 재부팅하지 않고 사용중인 바이너리를 대체 할 수 있다고 생각합니다.
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen 왜 모든 업데이트를 다시 시작
해야하는지

1
@Braiam 그들은하지 않습니다. 자세히 살펴보십시오. 바이너리를 업데이트 할 수는 있지만 커널은 (내 지식으로는) 부팅 할 수 없으므로 최신 버전으로 재부팅해야합니다. 이것은 대부분의 운영 체제 커널에 유효합니다. I보다 더 똑똑한 사람들이 실행하는 동안 리눅스 커널을 패치 할 수있는 리눅스 용 kpatch을 작성했습니다 - 볼 en.wikipedia.org/wiki/Kpatch
Thorbjørn Ravn 안데르센

@ ThorbjørnRavnAndersen "모든 Windows 업데이트"를 의미
Braiam

@Braiam 예-나도 그렇습니다. 자세히 살펴보십시오.
Thorbjørn Ravn Andersen

4

기본적으로 Unix와 ilk에서는 파일을 때 파일을 연결 / 찾기 위해 파일 이름 (파일을 여는 디렉토리 경로 포함)이 사용됩니다 (파일 실행은 파일을 여는 방법 중 하나입니다). 그 순간, 파일의 신원 ( "inode"를 통해)이 확립되고 더 이상 의문의 여지가 없습니다. 파일을 제거하고 이름을 바꾸고 권한을 변경할 수 있습니다. 프로세스 또는 파일 경로에 해당 파일 / 아이 노드에 대한 핸들이있는 한 프로세스 간 파이프처럼 파이프가 고착됩니다 (사실 역사적인 유닉스에서 파이프 크기에 맞는 이름이없는 inode 였습니다 . inode에서 "직접 블록"디스크 저장소 참조 (10 개 블록과 같은)

PDF 파일에서 PDF 뷰어를 연 경우 해당 파일을 삭제하고 동일한 이름으로 새 파일을 열 수 있습니다. 기존 뷰어가 열려있는 한 기존 파일을 계속보고있는 경우가 아니라면 여전히 기존 파일에 액세스하는 것이 좋습니다 파일이 원래 이름으로 사라질 때 알 수 있도록 파일 시스템).

임시 파일이 필요한 프로그램은 해당 파일을 일부 이름으로 연 다음 즉시 동안 (또는 디렉토리 항목)을 제거 할 수 있습니다. 그 후에는 이름으로 파일에 더 이상 액세스 할 수 없지만 파일에 열린 파일 디스크립터가있는 프로세스는 여전히 파일에 액세스 할 수 있으며, 프로그램이 예기치 않게 종료되면 파일이 제거되고 스토리지가 자동으로 재생됩니다.

따라서 파일 경로는 파일 자체의 속성이 아니며 (실제로 하드 링크는 여러 다른 경로를 제공 할 수 있음) 이미 열려있는 프로세스에 의한 지속적인 액세스가 아니라 파일을 열 때에 만 필요합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.