Inferno OS Wiki
Регистрация
Advertisement
Роб Пайк
rob@plan9.bell-labs.com
Bell Laboratories, Murray Hill, NJ, 07974
АБСТРАКТНО

Результируя многочисленными корректными путевыми именами для данного файла, символические ссылки делают файловую систему Unix неиерархической. Эта неоднозначность — источник неразберихи, особенно когда некоторые оболочки работают в сверхурочное время для представления последовательного вида ФС программам типа pwd, в то время как другие программы и ядро не делают ничего для устранения проблемы.

В Plan 9 нет символических ссылок, но присутствуют другие механизмы, создающие подобного рода трудности. Кроме того, Plan 9 основана на возможности управления окружением программы путем манипулирования ее пространством имен. Неоднозначные имена не подходят для операций наподобие копирования пространства имен через сеть.

Чтобы преодолеть эти проблемы, ядро Plan 9 было модифицировано для поддержки точного путевого имени для каждого активного файла (открытого файла, рабочего каталога, элемента таблицы монтирования) в системе. Определение «точный» (accurate) соответствует тому, что путевое имя файла обязательно будет образовано с использованием абсолютного имени. За управление этими именами ответственность несет эффективный метод, комбинирующий лексическую обработку — вроде оценки .. посредством удаления последнего путевого элемента каталога — с локальными операциями в пределах файловой системы для представления последовательного, легко понятного вида системы именования. Неоднозначные ситуации разрешаются путем обследования лексически поддерживаемых имен.

Новый вызов ядра, fd2path, возвращает имя файла, ассоциированное с открытым файлом, разрешая использование надежных имен для улучшения системных сервисов в диапазоне от pwd до отладки. Хотя эта работа была выполнена в Plan 9, Unix системы также могут выиграть от добавления метода восстановления точного имени открытого файла или текущего каталога.

Мотивации[]

Рассмотрим пример сессии работы Bourne shell в современной Unix системе:

% echo $HOME
/home/rob
% cd $HOME
% pwd
/n/bopp/v7/rob
% cd /home/rob
% cd /home/ken
% cd ../rob
../rob: bad directory
%

(Такие же результаты и у tcsh; теперь остановимся ненадолго на ksh.) Неофита, плавающего косяком в недрах иерархического пространства имен файлов, такое поведение может сбить с толку. Это, безусловно, последствие серий символических ссылок, предназначенных для создания пользователям иллюзии, что они используют диск, когда на самом деле их файлы разбросаны на нескольких устройствах:

% ls -ld /home/rob /home/ken
lrwxr-xr-x  1 root  sys   14 Dec 26  1998 /home/ken -> /n/bopp/v6/ken
lrwxr-xr-x  1 root  sys   14 Dec 23  1998 /home/rob -> /n/bopp/v7/rob
%

Введение символических ссылок превратило файловую систему Unix из истинной иерархии в ориентированный граф, посеяв неоднозначность и неразбериху.

Unix популяризировала иерархическое наименование, но введение символических ссылок сделало его странным и необычным. Хуже того, команда pwd, посредством основной библиотечной программы getwd, использует мудреный, дорогой алгоритм, который часто дает неправильные результаты. Начиная с текущего каталога, getwd открывает родительский каталог .., и выполняет в нем поиск по элементу, чей индексный идентификатор соответствует текущему каталогу; согласующаяся запись является последним путевым элементом окончательного результата. Применяя этот процесс итеративно, getwd действует в обратном направлении к корню. Поскольку getwd ничего не известно о символических ссылках, она будет исправлять удивительные имена для каталогов, которых достигла, как показано в примере; просматривая обратные пути, getwd не будет возвращаться в исходное состояние по ссылкам.

Частично для эффективности и частично для того, чтобы сделать cd и pwd более предсказуемыми, Korn shell ksh [3] реализует pwd как внутреннюю команду. (Вследствие того, что текущий каталог уникален для каждого процесса, команда cd должна быть внутренней в любой оболочке.) Чтобы попробовать замаскировать символические ссылки ksh обслуживает свой собственный вид файловой системы; в частности, cd и pwd включают немного лексической обработки (что-то наподобие функции cleanname, рассматриваемой позже), расширенной эвристикой вроде обследования окружения для имен типа $HOME и$PWD для подсобления инициализации состояния частного вида. [4]

Данный сеанс начинается с запуска Bourne shell:

% cd /home/rob
% pwd
/n/bopp/v7/rob
% ksh
$ pwd
/home/rob
$

Такой результат удовлетворительный. Другой пример, еще раз начнем с Bourne shell:

% cd /home/rob
% cd ../ken
../ken: bad directory
% ksh
$ pwd
/home/rob
$ cd ../ken
$ pwd
/home/ken
$

Хотя Korn shell, выполняя дополнительную работу, и обеспечивает более подходящее поведение, но и здесь можно столкнуться проблемой:

% cd /home/rob
% pwd
/n/bopp/v7/rob
% cd bin
% pwd
/n/bopp/v7/rob/bin
% ksh
$ pwd
/n/bopp/v7/rob/bin
$ exit
% cd /home/ken
% pwd
/n/bopp/v6/ken
% ksh
$ pwd
/n/bopp/v6/ken
$

В этих примерах, встроенная команда pwd оболочки ksh терпит неудачу при представлении результатов (/home/rob/bin и /home/ken), которые заставил нас ожидать предшествующий пример. Korn shell скрывает проблему, не предотвращая ее, и фактически даже скрывает не совсем удачно.

Возникает вопрос: должна ли вообще оболочка пытаться сделать pwd и cd более так сказать «удачноработовыполняющими». Если так, то библиотечный вызовgetwd и каждая программа, использующая его, будут вести себя дифферентно оболочке, т.е. ситуация несомненно неопределенная. Кроме того, возможность изменить каталог в ../ken с помощью команды cd Korn shell, а не посредством системного вызова chdir, — симптом больной системы, но никак не здоровой оболочки.

Операционная система должна обеспечивать имена, которые действительно работают и имеют смысл. Символические ссылки, все-же, должны оставаться, так что мы нуждаемся в решении, которое способно предоставить значительные, однозначные имена перед неиерархическим пространством имен. В этом документе показано как это требование было удовлетворено в Plan 9 — операционной системе с Unix-подобной системой присваивания имен.

Имена в Plan 9[]

За исключением нескольких деталей, связанных с самозагрузкой (bootstrapping), синтаксис имен файлов в Plan 9 эквивалентен таковому в Unix. В Plan 9 нет символических ссылок, но ее операторы построения пространства имен, bind и mount, делают возможным формирование такого же типа неиерархических структур как при символическом связывании каталогов в Unix.

Системный вызов mount (в Plan 9) получает файловый дескриптор и соединяет его с локальным пространством имен файловой системы:

mount(fd, "/dir", flags)

Здесь fd — файловый дескриптор порта связи, вроде pipe или сетевого соединения; на другом конце порта находится сервис, вроде файлового сервера, который общается с протоколом файловой системы 9P. После вызова, корневой каталог сервиса будет виден в точке монтирования /dir (такое поведение схоже с Unix вызовом mount). Аргумент flag определяет природу подключения: MREPL утверждает, что содержимое корневого каталога (появившегося) заменяет текущее содержимое /dir; MAFTER утверждает, что текущее содержимое dir остается видимым вместе с содержимым подмонтированного каталога, появляющегосяпосле любых существующих файлов; и MBEFORE говорит о том, что содержимое остается видимым вместе с содержимым подмонтированного каталога, появляющегося перед любым существующим файлом. Эти многокомпонентные (составные) каталоги называются союзными (union directories) и отчасти отличаются от союзных каталогов в 4.4BSD-Lite [5] тем, что здесь каталог верхнего уровня сам является связанным (а не его потомки) рекурсивно.

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

mount(rootfd, "/", MREPL);

После этого вызова все дерево файлового сервера, начиная с корня, появляется на локальной машине.

Пока mount соединяет новый сервис с локальным пространством имен, bind перестраивает существующее пространство имен:

bind("tofile", "fromfile", flags)

Вследствие чего последующие упоминания fromfile (который может быть как простым файлом, так и каталогом) будут перенаправляться на tofile. (Заметим, тем не менее, что по сравнению с ln -s здесь аргументы имеют противоположный порядок следования). Аргумент flags такой же как и в mount.

Например, следующие строки на этапе загрузки формируют в каталоге /bin все исполняемые файлы архитектуры SPARC:

bind("/sparc/bin", "/bin", MREPL);
bind("/usr/rob/sparc/bin", "/bin", MAFTER);

Эта возможность позволяет избегать использования переменной окружения $PATH. Если бы системой была Power PC, то sparc заменяла бы строка power и в/bin выполнялся бы сбор файлов Power PC, а не SPARC.

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

% cd /bin
% cd ..

/ или /sparc или /usr/rob/sparc? Мы вернемся к этому.

Между binds и символическими ссылками существуют важные различия. Первое, символические ссылки считаются статической частью файловой системы, в то время как связывания Plan 9 — временные, они создаются лишь на время работы системы и присутствуют пока ядро их поддерживает. Поскольку они известны ядру, а не файловой системе, то должны устанавливаться каждый раз, когда ядро загружается или пользователь входит в систему; постоянные связывания создаются путем редактирования инициализационных сценариев системы и пользовательских профайлов.

Ядро Plan 9 записывает какие связывания являются активными для процесса, потому что символические ссылки, хранящиеся на файловом сервере Unix, могут изменяться всякий раз, когда процесс оценивает имя файла. Также, символические ссылки относятся ко всем процессам, которые оценивают затрагиваемый файл, в то время как у bind локальная область использования, он применяется только к вызвавшему его процессу, и, возможно, к нескольким из его сверстников, как будет описано в следующем разделе. Символические ссылки не могут сформировать каталог типа /bin, построенный здесь; можно иметь составные каталоги, указывающие на /bin, не не наоборот.

И, наконец, символические ссылки символичны, подобно макросам: они оценивают связанные (ассоциированные) имена всякий раз, когда к ним производится доступ. Связывания, в свою очередь, оцениваются единожды, когда выполняется вызов bind; после того как связывание установлено, ядро ассоциирует основные файлы, а не их имена. Фактически, ядерное представление bind идентично представлению mount; в действительности же bind является монтированием tofile с fromfile. Связывания и монтирования сосуществуют в одной таблице монтирования.

Таблица монтирования[]

В Unix присутствует одна таблица монтирования для всех процессов в системе, в то время как в Plan 9 таблицы монтирования локальны для каждого процесса. По умолчанию таблица наследуется когда процесс раздваивается, так что монтирования и связывания позволяют одному процессу влиять на другой, но процесс может взамен унаследовать копию, таким образом модификации, которые он производит, будут незаметны другим процессам. Соглашение гласит следующее: родственные процессы, вроде процессов, запущенных в одном окне, совместно используют таблицу монтирования; группы процессов в разных окнах, в свою очередь, имеют раздельные частные таблицы монтирования. На практике, пространства имен двух окон будут выглядеть почти одинаково, но возможность разным процессам видеть отличные файлы (следовательно сервисы) под одним именем является фундаментальным для системы, влияя на конструкцию ключевых программ типа оконной системы [6].

Таблица монтирования Plan 9 несколько больше чем упорядоченный список пар, распределяющих fromfiles в tofiles. Для монтирований, tofile является элементом под названием Channel (подобный Unix vnode, указывающий на корень файлового сервиса), в то время как для связываний он — Channel, который указывает на tofile, упомянутый в вызове bind. В обоих случаях, запись fromfile в таблице будет Channel, непосредственно указывающим на fromfile.

Оценка имени файла выполняется следующим образом. Если имя начинается с косой черты (слэша), то совмещается с Channel для корня; в противном случае — совмещается с Channel для текущего каталога процесса. Для каждого путевого элемента в имени, скажем как usr в /usr/rob, осуществляется попытка «пройти» Channel к элементу [7]. Если проход удачен, выполняется проверка является ли результирующий Channel таким же как любой fromfile в таблице монтирования, если так — происходит его замена соответствующим tofile. Продвижение к следующему элементу и продолжение.

Здесь есть пара нюансов. Если пройденный каталог — союзный, предпринимается попытка прохода по элементам союза, по порядку их следования, пока проход удачен. Если ни один не добивается успеха, то операция прекращается. Также, когда месту назначения прохода отвечает каталог для цели вроде системного вызова chdir или fromfile в bind, как только последний проход последовательности завершается, операция прекращается; последняя проверка посредством таблицы монтирования не запускается. Между прочим, это упрощает управление союзными каталогами; к примеру, последующие bind-вызовы будут добавлять к союзу файлы, ассоциированные с основным fromfile взамен тем, что связаны с ним.

Точка-точка, определение[]

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

Значение .. простое, если каталог находится в локально иерархической части пространства имен. Но что должна определять .., когда текущий каталог представляет собой точку монтирования или союзный каталог или многочисленно символически связанную точку (которую мы в дальнейшем для краткости будем называть просто точкой монтирования)? Очевидного ответа на этот вопрос нет. Пространства имен были частью Plan 9 с самого начала, но определение.. изменялось несколько раз когда мы затрагивали этот злободневный вопрос. Фактически, несколько попыток разъяснить значение .. умным кодированием результировалось в определениях, которые могут вполне милосердно быть суммированы как «что дает реализация».

Огорченные этой ситуацией, и стремящиеся к получению хорошо-определенных имен для некоторых приложений, мы недавно предложили следующее определение для ..:

Родительским для каталога XX/.., считается тот каталог, который получается вследствие удаления последнего путевого элемента X.

Например, если мы находимся в каталоге /a/b/c и выполняем смену каталога в .., то результат должен быть точно такой же как при смене каталога в /a/b.

Данное определение легко понятно и кажется природным. Оно, тем не менее, является чисто лексическим определением, которое наотрез игнорирует оценку имен файлов, таблиц монтирования и других ядро-резидентных структур данных. Наше требование состоит в его эффективной реализации. Одна очевидная (и корректная) реализация: перезапись путевых элементов лексически для свертывания .., и затем оценка имени файла из корня, но она дорога и непривлекательна. Мы желаем использовать локальные операции для оценки имен файлов, но управлять глобальным, лексическим определением точки-точки. Это не так уж и сложно.

Реализация[]

Чтобы оперировать именами файлов лексически, мы связываем имя с каждым открытым файлом в ядре, то бишь, с каждой структурой данных Channel. Следовательно, первый шаг — сопоставление char* с каждым Channel в системе (назвав его Cname), при этом записывается абсолютное корневое имя файла дляChannel. Cnames хранятся как полные текстовые строки, распределенные копия-на-запись для эффективности. Задание состоит в поддержке каждого Cname как точного абсолютного имени с помощью только локальных операций.

При открытии файла, аргумент имени файла в вызове open (или chdir или bind или…) записывается в Cname результирующего Channel. Если имя файла начинается с косой черты, то сохраняется как есть, в противном случае оно (имя файла) является локальным и должно преобразовываться в абсолютное посредством префиксации к нему Cname текущего каталога, предваренного косой чертой. К примеру, если мы находимся в /home/rob и выполняем смену каталога в bin, Cname результирующего Channel будет строкой /home/rob/bin.

Предполагается, конечно, что локальное имя файла не содержит элементов ... Если так, то вместо записи, скажем /home/rob/.., мы удаляем последний элемент существующего имени и устанавливаем Cname как /home. Для поддержки лексического присваивания имен мы обязаны гарантировать, что результирующий Cname, если он подлежит оценке, будет давать каталог, идентичный тому, который мы получаем в результате локальной операции ...

Если текущий каталог не является точкой монтирования, лексические свойства легко поддерживать. Если же он все-таки точка монтирования, его все-еще можно поддерживать в Plan 9, поскольку таблица монтирования является ядро-резидентной структурой данных и содержит всю информацию о неиерархической связности пространства имен. (В Unix символические ссылки хранятся на файловом сервера, а не в ядре.) Кроме того, наличие полного имени файла для каждого Channel в таблице монтирования обеспечивает информацией, необходимой для решения неоднозначностей.

Когда происходит оценка имени, таблица монтирования обследуется в направлении from->to, но .. указывает на обратную иерархию, так что для оценки ..таблица монтирования должна проверяться в направлении to->from. («Как я сюда попал?»)

Когда присутствуют составные связывания (точки монтирования), которые указывают на каталоги, включающиеся при оценке .., величина последней неоднозначна. Для примера, вернемся к нашему первоначальному сценарию с каталогами /n/bopp/v6 (содержащий домашний каталог ken) и /n/bopp/v7(содержащий домашний каталог rob), собранными в /home. В таблице монтирования они представлены двумя записями: from=/home, to=/n/bopp/v6 иfrom=/home, to=/n/bopp/v7. Если мы установим текущим каталог /home/rob (физическое размещение которого /n/bopp/v7/rob), то он не будет точкой монтирования, в то время как его родительский — будет. Значение .. неоднозначно: оно может быть /home, /n/bopp/v7, или, возможно, даже /n/bopp/v6, и неоднозначность эта вызвана двумя tofiles, связанными в одном fromfile. В нашем определении, если мы сейчас оценим .., то получим /home; в противном случае, ../ken не может соответствовать каталогу ken, как ожидалось. С другой стороны, если бы мы первоначально перешли в /n/bopp/v7/rob, тогда имя../ken не оценивалось бы как домашний каталог ken из-за того, что каталога /n/bopp/v7/ken просто не существует. Проблема в том, что при использовании локальных файловых операций эти варианты невозможно различить: независимо от того, попали мы туда посредством /home/rob или /n/bopp/v7/rob, результирующий каталог один и тот же. Кроме того, таблица монтирования не содержит достаточно информации для устранения противоречия: когда мы выполняем локальную операцию для оценки .. и находимся в /n/bopp/v7, то узнаем, что каталог является элементом tofile в таблице монтирования; так нужно ли нам делать шаг через таблицу в /home или нет?

Решение приходит из самих Cnames. Независимо от того, сделан ли шаг через таблицу from=/home, to=/n/bopp/v7 при оценке .. в домашнем каталоге rob, задача тривиально решается постановкой следующего вопроса: Cname для каталога начинается с /home? Если так, тогда путь, который был оценен для получения текущего каталога, должен проходить через эту точку монтирования, и мы должны вернуться на шаг через него для оценки ..; если же нет, то этот элемент таблицы монтирования считается несоответствующим.

Более точно, оба перед и после каждого элемента .. в путевом имени оцениваются, если каталог — tofile в таблице монтирования, то взамен берется соответствующий fromfile, представленный Cname этого fromfile является префиксом для Cname первоначального каталога. Поскольку нам всегда известно полное имя каталога, которое оцениваем, то можем сравнивать его со всеми записями в таблице монтирования, которые указывают на него, этим самым избегая неоднозначных ситуаций и поддерживая лексические свойства ... Эта проверка также гарантирует тот факт, что мы не следуем по вводящей в заблуждение точке монтирования, подобно записи, указывающей на /home, когда мы в действительности находимся в /n/bopp/v7/rob. Хранение полных имен вместе сChannels позволяет использовать таблицу монтирования для определения как мы сюда попали и, следовательно, как вернуться назад.

В итоге, алгоритм следующий. Использование обычных операций файловой системы для перехода в ..; вызов результирующего каталога d. Лексическое удаление последнего элемента внутреннего имени файла. Проверка всех записей таблицы монтирования, чьи tofile — d и чьи fromfile имеют Cname, идентичные усеченному имени. Если он существует, тогда этот fromfile считается корректным результатом; по конструкции, он также имеет правильныйCname. В нашем примере, оценка .. в /home/rob (в действительности /n/bopp/v7/rob) установит d как /n/bopp/v7; это tofile, чей fromfile является /home. Удалив /rob из первоначального Cname, мы обнаруживаем имя /home, которое соответствует тому самому fromfile, вследствие чего результат — fromfile,/home.

Поскольку эта реализация для управления имена использует только локальные операции, есть вероятность спутывать их внешними изменениями в файловой системе. Удаление или переименование каталогов и файлов, являющихся частью Cname, или модификация таблицы монтирования, могут приводить к возникновению ошибок. Доработав эту реализацию, вероятно, можно устранить подобные недочеты, но в сетевой среде с машинами, совместно использующими удаленный файловый сервер, переименования и удаления, сделанные на одной машине, могут быть незамеченными другими. Проблемы такого рода, тем не менее, незначительные, необычные и, что самое главное, легко понятные. Метод управляет лексическими свойствами имен файлов, если внешний агент не изменяет имена тайно; в пределах стабильной файловой системы они всегда управляются и pwd возвращает корректный результат.

Итак, резюмируя, можно отметить, что для создания эффективного лексического определения .. комбинируются две ипостаси: 1) поддержка абсолютными именами файлов Channel лексически и 2) использование имен для устранения противоречий записей таблицы монтирования при оценке .. в точке монтирования.

Очистка имен[]

Лексическая обработка может генерировать неряшливые или излишние имена: с дополнительными косыми чертами или вложенными ../ или ./ элементами и другими внешними артефактами. Как часть реализации ядра мы написали процедуру под названием cleanname, которая перезаписывает имя на месте для канонизирования его вида. Процедура эта очень полезна, поэтому сейчас она часть библиотеки Plan 9 C и применяется множеством программ для удостоверения, что они всегда обеспечивают «чистые» имена файлов.

Cleanname во многом аналогична правилам очистки URL, описанных в RFC 1808 [1]. Cleanname итеративно выполняет каждую из следующих операций:

  1. Сокращение многочисленных косых черт до одной.
  2. Устранение . путевых элементов имени (текущий каталог).
  3. Устранение .. путевых элементов имени (родительский каталог) и без-. без-.. элементов, которые предшествуют им.
  4. Устранение .. элементов, которые начинают корневой путь, т.е. замена /.. / в начале пути.
  5. Оставление целых .. элементов, которые начинают безкорневой путь.

Если в результате осуществления этого процесса получается нулевая строка, cleanname возвращает строку «.», представляющую текущий каталог.

Системный вызов fd2path[]

В Plan 9 добавлен новый системный вызов, fd2path, чтобы позволить программам извлекать Cname, связанные с дескриптором открытого файла. Он берет три аргумента: файловый дескриптор, буфер и размер буфера:

int fd2path(int fd, char *buf, int nbuf)

Возвращает ошибку, если файловый дескриптор неправильный; в противном случае заполняет буфер именем, ассоциированным с fd. (Если имя слишком длинное, то оно усекается; возможно это условие также должно производить ошибку.) Системный вызов fd2path очень «дешевый», поскольку все, что он делает, — это копирует строку Cname в пользовательское пространство. Реализация getwd (Plan 9) использует fd2path вместо мудреного алгоритма, необходимого в Unix:

char*
getwd(char *buf, int nbuf)
{
	int n, fd;

	fd = open(".", OREAD);
	if(fd < 0)
		return NULL;
	n = fd2path(fd, buf, nbuf);
	close(fd);
	if(n < 0)
		return NULL;
	return buf;
}

(Спецификация Unix getwd не содержит аргумент-счетчик.) Эта версия getwd не только проста, но и достаточно эффективна, она уменьшает выгоду выполнения встроенной команды pwd, гарантируя что все команды, не только pwd, видят значительные имена каталогов.

Вот программа, печатающая имя файла, ассоциированное с каждым его открытым файловым дескриптором; она полезна для прослеживания файловых дескрипторов, оставленных открытыми сетевыми слушателями, текстовыми редакторами и т.п.:

void
openfiles(void)
{
	int i;
	char buf[256];

	for(i=0; i<NFD; i++)
		if(fd2path(i, buf, sizeof buf) >= 0)
			print("%d: %s\n", i, buf);
}

Использование хороших имен[]

Хотя pwd и была мотивацией для получения правильных имен, хорошие имена файлов полезны во многих контекстах и стали ключевой составляющей программной среды Plan 9. Компиляторы записывают в символьной таблице сгенерированного кода полное имя исходного файла, что делает возможным прослеживание источника дефектного, устаревшего ПО, а также разрешает реализацию программы, src, для автоматических слежений. Вызванная с аргументом — имен программы, src читает ее символьную таблицу, извлекает информацию о файле, и инициирует запуска редактора с исходным текстом программы. Без каких-либо догадок и эвристики.

Программа openfiles была вдохновением для нового файла в файловой системе /proc [2]. Для процесса n, файл /proc/n/fd является списком всех открытых им файлов, а также рабочим каталогом процесса, со связанной информацией, включая статус открытия, смещение ввода-вывода, уникальный идентификатор (аналогичен индексному идентификатору) и имя файла. Вот содержимое файла fd для процесса в оконной системе на машине, использовавшейся для написания этого документа.

% cat /proc/125099/fd 
/usr/rob
  0 r  M 5141 00000001.00000000        0 /mnt/term/dev/cons
  1 w  M 5141 00000001.00000000       51 /mnt/term/dev/cons
  2 w  M 5141 00000001.00000000       51 /mnt/term/dev/cons
  3 r  M 5141 0000000b.00000000     1166 /dev/snarf
  4 rw M 5141 0ffffffc.00000000      288 /dev/draw/new
  5 rw M 5141 00000036.00000000  4266337 /dev/draw/3/data
  6 r  M 5141 00000037.00000000        0 /dev/draw/3/refresh
  7 r  c    0 00000004.00000000  6199848 /dev/bintime
%

(Реализация /proc в Linux обеспечивает схожий сервис, представляя собой каталог, в котором каждый файловый-дескриптор-пронумерованный файл — символическая ссылка на сам файл.) При отладке блуждающего системного ПО, подобная информация может быть весьма ценной.

Другой мотивацией для получения правильных имен была необходимость в извлечении из системы точного описания таблицы монтирования, таким образом, чтобы переместить (или сымитировать) вычислительную среду через сеть, пространство имен процесса может быть воссоздано на другой машине. Одной из программ, выполняющих данное действие, является команда cpu, которая воссоздает локальное пространство имен на удаленной машине, типично — это большой быстрый мультипроцессор. Без точных имен, работу данного типа выполнить было бы невозможно; теперь /proc представляет описание пространства имен для каждого процесса, /proc/n/ns:

% cat /proc/125099/ns
bind  / /
mount -aC #s/boot / 
bind  #c /dev
bind  #d /fd
bind -c #e /env
bind  #p /proc
bind -c #s /srv
bind  /386/bin /bin
bind -a /rc/bin /bin
bind  /net /net
bind -a #l /net
mount -a #s/cs /net 
mount -a #s/dns /net 
bind -a #D /net
mount -c #s/boot /n/emelie 
bind -c /n/emelie/mail /mail
mount -c /net/il/134/data /mnt/term 
bind -a /usr/rob/bin/rc /bin
bind -a /usr/rob/bin/386 /bin
mount  #s/boot /n/emelieother other
bind -c /n/emelieother/rob /tmp
mount  #s/boot /n/dump dump
bind  /mnt/term/dev/cons /dev/cons
...
cd /usr/rob
%

(Указание # идентифицирует драйверы блочных устройств, в результате они могут подключаться к пространству имен.) Последняя строка в файле дает рабочий каталог процесса. Формат файла используется в библиотечной программе newns, которая читает текстовое описание схожего типа и восстанавливает пространство имен. За исключением потребности в символах #, вывод также есть сценарием оболочки, который вызывает команды пользовательского уровняbind и mount — интерфейсы основных системных вызовов. Тем не менее, файлы наподобие /net/il/134/data представляют сетевые соединения; чтобы узнать куда они указывают (для того чтобы соответствующие вызовы могли устанавливаться и для других процессов), они должны обследоваться более детально с использованием файлов сетевых устройств [8]. Еще одна программа — ns — выполняет эту операцию; она читает файл /proc/n/ns, декодирует и интерпретирует информацию, переводя сетевые адреса и при необходимости заключая имена в кавычки:

...
mount -a '#s/dns' /net 
...
mount -c il!135.104.3.100!12884 /mnt/term 
...

Эти средства позволяют собирать точное описание пространства имен процесса и воссоздавать его в другом месте. И подобно таблице дескрипторов открытых файлов, они помогают при отладке; всегда полезно знать точно какие ресурсы используются программой.

Адаптация к Unix[]

Данная работа была проведена для операционной системы Plan 9, основным преимуществом которой считается то, что все неиерархические аспекты пространства имен известны ядру. Все-же, должна быть возможность адаптировать реализацию к Unix системе. Проблема состоит в том, что в Unix нет ничего точно соответствующего структуре Channel, которая в Plan 9 представляет уникальный результат оценки имен. Структура vnode является распределенной и может представлять файл, известный под несколькими именами, в то время как структура file указывает только на открытые файлы, но, к примеру, текущий рабочий каталог процесса не открыт. Возможности адресации этого несоответствия включают введение Channel-подобной структуры, соединяющей имя иvnode, или создание отдельной управляющей таблицы для каждого процесса, которая отображала бы имена в vnodes. Если бы это можно было сделано, то результатом была бы реализация.., сводящая на нуль потребность во внутренней pwd в оболочке и обеспечивающая последовательную, значительную интерпретацию термина «родительский каталог».

Мы не осуществили эту адаптацию, но рекомендуем ее апробацию обществу Unix.

Вывод[]

Теперь должно быть легко узнавать хорошо-определенные, абсолютные путевые имен для каждого открытого файла и каталога в системе, даже для символических ссылок и прочих неиерархических элементов пространства имен файлов. В ранних версиях Plan 9 и всех современных версиях Unix, имена взамен могут быть противоречивыми и неразборчивыми.

Операционная система Plan 9 сейчас поддерживает точное имя для каждого файла с использованием недорогих лексических операций, связанных с действиями локальной файловой системы. Неоднозначности устранены путем обследования самих имен; поскольку они отражают путь, который использовался для достижения файла, а также обратный путь, восстанавливаемый даже при обратном прохождении через союзный каталог.

Имена вновь имеют смысл: они значительные и последовательные. Теперь, когда эти надежные имена доступны, системные сервисы могут зависеть от них, и недавняя работа в Plan 9 выполняется именно таким образом. Мы — общество пользователей Unix и Unix-подобных систем должны были сделать это давным-давно.

Благодарности[]

Фил Уинтерботтом (Phil Winterbottom) разработал команду ns и файлы fd и ns в /proc, основанные на ранней реализации управления путевыми именами. Расс Кокс (Russ Cox) написал последнюю версию cleanname и помог отладить код для обратимости таблицы монтирования. Кен Томпсон (Ken Thompson), Дейв Пресотто (Dave Presotto) и Джим МакКи (Jim McKie) проконсультировали и одобрили документ.

Литература[]

[1] R. Fielding, Relative Uniform Resource Locators, Network Working Group Request for Comments: 1808, June, 1995.
[2] T. J. Killian, Processes as Files, Proceedings of the Summer 1984 USENIX Conference, Salt Lake City, 1984, pp. 203-207.
[3] David G. Korn, ksh: An Extensible High Level Language, Proceedings of the USENIX Very High Level Languages Symposium, Santa Fe, 1994, pp. 129-146.
[4] David G. Korn, personal communication.
[5] Jan-Simon Pendry and Marshall Kirk McKusick, Union Mounts in 4.4BSD-Lite, Proceedings of the 1995 USENIX Conference, New Orleans, 1995.
[6] Rob Pike, 8½, the Plan 9 Window System, Proceedings of the Summer 1991 USENIX Conference, Nashville, 1991, pp. 257-265.
[7] Rob Pike, Dave Presotto, Ken Thompson, Howard Trickey, and Phil Winterbottom, The Use of Name Spaces in Plan 9, Operating Systems Review, 27, 2, April 1993, pp. 72-76.
[8] Dave Presotto and Phil Winterbottom, The Organization of Networks in Plan 9, Proceedings of the Winter 1993 USENIX Conference, San Diego, 1993, pp. 43-50.

Copyright © 2000 Lucent Technologies Inc. All rights reserved.

Copyright © 2003 Перевод Андрей С. Кухар.

Advertisement