Как Unix отслеживает рабочий каталог пользователя при навигации по файловой системе?

29

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

Команда для изменения рабочего каталога здесь очень проста для интуитивного понимания: у родительского узла есть список дочерних узлов, к которым он может получить доступ, и, вероятно, он использует (оптимизированный) вариант поиска, чтобы найти существование дочернего узла с введите имя пользователя, и затем рабочий каталог будет «изменен», чтобы соответствовать этому - поправьте меня, если я ошибаюсь. Может быть даже проще, что оболочка просто «наивно» пытается попытаться получить доступ к каталогу точно в соответствии с желаниями пользователя, и когда файловая система возвращает какой-либо тип ошибки, оболочка отображает ответ соответственно.

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

Учитывая мое неизвестное, предположительно «слепое» местоположение Documentsодного из, возможно, множества каталогов во всем дереве файловой системы с таким именем, как Unix определяет, где я должен быть расположен дальше? Делает ли это ссылку pwdи исследовать это? Если да, как pwdотслеживать текущее состояние навигации?

ReactingToAngularVues
источник

Ответы:

76

Другие ответы - упрощения, каждый из которых представляет только часть истории, и они ошибочны в нескольких моментах.

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

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

    % fstat -p $$ | head -n 5
    USER CMD PID FD MOUNT INUM MODE SZ | DV R / W
    JdeBP zsh 92648 текст / 24958 -r-xr-xr-x 702360 р
    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 root / 4 drwxr-xr-x 35 р
    % 

    Когда выполняется разрешение имени пути, оно начинается с того или другого из указанных ссылочных vnodes, в зависимости от того, является ли путь относительным или абсолютным. (Существует семейство …at()системных вызовов, которые позволяют начинать разрешение имен с vnode, на который ссылается открытый (каталог) файловый дескриптор в качестве третьей опции.)

    В микроядерных Unices структура данных находится в прикладном пространстве, но принцип хранения открытых ссылок на эти каталоги остается прежним.

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

    Если кто-то меняет относительный путь, он манипулирует строкой, чтобы добавить это имя. Если кто-то изменится на абсолютный путь, он заменит строку новым именем. В обоих случаях корректируется строка для удаления .и ..компонентов, а также для поиска символических ссылок, заменяя их связанными именами. ( Вот код оболочки Z для этого , например.)

    Имя во внутренней строковой переменной отслеживается переменной оболочки с именем PWD(или cwdв оболочках Си). Это обычно экспортируется как переменная окружения (именованная PWD) в программы, порожденные оболочкой.

Эти два метода отслеживания вещей выявлены с помощью -Pи -Lопций к cdи pwdоболочки встроенных команд, а также различия между оболочками встроенных pwdкоманд и как /bin/pwdкоманды и встроенные pwdкоманды вещей , как (среди прочих) ВИМ и НеоВИМ.

% mkdir a; ln -sab 
% (cd b; pwd; / bin / pwd; printenv PWD)
/ USR / дома / JdeBP / б
/ USR / дома / JdeBP / а
/ USR / дома / JdeBP / б
% (cd b; pwd -P; / bin / pwd -P)
/ USR / дома / JdeBP / а
/ USR / дома / JdeBP / а
% (cd b; pwd -L; / bin / pwd -L)
/ USR / дома / JdeBP / б
/ USR / дома / JdeBP / б
% (cd -P b; pwd; / bin / pwd; printenv PWD)
/ USR / дома / JdeBP / а
/ USR / дома / JdeBP / а
/ USR / дома / JdeBP / а
% (cd b; PWD = / привет / там / bin / pwd -L)
/ USR / дома / JdeBP / а
% 

Как вы можете видеть: получение «логического» рабочего каталога - это вопрос просмотра PWDпеременной оболочки (или переменной среды, если она не является программой оболочки); тогда как получение «физического» рабочего каталога - это вызов getcwd()функции библиотеки.

Работа /bin/pwdпрограммы при использовании -Lопции несколько тонкая. Он не может доверять значению PWDпеременной среды, которую он унаследовал. В конце концов, это не должно вызываться оболочкой, и промежуточные программы, возможно, не реализовали механизм оболочки, заставляющий PWDпеременную среды всегда отслеживать имя рабочего каталога. Или кто-то может сделать то, что я сделал только там.

Таким образом, он выполняет (как говорит стандарт POSIX) проверку того, что имя, указанное в, PWDдает то же самое, что и имя ., что можно увидеть с помощью трассировки системного вызова:

% ln -sac 
% (cd b; ферма / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / home / JdeBP / b", { mode = drwxr-xr-x, inode = 120932, размер = 2, blksize = 131072}) = 0 (0x0) 
stat (".", {mode = drwxr-xr-x, inode = 120932, размер = 2, blksize = 131072}) = 0 (0x0)
/ USR / дома / JdeBP / б
% (cd b; PWD = / usr / local / etc truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / local / etc" , {mode = 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 / дома / JdeBP / а
% (cd b; PWD = / привет / есть ферма / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ hello / there", 0x7fffffffe730) ERR # 2 'Нет такого файла или каталога' 
__getcwd ("/ usr / home / JdeBP / a", 1024) = 0 (0x0)
/ USR / дома / JdeBP / а
% (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 ", {mode = drwxr-xr-x, inode = 120932, size = 2, blksize = 131072}) = 0 (0x0) 
stat (". ", {Mode = drwxr-xr-x, inode = 120932 , size = 2, blksize = 131072}) = 0 (0x0)
/ USR / дома / JdeBP / с
%

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

Функция getcwd()библиотеки сама по себе является предметом. Но, прежде всего:

  • Первоначально это была чисто библиотечная функция, которая создавала путь из рабочего каталога обратно в корень, неоднократно пытаясь найти рабочий каталог в ..каталоге. Он останавливался, когда достигал цикла, в котором ..был такой же рабочий каталог, или когда при попытке открыть следующий ..файл произошла ошибка . Это было бы много системных вызовов под прикрытием.
  • В настоящее время ситуация несколько сложнее. Во FreeBSD, например (это быть верно и для других операционных систем, а), то это настоящий системный вызов, как вы можете видеть в трассировке системного вызова данной ранее. Весь обход от рабочего каталога vnode до корня выполняется в одном системном вызове, который использует такие преимущества, как прямой доступ кода режима ядра к кэшу записи каталога, для более эффективного поиска компонента pathname.

    Однако обратите внимание, что даже во FreeBSD и тех других операционных системах ядро не отслеживает рабочий каталог со строкой.

Навигация к ..снова является отдельной темой. Еще один пример: хотя каталоги условно (хотя, как уже упоминалось, это не обязательно) содержат фактические ..данные в структуре данных каталога на диске, ядро ​​отслеживает родительский каталог каждого vnode каталога и, таким образом, может перейти к ..vnode любого рабочий каталог. Это несколько усложняется точкой монтирования и измененными корневыми механизмами, которые выходят за рамки этого ответа.

В сторону

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

Традиционно, в отличие от случая с операционными системами Unix и Linux, программы Win32 не отображают эти переменные среды пользователям. Однако иногда их можно увидеть в Unix-подобных подсистемах, работающих в Windows NT, а также при использовании команд интерпретаторов SETкоманд определенным образом.

дальнейшее чтение

JdeBP
источник
1
Это гораздо больше, чем я ожидал. Спасибо и дополнительное спасибо за дальнейшее чтение!
ReactingToAngularVues
doc.cat-v.org/plan_9/4th_edition/papers/lexnames рассказывает о некоторых проблемах ..в контексте Plan9,
Икар
@JdeBP: Возможно, я что-то упустил. Вы говорите: «Внутри, внутри…, bash,… и… оболочка дополнительно отслеживает рабочий каталог, используя строковые манипуляции с внутренней строковой переменной. …, Он корректирует строку, которую нужно удалить, .и ..компоненты, и выслеживает символические ссылки, заменяя их их связанными именами. ... Имя во внутренней строковой переменной отслеживается переменной оболочки с именем PWD... »(выделение добавлено). … (Продолжение)
G-Man говорит: «Восстановите Монику»
(Продолжение) ... Но ваш пример показывает PWD= …/bпосле cd bкоманды, даже если bэто символическая ссылка на a- поэтому оболочка не «преследует» a -> bссылку. Вы ошиблись или я неправильно прочитал?
G-Man говорит «Восстановить Монику»
Я просто коснулся побочной точки и указал вам на код для деталей. См. Руководства различных оболочек, чтобы узнать, когда и как они решают использовать символические ссылки или нет. Оболочка Z легко вызывает свою опцию оболочки, которая является частью формулы принятия решения CHASE_LINKS.
JdeBP
1

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

Эта getcwd()функция - не системный вызов, а библиотечная функция, которая должна пройти до корневого каталога, записывая имена компонентов пути на пути.

Йохан Мирен
источник
0

Интересно, что традиционно cd ..это намного намного проще, чем pwd. Названные ..каталоги размещаются явно в файловой системе. Система отслеживает устройство / inode текущего каталога, так cd ..что, точнее говоря, системный вызов chdir("..")влечет за собой поиск имени «..» в файле, принадлежащем к inode текущего каталога, и замену устройства / inode текущего каталога на найденное там значение.

pwd(точнее /bin/pwd) ..последовательно переходит по ссылкам и читает соответствующие каталоги до тех пор, пока не найдет индекс, откуда он взялся, собирая список этих имен в обратном порядке, пока он не достигнет корневого каталога (в частности, не содержащего ..записи).

Теперь это оригинальное базовое поведение низкого уровня. Фактические команды оболочки pwdвместо этого полагаются на различные методы, кеширующие текущее имя пути. Но в сущности, на самом деле известен только его инод. Это подразумевает, что если для навигации по каталогам используются символические ссылки, то представления об имени текущего рабочего каталога в текущей оболочке и в системе /bin/pwdмогут расходиться.


источник