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

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

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

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

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

27 голосов | спросил ReactingToAngularVues 27 WedEurope/Moscow2017-12-27T06:50:57+03:00Europe/Moscow12bEurope/MoscowWed, 27 Dec 2017 06:50:57 +0300 2017, 06:50:57

3 ответа


68

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

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

  • Для каждого процесса, в структуре данных пространства ядра, которая представляет этот процесс, ядро ​​хранит две ссылки vnode на vnodes рабочего каталога и корневой каталог для этого процесса. Первая ссылка задается с помощью 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 text /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 root /4 drwxr-xr-x 35 r
    % 

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

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

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

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

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

Эти два метода отслеживания отображаются с помощью -P и -L для cd и pwd, а также различиями между командами встроенных команд pwd и как ---- +: = 17 =: + ---- и встроенные команды /bin/pwd таких вещей (среди прочих) VIM и NeoVIM.

% mkdir a; ln -s a b 
% (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 = /hello /there /bin /pwd -L) 
/USR /дома /JdeBP /а
% 

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

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

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

% ln -s a c 
% (cd b; truss /bin /pwd -L 3 & 1 1 & 2> & 3 | grep -E '^ stat | __getcwd') 
 stat ("/usr /home /JdeBP /b", {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 /б
% (cd b; PWD = /usr /local /etc truss /bin /pwd -L 3 & 1 1 & 2> & 3 | grep -E '^ stat | __getcwd') 
 stat ("/usr /local /etc", {mode = drwxr-xr-x, inode = 14835, size = 158, blksize = 10240}) = 0 (0x0) 
 stat (".", {mode = drwxr-xr-x, inode = 120932, size = 2, blksize = 131072}) = 0 (0x0)
__getcwd ("/usr /home /JdeBP /a", 1024) = 0 (0x0) 
/USR /дома /JdeBP /а
% (cd b; PWD = /hello /there truss /bin /pwd -L 3 & 1 1 & 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 truss /bin /pwd -L 3 & 1 1 & 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(). Он остановился, когда он достиг цикла, где .. был таким же, как и его рабочий каталог, или когда произошла ошибка, пытающаяся открыть следующий ---- +: = 32 =: + ---- вверх. Это будет много системных вызовов под обложками.
  • В настоящее время ситуация немного сложнее. Например, в FreeBSD (это справедливо и для других операционных систем), является истинным системным вызовом, как вы можете видеть в предыдущей таблице системных вызовов. Все обход из рабочего каталога vnode до корня выполняется одним системным вызовом, который использует преимущества, такие как прямой доступ к коду режима режима ядра к кешу каталога, для более эффективного поиска компонентов пути.

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

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

Помимо

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

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

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

ответил JdeBP 27 WedEurope/Moscow2017-12-27T11:20:40+03:00Europe/Moscow12bEurope/MoscowWed, 27 Dec 2017 11:20:40 +0300 2017, 11:20:40
1

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

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

ответил Johan Myréen 27 WedEurope/Moscow2017-12-27T10:03:07+03:00Europe/Moscow12bEurope/MoscowWed, 27 Dec 2017 10:03:07 +0300 2017, 10:03:07
0

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

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

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

ответил 28 ThuEurope/Moscow2017-12-28T15:46:09+03:00Europe/Moscow12bEurope/MoscowThu, 28 Dec 2017 15:46:09 +0300 2017, 15:46:09

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132