Я только что понял, что могу переместить работающую активную программу в другой каталог. По моему опыту, это было невозможно в MacOs или Windows. Как это работает в Ubuntu?
Изменить: Я думал, что это не возможно на Mac, но, видимо, это возможно, как подтверждают комментарии. Это возможно только в Windows. Спасибо за все ответы.
rename(2)
запустить исполняемый файл на OS X? Что происходит, вы получаетеEBUSY
или что-то? Почему это не работает? Страница man rename (2) не документируетETXTBUSY
этот системный вызов, а только говорит оEBUSY
возможности переименования каталогов, поэтому я не знал, что система POSIX может даже запретить переименование исполняемых файлов.Ответы:
Позвольте мне сломать это.
Когда вы запускаете исполняемый файл, выполняется последовательность системных вызовов, прежде всего
fork()
иexecve()
:fork()
создает дочерний процесс вызывающего процесса, который (в основном) является точной копией родительского процесса, причем оба по-прежнему используют один и тот же исполняемый файл (используя страницы памяти копирования при записи, поэтому он эффективен). Он возвращает дважды: в родительском он возвращает дочерний PID. В дочернем процесс возвращает 0. Обычно дочерний процесс вызывает execve сразу же:execve()
принимает полный путь к исполняемому файлу в качестве аргумента и заменяет вызывающий процесс исполняемым файлом. В этот момент вновь созданный процесс получает свое собственное виртуальное адресное пространство, то есть виртуальную память, и выполнение начинается в его точке входа (в состоянии, определяемом правилами платформы ABI для свежих процессов).На этом этапе загрузчик ELF ядра отобразил сегменты текста и данных исполняемого файла в память, как если бы он использовал
mmap()
системный вызов (с общими сопоставлениями только для чтения и частными сопоставлениями чтения и записи соответственно). BSS также отображается как будто с MAP_ANONYMOUS. (Кстати, я игнорирую динамическое связывание здесь для простоты: динамический компоновщикopen()
иmmap()
все динамические библиотеки перед переходом к точке входа основного исполняемого файла.)Только несколько страниц загружаются в память с диска до того, как вновь созданный exec () запустит собственный код. Другие страницы по требованию распределяются по мере необходимости, если / когда процесс касается этих частей своего виртуального адресного пространства. (Предварительная загрузка любых страниц кода или данных перед началом выполнения кода пользовательского пространства - это просто оптимизация производительности.)
Исполняемый файл идентифицируется индексом на нижнем уровне. После того, как файл начал выполняться, ядро сохраняет содержимое файла без изменений по ссылке на inode, а не по имени файла, как для открытых файловых дескрипторов или отображений памяти с файловой поддержкой. Таким образом, вы можете легко переместить исполняемый файл в другое место файловой системы или даже в другую файловую систему. В качестве примечания, чтобы проверить различные характеристики процесса, вы можете заглянуть в
/proc/PID
каталог (PID - это идентификатор процесса данного процесса). Вы даже можете открыть исполняемый файл/proc/PID/exe
, даже если он был удален с диска.Теперь давайте раскопаем движение:
Когда вы перемещаете файл в пределах одной и той же файловой системы, выполняется системный вызов
rename()
, который просто переименовывает файл в другое имя, его индекс остается неизменным.В то время как между двумя разными файловыми системами происходят две вещи:
Содержимое файла сначала копируется в новое местоположение,
read()
иwrite()
После этого файл отсоединяется от исходного каталога с помощью,
unlink()
и, очевидно, файл получит новый inode в новой файловой системе.rm
на самом деле простоunlink()
-ing заданный файл из дерева каталогов, поэтому, имея разрешение на запись в каталог, вы получите достаточное право удалить любой файл из этого каталога.А теперь представьте себе, что происходит, когда вы перемещаете файлы между двумя файловыми системами и у вас нет прав доступа к
unlink()
файлу из исходного кода?Ну, файл сначала будет скопирован в место назначения (
read()
,write()
), а затемunlink()
потерпит неудачу из-за недостаточного разрешения. Итак, файл останется в обеих файловых системах !!источник
mmap
иunmap
системные вызовы не используются для загрузки и выгрузки страниц по требованию, страницы загружаются ядром при доступе к их генерировать ошибку страницы, страницы выгружаются из памяти , когда операционная система чувствует RAM будет лучше использовать для чего - то еще. В этих операциях загрузки / выгрузки системный вызов не задействован.Ну, это довольно просто. Давайте возьмем исполняемый файл с именем / usr / local / bin / whoopdeedoo. Это только ссылка на так называемый inode (базовая структура файлов в файловых системах Unix). Это индекс, который помечается как «используемый».
Теперь, когда вы удаляете или перемещаете файл / usr / local / whoopdeedoo, единственное, что перемещается (или стирается), - это ссылка на индекс. Сам инод остается неизменным. Это в основном это.
Я должен это проверить, но я верю, что вы можете сделать это и в файловых системах Mac OS X.
Windows использует другой подход. Зачем? Кто знает...? Я не знаком с внутренностями NTFS. Теоретически, все файловые системы, которые используют ссылки на интенциональные структуры для имен файлов, должны быть в состоянии сделать это.
Признаюсь, я слишком упростил, но прочитал раздел «Последствия» в Википедии, который намного лучше меня работает.
источник
Одна вещь, которая кажется отсутствующей во всех других ответах, состоит в том, что: как только файл открыт и программа содержит открытый дескриптор файла, файл не будет удален из системы, пока этот дескриптор файла не будет закрыт.
Попытки удалить указанный inode будут отложены до тех пор, пока файл не будет закрыт: переименование в той же или другой файловой системе не может повлиять на открытый файл, независимо от поведения переименования, а также явного удаления или перезаписи файла новым. Единственный способ, которым вы можете испортить файл - это явно открыть его inode и связываться с содержимым, а не с операциями над каталогом, такими как переименование / удаление файла.
Более того, когда ядро выполняет файл, оно сохраняет ссылку на исполняемый файл, и это снова предотвратит любую его модификацию во время выполнения.
Таким образом, в конце концов, даже если кажется, что вы можете удалять / перемещать файлы, из которых состоит запущенная программа, фактически содержимое этих файлов сохраняется в памяти до завершения программы.
источник
execve()
не возвращает никакого FD, он просто выполняет программу. Так, например, если вы бежите ,tail -f /foo.log
то их это FD (/proc/PID/fd/<fd_num>
) , связанный сtail
для ,foo.log
но не для самого исполняемого файла, аtail
не на его родителей , а также. Это верно и для отдельных исполняемых файлов.execve
поэтому не понимаю, насколько это актуально. Как только ядро начинает выполнять файл, попытка заменить файл не изменит программу, которую ядро собирается загрузить, отрисовывая точечный спор. Если вы хотите «обновить» исполняемый файл во время его работы, вы можете позвонитьexecve
в какой-то момент, чтобы заставить ядро перечитать файл, но я не понимаю, как это имеет значение. Дело в том, что удаление «запущенного исполняемого файла» на самом деле не вызывает удаления данных до тех пор, пока исполняемый файл не остановится.execve()
и FD, когда в этом случае не участвует FD.open()
возвращает дескриптор файла , о котором здесь говорит Химейлexecve()
. Да, запущенный процесс имеет ссылку на свой исполняемый файл, но это не дескриптор файла. Возможно, даже если бы онmunmap()
отредактировал все свои сопоставления своего исполняемого файла, он все равно имел бы ссылку (отраженную в / proc / self / exe), которая не давала освобождать индекс. (Это было бы возможно без сбоев, если бы он делал это из библиотечной функции, которая никогда не возвращалась.) Кстати, усечение или изменение используемого исполняемого файла может дать вамETXTBUSY
, но может работать.В файловой системе Linux, когда вы перемещаете файл, пока он не пересекает границы файловой системы (читай: остается на том же диске / разделе), все, что вы изменяете, - это индекс
..
(родительский каталог) и новый каталог , Фактические данные вообще не перемещаются на диске, только указатель, чтобы файловая система знала, где их найти.Вот почему операции перемещения выполняются так быстро и, вероятно, почему нет проблем с перемещением работающей программы, поскольку вы на самом деле не перемещаете саму программу.
источник
Это возможно, потому что перемещение программы не влияет на запущенные процессы, запускаемые при ее запуске.
После запуска программы ее биты на диске защищены от перезаписи, но нет необходимости защищать файл для переименования, перемещения в другое место в той же файловой системе, что эквивалентно переименованию файла или перемещению в другую файловую систему, что эквивалентно копированию файла в другом месте и удалению его.
Удаление используемого файла: либо из-за того, что у процесса открыт дескриптор файла, либо из-за того, что процесс выполняет его, не удаляет данные файла, на которые ссылается индекс файла, а только удаляет запись каталога, т. е. путь, с которого может быть достигнут индекс.
Обратите внимание, что запуск программы не загружает все сразу в (физическую) память. Напротив, загружается только строгий минимум, необходимый для запуска процесса. Затем требуемые страницы загружаются по требованию в течение всей жизни процесса. это называется поиском по запросу. Если имеется нехватка ОЗУ, ОС может освободить ОЗУ, содержащее эти страницы, поэтому вполне возможно, что процесс может несколько раз загрузить одну и ту же страницу из исполняемого inode.
Первоначально причина, по которой это было невозможно с Windows, скорее всего связана с тем, что базовая файловая система (FAT) не поддерживала концепцию разделения записей каталога по сравнению с inode. Это ограничение больше не присутствовало в NTFS, но дизайн ОС сохранялся в течение длительного времени, что приводило к неприятному ограничению необходимости перезагрузки при установке новой версии двоичного файла, что больше не относится к последним версиям Windows.
источник
По сути, в Unix и его аналогах имя файла (включая ведущий к нему путь к каталогу) используется для связывания / поиска файла при его открытии (выполнение файла - это один из способов его открытия). После этого момента личность файла (через его "inode") устанавливается и больше не подвергается сомнению. Вы можете удалить файл, переименовать его, изменить его разрешения. Пока у любого процесса или пути к файлу есть дескриптор этого файла / индекса, он будет зависеть, как канал между процессами (фактически, в исторической UNIX канал был безымянным индексом с размером, который только что помещался в «Прямые блоки» ссылки на дисковое хранилище в inode, что-то вроде 10 блоков).
Если у вас есть средство просмотра PDF, открытое для файла PDF, вы можете удалить этот файл и открыть новый файл с тем же именем, и, пока старый просмотрщик открыт, он все равно будет нормально обращаться к старому файлу (если он не просматривает активно). файловая система, чтобы заметить, когда файл исчезает под своим первоначальным именем).
Программы, которым требуются временные файлы, могут просто открыть такой файл под каким-либо именем, а затем немедленно удалить его (или, скорее, запись в каталоге), пока он еще открыт. После этого файл больше не доступен по имени, но любой процесс, имеющий открытый файловый дескриптор файла, все равно может получить к нему доступ, и если впоследствии произойдет неожиданный выход из программы, файл будет удален, а хранилище будет восстановлено автоматически.
Таким образом, путь к файлу не является свойством самого файла (на самом деле жесткие ссылки могут предоставлять несколько различных таких путей) и необходим только для его открытия, а не для продолжения доступа процессами, уже открывшими его.
источник