Як Unix відслідковує робочий каталог користувача під час навігації по файловій системі?


29

Скажіть, я входжу в оболонку в системі Unix і починаю прослуховувати команди. Я спочатку починаю з домашнього каталогу свого користувача ~. Я можу звідти cdвниз до каталогу Documents.

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

Однак мене цікавить те, як працює той самий процес, коли я переходжу до каталогу, тобто до батьків або батьківських батьків.

З огляду на моє невідоме, імовірно «сліпе» розташування Documentsодного з можливо багатьох каталогів у всьому дереві файлової системи з таким ім’ям, як Unix визначає, де я повинен бути розміщений далі? Чи робить це посилання на це pwdі його вивчати? Якщо так, то як pwdслід відстежувати поточний стан навігації?


Відповіді:


76

Інші відповіді - це надмірні спрощення, кожна з яких представляє лише частини історії, і помиляються в парах пунктів.

Існує два способи відстеження робочого каталогу:

  • Для кожного процесу в структурі даних простір ядра, яка представляє цей процес, ядро ​​зберігає дві посилання vnode на vnode робочого каталогу та кореневу директорію цього процесу. Перше завдання встановлюється з допомогою chdir()і fchdir()системних викликів, останній від chroot(). Їх можна побічно бачити в /procопераційних системах Linux або за допомогою fstatкоманди на FreeBSD тощо:

    % fstat -p $$ | голова -n 5
    КОРИСТУВАЧ CMD PID FD МОНТАЖ РЕЖИМУ СУМ |
    JdeBP zsh 92648 текст / 24958 -r-xr-xr-x 702360 r
    JdeBP zsh 92648 ctty / dev 148 crw - w ---- pts / 4 rw
    JdeBP zsh 92648 wd / usr / home / JdeBP 4 drwxr-xr-x 124 r
    JdeBP zsh 92648 корінь / 4 drwxr-xr-x 35 r
    % 

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

    У Microkernel Unices структура даних знаходиться в просторі додатків, але принцип проведення відкритих посилань на ці каталоги залишається тим самим.

  • Внутрішньо, в оболонках, таких як оболонка Z, Korn, Bourne Again, C і Almquist, оболонка додатково відслідковує робочий каталог, використовуючи рядкові маніпуляції з внутрішньою змінною рядка. Це робиться кожен раз, коли це викликає причину дзвінка chdir().

    Якщо змінено відносне ім'я шляху, він маніпулює рядком для додавання цього імені. Якщо один зміниться на абсолютне ім'я шляху, він замінить рядок новим іменем. В обох випадках він налаштовує рядок для видалення .та ..компонентів та переслідує символічні посилання, замінюючи їх своїми іменами, пов’язаними з ними. ( Ось , наприклад , код оболонки Z для цього .)

    Ім'я у змінній внутрішньої рядка відстежується змінною оболонки з назвою PWD(або cwdв оболонках C). Це умовно експортується як змінна середовища (названа PWD) до програм, породжених оболонкою.

Ці два методи відстеження речей виявлено з допомогою -Pі -Lопцій до cdі pwdоболонки вбудованих команд, а також відмінності між оболонками вбудованих pwdкоманд і як /bin/pwdкоманди і вбудовані pwdкоманди речей , як (серед інших) VIM і NeoVIM.

% mkdir a; ln -sab 
% (cd b; pwd; / bin / pwd; printenv PWD)
/ usr / home / JdeBP / b
/ usr / home / JdeBP / a
/ usr / home / JdeBP / b
% (cd b; pwd -P; / bin / pwd -P)
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
% (cd b; pwd -L; / bin / pwd -L)
/ usr / home / JdeBP / b
/ usr / home / JdeBP / b
% (cd -P b; pwd; / bin / pwd; принтенв PWD)
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
% (cd b; PWD = / привіт / там / bin / pwd -L)
/ usr / home / JdeBP / a
% 

Як бачите: отримання «логічного» робочого каталогу - це питання перегляду PWDзмінної оболонки (або змінної середовища, якщо вона не є програмою оболонки); тоді як отримання «фізичного» робочого каталогу - це питання викликати функцію getcwd()бібліотеки.

Функціонування /bin/pwdпрограми при використанні -Lопції дещо тонке. Він не може довіряти значенню PWDзмінної середовища, яке він успадкував. Зрештою, вона не повинна викликати оболонку, і втручаються програми, можливо, не реалізували механізм оболонки, щоб PWDсередовище змінної завжди відстежувало назву робочого каталогу. Або хтось може робити те, що я робив саме там.

Отож, це робиться (як говорить стандарт POSIX), перевірте, чи вказане ім'я PWDдає те саме, що і ім'я ., як це можна побачити з трасуванням системного виклику:

% ln -sac 
% (cd b; truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / home / JdeBP / b", { режим = drwxr-xr-x, inode = 120932, розмір = 2, blksize = 131072}) = 0 (0x0) 
stat (".", {mode = drwxr-xr-x, inode = 120932, розмір = 2, blksize = 131072}) = 0 (0x0)
/ usr / home / JdeBP / b
% (cd b; PWD = / usr / local / etc truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / local / тощо" , {режим = drwxr-xr-x, inode = 14835, розмір = 158, blksize = 10240}) = 0 (0x0) 
stat (".", {mode = drwxr-xr-x, inode = 120932, розмір = 2 , blksize = 131072}) = 0 (0x0)
__getcwd ("/ usr / home / JdeBP / a", 1024) = 0 (0x0)
/ usr / home / JdeBP / a
% (cd b; PWD = / hello / там ферми / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ hello / there", 0x7fffffffefe30) ERR # 2 'Немає такого файлу чи каталогу' 
__getcwd ("/ usr / home / JdeBP / a", 1024) = 0 (0x0)
/ usr / home / JdeBP / a
% (cd b; PWD = / usr / home / JdeBP / c ферми / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / home / JdeBP / c ", {режим = drwxr-xr-x, inode = 120932, розмір = 2, blksize = 131072}) = 0 (0x0) 
stat (". ", {Mode = drwxr-xr-x, inode = 120932 , розмір = 2, розмір зображення = 131072}) = 0 (0x0)
/ usr / home / JdeBP / c
%

Як бачите: він дзвонить лише у getcwd()випадку виявлення невідповідності; і його можна обдурити, встановивши PWDрядок, який справді називає той самий каталог, але іншим маршрутом.

Функція getcwd()бібліотеки є предметом сама по собі. Але до précis:

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

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

Навігація до ..- знову ж таки сам предмет. Інший принцип: Хоча умовні каталоги (хоча, як уже згадувалося, це не потрібно) містять фактичну ..структуру даних каталогів на диску, ядро ​​відслідковує батьківський каталог кожної директорії vnode і, таким чином, може переходити до ..vnode будь-якого робочий каталог. Це дещо ускладнюється монтовою точкою та зміненими кореневими механізмами, які виходять за рамки цієї відповіді.

Убік

Насправді Windows NT робить подібне. Є один робочий каталог на кожен процес, встановлений SetCurrentDirectory()викликом API і відслідковується за процесом ядром через (внутрішню) відкриту ручку файлу до цього каталогу; а також існує набір змінних оточуючих середовищ, які програми Win32 (не лише інтерпретатори команд, але й усі програми Win32) використовують для відстеження імен декількох робочих каталогів (по одному на диск), додаючи або перезаписуючи їх щоразу, коли вони змінюють каталог.

Зазвичай, на відміну від операційних систем Unix та Linux, програми Win32 не відображають користувачам ці змінні середовища. Іноді їх можна побачити в Unix-подібних підсистемах, що працюють на Windows NT, а також, використовуючи команди команду інтерпретаторів SETпевним чином.

Подальше читання


1
Це набагато більше, ніж я коли-небудь очікував. Дякую та зайва подяка за подальше читання!
ReactingToAngularVues

doc.cat-v.org/plan_9/4th_edition/papers/lexnames розповідає про деякі проблеми, пов'язані з ..планом 9,
icarus

@JdeBP: Можливо, мені щось не вистачає. Ви кажете: "Внутрішньо, в межах ..., bash, ... і ..., оболонка додатково відслідковує робочий каталог, використовуючи маніпулювання рядком внутрішньої змінної рядка. …, Він налаштовує рядок для видалення .та ..компонентів та переслідує символічні посилання, замінюючи їх своїми іменами, пов’язаними з ними. … Ім'я у внутрішній змінній рядка відстежується змінною оболонки під назвою PWD… ”(наголос додано). … (Продовжував)
G-Man каже: «Відновити Моніку»

(Продовження)… Але ваш приклад показує PWD= …/bпісля cd bкоманди, хоча bце символічне посилання на a- тому оболонка не «переслідує» a -> bпосилання. Ви помилялися, чи я неправильно прочитав?
G-Man каже: "Відновіть Моніку"

Я просто заштрихував бічну точку і вказав вам на код для деталей. Дивіться посібники з різних оболонок, коли і як вони вирішили переслідувати символічні посилання чи ні. Z оболонка зручніше називає її варіант оболонки , яка є однією частиною рішення формули CHASE_LINKS.
JdeBP

1

Ядро не відслідковує імена каталогів або файлів; файл або каталог представлені в ядрі парою inode / device. Системні виклики , такі як chdir(), open()і т.д. прийняти шлях в якості параметра, який може бути абсолютним (наприклад /etc/passwd), або щодо поточного каталогу (приклади: Documents, ..). Коли процес виконується chdir("Documents"), робиться пошук Documentsу поточному робочому каталозі, а робочий каталог процесу оновлюється для посилання на цей каталог. З точки зору ядра, в імені ".." немає нічого особливого, це лише умова у файловій системі, що ..посилається на батьківський каталог.

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


0

Цікаво, що традиційно cd ..набагато набагато простіше, ніж pwd. Імена каталогів ..явно розміщуються у файловій системі. Система відслідковує пристрій / inode поточного каталогу, тому cd ..або, точніше, системний виклик chdir("..")просто тягне за собою пошук імені ".." у файлі, що належить до inode поточного каталогу, і зміна пристрою / inode поточного каталогу на Значення, знайдене там.

pwd(точніше /bin/pwd) ..послідовно слідкує за посиланнями і читає відповідні каталоги, поки не знайде inode, звідки вона походить, складання списку цих імен у зворотному напрямку, поки воно не дійде до кореневого каталогу (зокрема, не містить ..запису).

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

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