Чому можна перемістити запущену програму в Ubuntu?


24

Я щойно зрозумів, що в змозі перемістити запущену активну програму до іншого каталогу. На мій досвід, це не було можливо в MacO або Windows. Як це працює в Ubuntu?

Редагувати: Я думав, що це не можливо на Mac, але, мабуть, це можливо, як підтверджують коментарі. Можливо, це не можливо в Windows. Дякую за всі відповіді.


2
Досить багато меж-сайтів: stackoverflow.com/a/196910/1394393 .
jpmc26

1
Ви не rename(2)можете запустити виконуваний файл в ОС X? Що трапляється, ви отримуєте EBUSYчи щось? Чому це не працює? Сторінка перейменування (2) не містить документа ETXTBUSYдля цього системного виклику і говорить лише про EBUSYможливість перейменування каталогів, тому я не знав, що система POSIX навіть може заборонити перейменування виконуваних файлів.
Пітер Кордес

3
Програми macOS також можна переміщувати під час їх запуску, лише не руйнуючись. Я припускаю, що деякі програми можуть помилитися після цього, наприклад, якщо вони зберігають URL-адреси файлів у своїх бінарних або пакетних ресурсах десь у вигляді змінної, а не генерують таку URL-адресу через NSBundle та ін. Я підозрюю, що це відповідність POSIX для macOS.
Константіно Царухас

1
Це насправді працює так, як Linux має намір, ви повинні знати, що ви робите. : P
userDepth

2
Я думаю, інший спосіб подумати над цим, чому б це було неможливо? Тільки тому, що Windows не дозволяє вам, це не означає, що принципово це неможливо через те, як працюють процеси чи щось.
Томас

Відповіді:


32

Дозвольте мені її розбити.

При запуску виконуваного файлу, послідовність системних викликів виконуються, в першу чергу , fork()і execve():

  • fork()створює дочірній процес процесу виклику, який є (здебільшого) точною копією батьківського, обидва досі працюють однаковим виконуваним файлом (використовуючи сторінки пам'яті копіювати при записі, тому це ефективно). Він повертається двічі: у батьків він повертає дочірній ПІД. У дитини він повертає 0. Зазвичай дочірній процес викликає execve відразу:

  • execve()приймає повний шлях до виконуваного файлу як аргумент і замінює процес виклику виконуваним файлом. У цей момент новостворений процес отримує власний віртуальний адресний простір, тобто віртуальну пам'ять, і виконання починається в точці входу (у стані, визначеному правилами платформи ABI для свіжих процесів).

У цей момент завантажувач ELF ядра відобразив текстові та сегменти даних виконуваного файлу в пам'ять, як ніби він використовував mmap()системний виклик (із спільним відображенням лише для читання та приватним читанням-записом). BSS також відображається як би з MAP_ANONYMOUS. (BTW, я ігнорую тут динамічне посилання для простоти: динамічний лінкер open()s і mmap()s всі динамічні бібліотеки перед переходом до точки входу головного виконуваного файлу.)

Лише кілька сторінок фактично завантажуються в пам'ять з диска до того, як нещодавно виконаний () ed почне запускати власний код. Подальші сторінки запитуються під час посилання , якщо / коли процес торкнеться тих частин свого віртуального адресного простору. (Попереднє завантаження будь-яких сторінок коду чи даних перед початком виконання коду простору користувача - це лише оптимізація продуктивності.)


Виконавчий файл ідентифікується за допомогою inode на нижньому рівні. Після того, як файл починає виконуватись, ядро ​​зберігає вміст файлу неушкодженим за посиланням inode, а не за ім'ям файлу, як для відкритих дескрипторів файлів або підтримуваних файлами відображень пам'яті. Таким чином, ви можете легко перемістити виконуваний файл в інше місце файлової системи або навіть в іншу файлову систему. В якості бічної примітки, щоб перевірити різні статистичні дані процесу, ви можете зазирнути до /proc/PIDкаталогу (PID - ідентифікатор процесу даного процесу). Ви навіть можете відкрити виконуваний файл як /proc/PID/exe, навіть він від’єднаний від диска.


Тепер давайте викопаємо рухоме:

При переміщенні файлу в одній і тій же файловій системі виконується системний виклик rename(), який просто перейменовує файл на інше ім'я, вкладення файлу залишається незмінним.

Тоді як між двома різними файловими системами трапляються дві речі:

  • Вміст файлу в спочатку копіюються в нове місце, з допомогою read()іwrite()

  • Після цього файл від’єднується від вихідного каталогу за допомогою unlink()та, очевидно, файл отримає новий вклад у новій файловій системі.

rmнасправді просто unlink()-наведений файл із дерева директорій, тож маючи дозвіл на запис у каталог, ви отримаєте достатнє право видалити будь-який файл із цього каталогу.

Тепер для розваги уявіть, що відбувається, коли ви переміщуєте файли між двома файлами файлів, і у вас немає дозволу на unlink()файл з джерела?

Ну, файл спочатку буде скопійовано до місця призначення ( read(), write()), а потім unlink()вийде з ладу через недостатній дозвіл. Отже, файл залишиться в обох файлових системах !!


5
Ви дещо плутаєте віртуальну та фізичну пам'ять. Ваш опис способу завантаження програми у фізичну пам'ять неточний. Системний виклик exec зовсім не копіює різні розділи виконуваного файлу у фізичну пам'ять, а завантажує лише той, який йому потрібен для запуску процесу. Після цього потрібні сторінки завантажуються на вимогу, можливо, довгий час. Байти виконуваних файлів є частиною віртуальної пам'яті процесу і можуть бути прочитані та, можливо, знову прочитані протягом усього життя процесу.
jlliagre

@jlliagre Відредаговано, я сподіваюся, що це зараз уточнено. Спасибі.
heemayl

6
"Процес більше не використовує файлову систему", заява залишається сумнівною.
jlliagre

2
Основне розуміння того, що даний файл у файловій системі безпосередньо не ідентифікується ім'ям файлу, має бути набагато зрозумілішим.
Thorbjørn Ravn Andersen

2
У вашому оновлення все ще залишаються неточності. В mmapі unmapсистемні виклики не використовуються для завантаження і вивантаження сторінок на вимогу, сторінки завантажуються ядром при доступі до їх генерувати помилку сторінки, сторінки вивантажуються з пам'яті , коли операційна система відчуває RAM буде краще використовувати для чого - то ще. Жоден системний виклик не бере участь у цих операціях завантаження / вивантаження.
jlliagre

14

Ну, це досить прямо. Візьмемо виконуваний файл з ім’ям / usr / local / bin / whoopdeedoo. Це лише посилання на так званий inode (основна структура файлів у Unix Filesystems). Це inode, який стає позначеним "у використанні".

Тепер, коли ви видаляєте або переміщаєте файл / usr / local / whoopdeedoo, єдине, що переміщується (або стирається) - посилання на inode. Сама індеда залишається незмінною. Це в основному все.

Я повинен це перевірити, але я вважаю, що ви можете це зробити і у файлових системах Mac OS X.

Windows застосовує інший підхід. Чому? Хто знає...? Я не знайомий із внутрішніми стандартами NTFS. Теоретично всі файлові системи, які використовують посилання на інтенальні структури для імен файлів, повинні це робити.

Зізнаюся, я надто спростив, але переходьте читати розділ "Наслідки" у Вікіпедії, який робить набагато кращу роботу, ніж я.


1
Добре, якщо ви використовуєте ярлик у Windows для запуску виконуваного файлу, ви можете також стерти ярлик, якщо ви хочете порівняти його так, можливо? = 3
Рей

2
Ні, це було б як протирати символічне посилання. Десь в інших коментарях зазначається, що поведінка пояснюється застарілою підтримкою файлових систем FAT. Це звучить як вірогідна причина.
jawtheshark

1
Це не має нічого спільного конкретно з дескрипторами. NTFS використовує записи MFT для відстеження стану файлів, а FAT використовує для цього записи каталогів, але Linux все ще працює аналогічно з цими файловими системами - з точки зору користувача.
Руслан

13

Одне, що здається відсутнім у всіх інших відповідях, це те, що: після відкриття файлу і програми містить дескриптор відкритого файлу, файл не буде видалений із системи, поки дескриптор цього файлу не буде закритий.

Спроба видалити посилання, що посилається, буде відкладено, поки файл не буде закрито: перейменування в одній або іншій файловій системі не може вплинути на відкритий файл незалежно від поведінки перейменування, а також явно видалити або перезаписати файл новим. Єдиний спосіб, коли ви можете зіпсувати файл, - це явно відкрити його inode та зіпсувати вміст, а не за допомогою операцій над каталогом, таким як перейменування / видалення файлу.

Більше того, коли ядро ​​виконує файл, воно зберігає посилання на виконуваний файл, і це знову запобіжить будь-якій модифікації його під час виконання.

Зрештою, навіть якщо це виглядає так, що ви можете видалити / перемістити файли, що складають запущену програму, фактично вміст цих файлів зберігається в пам'яті до завершення програми.


1
Це неправильно. execve()не повертає жодного FD, він просто виконує програму. Так, наприклад, якщо ви запускаєте, tail -f /foo.logто їх є FD ( /proc/PID/fd/<fd_num>), пов'язаний з tailдля, foo.logале не для самого виконуваного файлу tail, а також не для його батьків. Це справедливо і для одиночних виконуваних файлів.
heemayl

@heemayl Я не згадував, execveтому не бачу, як це актуально. Після того, як ядро ​​почне виконувати файл, спроба його заміни не змінить програму, ядро ​​буде завантажувати візуалізацію точок спору. Якщо ви хочете "оновити" виконуваний файл під час його роботи, ви можете зателефонувати execveв якийсь момент, щоб ядро ​​перечитало файл, але я не бачу, як це має значення. Суть у тому, що: видалення "запущеного виконуваного файлу" насправді не викликає видалення даних, поки виконуваний файл не зупиниться.
Бакуріу

Я говорю про цю частину, якщо програма складається з одного виконуваного файлу, коли ви запустите виконання, програма буде працювати нормально незалежно від будь-яких змін у каталозі: перейменування в тій же чи іншій файловій системі не може вплинути на відкритий обробник , ви обов'язково говорите про execve()та FD, коли в цій справі не бере участь ФД.
heemayl

2
Для того, щоб мати посилання на файл, вам не потрібна обробка файлів - достатньо також розміщення сторінок на карті.
Саймон Ріхтер

1
У Unix немає "файлових ручок". open()повертає дескриптор файлів , про який тут йде мова в heemayl execve(). Так, запущений процес має посилання на його виконуваний файл, але це не дескриптор файлу. Можливо, навіть якщо він munmap()редагував би всі свої відображення виконуваного файлу, він все одно матиме посилання (відображене в / proc / self / exe), що зупинило звільнення inode. (Це було б можливо без збоїв, якби це було зроблено за допомогою функції бібліотеки, яка ніколи не поверталася.) BTW, обрізка або зміна виконуваного файлу, який використовується під час використання, може дати вам ETXTBUSY, але може працювати.
Пітер Кордес

7

У файловій системі Linux, коли ви переміщуєте файл, доки він не перетинає межі файлової системи (читайте: залишається на одному диску / розділі), все, що ви змінюєте, - це вкладення ..(батьківський каталог) у нове місце . Фактичні дані зовсім не перемістилися на диску, а лише вказівник, щоб файлова система знала, де їх знайти.

Ось чому операції з переміщення є настільки швидкими і, ймовірно, чому немає проблеми з переміщенням запущеної програми, оскільки ви фактично не переміщуєте саму програму.


Здається, ваша відповідь передбачає, що переміщення бінарного виконуваного файлу в іншу файлову систему вплине на запущені процеси, запущені з цього бінарного файлу.
jlliagre

6

Це можливо, оскільки переміщення програми не впливає на запущені процеси, запущені її запуском.

Після запуску програми її бітові диски захищені від перезапису, але немає потреби захищати файл, який слід перейменовувати, переміщувати в інше місце в тій же файловій системі, що еквівалентно перейменуванню файлу або переміщенню до іншої файлової системи, що еквівалентно копіювати файл в іншому місці та видалити його.

Видалення файлу, який використовується, або тому, що в процесі відкритий дескриптор файлу, або через те, що процес виконує його, не видаляє дані файлу, на які залишається посилання файлом inode, а видаляє лише запис каталогу, тобто шлях, з якого можна дістатися до inode.

Зауважте, що запуск програми не завантажує все одразу у (фізичну) пам'ять. Навпаки, завантажується лише суворий мінімум, необхідний для початку процесу. Потім потрібні сторінки завантажуються на вимогу протягом усього життя процесу. це називається пейджингом попиту. Якщо є дефіцит оперативної пам’яті, ОС може звільнити оперативну пам’ять, на якій розміщені ці сторінки, тому процес цілком може завантажувати кілька разів ту саму сторінку з виконуваного inode.

Причина, по якій це було неможливо в Windows, спочатку вірогідна через те, що базисна файлова система (FAT) не підтримує розділену концепцію записів каталогів проти inodes. Це обмеження більше не було в NTFS, але дизайн ОС зберігався тривалий час, що призводило до неприємного обмеження, яке доведеться перезавантажувати під час встановлення нової версії бінарного файлу, що більше не стосується останніх версій Windows.


1
Я вважаю, що новіші версії Windows можуть замінити бінарні файли, які використовуються, без перезавантаження.
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen Цікаво, чому всі оновлення все ще потребують перезавантаження :(
Braiam

1
@Braiam Вони ні. Придивіться уважніше. Навіть незважаючи на те, що бінарні файли можуть бути оновлені, ядро ​​не може (наскільки мені відомо) і вимагає перезавантаження замінити новою версією. Це справедливо для більшості ядер операційної системи. Розумніші люди, ніж я, написали kpatch для Linux, які під час запуску можуть виправити ядро ​​Linux - див. En.wikipedia.org/wiki/Kpatch
Thorbjørn Ravn Andersen

@ ThorbjørnRavnAndersen Я мав на увазі "всі оновлення Windows"
Braiam

@Braiam так - так само зробив я.
Thorbjørn Ravn Andersen

4

В основному, в Unix та його ilk ім'я файлу (включаючи шлях до каталогу, що веде до нього) використовується для асоціації / пошуку файлу, коли його відкритті (виконання файлу - це один із способів його відкриття). Після цього моменту ідентифікація файлу (через його "inode") встановлюється і більше не ставиться під сумнів. Ви можете видалити файл, перейменувати його, змінити його дозволи. Поки будь-який процес або шлях до файлу має ручку для цього файлу / inode, він буде триматися навколо, як і труба між процесами (насправді, в історичному UNIX труба була безіменною inode розміром, який щойно вписався в "прямі блоки" посилання на зберігання диска в inode, щось на зразок 10 блоків).

Якщо у вашому PDF-файлі відкрито переглядач PDF, ви можете видалити цей файл і відкрити новий з тим же ім’ям, і поки старий переглядач відкритий, він все одно буде добре отримувати доступ до старого файлу (якщо він активно не дивиться файлова система, щоб помітити, коли файл зникає під початковим іменем).

Програми, що потребують тимчасових файлів, можуть просто відкрити такий файл під якимсь іменем, а потім негайно видалити його (а точніше його запис у каталог), поки він ще відкритий. Після цього файл вже не доступний за назвою, але будь-які процеси, що мають відкритий дескриптор файлу до файлу, можуть все-таки отримати доступ до нього, і якщо після цього стане несподіваний вихід програми, файл буде видалений, а сховище відтворено автоматично.

Таким чином, шлях до файлу не є властивістю самого файлу (насправді, жорсткі посилання можуть передбачати декілька різних таких шляхів) і потрібен лише для його відкриття, а не для постійного доступу процесами, які вже відкрили його.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.