Функции posix


Содержание

Что означает «POSIX»?

Что такое POSIX? Я прочитал статью в Википедии и читаю ее каждый раз, когда сталкиваюсь с термином. Дело в том, что я никогда не понимал, что это такое.

Может ли кто-нибудь объяснить мне, объясняя «необходимость POSIX» тоже?

POSIX — это семейство стандартов, указанных IEEE, чтобы прояснить и унифицировать интерфейсы прикладного программирования (и вспомогательные проблемы, такие как утилиты командной строки командной строки), предоставляемые операционными системами Unix-y. Когда вы пишете свои программы, чтобы полагаться на стандарты POSIX, вы можете быть уверены, что сможете легко переносить их среди большого семейства Unix-производных (включая Linux, но не ограничиваясь этим!); если и когда вы используете некоторый Linux API, который не стандартизован как часть Posix, вам будет труднее, если и когда вы захотите перенести эту программу или библиотеку в другие Unix-y-системы (например, MacOSX) в будущем.

Самые важные вещи POSIX 7 определяет

Значительно расширяет ANSI C такими вещами, как:

  • дополнительные файловые операции: mkdir , dirname , symlink , readlink , link (жесткие ссылки), poll() , stat , sync , nftw()
  • процесс и потоки: fork , execl , wait , pipe , семафоры sem_* , общая память ( shm_* ), kill , планирование параметры ( nice , sched_* ), sleep , mkfifo , setpgid()
  • сеть: socket()
  • управление памятью: mmap , mlock , mprotect , madvise , brk()
  • утилиты: регулярные выражения ( reg* )

Эти API также определяют основные концепции системы, от которых они зависят, например, fork требует концепции процесса.

Существует множество системных вызовов Linux для реализации конкретной функции API POSIX C и обеспечения совместимости с Linux, например, sys_write , sys_read . Многие из этих системных вызовов также имеют специфичные для Linux расширения.

Основная реализация Linux для настольных компьютеров: glibc, которая во многих случаях просто обеспечивает поверхностную оболочку для системных вызовов.

Например: cd , ls , echo .

Многие утилиты являются прямыми оболочками для соответствующей функции C API, например mkdir .

Основная реализация Linux для настольных компьютеров: GNU Coreutils для маленьких, отдельные проекты GNU для больших: sed , grep , awk . Некоторые утилиты CLI реализованы Bash как встроенные модули.

Например, a=b; echo «$a»

Основная реализация Linux для настольных ПК: GNU Bash.

Например: HOME , PATH .

ANSI C говорит 0 или EXIT_SUCCESS для успеха, EXIT_FAILURE для сбоя и оставляет определение остальной реализации.

126 : команда найдена, но не выполняется.

127 : команда не найдена.

> 128 : прекращается сигналом.

Существует два типа: BRE (базовый) и ERE (расширенный). Basic устарел и поддерживается только для того, чтобы не нарушать API.

Они реализуются функциями API C и используются в утилитах CLI, например, grep принимает BRE по умолчанию и ERE с -E .

Например: echo ‘a.1’ | grep -E ‘a.[[:digit:]]’

Основная реализация Linux: glibc реализует функции в regex.h, которые такие программы, как grep , могут использовать в качестве бэкэнда.

Например: /dev/null , /tmp

Linux FHS значительно расширяет POSIX.

  • / является разделителем пути
  • NUL нельзя использовать
  • . является cwd , .. родитель
  • переносимые имена файлов
    • используйте максимум 14 символов и 256 для полного пути
    • может содержать только: a-zA-Z0-9._-

Не обязательно, используется POSIX, но почти нигде, особенно в GNU. Но правда, это слишком ограничительно, например, только однобуквенные флаги (например, -a ), без длинных версий с двойным дефисом (например, —all ).

Несколько широко используемых соглашений:

  • — означает стандартный ввод файла, где ожидается
  • — завершает флаги, например, ls — -l для просмотра каталога с именем -l

«POSIX ACL» (списки контроля доступа), например, используется в качестве бэкэнда для setfacl .

Это было отозвано, но оно было реализовано в нескольких ОС, включая в Linux с setxattr .

Кто соответствует POSIX?

Многие системы строго следуют POSIX, но немногие фактически сертифицированы Open Group, которая поддерживает стандарт. Известные сертифицированные включают в себя:

  • OS X (Apple) X обозначает как 10, так и UNIX. Была ли первая система Apple POSIX выпущена около 2001 года. См. также: Является ли OSX операционной системой POSIX?
  • AIX (IBM)
  • HP-UX (HP)
  • Солярис (Oracle)

Большинство дистрибутивов Linux очень совместимы, но не сертифицированы, поскольку не хотят оплачивать проверку соответствия. Inspur K-UX и Huawei EulerOS — два сертифицированных примера.

Официальный список сертифицированных систем можно найти по адресу: https://www.opengroup.org/openbrand/register/, а также на вики-странице.

Окна

Windows внедрила POSIX в некоторых своих профессиональных дистрибутивах.

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

Поддержка устарела в Windows 8:

В 2020 году был объявлен новый официальный Linux-подобный API под названием «Windows Subsystem for Linux». Он включает системные вызовы Linux, запуск ELF, части файловой системы /proc , Bash, GCC, (вероятно, TODO glibc?), apt-get и другие: https://channel9.msdn.com/Events/Build/2020/P488, так что я считаю, что это позволит Windows работать много, если не все, из POSIX. Тем не менее, он ориентирован на разработчиков/развертывание, а не на конечных пользователей. В частности, не было планов разрешить доступ к графическому интерфейсу Windows.

Cygwin — это широко известный сторонний проект GPL, который «предоставляет существенные функциональные возможности API POSIX» для Windows, но требует, чтобы вы «перестраивали приложение из исходного кода, если хотите, чтобы оно работало в Windows». MSYS2 — это связанный проект, который, кажется, добавляет больше функциональности поверх Cygwin.

Android

У Android есть собственная библиотека C (Bionic), которая не полностью поддерживает POSIX с Android O: Совместима ли Android POSIX?

Бонусный уровень

Стандартная база Linux расширяет возможности POSIX.

Используйте некадровые индексы, они намного более читабельны и доступны для поиска: http://pubs.opengroup.org/onlinepubs/9699919799/nfindex.html

Получите полную версию HTML-страниц для ознакомления: Где находится список функций POSIX C API?

Функции posix

В ответ на вопрос, что такое POSIX, довольно часто можно услышать, что это стандарт на операционную систему, а некоторые при этом добавят «класса UNIX». Ответ неправильный. Об этом и других «открытиях», сделанных в процессе перевода стандарта ISO/IEC 9945-1 [1] на русский язык, а также о некоторых ассоциациях, возникших у автора при чтении оригинального текста, говорится в предлагаемой статье.

О стандартах вообще

Среди практикующих программистов бытует мнение, что стандарты в программировании вообще не нужны, поскольку:

  1. они изначально бессмысленны, так как их авторы не пишут компьютерных программ;
  2. они сковывают инициативу программистов;
  3. программисты всегда договорятся и без стандартов.

Может быть, на это мнение не следовало бы обращать внимания, если бы не два обстоятельства:

  1. его высказывают практики, то есть именно те, кто «выдает программную продукцию»;
  2. приведенная выше аргументация была обнаружена автором данной статьи в одной из публикаций в Internet, посвященной стандарту на язык программирования Си, из чего стало ясно, что такое мнение распространено «в международном масштабе», а не только среди заносчивых российских «суперпрограммистов».

Слово «стандарт» ассоциируется обычно с чем-то материальным (стандартные габариты, стандартное электрическое напряжение и т.д.), в то время как компьютерная программа — объект нематериальный («The new intangible»), и может быть, стандарты в нематериальной сфере действительно бессмысленны?

Существует, однако, опровергающий пример. Совокупность правил орфографии русского языка по существу представляет собой стандарт, хотя и не утвержденный органами стандартизации. Далее, кроме правил (или, если угодно, требований) орфографии, существуют синтаксические правила и, самое главное, семантика. Последнее хорошо иллюстрирует «детский» вопрос: почему кошку называют кошкой? На этот вопрос существует точный ответ: потому, что наши предки так договорились; предки англичан договорились называть этого же зверя cat, предки немцев — kitten, и т.д. И вообще, смысл, или семантика, или правила интерпретации любого слова или сочетания слов — вопрос договоренности.

Назначение и «сверхзадача» стандарта POSIX

Как следует из названия, POSIX (Portable Operating System Interface) — это стандарт на сопряжение (интерфейс) между операционной системой и прикладной программой. Времена, когда программисты писали программы для «голой» машины (реализуя собственные пакеты программ ввода/вывода, тригонометрических функций и т.п.), прошли безвозвратно. В тексте POSIX неоднократно подчеркивается, что стандарт не выдвигает никаких требований к деталям реализации операционной системы; его можно рассматривать как совокупность договоренностей между прикладными программистами и разработчиками операционных систем. Таким образом (опять же вопреки довольно распространенному мнению), POSIX представляет интерес не только для разработчиков операционных систем, но прежде всего для гораздо более многочисленной категории программистов — прикладных.

Потребность в стандарте такого рода была осознана еще в 1980-е годы, когда получили широкое распространение операционные системы UNIX. Оказалось, что хотя эта система и задумывалась как унифицированная, различия между конкретными ее реализациями приводили к тому, что прикладные программы, написанные для одной системы, далеко не всегда могли исполняться в другой. На решение именно этой проблемы, известной как проблема мобильности программного обеспечения, нацелен POSIX. Первая редакция стандарта была выпущена в 1988 году (имеется перевод, см. [2]), в которой все многообразие вопросов, связанных с мобильностью программ, было разбито на две части: (1) интерфейс прикладных программ, (2) командный интерпретатор и утилиты (интерфейс пользователя); эти части получили название POSIX.1 и POSIX.2 соответственно 1 .

Уточним, что в данной статье речь пойдет только о стандарте на интерфейс прикладных программ, POSIX.1, вторая (и последняя к настоящему времени) редакция которого была утверждена 12 июля 1996 года.

В информативной части стандарта подчеркивается также, что POSIX представляет собой не описание интерфейса некоторой «идеальной» операционной системы, а результат обобщения и систематизации опыта, накопленного при разработке операционных систем UNIX. Кроме того, POSIX не может служить руководством или учебным пособием по операционным системам, хотя в информативной части содержатся рекомендации программистам и фрагменты программ.


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

О семантике

Как уже говорилось, POSIX можно рассматривать как совокупность договоренностей между разработчиком операционной системы и прикладным программистом. «Договоренность» означает прежде всего одинаковость интерпретации (семантики) слов и выражений. Далее приводятся примеры, иллюстрирующие трудность задачи достижения «договоренности».

Как передать смысл при переводе

Прежде всего, нужно напомнить, что стандарт POSIX изложен на английском языке, который по своей природе провоцирует двусмысленности (например, одно и то же слово может быть существительным, прилагательным и глаголом), и «семантические ловушки» подстерегают чуть ли не на каждой странице. Хорошей иллюстрацией сказанному служит пример из художественной литературы. Одно из самых известных произведений Оскара Уайльда, который блестяще пользовался этой особенностью английского языка, — The Importance of being Earnest — известно на русском под названием «Как важно быть серьезным». Однако английское название имеет второй смысл: Earnest (серьезный) — фамилия одного из персонажей, и название можно было бы перевести иначе: «Как важно быть Эрнстом». Есть русская фамилия Серебряный, и если бы была фамилия Серьезный, перевод был бы идеально точным, с передачей обоих смыслов.

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

Семантика слова «стандарт»

Основной текст стандарта предваряется преамбулой, в которой разъясняется смысл слов IEEE Standards 2 . Как следует из этих разъяснений, существует по крайней мере три смысловых отличия от русскоязычного термина ГОСТ:

  1. Интуитивно считается, что ГОСТ имеет силу закона, нарушение которого преследуется; POSIX — совокупность требований, следование которым — дело исключительно добровольное.
  2. ГОСТ действует вплоть до отмены (многим, наверное, приходилось слышать выражение «ГОСТ никто не отменял»); в преамбуле к POSIX говорится, что если стандарт не пересматривался в течение 5 лет, это означает, что рассматриваемые в нем вопросы, скорее всего, потеряли актуальность, и его можно считать отмененным автоматически;
  3. ГОСТ анонимен; во вводной части POSIX приводится список тех лиц, которые участвовали в разработке этого стандарта, а также дается адрес, по которому можно направлять запросы по интерпретации; говорится также, что ответ на каждый запрос подвергается процедуре согласования (иными словами, авторы стандарта договариваются между собой, прежде чем дать ответ).

Таким образом, перевод даже такого общеизвестного слова как Standard словом «стандарт» требует комментариев.

Семантика слов «должен», «не специфицировано», «не определено», «определяется реализацией»

Раздел 2 стандарта называется «Терминология и общие требования». В нем содержатся определения не только специальных терминов (вроде «процесс» или «семафор»), но и таких, казалось бы, самоочевидных слов, как «должен» или «может». Поскольку POSIX.1 — стандарт на интерфейс, то его требования относятся как к операционной системе, так и к прикладной программе. Явное требование выражается словом «должен» (shall), например: «В случае успешного завершения функция link() должна возвращать нулевое значение». В данном примере речь идет о требовании к операционной системе: функция link() должна быть реализована так, чтобы при успешном завершении возвращала нулевое значение.

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

Термин «не специфицировано» означает, что множество вариантов (исходов, результатов вызова функции и т.д.) предполагается известным, и стандарт допускает любой из них; в конкретной реализации операционной системы может быть выбран любой, и такая система считается соответствующей стандарту. Прикладная программа (строго соответствующая стандарту) должна быть написана так, чтобы корректно работала при любом варианте; по существу, этим термином неявно выражается требование к прикладной программе.

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

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

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

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

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

Использование в прикладной программе данных из буфера в случае ошибки функции read() стандарт запрещает, и последствия нарушения этого требования возлагает на прикладного программиста.

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

Ни один регламентирующий документ не может охватить всего многообразия случаев, которые могут встретиться на практике, поэтому он неизбежно о чем-то умалчивает 3 . Например, в описании функции может быть сказано, что ее аргумент может принимать значения из некоторого диапазона, но ничего не говорится о том, каков будет результат, если аргумент не попадает в этот диапазон. Очевидно, что во избежание недоразумений необходимо иметь единую семантику умолчания. В информативной части стандарта имеется любопытная фраза: «общепринятая семантика умолчания — запрещено». Это утверждение противоречит известному лозунгу десятилетней давности «разрешено все, что явно не запрещено». По-видимому, он настолько укоренился в сознании граждан, что многие, даже программисты, не соглашаются с процитированным утверждением стандарта. Между тем, если применение какой-либо конструкции явно не разрешено и не следует из описания, то любой практикующий программист осознает, что использование ее рискованно, и если она не срабатывает, ему не приходит в голову предъявлять претензии.

Процессы и потоки управления

Оба этих термина выражают идею параллельности исполнения. Операционная система UNIX была изначально задумана как многопользовательская, и программы, запускаемые разными пользователями, должны быть надежно изолированы друг от друга, чтобы случайно не исказить «чужие» данные. Эта изоляция обеспечена тем, что программа пользователя исполняется в рамках некоторого процесса, развивающегося в собственном виртуальном адресном пространстве. Даже если в программе есть глобальные данные, при запуске ее в разных процессах они будут автоматически «разведены» по разным адресным пространствам.

Однако механизм процессов не вполне удовлетворителен при программировании задач реального времени. Прикладная программа реального времени (исполняющаяся от имени одного и того же пользователя) часто может быть естественным образом представлена в виде параллельно исполняющихся частей, которые называют «потоками управления» (thread). Наиболее существенное их отличие от процессов состоит в том, все потоки управления развиваются в едином адресном пространстве; этим обеспечивается быстрый доступ к глобальным данным, но одновременно возникает риск непреднамеренного их искажения, и чтобы этого не происходило, необходимо соблюдать некоторую дисциплину программирования. Соответствующие рекомендации содержатся в информативной части стандарта.

Нужно подчеркнуть, что идея многопоточности реализована во многих операционных системах реального времени, и реализована по-разному в том смысле, что каждому потоку управления соответствуют разные множества атрибутов и интерфейсных функций; иногда вместо термина «поток управления» (thread) используется термин «задача» (task). Для того чтобы избежать недоразумений, в стандарте подчеркивается, что речь в нем идет исключительно о потоках управления POSIX, а названия соответствующих интерфейсных функций имеют префикс pthread_ (например, pthread_create(), pthread_join() и т.д.).

Соответствие стандарту. Семантика слова «соответствует»

Интуитивно считается, что если два предмета изготовлены в соответствии с одним и тем же стандартом, то они гарантированно «сопрягаются» друг с другом и будут совместно работать в паре; именно в этом состоит цель введения стандарта на сопряжение (интерфейс). Поскольку POSIX — стандарт на интерфейс, то можно говорить о соответствии стандарту как операционной системы, так и прикладной программы.

Стандарт POSIX.1 содержит несколько сотен (если не тысяч) требований; считается самоочевидным, что если не выполнено хотя бы одно из них, то система (или прикладная программа) не удовлетворяет стандарту. Вместе с тем, к настоящему времени написано такое количество операционных систем класса UNIX и прикладных программ для них, что вряд ли разумно требовать полного соответствия в указанном смысле. Трудности разработки международного стандарта такого рода усугубляются существованием разных национальных языков. Даже если забыть о прикладных программах, предназначенных для обработки текстов на национальных языках, практически любая прикладная программа должна выдавать какие-то диагностические сообщения и/или воспринимать тексты, вводимые оператором.

Осознавая такого рода трудности, авторы POSIX предлагают уточненную семантику слова «соответствует». Во-первых, вводится несколько видов соответствия (прикладной программы стандарту):

  • строгое соответствие стандарту POSIX.1;
  • соответствие международной версии POSIX.1;
  • соответствие национальной версии POSIX.1;
  • соответствие POSIX.1 с расширениями.

Во-вторых, многие интерфейсные средства объявлены факультативными (optional); стандарт требует, чтобы факультативные интерфейсные функции либо отрабатывались так, как предписано стандартом, либо всегда возвращали особый код ошибки, ENOSYS (означающий, что функция не реализована). Факультативные средства разбиты на несколько групп, каждой из которых соответствует некоторая конфигурационная константа, которая декларируется (оператором #define) в соответствующем заголовочном файле; тем самым обеспечивается возможность выяснения, реализована ли функция, на фазе компиляции.

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

Объекты стандартизации и структура стандарта

Если говорить кратко, то объектами стандартизации POSIX.1 являются имена и семантика. Более конкретно, речь идет о следующем.

  • Имена интерфейсных функций. Стандартизовано 357 функций, причем 107 функций взяты из стандартных Си-библиотек (математические, обработка строк, ввод/вывод и др.); эти функции считаются частью стандарта POSIX.1, однако их полное описание содержится в стандарте на язык программирования Си [3].
  • Имена системных типов данных. Эти имена имеют суффикс _t.
  • Имена заголовочных файлов, а также минимальный состав этих файлов.
  • Имена общесистемных глобальных переменных (например, errno).
  • Символьные имена кодов ошибок, которые могут быть установлены при отработке функций. Эти имена начинаются с буквы E (EPERM, ENOTEMPTY и т.д.).
  • Имена конфигурационных констант. Эти имена имеют префикс _POSIX_.
  • Символьные имена номеров сигналов; эти имена имеют префикс SIG. В дополнение к 20 «традиционным» сигналам (SIGABRT, SIGALRM и др.) стандартизированы сигналы реального времени, номера которых должны занимать некоторый сплошной диапазон от SIGRTMIN до SIGRTMAX включительно, содержащий не менее RTSIG_MAX номеров.
  • Символьные имена, соответствующие значениям отдельных аргументов некоторых функций (например, аргумент cmd функции fcntl() может принимать значения F_DUPFD, F_GETFD, F_GETLK и т.д.).
  • Имена макросов, констант, битовых флагов, переменных окружения.

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

В целом стандарт состоит из двух больших частей примерно одинакового объема. Первая половина — нормативная часть — содержит требования и рекомендации стандарта (18 разделов), вторая — информативная часть — содержит Приложения, в которых приводится список литературы, комментарии и разъяснения к нормативной части, состав заголовочных файлов, пример профиля («проекции») стандарта (для Дании), характеристики и методика измерения производительности наиболее важных функций, а также описание дополнительных интерфейсных функций для работы с файлами в реальном времени; ожидается, что в следующих редакциях стандарта эти функции будут включены в нормативную часть.

Представление о том, какие именно виды услуг операционной системы охватываются стандартом, дает врезка «Краткое содержание разделов стандарта».

Заключение

Основное содержание стандарта POSIX — семантика интерфейсных функций. Стандартизация семантики — дело само по себе нелегкое (каждый знает, как трудно бывает договорится даже двум персонам), и трудности усугубляются тем, что в программистскую деятельность в настоящее время вовлечено очень много людей. Например, парадигма параллельного исполнения выражается такими терминами, как «процесс», «задача» и «поток управления», однако с точки зрения практического программирования «задача» в операционной системе IBM OS/360 и в операционной системе реального времени VxWorks — совсем не одно и то же. Еще один пример — семафоры. Семафоры бывают двоичные, целочисленные («со счетчиком») и взаимного исключения (которые, между прочим, программисты называют между собой «мьютексами», стихийно стремясь избегать недоразумений). И целочисленные семафоры например, в операционной системе VxWorks, вовсе не то же самое, что семафоры POSIX.

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

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

POSIX Функции

Также может оказаться полезным раздел «Функции управления процессами».

Содержание

  • posix_access — Определяет доступность файла
  • posix_ctermid — Возвращает путь управляющего терминала
  • posix_errno — Псевдоним posix_get_last_error
  • posix_get_last_error — Возвращает номер ошибки, которая произошла в последней posix функции, завершившейся неудачей
  • posix_getcwd — Возвращает путь текущей директории
  • posix_getegid — Возвращает эффективный идентификатор группы текущего процесса EGID
  • posix_geteuid — Возвращает эффективный идентификатор пользователя текущего процесса EUID
  • posix_getgid — Возвращает действительный ID группы текущего процесса GID
  • posix_getgrgid — Возвращает информацию о группе по её ID
  • posix_getgrnam — Возвращает информацию о группе, используя её имя
  • posix_getgroups — Возвращает список групп для текущего процесса
  • posix_getlogin — Возвращает логин владельца процесса
  • posix_getpgid — Возвращает ID группы текущего процесса для менеджера задач
  • posix_getpgrp — Возвращает идентификатор группы текущего процесса
  • posix_getpid — Возвращает идентификатор текущего процесса
  • posix_getppid — Возвращает идентификатор родительского процесса
  • posix_getpwnam — Возвращает информацию о пользователе по его имени
  • posix_getpwuid — Возвращает информацию о пользователе, используя его ID
  • posix_getrlimit — Возвращает информацию об ограничениях системных ресурсов
  • posix_getsid — Возвращает текущий SID процесса
  • posix_getuid — Возвращает фактический идентификатор пользователя текущего процесса UID
  • posix_initgroups — Определяет уровень доступа для группы
  • posix_isatty — Определяет является ли файловый дескриптор интерактивным терминалом
  • posix_kill — Отправляет сигнал соответствующему процессу
  • posix_mkfifo — Создает специальный fifo файл (именованный канал- pipe)
  • posix_mknod — Создает специальный или обычный файл (POSIX.1)
  • posix_setegid — Устанавливает эффективный идентификатор группы для текущего процесса EGID
  • posix_seteuid — Устанавливает эффективный идентификатор пользователя для текущего процесса EUID
  • posix_setgid — Устанавливает идентификатор группы для текущего процесса GID
  • posix_setpgid — Устанавливает идентификатор группы процесса для менеджера задач
  • posix_setrlimit — Set system resource limits
  • posix_setsid — Делает текущий процесс лидером сессии
  • posix_setuid — Устанавливает UID текущего процесса
  • posix_strerror — Возвращает системное сообщение об ошибке, основываясь на полученном номере ошибки
  • posix_times — Возвращает информацию об использовании процессорного времени
  • posix_ttyname — Определяет имя терминального устройства
  • posix_uname — Возвращает информацию о системе

Функции posix

Стандарт состоит из четырёх основных разделов:

  • Основные определения (Base definitions) — список основных определений и соглашений, используемых в спецификациях, и список заголовочных файлов языка Си, которые должны быть предоставлены соответствующей стандарту системой.
  • Оболочка и утилиты (Shell and utilities) — описание утилит и командной оболочки sh, стандарты регулярных выражений.
  • Системные интерфейсы (System interfaces) — список системных вызовов языка Си.
  • Обоснование (Rationale) — объяснение принципов, используемых в стандарте.

Версии

  • POSIX.1, Корневые службы (включает стандарт ANSI C) (IEEE Std 1003.1-1988)
    • Создание и управление процессами
    • Сигналы[6]
    • Исключения плавающей точки
    • Нарушение сегментации
    • Запрещенные директивы
    • Ошибки шины
    • Таймеры
    • Операции над файлами (директориями)
    • Каналы
    • Библиотека C (Стандартный С)
    • Интерфейс и контроль ввода/вывода
    • Триггеры процессов
  • POSIX.1b, Расширения реального времени (IEEE Std 1003.1b-1993)
    • Планировка приоритетов
    • Сигналы реального времени
    • Таймеры и синхронизация
    • Семафоры
    • Передача сообщений
    • Разделяемая память
    • Асинхронный и синхронный ввод/вывод
    • Интерфейс блокировки памяти
  • POSIX.1c, Расширения потоков (IEEE Std 1003.1c-1995)
    • Создание, контроль и завершение выполнения потоков
    • Планировщик потоков
    • Синхронизация потоков
    • Обработка сигналов

POSIX-совместимые ОС

В зависимости от степени совместимости со стандартами, ОС могут быть полностью или частично совместимы с POSIX. Сертифицированные продукты могут быть найдены на сайте IEEE. [2] Бесплатный доступ к версии стандарта IEEE Std 1003.1-2004 находится на сайте The Open Group. [3]

Полностью POSIX-совместимые


Полностью соответствующие одной из версий стандарта POSIX.

По большей части POSIX-совместимые

Официально не сертифицированные как POSIX-совместимые, но соответствующие по большей части.

POSIX для Windows

  • Cygwin — обеспечивает частичное соответствие POSIX для некоторых продуктов Microsoft Windows.
  • UnxUtils — набор утилит из POSIX окружения.
  • Microsoft POSIX subsystem, необязательная подсистема Windows.
  • Microsoft Windows Services for UNIX — обеспечивает полное соответствие POSIX для некоторых продуктов Microsoft Windows. Операционные системы на базе Windows NT до Windows 2000 имели POSIX уровень встроенный в ОС, и UNIX Services for Windows предоставляло UNIX-подобное окружение. Для Windows XP, Windows Services for UNIX должны быть установлены для POSIX совместимости. UNIX подсистема встроена в Enterprise и Ultimate редакции Windows Vista, и не могут быть добавлены в другие редакции.
  • UWIN от AT&T Research обеспечивает POSIX поверх Win32 API.

См. также

Примечания

  1. 12POSIX® 1003.1 Frequently Asked Questions (FAQ Version 1.12) (англ.) (2 February 2006). Архивировано из первоисточника 22 августа 2011.Проверено 5 марта 2008.
  2. POSIX. Standards. IEEE. Архивировано из первоисточника 22 августа 2011.
  3. The Open Group. Архивировано из первоисточника 3 февраля 2012.

Литература

  • System application program interface (API) [C Language]: ISO/IEC 9945-1, ANSI/IEEE Std 1003.1. — New York: IEEE, 1996. — ISBN 1-55937-573-6
  • Информационная технология — интерфейс мобильной операционной системы (POSIX). Часть 1: Интерфейс прикладных программ (API) [Язык программирования С]: в 2 т. — М.: НИИСИ РАН, 1999. — ISBN 5-93838-001-4
    • Т. 1: Введение и нормативная часть. — 422 с. — ISBN 5-93838-002-2
    • Т. 2: Информативная часть (Приложения). — 374 с. — ISBN 5-93838-003-0

Ссылки

  • The Open Group
  • IEEE Std 1003.1™-2008
  • Введение в POSIX’ивизм
  • The Portable Application Standards Committee
  • Текст Единой спецификации «Юникс» (очередной версии ISO/IEC 9945 в разработке).
Стандарты The Open Group
Стандарты ISO
Перечни:Перечень стандартов ИСО • Перечень романизаций ISO • Перечень стандартов IEC
Категории: Категория:Стандарты ISO • Категория:Протоколы OSI
1
по
9999
1 • 2 • 3 • 4 • 5 • 6 • 7 • 9 • 16 • 31 (-0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13) • 128 • 216 • 217 • 226 • 228 • 233 • 259 • 269 • 296 • 302 • 306 • 428 • 639 (-1, -2, -3, -5, -6) • 646 • 690 • 732 • 764 • 843 • 898 • 1000 • 1004 • 1007 • 1073-1 • 1413 • 1538 • 1745 • 2014 • 2015 • 2022 • 2108 • 2145 • 2146 • 2281 • 2709 • 2711 • 2788 • 3029 • 3103 • 3166 (-1, -2, -3) • 3297 • 3307 • 3602 • 3864 • 3901 • 3977 • 4031 • 4157 • 4217 • 5218 • 5775 • 5776 • 5964 • 6166 • 6344 • 6346 • 6425 • 6429 • 6438 • 6523 • 6709 • 7001 • 7002 • 7098 • 7185 • 7388 • 7498 • 7736 • 7810 • 7811 • 7812 • 7813 • 7816 • 8000 • 8217 • 8571 • 8583 • 8601 • 8632 • 8652 • 8691 • 8807 • 8820-5 • 8859 (-1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16) • 8879 • 9000 • 9075 • 9126 • 9241 • 9362 • 9407 • 9506 • 9529 • 9564 • 9594 • 9660 • 9897 • 9945 • 9984 • 9985 • 9995
10000
по
19999
10006 • 10118-3 • 10160 • 10161 • 10165 • 10179 • 10206 • 10303 • 10303-11 • 10303-21 • 10303-22 • 10303-238 • 10303-28 • 10383 • 10487 • 10585 • 10589 • 10646 • 10664 • 10746 • 10861 • 10957 • 10962 • 10967 • 11073 • 11170 • 11179 • 11404 • 11544 • 11783 • 11784 • 11785 • 11801 • 11898 • 11940 • 11941 • 11941 (TR) • 11992 • 12006 • 12164 • 12182:1998 • 12207:1995 • 12207:2008 • 12234-2 • 13211 (-1, -2) • 13216 • 13250 • 13399 • 13406-2 • 13407 • 13450 • 13485 • 13490 • 13567 • 13568 • 13584 • 13616 • 14000 • 14031 • 14396 • 14443 • 14496-10 • 14496-14 • 14644 (-1, -2, -3, -4, -5, -6, -7, -8, -9) • 14649 • 14651 • 14698 • 14698-2 • 14750 • 14882 • 14971 • 15022 • 15189 • 15288 • 15291 • 15292 • 15408 • 15444 • 15445 • 15438 • 15504 • 15511 • 15686 • 15693 • 15706 • 15706-2 • 15707 • 15897 • 15919 • 15924 • 15926 • 15926 WIP • 15930 • 16023 • 16262 • 16750 • 17024 • 17025 • 17369 • 17799 • 18000 • 18004 • 18014 • 18245 • 18629 • 18916 • 19005 • 19011 • 19092-1 • 19092-2 • 19114 • 19115 • 19439 • 19501:2005 • 19752 • 19757 • 19770 • 19775-1 • 19794-5
20000+ 20000 • 20022 • 21000 • 21047 • 21827:2002 • 22000 • 23008-2 • 23270 • 23360 • 24613 • 24707 • 25178 • 26000 • 26300 • 26324 • 27000 series • 27000 • 27001 • 27002 • 27003 • 27004 • 27005 • 27006 • 27007 • 27729 • 27799 • 29199-2 • 29500 • 31000 • 32000 • 38500 • 42010 • 50001 • 80000
См. также:Все статьи, начинающиеся с «ISO»
Илон Маск рекомендует:  Что такое код hw_api >srcanchors

Wikimedia Foundation . 2010 .

Смотреть что такое «POSIX» в других словарях:

POSIX — (IPAEng|ˈpɒzɪks) or Portable Operating System Interface cite web | title = POSIX | url = http://standards.ieee.org/regauth/posix/ | work = Standards | publisher = IEEE] is the collective name of a family of related standards specified by the IEEE … Wikipedia

POSIX — est le nom d une famille de standards définie depuis 1988 par l Institute of Electrical and Electronics Engineers et formellement désignée IEEE 1003. Ces standards ont émergé d un projet de standardisation des API des logiciels destinés à… … Wikipédia en Français

Posix — est le nom d une famille de standards définie depuis 1988 par l IEEE et formellement désignée IEEE 1003. Ces standards ont émergé d un projet de standardisation des API des logiciels destinés à fonctionner sur des variantes du système d… … Wikipédia en Français

POSIX — es el acrónimo de Portable Operating System Interface; la X viene de UNIX como seña de >Wikipedia Español

POSIX — [Abk. für Portable Operating System Interface for Unix, dt. »portierbare Betriebssystemschnittstelle für Unix«], 1986 im Standard 1003.1 der IEEE niedergelegte Spezifikation für Zugriffe auf Systemfunktionen unter Unix. Sowohl Unix Sy … Universal-Lexikon

POSIX — standartai statusas T sritis informatika apibrėžtis Standartų grupė, apibrėžianti operacinės sistemos sąsajas tarp joje veikiančių programų bei tarnybų. Pirmuosius standartus sukūrė Elektros ir elektronikos inžinierių institutas (IEEE) Linukso… … Enciklopedinis kompiuterijos žodynas

POSIX — es el acrónimo de Portable Operating System Interface, viniendo la X de UNIX con el significado de la herencia de la API (Se traduciría como Sistema Operativo Portable basado en UNIX). Estos son una familia de estándares de llamadas al sistema… … Enciclopedia Universal

POSIX — (Portable Operating System Interface based on uniX) n. collection of standards for operating systems that are based on Unix (Computers) … English contemporary dictionary

POSIX — Das Portable Operating System Interface (POSIX [ˈpɒsɪks]) ist ein gemeinsam von der IEEE und der Open Group für Unix entwickeltes standardisiertes Application Programming Interface, das die Schnittstelle zwischen Applikation und dem… … Deutsch Wikipedia

Posix — Das Portable Operating System Interface (POSIX [ˈpɒsɪks]) ist ein gemeinsam von der IEEE und der Open Group für Unix entwickeltes standardisiertes Application Programming Interface, das die Schnittstelle zwischen Applikation und dem… … Deutsch Wikipedia

6. Администратор процессов

В ОС QNX Neutrino микроядро вместе с администратором процессов (Process Manager) находится в одном модуле ( procnto ). Этот модуль требуется для всех систем среды исполнения.

Администратор процессов может создавать множество POSIX-процессов (каждый из которых может содержать множество POSIX-потоков). Перечислим основные функции администратора процессов.

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

Управление памятью. Администратор процессов управляет различными функциями защиты памяти, разделяемыми библиотеками и POSIX-примитивами разделяемой памяти.

Управление именами путей. Администратор процессов управляет пространством имен путей, к которому могут присоединяться администраторы ресурсов.

Процессы могут использовать функции микроядра напрямую (с помощью вызовов ядра) или посредством администратора процессов (с помощью передачи сообщений модулю procnto ). Обратим внимание на то, что пользовательский процесс может передать сообщение ядру посредством вызова MsgSend*() .

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

Хотя администратор процессов и микроядро разделяют одно и то же адресное пространство, это не значит, что у них есть какой-то «специальный» или «индивидуальный» интерфейс. Все потоки в системе используют один и тот же общий интерфейс ядра, и все выполняют переключение допуска ввода/вывода (privity switch) при вызове микроядра. Управление процессами Первой основной функцией модуля procnto является динамическое создание новых процессов. Впоследствии созданные процессы зависят от других функций этого модуля, связанных с управлением памятью и именами путей.

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

Существует следующие четыре примитива процессов:

vfork() — UNIX BSD ;

posix_spawn() Функция posix_spawn() создает дочерний процесс путем прямого указания подлежащего запуску исполняемого модуля. Те, кто знаком с системами UNIX , могут заметить, что этот вызов аналогичен функции fork() , за которой следует функция exec*() . Однако вызов posix_spawn() намного более эффективен, так как он не требует дублирования адресных пространств, как это происходит, когда сначала вызывается функция fork() , а затем дублированное адресное пространство уничтожается посредством вызова exec*() .

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

идентификаторы владельца и группы;

атрибуты адаптивного квотирования.

Существует также вспомогательная функция posix_spawnp() , которая не требует указания полного путевого имени запускаемой программы, а вместо этого ищет программу, используя переменную PATH вызывающего процесса .


Для создания новых дочерних процессов использование функций posix_spawn() является предпочтительным. spawn() Функция QNX Neutrino spawn() подобна функции posix_spawn() . Функция spawn() управлять следующими параметрами и атрибутами дочернего процесса:

идентификаторы владельца и группы;

узел, на котором создается процесс;

параметры диспетчеризации (приоритет);

максимальный размер стека;

процессорная маска (для систем SMP).

spawn() — п ородить процесс с явно заданным путем;

spawnp() — выполнить поиск в текущем значении переменной PATH и применить функцию spawn() к первому подходящему исполняемому модулю.

spawnl() — породить процесс с аргументами, явно заданными в командной строке;

spawnle() — аналогично предыдущей функции в сочетании с явно переданными переменными окружения;

spawnlp() — аналогично функции spawnp() в сочетании с поиском исполняемого модуля в переменной PATH;

spawnlpe() — аналогично предыдущей функции в сочетании с явно переданными переменными окружения;

spawnv() — породить процесс с аргументами, явно заданными в командной строке группой указателей;

spawnve() — аналогично предыдущей функции в сочетании с явно переданными переменными окружения;

spawnvp() — аналогично функции spawnv() в сочетании с поиском исполняемого модуля в переменной PATH;

spawnvpe() — аналогично предыдущей функции в сочетании с явно переданными переменными окружения.

идентификатор группы процессов (при условии, что параметр SPAWN_SETGROUP не установлен в inherit.flags );

участие в сессии;

действительный пользовательский идентификатор и действительный идентификатор группы;

дополнительные идентификаторы группы;

приоритет и дисциплина планирования;

текущий рабочий каталог и корневой каталог;

маска создания файлов;

маска сигналов (при условии, что параметр SPAWN_SETSIGMASK не установлен в inherit.flags );

действия по сигналам, заданные как SIG_DFL;

действия по сигналам, заданные как SIG_IGN (за исключением измененных inherit.sigdefault , при условии, что параметр SPAWN_SETSIGDEF не установлен в inherit.flags ).

сигналам, которые задаются как перехватываемые родительским процессом, назначается действие, принятое по умолчанию (SIG_DFL);

параметры tms_utime , tms_stime , tms_cutime и tms_cstime дочернего процесса отслеживаются отдельно от родительских;

количество секунд до генерации сигнала SIGALRM устанавливается равным нулю для дочернего процесса;

набор ожидающих сигналов для дочернего процесса обнуляется;

блокировки файлов, установленные родительским процессом, не наследуются;

таймеры, созданные родительским процессом, не наследуются;

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

Если дочерний процесс порожден на удаленном узле, ему не присваивается идентификатор группы процессов и идентификатор сессии (session membership); он помещается в новую сессию и группу процессов.

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

Для получения дополнительной информации см. описание функции spawn() в электронном документе «Library Reference». fork() Функция fork() создает новый дочерний процесс посредством совместного использования с вызывающим процессом одного и того же программного кода, а также посредством копирования (дублирования) данных вызывающего процесса для дочернего процесса. Большинство ресурсов процесса наследуется. Тем не менее, некоторые ресурсы не могут быть наследованы, а именно:

идентификатор родительского процесса;

ожидающие сигналы и события тревоги (alarms);

Функция fork() обычно используется для двух целей:

создать новый экземпляр текущего окружения;

создать новый процесс для выполнения другой программы.

При создании нового потока общие данные помещаются в специально создаваемую область разделяемой памяти. До появления стандарта POSIX для потоков это был единственный способ. С появлением стандарта POSIX-потоков функция fork() реализуется созданием потоков внутри одного процесса с помощью функции pthread_create() .

При создании процесса для выполнения другой программы за вызовом функции fork() сразу же следует вызов одной из функций exec*() . Это тоже более эффективно выполняется с помощью одного вызова POSIX-функции spawn() , которая совмещает в себе обе эти операции.

Поскольку ОС QNX Neutrino имеет более эффективные средства, чем функция fork() , использование этой функции больше всего подходит в тех случаях, когда необходимо обеспечить переносимость существующего кода, а также для написания переносимого кода, предназначенного для UNIX-систем, не поддерживающих POSIX-совместимых функций pthread_create() и spawn() .

Функция fork() может вызываться из процесса, который содержит только один поток. vfork() Функция vfork() (которая может вызываться только из процесса, содержащего всего один поток) полезна для создания нового контекста с целью вызова одной из функций exec*() . Функция vfork() отличается от функции fork() тем, что для дочернего процесса не создается копия данных вызывающего процесса. Вместо этого дочерний процесс использует память родительского процесса и его основной поток (thread of control) до тех пор, пока не выполнится вызов одной из функций exec*() . Вызывающий процесс приостанавливается на то время, пока дочерний процесс использует его ресурсы.

Дочерний процесс, созданный функцией vfork() , не может вернуться из процедуры, вызвавшей функцию vfork() , поскольку в этом случае возможный выход из родительской функции vfork() может произойти в несуществующую область стека. exec*() Семейство функций exec*() замещает текущий процесс новым процессом, загруженным из файла с исполняемым модулем. Поскольку вызывающий процесс замещается, успешный возврат (т. е. с нулевым кодом завершения) не происходит.

Существуют следующие функции типа exec*() :

execl() — выполнить с аргументами командной строки, заданными в параметрах;

execle() — аналогично предыдущей функции в сочетании с явно переданными переменными окружения;

execlp() — аналогично функции execle() в сочетании с поиском исполняемого модуля в переменной PATH;

execlpe() — аналогично функции execlp() в сочетании с явно переданными переменными окружения;

execv() — аналогично функции execl() с аргументами заданными в командной строке группой указателей;

execve() — аналогично функции execv() в сочетании с явно переданными переменными окружения;

execvp() — аналогично функции execv() в сочетании с поиском исполняемого модуля в переменной PATH;

execvpe() — аналогично функции execvp() в сочетании с явно переданными переменными окружения.

Функции exec*() , как правило, следуют за функцией fork() или vfork() , с тем чтобы загрузить новый дочерний процесс. Однако более эффективным является новый POSIX-совместимый вызов spawn() . Загрузка процессов Процессы, загружаемые из файловой системы с помощью вызовов exec*() или spawn() , имеют формат ELF (Executable and Linkable Format, формат исполняемых и компонуемых модулей). Если файловая система реализована на блок-ориентированном устройстве, программный код и данные загружаются в основную память.

Если файловая система отображена в памяти (например, в ПЗУ или флеш-памяти), загрузка программного кода в оперативную память не требуется, и этот код может быть исполнен на месте. При таком подходе вся оперативная память может использоваться для данных и стека, а программный код остается в ПЗУ или флеш-памяти. В любом случае, если некоторый процесс загружается несколько раз, его программный код используется совместно всеми его сущностями. Управление памятью Некоторые ядра или исполняемые модули реального времени обеспечивают поддержку защиты памяти в среде разработки. Однако редко когда они имеют поддержку защиты памяти в среде исполнения, что, как правило, объясняется слишком большими накладными расходами памяти и производительности. Тем не менее, защита памяти все больше применяется во встраиваемых процессорах, так как ее преимущества значительно перевешивают те небольшие потери, которые связаны с ее обеспечением.

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

При наличии защиты памяти, если один из процессов в многозадачной среде пытается получить доступ к области памяти, для которой данный тип доступа не был назначен явным образом, MMU-оборудование может известить об этом ОС, которая в этом случае может снять данный процесс.


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

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

С помощью блока управления памятью (MMU) операционная система может завершить процесс сразу после возникновения ошибки доступа к памяти. Таким образом, программист может быстро узнать о возникшей ошибке вместо того, чтобы через какое-то время получить загадочный отказ системы. ОС может сообщить о месте ошибочной инструкции в процессе или запустить символьный отладчик прямо на этой инструкции. Блоки управления памятью Говоря в общем, блок управления памятью занимается тем, что делит физическую память на страницы размером 4 Кбайта. Процессор использует набор из множества таблиц страниц, хранящихся в системной памяти. Эти таблицы служат для описания того, как виртуальные адреса (т. е. адреса памяти, используемые приложением) отображаются в адреса, выделяемые процессором для доступа к физической памяти.

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

Рис. 6.1. Отображение виртуальных адресов (на семействе процессоров x86).

Для большого адресного пространства со множеством процессов и потоков количество записей в таблицах страниц, необходимое для описания отображений, может быть весьма большим — больше чем может хранить процессор. Для сохранения высокой производительности процессор выполняет кеширование часто используемых сегментов внешних таблиц страниц в буфере быстрого преобразования адреса (Translation Look-aside Buffer, TLB).

Однако обработка «пропавших» записей в TLB-кеше — одна из причин повышения накладных расходов, связанных с работой блоков управления памятью. Для снижения этих накладных расходов в ОС QNX Neutrino предусмотрено несколько различных механизмов на уровне таблиц страниц.

С таблицами страниц связаны биты, которые определяют атрибуты каждой страницы памяти. Страницы могут иметь различные типы доступа: «только чтение», «чтение/запись» и т. д. Как правило, память выполняемого процесса определяется страницами с доступом «только чтение» для кода и «чтение/запись» для данных и стека.

Когда ОС QNX Neutrino применяет операцию переключения контекстов (т. е. приостанавливает выполнение одного потока и возобновляет выполнение другого потока), она указывает блоку управления памятью применить для возобновленного потока потенциально иной набор таблиц страниц. Если ОС переключает контексты между потоками внутри одного процесса, задействование блока управления памятью не требуется.

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

Проверяя значение указателя команд, записываемое в стек при возникновении прерывания, ОС может определить адрес команды, вызвавшей ошибку обращения к памяти, и выполнить необходимые в этом случае операции. Защита памяти в режиме исполнения Защита памяти полезна не только в процессе разработки — она также обеспечивает повышенную степень отказоустойчивости во время работы встраиваемых систем. Во многих встраиваемых системах используются программируемые сторожевые таймеры (watchdog timer), предназначенные для того, чтобы следить за системой и ее программным обеспечением и определять, не «зависла» ли она. Однако такой метод менее точен, чем сигнальные устройства на основе MMU.

Аппаратные сторожевые таймеры обычно реализуются в виде сбрасываемого таймера с одним устойчивым состоянием (retriggerable monostable timer). Этот таймер присоединяется к шине сброса процессора. Если системное программное обеспечение перестает передавать сторожевому таймеру стробирующий сигнал (strobe), то через заданное временное значение таймер истекает и вызывает процедуру перезагрузки процессора. Как правило, в системе предусмотрен специальный программный компонент, который проверяет состояние системы и передает сторожевому таймеру стробирующий сигнал, сообщающий о нормальной работе системы.

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

завершение процесса, в котором произошла ошибка из-за нарушения доступа к памяти, и перезапуск этого процесса без закрытия системы;

завершение процесса, в котором произошла ошибка, и других, связанных с ним процессов; переключение оборудования в «безопасный» режим и перезапуск связанных процессов по заданной процедуре;

при возникновении аварийного останова закрытие всей системы по заданной процедуре и включение аварийного звукового сигнала.

Важной особенностью здесь является то, что сохраняется интеллектуальное программируемое управление встраиваемой системы, даже если возник сбой в нескольких процессах и потоках в управляющем программном обеспечении. Аппаратный сторожевой таймер все еще остается полезным для восстановления после зависаний по аппаратным причинам (latch-ups). Однако для сбоев в программном обеспечении существует более эффективное средство.

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

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

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

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

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

Поскольку после выхода продукта, представляющего собой весьма сложную встраиваемую систему, нет возможности что-либо кардинально в нем изменить, то следует изначально разрабатывать системы с высокой отказоустойчивостью и автоматическим восстановлением после сбоев. Для этого необходимо использовать защиту памяти с помощью блоков MMU, интегрированных во встраиваемые системы. Модель полной защиты памяти В соответствии с моделью полной защиты памяти (full-protection model), которая используется в ОС QNX Neutrino, весь программный код, находящийся в образе, перемещается в новое виртуальное пространство, при этом включается оборудование, реализующее блок управления памяти, и устанавливаются начальные значения отображения таблиц страниц. Это позволяет запустить модуль procnto корректным образом и в среде со включенным блоком управления памятью. Управление данной средой переключается на администратор процессов, который изменяет таблицы отображения в соответствии с запускаемыми процессами. Изолированное виртуальное адресное пространство В модели полной защиты памяти для каждого процесса определяется изолированное виртуальное адресное пространство (private virtual memory), которое может охватывать от 2 до 3,5 Гбайт (в зависимости от типа процессора). Это осуществляется с помощью блока управления памятью. Общие накладные расходы, связанные с переключением процессов и передачей сообщений, возрастают из-за повышения сложности в механизме адресации между двумя абсолютно изолированными адресными пространствами.

Рис. 6.2. Виртуальная память с полной защитой (на процессорах семейства x86).

• Можно сделать размер страницы больше, чем 4 Kбайт. В результате, система будет использовать меньше записей TLB.

• Меньше промахов TLB.

Если необходимо отключить поддержку изменяемого размера страниц, необходимо в файле построения задать модулю procnto опцию -m˜v. Опция -mv включает такую поддержку. Блокирование памяти QNX Neutrino поддерживает блокирование памяти в соответствии с POSIX, поэтому процесс может избежать потери времени на загрузку страницы памяти путем блокирования памяти так, что страница становится резидентной в памяти (т.е. остается в физической памяти).

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

Неблокированная (Unlocked) память может загружаться и выгружаться. Память выделяется при отображении, но элементы таблицы страниц не создаются. При первой попытке доступа быть к этой памяти возникнет ошибка и поток заблокируется в состоянии WAITPAGE до тех пор, пока менеджер памяти инициализирует память и создает соответствующие записи таблицы страниц.

В случае неудачи при инициализации страницы будет сгенерирован сигнал SIGBUS.

Блокированная (Locked) память не может быть загружаемой и выгружаемой. С целью сопровождения статистики использования и модификации будут по прежнему возникать ошибки при доступе к данной памяти. Страницы, которые были заданы как PROT_WRITE в действительности будут PROT_READ.

Для блокирования и разблокирования части памяти потока используются функции mlock() и munlock(); для блокирования и разблокирования всей памяти потока используются функции mlockall() и munlockall(). Память остается блокированной до тех пор пока процесс не разблокирует ее, завершится или вызовет функцию exec*(). Если процесс вызывает какую-либо из функций fork(), posix_spawn*() или spawn*(), то блокировки памяти в дочернем процессе снимаются.

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

Блокировки памяти не суммируются — если поток не единожды блокирует один и тот же регион, то однократное разблокирование снимет все блокировки этого процесса на данном регионе.

Для блокирования всей памяти для всех приложений необходимо задать модулю procnto опцию -ml. При этом все страницы, по меньшей мере, инициализируются.

( Расширение QNX Neutrino) Ошибки доступа к памяти вообще не разрешаются; как только память отображена, она вся должна быть инициализирована и приватизирована, а права доступа должны быть установлены. Суперблокирование (Superlocking) покрывает все адресное пространство потока.

Для суперблокирования памяти необходимо перевести поток в привилегированный режим ввода-вывода с помощью функции ThreadCtl(), установив флаг _NTO_TCTL_IO:

ThreadCtl( _NTO_TCTL_IO, 0 );

Для суперблокирования всей памяти для всех приложений необходимо задать модулю procnto опцию -mL.

Для всех указанных выше типов память для отображений MAP_LAZY не выделяется и не отображается до тех пор, пока к ней не обратятся в первый раз. Как только к ней обратились, она подчиняется указанным выше правилам — поэтому обращение к такой области памяти MAP_LAZY, к которой еще не осуществлялся доступ, из критической секции кода (при выключенных прерываниях или в ISR) является ошибкой программиста.

Дефрагментация физической памяти

Большинство пользователей компьютеров хорошо знакомы с принципом дефрагментации диска, во время которой свободное пространство на диске разбивается на небольшие блоки, беспорядочно-распределенные между используемыми блоками. При этом, самое сложное действие – выделить свободные части физической памяти; по истечении некоторого времени начинается фрагментация физической памяти. В результате, может получиться так, что значительное количество памяти будет свободно, но она будет фрагментирована настолько, что запрос большого объёма непрерывной памяти завершится с ошибкой.

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

Алгоритм, используемый QNX Neutrino для выделения физической пам яти способствуют значительному уменьшению количества фрагментации . Однако, неважно насколько «разумны» данные алгоритмы, установленный режим работы приложения обеспечит фрагментацию свободной памяти. Рассмотрим полностью дегенеративное приложение, которое обычно резервирует 8 Кб памяти, а затем освобождает половину. Если такое приложение запущено достаточно долго, оно достигает такого состояния, что половина памяти системы свободна, но нет свободных блоков размером больше 4 Кб.

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

Термин «фрагментация» может быть применим как к занятой, так и к свободной памяти.

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

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

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

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

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

Память, заблокированная приложением (см. mlock() и mlockall()) не может быть перемещена: при блокировании памяти, приложение сообщает, что перемещение памяти недоступно.

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

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

если дефрагментация запрещена, происходит сбой mmap();

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

Примечание . Во время дефрагментации поток вызова mmap() блокируется.

Сжатие может занять значительное время (особенно в системах с большим объемом памяти), но это не повлияет на большинство других действий операционной системы.

Если в системе запущено одновременно несколько задач, алгоритм дефрагментации просчитывает, как может измениться распределение памяти, при запуске алгоритма.

По умолчанию, дефрагментация разрешена. Запретить дефрагментацию можно используя опции командной строки procnto -m˜d, и разрешить с помощью опции -md.

Автоматическая маркировка памяти, как неперемещаемой

Память, зарезервированная, как физически непрерывная, маркируется как «неперемещаемая» для алгоритма сжатия. Это действие выполняется, потому что при задании распределения непрерывной памяти предполагается, что она будет использоваться а случаях, когда важен физический адрес, и перемещение памяти может прервать работу приложения, зависящего от этих характеристик.

Дополнительно, память, при выполнении mem_offset() – выводе физического адреса из виртуального – должна быть защищена от перемещения алгоритмом сжатия. Однако, операционная система не маркирует всю память, как неперемещаемую, т. к. программа может вызвать mem_offset() «из любопытства» (как, например, программа протоколирования памяти >.

С другой стороны, если приложение зависит от результата вызова mem_offset(), и операционная система позже переместила распределение памяти, это может прервать работу приложения. Такое приложение должно заблокировать используемую память (с помощью вызова mlock()), но но поскольку QNX Neutrino не всегда перемещает память в прошлом, то ОС не может считать, что все приложения работают правильно.

Поэтому, procnto поддерживает опции командной строки -ma. При установке данной опции, любые вызовы функции mem_offset() маркируют блоки памяти, как неперемещаемые. Следует заметить, при распределении памяти, как непрерывной или блокировании посредством mlock(), она становиться неперемещаемой, т.о. данная опция не актуальна. Опция полезна только, если доступны параметры дефрагментации.

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

Ресурсы ввода/вывода не встроены в микроядро, а реализуются специальными процессами — администраторами ресурсов, которые могут динамически запускаться во время работы системы. Администратор procnto позволяет (посредством стандартного программного интерфейса) назначать администраторам ресурсов свои области в пространстве имен путей как некие «области ответственности» (domains of authority), при этом модуль procnto управляет деревом имен путей и следит за процессами, владеющими теми или иными частями пространства имен путей. Назначенное путевое имя иногда называют «префиксом», так как оно предшествует всем нижележащим именам путей. Назначенное путевое имя еще называют точкой монтирования (mountpoint) , так как именно в этой точке сервер подсоединяется к путевому имени.

Такой подход к управлению пространством имен путей позволяет сохранить в ОС QNX Neutrino семантику POSIX для реализации доступа к устройствам и файлам и, вместе с тем, при необходимости не использовать соответствующие POSIX-службы в небольших встраиваемых системах.

При начальном запуске модуль procnto помещает в пространстве имен путей следующие префиксы — табл. 6.1.

Таблица 6.1. Префиксы пространства имен

Файловая иерархия в POSIX-системах

Алексей Федорчук
2005 г

Одной из отличительных особенностей логического устройства файловой системы операционок POSIX-семейства является их иерархическая, или древовидная, организации (правда, как я уже говорил дерево выглядит это немного странно). То есть здесь нет, как в DOS или Windows любого рода, обозначений (например, буквенных, или каких-либо иных) для отдельных носителей и их разделов: все они включаются в единую структуру в качестве подкаталогов главного каталога. называемого корневым. Процесс подключения файловых систем на самостоятельных физических носителях (и их разделах) к корню файлового древа называется монтированием, а подкаталоги, содержимое которых они составляют, именуются точками монтирования.

Содержание

Принципы построения иерархии каталогов


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

Такое положение дел затрудняет сочинение кросс-платформенных приложений. И потому существует и активно развивается проект стандартизации файловой иерархии — FHS (Filesystem Hierarchy Standard).

Проект FHS был направлен первоначально на упорядочивание структуры каталогов в многочисленных дистрибутивах Linux. Позднее он был приспособлен для других Unix-подобных систем (в том числе и BSD-клана). И ныне предпринимаются активные (но не очень успешные) усилия к тому, чтобы сделать его стандартом для POSIX-систем не только по имени, но и фактически.

Стандарт FHS покоится на двух основополагающих принципах — четком отделении в файловой иерархии каталогов разделяемых и неразделяемых, с одной стороны, и неизменяемых и изменяемых — с другой.

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

Суть противопоставления неизменяемых и изменяемых каталогов легко пояснить на примере. Так, те же общие пользовательские программы по природе своей должны быть неизменяемыми (вернее, доступными для модификации только администратору системы, но не самому пользователю, применяющему их в своей работе). В то же время эти программы при своей работе генерируют не только файлы данных, скажем, тексты или изображения (изменяемая их природа ясна без комментариев), но всякого рода служебную информацию, типа log-файлов, временных файлов и тому подобного). Каковая и должна группироваться в каталогах, отделенных от собственно исполнимых файлов программ, необходимых для их запуска библиотек, конфигурационных файлов и т.д.

Илон Маск рекомендует:  Что такое код create table

Тем не менее, не смотря на активное продвижение во многих дистрибутивах Linux из числа наиболее распространенных, статуса истинного стандарта FHS не обрел. Существует немало дистрибутивов Linux, не использующие некоторые его положения. А с традиционной файловой иерархией BSD-систем он соотносится лишь частично.

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

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

Кроме того, в BSD-системах и Source Based дистрибутивах Linux к трудновосстановимым каталогам я отнес бы все, связанное с пакетным менеджментом — дерево портов FreeBSD или pkgsrc в NetBSD (и системах, его заимствовавших), их аналоги в дистрибутивах Linux, собственно исходники портированных программ, да и исходные тексты системы тоже. Ибо, даже если все это имеется на дистрибутиве, эти компоненты файловой системы, как правило, поддерживаются пользователем в актуальном состоянии путем синхронизации по Сети с серверами проекта (иначе их использование лишено смысла). И их утрата повлечет как временные (особенно при модемном подключении), так и финансовые (мало кто является счастливым обладателем бесплатного доступа в Интернет) потери.

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

Типовой набор каталогов POSIX-системы

Собственно говоря, для функционирования абсолютно необходимо наличие лишь одной файловой системы — той, что монтируется в корневой каталог файлового древа (своего рода аналог мирового дерева Иггдрассиль). Корневой каталог и его непременные ветви обязательно должны составлять единую файловую систему, расположенную на одном носителе — диске, дисковом разделе, программном либо аппаратном RAID-массиве, или логическом томе в понимании LVM. И в нем должны располагаться все компоненты, необходимые для старта системы и, в идеале, — ничего сверх того.

Просмотреть состав корневого каталога можно командой

которая в любой POSIX-системе покажет некий минимальный джентльменский набор каталогов:

Именно в них собраны все файлы, без которых система не может существовать. Прочие каталоги — примерно такие:

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

Кроме этого, в большинстве случаев в корне файловой системы POSIX-совместимых ОС присутствуют еще два подкаталога:

Это обычно — точки монтирования виртуальных файловых систем — устройств и процессов, соответственно (хотя, если файловая система устройств не используется, каталог /dev обязательно должен быть компонентом корневой файловой системы. Наконец, в Linux-системах, как правило, в корне файлового древа лежит еще и каталог /lib , предназначенный для главных системных библиотек. А при использовании механизма udev неизбежным оказывается еще и каталог /sys , в который монтируется виртуальная файловая система sysfs .

Корневая файловая система

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

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

В соответствие с этим старт машины обеспечивается файлами каталогов /boot и /etc . В первом размещаются ядро системы — исполнимый файл «особого назначения», — и все, что требуется для его загрузки: в Linux, например, это системная карта (файл /etc/System.map ), а во FreeBSD — загружаемые модули ядра. Впрочем, подчас ядро размещается непосредственно в корне файловой системы, и тогда каталог /boot может отсутствовать вовсе, а под модули ядра может отводиться каталог /modules .

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

Минимально необходимая функциональность обеспечивается содержимым каталогов /bin и /sbin — в них собраны исполнимые файлы важнейших пользовательских и системных программ, соответственно, тех самых, которые позволят выполнить комплекс ремонтно-спасательных мероприятий и привести машину в человеческий вид после сбоя.

Разнесение системных и пользовательских программ по подкаталогам корня — достаточно условно. Ни одна из команд этих для решения пользовательских задач по настоящему не предназначена. Просто в каталоге /bin собраны команды администрирования, к которым время от времени обращается (или может обратиться) и обычный пользователь, а каталог sbin предназначен для команд, о которых пользователю и знать-то не положено. И которыми он, в большинстве случаев, все равно не сможет воспользоваться по причине отсутствия соответствующих полномочий (например, требуемых прав доступа к файлам устройств).

Для запуска POSIX-программ (в том числе и тех, что собраны в каталогах /bin и sbin ), как правило, требуется доступ к функциям общесистемных библиотек (в первую очередь — главной библиотеки glibc ). И потому (почти) непременный компонент корневого каталога — подкаталог /lib , в коем они и собраны.

В Linux каталог /lib служит еще одной важной цели — в его подкаталоге ( /lib/modules ) собраны загружаемые модули ядра (во FreeBSD их место — каталог /boot/kernel ).

Во FreeBSD каталога /lib в корневой файловой системе не обнаруживается — соответствующие компоненты здесь размещаются в /usr/lib (см. далее). Это связано с тем, что исторически во FreeBSD важнейшие общесистемные программы собирались так, что требуемые им библиотечные функции встраивались в их исполнимые файлы (так называемая статическая линковка, о которой речь пойдет в главе 14). Во FreeBSD 5-й ветки программы из каталогов /bin и /sbin линкуются динамически, то есть при отсутствии каталога /usr (а во Free это почти всегда отдельная ветвь файловой системы) они не функционируют. В компенсацию чего предусмотрен выходящий за рамки стандартов каталог /restore , содержащий те же программы, но слинкованные статически (как следует из имени каталога, единственным назначением его содержимого являются аварийно-спасательные работы).

И, наконец, /root . Это — обычный домашний каталог одноименного пользователя, сиречь администратора системы. Поскольку никакой практической работы он не делает (или, по крайней мере, делать не должен), содержимое его — лишь собственные конфигурационные файлы суперпользователя (пользовательской командной оболочки, любимого редактора и так далее).

Ветвь /usr

Исторически каталог /usr предназначался для пользовательских программ и данных. Ныне эти функции распределены между каталогами /usr/local и /home (хотя и сейчас во FreeBSD по умолчанию последний представляет собой символическую ссылку на /usr/home ). Каталог же /usr — не изменяемый, но разделяемый, — служит вместилищем основной части прикладных программ и всего, что к ним относится — исходных текстов, конфигурационных файлов, разделяемых библиотек, документации и тому подобного хозяйства.

Состав каталога /usr существенно различается в BSD-системах и в Linux. В первых в него помещаются только неотъемлемые части операционной системы (того, что во FreeBSD объединяется понятием Distributions). Приложения же, устанавливаемые из портов или пакетов, имеют место своей прописки подкаталог /usr/local , который может представлять отдельную ветвь файлового древа.

В Linux каталог /usr служит вместилищем всех программ (и их компонентов), штатно включенных в состав дистрибутива. А подкаталог /usr/local предназначается обычно для программ, самостоятельно собираемых из исходников.

В любом случае, обычный состав каталога /usr следующий (по выводу команды ls -1 ):

Как уже говорилось, подкаталог /usr/local — отдельная ветвь файлового древа, и потому будет рассмотрен отдельно же. Назначение же прочих каталогов таково:

  • /usr/bin и /usr/sbin предназначены для исполнимых файлов пользовательских и системных программ (здесь граница между ними еще более условна, чем в случае корневого каталога), назначение которых выходит за рамки обеспечения базового функционирования системы;
  • /usr/etc предназначается для конфигурационных файлов отдельных приложений;
  • /usr/include содержит так называемые заголовочные файлы, необходимые для линковки исполняемых файлов с библиотечными компонентами;
  • /usr/lib и /usr/libexec — каталоги для разделяемых библиотек, от которых зависят пользовательские приложения;
  • /usr/share — вместилище самых разнообразных, т.н. архитектурно независимых, компонентов: здесь можно видеть и документацию в разных форматах, и примеры конфигурационных файлов, и данные, используемые программами управления консолью (шрифты, раскладки клавиатуры), и описание часовых поясов;
  • /usr/src — каталог для исходных текстов; в Linux тут штатно помещаются только исходники ядра (ядер) системы, в BSD же клонах — полный набор исходников того комплекса, который во FreeBSD именуется Distributions; исходники самостоятельно собираемых программ помещать сюда, как правило, нежелательно;
  • /usr/X11R6 — каталог для компонентов оконной системы Икс — исполнимых файлов ( /usr/X11R6/bin ), библиотек ( /usr/X11R6/lib ), заголовков ( /usr/X11R6/include ), документации ( /usr/X11R6/man ); файлы Иксовых приложений сюда помещаться не должны (за исключением, разве что, оконных менеджеров) — их место в /usr , /usr/local или /opt , в зависимости от системы.

Кроме этого, в каталоге /usr могут обнаружиться подкаталоги /usr/var и /usr/tmp — обычно символические ссылки на соответствующие ветви корневого каталога. А в некоторых дистрибутивах Linux непосредственно в /usr помещается и основная общесистемная документация — man-страницы (в подкаталог /usr/man ).

Наконец, в BSD-системах и некоторых Source Based дистрибутивах Linux (например, Gentoo) в каталоге /usr размещается подкаталог для системы управления пакетами — портов FreeBSD и OpenBSD ( /usr/ports ), их аналогов в других системах ( /usr/portage в Gentoo). Хотя с точки зрения следования букве и духу стандарта FHS (сам он о портах и подобных системах не упоминает ни словом), более логичным местом их размещения был бы каталог /var (см. ниже) — и именно так делается в таких дистрибутивах, как CRUX и Archlinux.

Ветвь /usr/local

Как уже было сказано, ветвь /usr/local в Linux предназначена для самостоятельно собираемых из исходников (не входящих в данный дистрибутив) программ. А во FreeBSD она служит вместилищем большей части пользовательских приложений — почти всего того, что выходит за рамки Distributions и устанавливается из пакетов или портов. Соответственно этому, структура каталога в целом повторяет таковую ветви /usr (с понятными исключениями):

Содержимое подкаталогов также аналогично: исполнимые файлы программ ( /usr/local/bin и /usr/local/sbin ), их конфиги ( /usr/local/etc ), библиотеки, с которым они связаны, и их заголовочные файлы ( /usr/local/lib и /usr/local/include , соответственно), man-страницы ( /usr/local/man ) и всякая архитектурно независимая всячина ( /usr/local/share ), в том числе и документация в иных форматах.

Ветвь /opt

Каталог /opt предусмотрен стандартом FHS, но реально используется не во всех дистрибутивах Linux, а в BSD-системах и вовсе отсутствует. Тем не менее, все больше программ пишется в рассчете на умолчальную инсталляцию именно в него.

Исторически каталог /opt предназначался в Linux для коммерческих приложений и всякого рода программ не вполне свободного характера. Ныне же его назначение — размещение больших самодостаточных программных комплексов, таких, как библиотека Qt, KDE со всеми его компонентами и приложениями, OpenOffice.org и тому подобных. Структура каталога должна быть такой: /opt/pkg_name . Вот как выглядит она а в моей системе (Archlinux):

Каждый из подкаталогов имеет собственную внутреннюю структуру:

Назначение подкаталогов внутри /opt/pkg_name легко угадывается по аналогии с /usr и /usr/local . Например /opt/kde/bin предназначается для исполнимых файлов системы KDE и ее приложений, /opt/kde/etc — для конфигурационных ее файлов, /opt/kde/include — для файлов заголовков, /opt/kde/lib — для библиотек и /opt/kde/share — для разделяемых файлов, в том числе и документации. В KDE нет документации в man-формате, если же она имеется, то (как в случае Gnome — я его не ставил, это то, что потянули Gimp и тому подобные Gtk-приложения) можно видеть подкаталог /opt/pkg_name/man .

Можно видеть, что структура каталога /opt отступает от исторически сложившейся (и внутренне обоснованной POSIX-традиции объединения в каталоги однотипных компонентов — исполняемых файлов, библиотек и так далее. И при большом количестве инсталлированных в него программ создает определенные трудности: приходится либо перегружать значениями переменную $PATH , обеспечивающую быстрый доступ к командам (о чем будет говориться в главе 12), либо создавать специальный каталог /opt/bin и помещать в него символические ссылки на исполняемые бинарники программ. Поэтому в ряде дистрибутивов Linux (например, в CRUX) каталог /opt не используется принципиально. Как, впрочем, и во всех BSD-системах. Вполне возможно, что так оно и лучше…

Ветвь /var

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

Внутренняя структура /var очень сильно меняется от системы к системе, и поэтому на деталях ее устройства я задерживаться не буду. Замечу только, что этот каталог — логичное место для помещения компонентов всякого рода портообразных систем управления пакетами, как это сделано, например, в дистрибутиве Archlinux, где под нее отведен подкаталог /var/abs (abs — Archlinux Building System).

Каталог /mnt

Каталог /mnt предназначен для монтирования временно используемых файловых систем, располагающихся, как правило, на сменных носителях. В всежеустановленной системе он обычно пуст, и структура его никак не регламентирована. Пользователю вольно создать в нем подкаталоги для отдельных видов носителей. Например, в моей системе это /mnt/cd , /mnt/dvd , /mnt/usb и /mnt/hd — для дисков CD, DVD, флэшки и съемного винчестера.

Во FreeBSD штатными каталогами для монтирования CD и дискет являются /cdrom и /floppy непосредственно в корневом каталоге. Что не вполне согласуется со стандартом, но по своему логично — в корень вынесены точки монтирования устройств, существующих (как CD ROM) или до недавнего времени существовавших (флоппи-дисковод) в любой машине.

Ветвь /home

Каталог /home предназначен для помещения домашних каталогов пользователей. Содержимое его никак не регламентировано, но обычно он имеет вид вроде /home/ . Хотя в крупных системах с большим количеством пользователей их домашние каталоги могут быть объединены в группы.

В каталоге /home могут располагаться домашние каталоги не только реальных, но и некоторых виртуальных пользователей. Так, если машина используется в качестве web- или ftp-сервера, можно видеть такие подкаталоги, как /home/www или /home/ftp , соответственно.

Ветвь /tmp

Осталось поговорить только о каталоге для хранения временных файлов — /tmp . Как и компоненты /var , они генерируются различными программами в ходе нормальной их жизнедеятельности. Но, в отличие от /var , для компонентов /tmp не предполагается их сохранения вне текущего сеанса работы. Более того, все руководства по системному администрированию рекомендуют регулярно (например, при рестарте машины) или периодически очищать этот каталог. И потому в качестве /tmp целесообразно монтировать файловые системы в оперативной памяти — tmpfs (в Linux) или mfs (во FreeBSD). Кроме того, что это гарантирует очистку его содержимого при перезагрузке, так еще и способствует быстродействию, например, компиляции программ, временные продукты которой не записываются на диск, а помещаются в виртуальный каталог типа /tmp/obj .

Во многих системах можно увидеть каталоги вроде /usr/tmp и /var/tmp . Это, как правило, символические ссылки на /tmp .

Стратегия разделения файловых систем

В заключение разговора о файловой иерархии следует подчеркнуть, что гарантированно на одной файловой системе (фигурально говоря, на одном дисковом разделе, хотя это и не совсем точно) должны находиться только каталоги, перечисленные в параграфе Корневая файловая система. Все же прочие каталоги — /usr , /opt , /var , /tmp и, конечно же, /home могут представлять точки монтирования самостоятельных файловых систем на отдельных физических носителях или их разделах.

Более того, в локальной сети каталоги эти вполне могут располагаться даже на разных машинах. Так, один компьютер, выполняющий роль сервера приложений, может содержать разделяемые в сети каталоги /usr и /opt , другой — файл-сервер, — вмещать все домашние каталоги пользователей, и так далее.

Осталось только решить — какие файловые системы целесообразно вычленить из общего файлового древа, и зачем это делать. Ответ на эти вопросы очень сильно зависит от используемой ОС, а в случае с Linux — еще и от его дистрибутива. Тем не менее, общие принципы отделения файловых систем наметить можно. Для чего следует вспомнить о противопоставлении, с одной стороны, неизменяемых и изменяемых каталогов, с другой — легко восстановимых, трудно восстановимых и практически невосстановимых данных. Проделаем такую попытку применительно к пользовательскому десктопу — в случае сервера рассчеты будут существенно иными.

Очевидно, что корневая файловая система в составе каталогов /bin , /boot , /etc , /root , /sbin , содержащих легко восстановимые с дистрибутивного носителя и практически не изменяемые данные, должны лежать на изолированном дисковом разделе. В Linux к ним должен добавиться еще и каталог /lib . С другой стороны, при использовании в качестве загрузчика GRUB (вне зависимости от операционной системы) рекомендуется вынести на отдельный раздел каталог /boot .

В старых источниках о Linux можно прочитать о другом резоне к выделению раздела для каталога /boot , причем в самом начале диска: из-за невозможности загрузки ядра программой Lilo с цилиндра номером выше, чем 1023. В современных версиях загрузчиков таких ограничений нет. Тем не менее, если уж раздел под /boot создается, резонно сделать его первым на диске, а непосредственно за ним разместить раздел подкачки: это добавит пять копеек быстродействия при осуществлении своппинга.

И еще из области истории: требование, чтобы корневой и загрузочный разделы были непременно первичными, также давно снято. И эти файловые системы вполне могут помещаться на логических разделах внутри Extended Partition.

Столь же ясно, что изменяемые ветви файловой системы — каталоги /var и /tmp , — должны быть вынесены за пределы корневого раздела. Причем последний, как неоднократно говорилось ранее, вообще целесообразно разместить на файловой системе в оперативной памяти (tmpfs или mfs). В случае, если каталог /var содержит подкаталоги для портообразных систем пакетного менеджмента, подобно /var/abs , /var/cache/pacman/src и /var/cache/pacman/pkg в Archlinux, они также должны образовывать самостоятельные файловые системы


Теперь — каталог /usr , содержащий либо компоненты базовой системы (как в BSD), либо — основную массу пользовательских приложений (как в большинстве дистрибутивов Linux). Он содержит легковосстановимые данны и, по хорошему, должен бы быть практически неизменяемым, и потому, безусловно, заслуживает выделения на самостоятельном разделе. Причем из его состава целесообразно вычленить, с одной стороны, подкаталоги /usr/X11R6 и /usr/local , с другой — подкаталоги для портообразных систем пакетного менеджмента: /usr/ports , /usr/pkgsrc и /usr/pkg в BSD-системах, /usr/portages в Gentoo Linux, и так далее. Причем от последних следует обособить подкаталоги для помещения исходников, скачиваемых из сети при сборке портов — /usr/ports/distfiles , /usr/pkgsrc/disfiles , /usr/portages/distfiles и подобные им.

В BSD-системах, кроме этого, из каталога /usr имеет смысл выделить подкаталоги /usr/src и /usr/obj , содержащие исходные тексты базовых компонентов (включая ядро) и промежуточные продукты их компиляции, образумемые в результате процедур make buildworld и make buildkernel .

И, наконец, каталог /home , содержащий изменяемые и часто невосстановимые данные, подлежит вынесению из корня файловой иерархии в обязательном порядке. Причем я всегда стараюсь разместить его либо на отдельном слайсе (в BSD), либо на первичном разделе (в Linux).

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

Дополнительный ее плюс — в том, что для отдельных ветвей файлового древа, в зависимости от характера размещенных на ней данных, в Linux можно подобрать физически оптимальную файловую систему. Например, для раздела под /boot нет смысла использовать что-либо помимо Ext2fs. Корневой раздел обычно рекомендуется форматировать в надежной и при этом наиболее совместимой Ext3fs. Под каталоги с огромным количеством мелких файлов, такие, как /var/abs в Archlinux, /usr/portages в Gentoo, целесообразно задействовать ReiserFS: ведь умелое обращение с мелкими файлами — это ее профиль. А в каталоге /home , где возможно появление огромных мультимедийных файлов (и который сам по себе обычно очень велик), ко двору может прийтись XFS (хотя, как показывают измерения, и ReiserFS выглядит тут вполне достойно). Такие меры могут способствовать повышению и надежности хранения данных, и быстродействию файловых операций.

Пользователи BSD-операционок безальтернативно привязаны к файловым система типа FFS. Однако и у них есть пространство для маневра. Во-первых — за счет варьирования размеров блока и фрагмента отдельных файловых систем, способствующего либо производительности дисковых операций, либо экономии дискового пространства. А во-вторых, некоторые ветви файлового древа (такие, как /tmp или /usr/obj , вопреки рекомендациям, можно безбоязненно монтировать в чисто асинхронном режиме, выиграв на этом процент-другой производительности.

Что такое POSIX?

Традиционно говорят о двух мирах, двух системах мировоззрения, присущих пользователям операционных систем Windows и UNIX. И если пользователей первой системы легко можно найти практически повсеместно, то тех, кто исповедует идеологию UNIX, отыскать несколько труднее. И уж совсем редко два этих мира контактируют между собой. Поэтому многие пользователи Windows затрудняются дать ответ на вопрос «POSIX-система и UNIX-система — это одно и то же или нет?». Естественно, чтобы дать ответ, нужно разобраться в том, что же такое этот самый POSIX.

POSIX — это стандарт, описывающий интерфейс между операционной системой и прикладной программой. Фактически, этот стандарт описывает то, как программа должна вызывать системные функции для того, чтобы свободно запускаться под всеми операционными системами, отвечающими данному стандарту. Сама аббревиатура POSIX расшифровывается как Portable Operating System Interface for UNIX. Стандарт POSIX не накладывает никаких ограничений на фантазию разработчиков операционных систем в плане реализации тех или иных функций, которые он описывает.

Однако никто не запрещает системам, не являющимся клонами (или, лучше будет сказать, потомками) UNIX, поддерживать стандарт POSIX. Классическим примером POSIX-системы, не являющейся UNIX-системой, является операционная система реального времени QNX, о которой я не так давно рассказывал на страницах «Компьютерных вестей». Хотя, как написано на сайте posix.ru, «аббревиатура POSIX. является почти синонимом термина Unix-подобная операционная система».

Но корни POSIX всё же тесно связаны с UNIX’ами. А поскольку история UNIX-систем началась довольно давно, то и POSIX уже не самый молодой из стандартов. Первая версия стандарта была выпущена ещё в далёком 1988 году. А аббревиатуру POSIX, которую правильно читать как «пазекс», придумал не кто иной, как легендарный Ричард Столлман.

Стоит отметить, что стандарт POSIX не является полным и исчерпывающим. Так, например, в нём отсутствует описание графического интерфейса пользователя и сетевых функций системы. Но поскольку хоть какой-то стандарт всё же лучше, чем полное его отсутствие, то стандарт POSIX, будучи далёким от совершенства, стал популярен и среди разработчиков операционных систем, и среди прикладных программистов. Стандарт продолжает развиваться и совершенствоваться, как и операционные системы, основанные на нём. Так что знайте, если вас спросят, что Linux, FreeBSD, Solaris, MacOS X, QNX — это всё POSIX-системы.

ЧАСТЬ 1

ВВЕДЕНИЕ В IPC UNIX

ГЛАВА 1

Обзор средств взаимодействия процессов Unix

1.1. Введение

Аббревиатура IPC расшифровывается как interprocess communication, то есть взаимодействие процессов. Обычно под этим понимается передача сообщений различных видов между процессами в какой-либо операционной системе. При этом могут использоваться различные формы синхронизации, требуемой современными видами взаимодействия, осуществляемыми, например, через разделяемую память.

В процессе развития операционных систем семейства Unix за последние 30 лет методы передачи сообщений эволюционировали следующим образом:

■ Каналы (pipes — глава 4) были первой широко используемой формой взаимодействия процессов, доступной программам и пользователю (из интерпретатора команд). Основным недостатком каналов является невозможность их использования между процессами, не имеющими общего родителя (ancestor), но этот недостаток был устранен с появлением именованных каналов (named pipes), или каналов FIFO (глава 4).

■ Очереди сообщений стандарта System V (System V message queues — глава 4) были добавлены к ядрам System V в начале 80-х. Они могут использоваться для передачи сообщений между процессами на одном узле вне зависимости от того, являются ли эти процессы родственными. Несмотря на сохранившийся префикс «System V», большинство современных версий Unix, включая и те, которые не произошли от System V, поддерживают эти очереди.

В отношении процессов Unix термин «родство» означает, что у процессов имеется общий предок. Подразумевается, что процессы, являющиеся родственниками, были созданы этим процессом-предком с помощью одной или нескольких «вилок» (forks). Простейшим примером будет вызов fork некоторым процессом дважды, что приведет к созданию двух порожденных процессов. Тогда можно говорить о родстве этих процессов между собой. Естественно, каждый порожденный процесс является родственником породившего. Родитель может позаботиться о возможности взаимодействия с порожденным процессом (создав канал или очередь сообщений) перед вызовом fork, и этот объект IPC будет унаследован порожденным процессом. Более подробно о наследовании объектов IPC рассказано в табл. 1.4. Нужно также отметить, что все процессы Unix теоретически являются потомками процесса init, который запускает все необходимое в процессе загрузки системы (bootstrapping). С практической точки зрения отсчет родства процессов лучше вести с оболочки (login shell) и всех процессов, ею созданных. В главе 9 [24] рассказано о сеансах и родственных отношениях процессов более подробно.

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

■ Очереди сообщений Posix (Posix message queues — глава 5) были добавлены в стандарт Posix (1003.1b-1993, о котором более подробно рассказано в разделе 1.7). Они могут использоваться для взаимодействия родственных и неродственных процессов на каком-либо узле.

■ Удаленный вызов процедур (remote procedure calls — RPC, часть 5) появился в 80-х в качестве средства для вызова функций на одной системе (сервере) программой, выполняемой на другой системе (клиенте). Это средство было разработано в качестве альтернативы для упрощения сетевого программирования. Поскольку между клиентом и сервером обычно передается информация (передаются аргументы для вызова функции и возвращаемые значения) и поскольку удаленный вызов процедур может использоваться между клиентом и сервером на одном узле, RPC можно также считать одной из форм передачи сообщений.

Интересно также взглянуть на эволюцию различных форм синхронизации в процессе развития Unix:

■ Самые первые программы, которым требовалась синхронизация (чаще всего для предотвращения одновременного изменения содержимого файла несколькими процессами), использовали особенности файловой системы, некоторые из которых описаны в разделе 9.8,

■ Возможность блокирования записей (record locking — глава 9) была добавлена к ядрам Unix в начале 80-х и стандартизована в версии Posix.1 в 1988.

■ Семафоры System V (System V semaphores — глава 11) были добавлены вместе с возможностью совместного использования памяти (System V shared memory — глава 14) и одновременно с очередями сообщений System V (начало 80-х). Эти IPC поддерживаются большинством современных версий Unix.

■ Семафоры Posix (Posix semaphores — глава 10) и разделяемая память Posix (Posix shared memory— глава 13) были также добавлены в стандарт Posix (1003.1b-1993, который ранее упоминался в связи с очередями сообщений Posix).

■ Взаимные исключения и условные переменные (mutex, conditional variable — глава 7) представляют собой две формы синхронизации, определенные стандартом программных потоков Posix (Posix threads, Pthreads — 1003.1с-1995). Хотя обычно они используются для синхронизации между потоками, их можно применять и при организации взаимодействия процессов.

■ Блокировки чтения-записи (read-write locks — глава 8) представляют собой дополнительную форму синхронизации. Она еще не включена в стандарт Posix, но, вероятно, скоро будет.

1.2. Процессы, потоки и общий доступ к информации

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

Рис. 1.1. Совместное использование информации процессами

1. Два процесса в левой части совместно используют информацию, хранящуюся в одном из объектов файловой системы. Для доступа к этим данным каждый процесс должен обратиться к ядру (используя функции read, write, lseek, write, lseek и аналогичные). Некоторая форма синхронизации требуется при изменении файла, для исключения помех при одновременной записи в файл несколькими процессами и для защиты процессов, читающих из файла, от тех, которые пишут в него.

2. Два процесса в середине рисунка совместно используют информацию, хранящуюся в ядре. Примерами в данном случае являются канал, очередь сообщений или семафор System V. Для доступа к совместно используемой информации в этом случае будут использоваться системные вызовы.

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

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

Потоки

Хотя концепция процессов в системах Unix используется уже очень давно, возможность использовать несколько потоков внутри одного процесса появилась относительно недавно. Стандарт потоков Posix.1, называемый Pthreads, был принят в 1995 году. С точки зрения взаимодействия процессов все потоки одного процесса имеют общие глобальные переменные (то есть поточной модели свойственно использование общей памяти). Однако потокам требуется синхронизация доступа к глобальным данным. Вообще, синхронизация, не являясь собственно формой IPC, часто используется совместно с различными формами IPC для управления доступом к данным.

В этой книге описано взаимодействие между процессами и между потоками. Мы предполагаем наличие среды, в которой поддерживается многопоточное программирование, и будем использовать выражения вида «если канал пуст, вызывающий поток блокируется до тех пор, пока какой-нибудь другой поток не произведет запись в канал». Если система не поддерживает потоки, можно в этом предложении заменить «потоки» на «процессы» и получится классическое определение блокировки в Unix, возникающей при считывании из пустого канала командой read. Однако в системе, поддерживающей потоки, блокируется только поток, запросивший данные из пустого канала, а все остальные потоки процесса будут продолжать выполняться. Записать данные в канал сможет другой поток этого же процесса или какой-либо поток другого процесса.

В приложении Б сведены некоторые основные характеристики потоков и дано описание пяти основных функций Pthread, используемых в программах этой книги.

1.3. Живучесть объектов IPC

Можно определить живучесть (persistence) любого объекта IPC как продолжительность его существования. На рис. 1.2 изображены три возможные группы, к которым могут быть отнесены объекты по живучести.

Рис. 1.2. Живучесть объектов IPC

1. Объект IPC, живучесть которого определяется процессом (process-persistent), существует до тех пор, пока не будет закрыт последним процессом, в котором он еще открыт. Примером являются неименованные и именованные каналы (pipes, FIFO).

2. Объект IPC, живучесть которого определяется ядром (kernel-persistent), существует до перезагрузки ядра или до явного удаления объекта. Примером являются очереди сообщений стандарта System V, семафоры и разделяемая память. Живучесть очередей сообщений Posix, семафоров и разделяемой памяти должна определяться по крайней мере ядром, но может определяться и файловой системой в зависимости от реализации.

3. Объект IPC, живучесть которого определяется файловой системой (filesystem-persistent), существует до тех пор, пока не будет удален явно. Его значение сохраняется даже при перезагрузке ядра. Очереди сообщений Posix, семафоры и память с общим доступом обладают этим свойством, если они реализованы через отображаемые файлы (так бывает не всегда).

Следует быть аккуратным при определении живучести объекта IPC, поскольку она не всегда очевидна. Например, данные в канале (pipe) обрабатываются ядром, но живучесть каналов определяется процессами, а не ядром, потому что после того, как последний процесс, которым канал был открыт на чтение, закроет его, ядро сбросит все данные и удалит канал. Аналогично, хотя каналы FIFO и обладают именами в файловой системе, живучесть их также определяется процессами, поскольку все данные в таком канале сбрасываются после того, как последний процесс, в котором он был открыт, закроет его.

В табл. 1.1 сведена информация о живучести перечисленных ранее объектов IPC.

Таблица 1.1. Живучесть различных типов объектов IPC

Тип IPC Живучесть определяет
Программный канал (pipe) Процесс
Именованный канал (FIFO) Процесс
Взаимное исключение Posix (mutex) Процесс
Условная переменная Posix (condition variable) Процесс
Блокировка чтения-записи Posix (lock) Процесс
Блокировка записи fcntl Процесс
Очередь сообщений Posix (message queue) Ядро
Именованный семафор Posix (named semaphore) Ядро
Семафор Posix в памяти (memory-based semaphore) Процесс
Разделяемая память Posix (shared memory) Ядро
Очередь сообщений System V Ядро
Семафор System V Ядро
Память с общим доступом System V Ядро
Сокет TCP (TCP socket) Процесс
Сокет UDP (UDP socket) Процесс
Доменный сокет Unix (Unix domain socket) Процесс

Обратите внимание, что ни один тип IPC в этой таблице не обладает живучестью, определяемой файловой системой. Мы уже упомянули о том, что три типа объектов IPC в стандарте Posix могут иметь этот тип живучести в зависимости от реализации. Очевидно, что запись данных в файл обеспечивает живучесть, определяемую файловой системой, но обычно IPC таким образом не реализуются. Большая часть объектов IPC не предназначена для того, чтобы существовать и после перезагрузки, потому что ее не переживают процессы. Требование живучести, определяемой файловой системой, скорее всего, снизит производительность данного типа IPC, а обычно одной из задач разработчика является именно обеспечение высокой производительности.

Илон Маск рекомендует:  Что такое код cpdf_rlineto

1.4. Пространства имен

Если два неродственных процесса используют какой-либо вид IPC для обмена информацией, объект IPC должен иметь имя или идентификатор, чтобы один из процессов (называемый обычно сервером — server) мог создать этот объект, а другой процесс (обычно один или несколько клиентов — client) мог обратиться к этому конкретному объекту.

Программные каналы (pipes) именами не обладают (и поэтому не могут использоваться для взаимодействия между неродственными процессами), но каналам FIFO сопоставляются имена в файловой системе, являющиеся их идентификаторами (поэтому каналы FIFO могут использоваться для взаимодействия неродственных процессов). Для других типов IPC, рассматриваемых в последующих главах, используются дополнительные соглашения об именовании (naming conventions). Множество возможных имен для определенного типа IPC называется его пространством имен (name space). Пространство имен — важный термин, поскольку для всех видов IPC, за исключением простых каналов, именем определяется способ связи клиента и сервера для обмена сообщениями.

В табл. 1.2 сведены соглашения об именовании для различных видов IPC.

Таблица 1.2. Пространства имен для различных типов IPC

Тип IPC Пространство имен для создания или открытия Идентификатор после открытия Posix.1 1996 Unix 98
Канал (Без имени) Дескриптор
FIFO Имя файла (pathname) Дескриптор
Взаимное исключение Posix (Без имени) Указатель типа pthread_mutex_t
Условная переменная Posix (Без имени) Указатель типа pthread_cond_t
Блокировка чтения-записи Posix (Без имени) Указатель типа pthread_rwlock_t
Блокировка записей fcntl Имя файла Дескриптор
Разделяемая память Posix Posix-имя IPC Дескриптор
Очередь сообщений System V Ключ key_t Идентификатор IPC System V
Семафор System V Ключ key_t Идентификатор IPC System V
Разделяемая память System V Ключ key_t Идентификатор IPC System V
Двери (doors) Имя файла Дескриптор
Удаленный вызов процедур (RPC) Sun Программа/версия Дескриптор (handle) RPC
Сокет TCP IP-адрес и порт TCP Дескриптор .1g
Сокет UDP IP-адрес и порт TCP Дескриптор .1g
Доменный сокет Unix (domain socket) Полное имя файла Дескриптор .1g

Здесь также указано, какие формы IPC содержатся в стандарте Posix.1 1996 года и какие были включены в стандарт Unix 98. Об обоих этих стандартах более подробно рассказано в разделе 1.7. Для сравнения мы включили в эту таблицу три типа сокетов, которые подробно описаны в [24]. Обратите внимание, что интерфейс сокетов (Application Program Interface — API) стандартизируется рабочей группой Posix.1g и должен в будущем стать частью стандарта Posix.1.

Хотя стандарт Posix. 1 и дает возможность использования семафоров, их поддержка не является обязательной для производителей. В табл. 1.3 сведены функции, описанные в стандартах Posix.1 и Unix 98. Каждая функция может быть обязательной (mandatory), неопределенной (not defined) или необязательной (дополнительной — optional). Для необязательных функций мы указываем имя константы (например, _POSIX_THREADS), которая будет определена (обычно в заголовочном файле ), если эта функция поддерживается. Обратите внимание, что Unix 98 содержит в себе Posix.1 в качестве подмножества.

Таблица 1.3. Доступность различных форм IPC

Тип IPC Posix.1 1996 Unix 98
Программный канал Обязателен Обязателен
FIFO Обязателен Обязателен
Взаимное исключение Posix _POSIX_THREADS Обязателен
Условная переменная Posix _POSIX_THREADS Обязателен
Взаимные исключения и условные переменные между процессами _POSIX_THREADS_PROCESS_SHARED Обязателен
Блокировка чтения-записи Posix (He определен) Обязателен
Блокировка записей fcntl Обязателен Обязателен
Очередь сообщений Posix _POSIX_MESSAGE_PASSING _XOPEN_REALTIME
Семафоры Posix _POSIX_SEMAPHORES_ _XOPEN_REALTIME
Память с общим доступом Posix _POSIX_SHARED_MEMORY_OBJECTS _XOPEN_REALTIME
Очередь сообщений System V (He определен) Обязателен
Семафор System V (He определен) Обязателен
Память с общим доступом System V (He определен) Обязателен
Двери (doors) (He определен) (Не определен)
Удаленный вызов процедур Sun (He определен) (Не определен)
Отображение памяти mmap _POSIX_MAPPED_FILES или POSIX_SHARED_MEMORY_OBJECTS Обязателен
Сигналы реального времени (realtime signals) _POSIX_REALTIME_SIGNALS _XOPEN_REALTIME

1.5. Действие команд fork, exec и exit на объекты IPC

Нам нужно достичь понимания действия функций fork, exec и _exit на различные формы IPC, которые мы обсуждаем (последняя из перечисленных функций вызывается функцией exit). Информация по этому вопросу сведена в табл. 1.4.

Большинство функций описаны далее в тексте книги, но здесь нужно сделать несколько замечаний. Во-первых, вызов fork из многопоточного процесса (multithreaded process) приводит к беспорядку в безымянных переменных синхронизации (взаимных исключениях, условных переменных, блокировках и семафорах, хранящихся в памяти). Раздел 6.1 книги [3] содержит необходимые детали. Мы просто отметим в добавление к таблице, что если эти переменные хранятся в памяти с общим доступом и создаются с атрибутом общего доступа для процессов, они будут доступны любому процессу, который может обращаться к этой области памяти. Во-вторых, три формы IPC System V не могут быть открыты или закрыты. Из листинга 6.6 и упражнений 11.1 и 14.1 видно, что все, что нужно знать, чтобы получить доступ к этим трем формам IPC, — это идентификатор. Поэтому они доступны всем процессам, которым известен этот идентификатор, хотя для семафоров и памяти с общим доступом требуется некая особая обработка.

Таблица 1.4. Действие fork, exec и _exit на IPC


Тип IPC fork exec _exit
Неименованные и именованные каналы Порожденный процесс получает копии всех дескрипторов родительского процесса Все открытые дескрипторы остаются открытыми, если для них не установлен бит FD_CLOEXEC Все открытые дескрипторы закрываются, данные из программного канала и FIFO удаляются после последнего закрытия
Очереди сообщений Posix Порожденный процесс получает копии всех открытых родительских процессов Все открытые дескрипторы очередей сообщений закрываются Все открытые дескрипторы очередей сообщений закрываются
Очереди сообщений System V Не действует Не действует Не действует
Взаимные исключения и условные переменные Posix Общий доступ, если используется разделяемая память с атрибутом разделения между процессами Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения Исчезает, если не находится в разделяемой памяти, которая остается открытой и имеет атрибут разделения
Блокировки чтения-записи Posix Общий доступ, если используется память с общим доступом и атрибутом разделения между процессами Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения
Семафоры Posix, хранящиеся в памяти Общий доступ, если используется память с общим доступом и атрибутом разделения между процессами Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения Исчезает, если не хранится в разделяемой памяти, которая остается открытой и имеет атрибут разделения
Именованные семафоры Posix Все открытые в родительском процессе остаются открытыми в порожденном Все открытые закрываются Все открытые закрываются
Семафоры System V Все значения semadj в порожденном процессе устанавливаются в 0 Все значения semadj передаются новой программе Все значения semadj добавляются к значению соответствующего семафора
Блокировка записей fcntl Блокировки в родительском процессе не наследуются порожденным процессом Блокировки не изменяются до тех пор, пока не закроется дескриптор Все несброшенные блокировки, установленные процессом, снимаются
Отображение памяти Отображения памяти родительского процесса сохраняются в порожденном Отображения памяти сбрасываются (unmap) Отображения памяти сбрасываются
Разделяемая память Posix Отображения памяти родительского процесса сохраняются в порожденном Отображения памяти сбрасываются Отображения памяти сбрасываются
Разделяемая память System V Присоединенные сегменты разделяемой памяти остаются присоединенными в порожденном процессе Присоединенные сегменты разделяемой памяти отсоединяются Присоединенные сегменты разделяемой памяти отсоединяются
Двери (doors) Порожденный процесс получает копии всех открытых дескрипторов родительского процесса, но только родительский процесс является сервером при активизации дверей через дескрипторы Все дескрипторы дверей должны быть закрыты, потому что они создаются с установленным битом FD_CLOEXEC Все открытые дескрипторы закрываются

1.6. Обработка ошибок: функции-обертки

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

Пример функции-обертки приведен в листинге 1.1[1]

Листинг 1.1. Функция-обертка к функции sem_post

Файловая иерархия в POSIX-системах

Алексей Федорчук
2005 г

Одной из отличительных особенностей логического устройства файловой системы операционок POSIX-семейства является их иерархическая, или древовидная, организации (правда, как я уже говорил дерево выглядит это немного странно). То есть здесь нет, как в DOS или Windows любого рода, обозначений (например, буквенных, или каких-либо иных) для отдельных носителей и их разделов: все они включаются в единую структуру в качестве подкаталогов главного каталога. называемого корневым. Процесс подключения файловых систем на самостоятельных физических носителях (и их разделах) к корню файлового древа называется монтированием, а подкаталоги, содержимое которых они составляют, именуются точками монтирования.

Содержание

Принципы построения иерархии каталогов

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

Такое положение дел затрудняет сочинение кросс-платформенных приложений. И потому существует и активно развивается проект стандартизации файловой иерархии — FHS (Filesystem Hierarchy Standard).

Проект FHS был направлен первоначально на упорядочивание структуры каталогов в многочисленных дистрибутивах Linux. Позднее он был приспособлен для других Unix-подобных систем (в том числе и BSD-клана). И ныне предпринимаются активные (но не очень успешные) усилия к тому, чтобы сделать его стандартом для POSIX-систем не только по имени, но и фактически.

Стандарт FHS покоится на двух основополагающих принципах — четком отделении в файловой иерархии каталогов разделяемых и неразделяемых, с одной стороны, и неизменяемых и изменяемых — с другой.

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

Суть противопоставления неизменяемых и изменяемых каталогов легко пояснить на примере. Так, те же общие пользовательские программы по природе своей должны быть неизменяемыми (вернее, доступными для модификации только администратору системы, но не самому пользователю, применяющему их в своей работе). В то же время эти программы при своей работе генерируют не только файлы данных, скажем, тексты или изображения (изменяемая их природа ясна без комментариев), но всякого рода служебную информацию, типа log-файлов, временных файлов и тому подобного). Каковая и должна группироваться в каталогах, отделенных от собственно исполнимых файлов программ, необходимых для их запуска библиотек, конфигурационных файлов и т.д.

Тем не менее, не смотря на активное продвижение во многих дистрибутивах Linux из числа наиболее распространенных, статуса истинного стандарта FHS не обрел. Существует немало дистрибутивов Linux, не использующие некоторые его положения. А с традиционной файловой иерархией BSD-систем он соотносится лишь частично.

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

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

Кроме того, в BSD-системах и Source Based дистрибутивах Linux к трудновосстановимым каталогам я отнес бы все, связанное с пакетным менеджментом — дерево портов FreeBSD или pkgsrc в NetBSD (и системах, его заимствовавших), их аналоги в дистрибутивах Linux, собственно исходники портированных программ, да и исходные тексты системы тоже. Ибо, даже если все это имеется на дистрибутиве, эти компоненты файловой системы, как правило, поддерживаются пользователем в актуальном состоянии путем синхронизации по Сети с серверами проекта (иначе их использование лишено смысла). И их утрата повлечет как временные (особенно при модемном подключении), так и финансовые (мало кто является счастливым обладателем бесплатного доступа в Интернет) потери.

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

Типовой набор каталогов POSIX-системы

Собственно говоря, для функционирования абсолютно необходимо наличие лишь одной файловой системы — той, что монтируется в корневой каталог файлового древа (своего рода аналог мирового дерева Иггдрассиль). Корневой каталог и его непременные ветви обязательно должны составлять единую файловую систему, расположенную на одном носителе — диске, дисковом разделе, программном либо аппаратном RAID-массиве, или логическом томе в понимании LVM. И в нем должны располагаться все компоненты, необходимые для старта системы и, в идеале, — ничего сверх того.

Просмотреть состав корневого каталога можно командой

которая в любой POSIX-системе покажет некий минимальный джентльменский набор каталогов:

Именно в них собраны все файлы, без которых система не может существовать. Прочие каталоги — примерно такие:

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

Кроме этого, в большинстве случаев в корне файловой системы POSIX-совместимых ОС присутствуют еще два подкаталога:

Это обычно — точки монтирования виртуальных файловых систем — устройств и процессов, соответственно (хотя, если файловая система устройств не используется, каталог /dev обязательно должен быть компонентом корневой файловой системы. Наконец, в Linux-системах, как правило, в корне файлового древа лежит еще и каталог /lib , предназначенный для главных системных библиотек. А при использовании механизма udev неизбежным оказывается еще и каталог /sys , в который монтируется виртуальная файловая система sysfs .

Корневая файловая система

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

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

В соответствие с этим старт машины обеспечивается файлами каталогов /boot и /etc . В первом размещаются ядро системы — исполнимый файл «особого назначения», — и все, что требуется для его загрузки: в Linux, например, это системная карта (файл /etc/System.map ), а во FreeBSD — загружаемые модули ядра. Впрочем, подчас ядро размещается непосредственно в корне файловой системы, и тогда каталог /boot может отсутствовать вовсе, а под модули ядра может отводиться каталог /modules .

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

Минимально необходимая функциональность обеспечивается содержимым каталогов /bin и /sbin — в них собраны исполнимые файлы важнейших пользовательских и системных программ, соответственно, тех самых, которые позволят выполнить комплекс ремонтно-спасательных мероприятий и привести машину в человеческий вид после сбоя.

Разнесение системных и пользовательских программ по подкаталогам корня — достаточно условно. Ни одна из команд этих для решения пользовательских задач по настоящему не предназначена. Просто в каталоге /bin собраны команды администрирования, к которым время от времени обращается (или может обратиться) и обычный пользователь, а каталог sbin предназначен для команд, о которых пользователю и знать-то не положено. И которыми он, в большинстве случаев, все равно не сможет воспользоваться по причине отсутствия соответствующих полномочий (например, требуемых прав доступа к файлам устройств).

Для запуска POSIX-программ (в том числе и тех, что собраны в каталогах /bin и sbin ), как правило, требуется доступ к функциям общесистемных библиотек (в первую очередь — главной библиотеки glibc ). И потому (почти) непременный компонент корневого каталога — подкаталог /lib , в коем они и собраны.

В Linux каталог /lib служит еще одной важной цели — в его подкаталоге ( /lib/modules ) собраны загружаемые модули ядра (во FreeBSD их место — каталог /boot/kernel ).

Во FreeBSD каталога /lib в корневой файловой системе не обнаруживается — соответствующие компоненты здесь размещаются в /usr/lib (см. далее). Это связано с тем, что исторически во FreeBSD важнейшие общесистемные программы собирались так, что требуемые им библиотечные функции встраивались в их исполнимые файлы (так называемая статическая линковка, о которой речь пойдет в главе 14). Во FreeBSD 5-й ветки программы из каталогов /bin и /sbin линкуются динамически, то есть при отсутствии каталога /usr (а во Free это почти всегда отдельная ветвь файловой системы) они не функционируют. В компенсацию чего предусмотрен выходящий за рамки стандартов каталог /restore , содержащий те же программы, но слинкованные статически (как следует из имени каталога, единственным назначением его содержимого являются аварийно-спасательные работы).

И, наконец, /root . Это — обычный домашний каталог одноименного пользователя, сиречь администратора системы. Поскольку никакой практической работы он не делает (или, по крайней мере, делать не должен), содержимое его — лишь собственные конфигурационные файлы суперпользователя (пользовательской командной оболочки, любимого редактора и так далее).

Ветвь /usr

Исторически каталог /usr предназначался для пользовательских программ и данных. Ныне эти функции распределены между каталогами /usr/local и /home (хотя и сейчас во FreeBSD по умолчанию последний представляет собой символическую ссылку на /usr/home ). Каталог же /usr — не изменяемый, но разделяемый, — служит вместилищем основной части прикладных программ и всего, что к ним относится — исходных текстов, конфигурационных файлов, разделяемых библиотек, документации и тому подобного хозяйства.

Состав каталога /usr существенно различается в BSD-системах и в Linux. В первых в него помещаются только неотъемлемые части операционной системы (того, что во FreeBSD объединяется понятием Distributions). Приложения же, устанавливаемые из портов или пакетов, имеют место своей прописки подкаталог /usr/local , который может представлять отдельную ветвь файлового древа.

В Linux каталог /usr служит вместилищем всех программ (и их компонентов), штатно включенных в состав дистрибутива. А подкаталог /usr/local предназначается обычно для программ, самостоятельно собираемых из исходников.

В любом случае, обычный состав каталога /usr следующий (по выводу команды ls -1 ):

Как уже говорилось, подкаталог /usr/local — отдельная ветвь файлового древа, и потому будет рассмотрен отдельно же. Назначение же прочих каталогов таково:

  • /usr/bin и /usr/sbin предназначены для исполнимых файлов пользовательских и системных программ (здесь граница между ними еще более условна, чем в случае корневого каталога), назначение которых выходит за рамки обеспечения базового функционирования системы;
  • /usr/etc предназначается для конфигурационных файлов отдельных приложений;
  • /usr/include содержит так называемые заголовочные файлы, необходимые для линковки исполняемых файлов с библиотечными компонентами;
  • /usr/lib и /usr/libexec — каталоги для разделяемых библиотек, от которых зависят пользовательские приложения;
  • /usr/share — вместилище самых разнообразных, т.н. архитектурно независимых, компонентов: здесь можно видеть и документацию в разных форматах, и примеры конфигурационных файлов, и данные, используемые программами управления консолью (шрифты, раскладки клавиатуры), и описание часовых поясов;
  • /usr/src — каталог для исходных текстов; в Linux тут штатно помещаются только исходники ядра (ядер) системы, в BSD же клонах — полный набор исходников того комплекса, который во FreeBSD именуется Distributions; исходники самостоятельно собираемых программ помещать сюда, как правило, нежелательно;
  • /usr/X11R6 — каталог для компонентов оконной системы Икс — исполнимых файлов ( /usr/X11R6/bin ), библиотек ( /usr/X11R6/lib ), заголовков ( /usr/X11R6/include ), документации ( /usr/X11R6/man ); файлы Иксовых приложений сюда помещаться не должны (за исключением, разве что, оконных менеджеров) — их место в /usr , /usr/local или /opt , в зависимости от системы.

Кроме этого, в каталоге /usr могут обнаружиться подкаталоги /usr/var и /usr/tmp — обычно символические ссылки на соответствующие ветви корневого каталога. А в некоторых дистрибутивах Linux непосредственно в /usr помещается и основная общесистемная документация — man-страницы (в подкаталог /usr/man ).

Наконец, в BSD-системах и некоторых Source Based дистрибутивах Linux (например, Gentoo) в каталоге /usr размещается подкаталог для системы управления пакетами — портов FreeBSD и OpenBSD ( /usr/ports ), их аналогов в других системах ( /usr/portage в Gentoo). Хотя с точки зрения следования букве и духу стандарта FHS (сам он о портах и подобных системах не упоминает ни словом), более логичным местом их размещения был бы каталог /var (см. ниже) — и именно так делается в таких дистрибутивах, как CRUX и Archlinux.

Ветвь /usr/local

Как уже было сказано, ветвь /usr/local в Linux предназначена для самостоятельно собираемых из исходников (не входящих в данный дистрибутив) программ. А во FreeBSD она служит вместилищем большей части пользовательских приложений — почти всего того, что выходит за рамки Distributions и устанавливается из пакетов или портов. Соответственно этому, структура каталога в целом повторяет таковую ветви /usr (с понятными исключениями):

Содержимое подкаталогов также аналогично: исполнимые файлы программ ( /usr/local/bin и /usr/local/sbin ), их конфиги ( /usr/local/etc ), библиотеки, с которым они связаны, и их заголовочные файлы ( /usr/local/lib и /usr/local/include , соответственно), man-страницы ( /usr/local/man ) и всякая архитектурно независимая всячина ( /usr/local/share ), в том числе и документация в иных форматах.

Ветвь /opt

Каталог /opt предусмотрен стандартом FHS, но реально используется не во всех дистрибутивах Linux, а в BSD-системах и вовсе отсутствует. Тем не менее, все больше программ пишется в рассчете на умолчальную инсталляцию именно в него.

Исторически каталог /opt предназначался в Linux для коммерческих приложений и всякого рода программ не вполне свободного характера. Ныне же его назначение — размещение больших самодостаточных программных комплексов, таких, как библиотека Qt, KDE со всеми его компонентами и приложениями, OpenOffice.org и тому подобных. Структура каталога должна быть такой: /opt/pkg_name . Вот как выглядит она а в моей системе (Archlinux):

Каждый из подкаталогов имеет собственную внутреннюю структуру:

Назначение подкаталогов внутри /opt/pkg_name легко угадывается по аналогии с /usr и /usr/local . Например /opt/kde/bin предназначается для исполнимых файлов системы KDE и ее приложений, /opt/kde/etc — для конфигурационных ее файлов, /opt/kde/include — для файлов заголовков, /opt/kde/lib — для библиотек и /opt/kde/share — для разделяемых файлов, в том числе и документации. В KDE нет документации в man-формате, если же она имеется, то (как в случае Gnome — я его не ставил, это то, что потянули Gimp и тому подобные Gtk-приложения) можно видеть подкаталог /opt/pkg_name/man .

Можно видеть, что структура каталога /opt отступает от исторически сложившейся (и внутренне обоснованной POSIX-традиции объединения в каталоги однотипных компонентов — исполняемых файлов, библиотек и так далее. И при большом количестве инсталлированных в него программ создает определенные трудности: приходится либо перегружать значениями переменную $PATH , обеспечивающую быстрый доступ к командам (о чем будет говориться в главе 12), либо создавать специальный каталог /opt/bin и помещать в него символические ссылки на исполняемые бинарники программ. Поэтому в ряде дистрибутивов Linux (например, в CRUX) каталог /opt не используется принципиально. Как, впрочем, и во всех BSD-системах. Вполне возможно, что так оно и лучше…

Ветвь /var

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

Внутренняя структура /var очень сильно меняется от системы к системе, и поэтому на деталях ее устройства я задерживаться не буду. Замечу только, что этот каталог — логичное место для помещения компонентов всякого рода портообразных систем управления пакетами, как это сделано, например, в дистрибутиве Archlinux, где под нее отведен подкаталог /var/abs (abs — Archlinux Building System).

Каталог /mnt

Каталог /mnt предназначен для монтирования временно используемых файловых систем, располагающихся, как правило, на сменных носителях. В всежеустановленной системе он обычно пуст, и структура его никак не регламентирована. Пользователю вольно создать в нем подкаталоги для отдельных видов носителей. Например, в моей системе это /mnt/cd , /mnt/dvd , /mnt/usb и /mnt/hd — для дисков CD, DVD, флэшки и съемного винчестера.

Во FreeBSD штатными каталогами для монтирования CD и дискет являются /cdrom и /floppy непосредственно в корневом каталоге. Что не вполне согласуется со стандартом, но по своему логично — в корень вынесены точки монтирования устройств, существующих (как CD ROM) или до недавнего времени существовавших (флоппи-дисковод) в любой машине.

Ветвь /home

Каталог /home предназначен для помещения домашних каталогов пользователей. Содержимое его никак не регламентировано, но обычно он имеет вид вроде /home/ . Хотя в крупных системах с большим количеством пользователей их домашние каталоги могут быть объединены в группы.

В каталоге /home могут располагаться домашние каталоги не только реальных, но и некоторых виртуальных пользователей. Так, если машина используется в качестве web- или ftp-сервера, можно видеть такие подкаталоги, как /home/www или /home/ftp , соответственно.

Ветвь /tmp

Осталось поговорить только о каталоге для хранения временных файлов — /tmp . Как и компоненты /var , они генерируются различными программами в ходе нормальной их жизнедеятельности. Но, в отличие от /var , для компонентов /tmp не предполагается их сохранения вне текущего сеанса работы. Более того, все руководства по системному администрированию рекомендуют регулярно (например, при рестарте машины) или периодически очищать этот каталог. И потому в качестве /tmp целесообразно монтировать файловые системы в оперативной памяти — tmpfs (в Linux) или mfs (во FreeBSD). Кроме того, что это гарантирует очистку его содержимого при перезагрузке, так еще и способствует быстродействию, например, компиляции программ, временные продукты которой не записываются на диск, а помещаются в виртуальный каталог типа /tmp/obj .

Во многих системах можно увидеть каталоги вроде /usr/tmp и /var/tmp . Это, как правило, символические ссылки на /tmp .

Стратегия разделения файловых систем

В заключение разговора о файловой иерархии следует подчеркнуть, что гарантированно на одной файловой системе (фигурально говоря, на одном дисковом разделе, хотя это и не совсем точно) должны находиться только каталоги, перечисленные в параграфе Корневая файловая система. Все же прочие каталоги — /usr , /opt , /var , /tmp и, конечно же, /home могут представлять точки монтирования самостоятельных файловых систем на отдельных физических носителях или их разделах.

Более того, в локальной сети каталоги эти вполне могут располагаться даже на разных машинах. Так, один компьютер, выполняющий роль сервера приложений, может содержать разделяемые в сети каталоги /usr и /opt , другой — файл-сервер, — вмещать все домашние каталоги пользователей, и так далее.

Осталось только решить — какие файловые системы целесообразно вычленить из общего файлового древа, и зачем это делать. Ответ на эти вопросы очень сильно зависит от используемой ОС, а в случае с Linux — еще и от его дистрибутива. Тем не менее, общие принципы отделения файловых систем наметить можно. Для чего следует вспомнить о противопоставлении, с одной стороны, неизменяемых и изменяемых каталогов, с другой — легко восстановимых, трудно восстановимых и практически невосстановимых данных. Проделаем такую попытку применительно к пользовательскому десктопу — в случае сервера рассчеты будут существенно иными.

Очевидно, что корневая файловая система в составе каталогов /bin , /boot , /etc , /root , /sbin , содержащих легко восстановимые с дистрибутивного носителя и практически не изменяемые данные, должны лежать на изолированном дисковом разделе. В Linux к ним должен добавиться еще и каталог /lib . С другой стороны, при использовании в качестве загрузчика GRUB (вне зависимости от операционной системы) рекомендуется вынести на отдельный раздел каталог /boot .

В старых источниках о Linux можно прочитать о другом резоне к выделению раздела для каталога /boot , причем в самом начале диска: из-за невозможности загрузки ядра программой Lilo с цилиндра номером выше, чем 1023. В современных версиях загрузчиков таких ограничений нет. Тем не менее, если уж раздел под /boot создается, резонно сделать его первым на диске, а непосредственно за ним разместить раздел подкачки: это добавит пять копеек быстродействия при осуществлении своппинга.

И еще из области истории: требование, чтобы корневой и загрузочный разделы были непременно первичными, также давно снято. И эти файловые системы вполне могут помещаться на логических разделах внутри Extended Partition.

Столь же ясно, что изменяемые ветви файловой системы — каталоги /var и /tmp , — должны быть вынесены за пределы корневого раздела. Причем последний, как неоднократно говорилось ранее, вообще целесообразно разместить на файловой системе в оперативной памяти (tmpfs или mfs). В случае, если каталог /var содержит подкаталоги для портообразных систем пакетного менеджмента, подобно /var/abs , /var/cache/pacman/src и /var/cache/pacman/pkg в Archlinux, они также должны образовывать самостоятельные файловые системы

Теперь — каталог /usr , содержащий либо компоненты базовой системы (как в BSD), либо — основную массу пользовательских приложений (как в большинстве дистрибутивов Linux). Он содержит легковосстановимые данны и, по хорошему, должен бы быть практически неизменяемым, и потому, безусловно, заслуживает выделения на самостоятельном разделе. Причем из его состава целесообразно вычленить, с одной стороны, подкаталоги /usr/X11R6 и /usr/local , с другой — подкаталоги для портообразных систем пакетного менеджмента: /usr/ports , /usr/pkgsrc и /usr/pkg в BSD-системах, /usr/portages в Gentoo Linux, и так далее. Причем от последних следует обособить подкаталоги для помещения исходников, скачиваемых из сети при сборке портов — /usr/ports/distfiles , /usr/pkgsrc/disfiles , /usr/portages/distfiles и подобные им.

В BSD-системах, кроме этого, из каталога /usr имеет смысл выделить подкаталоги /usr/src и /usr/obj , содержащие исходные тексты базовых компонентов (включая ядро) и промежуточные продукты их компиляции, образумемые в результате процедур make buildworld и make buildkernel .

И, наконец, каталог /home , содержащий изменяемые и часто невосстановимые данные, подлежит вынесению из корня файловой иерархии в обязательном порядке. Причем я всегда стараюсь разместить его либо на отдельном слайсе (в BSD), либо на первичном разделе (в Linux).

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

Дополнительный ее плюс — в том, что для отдельных ветвей файлового древа, в зависимости от характера размещенных на ней данных, в Linux можно подобрать физически оптимальную файловую систему. Например, для раздела под /boot нет смысла использовать что-либо помимо Ext2fs. Корневой раздел обычно рекомендуется форматировать в надежной и при этом наиболее совместимой Ext3fs. Под каталоги с огромным количеством мелких файлов, такие, как /var/abs в Archlinux, /usr/portages в Gentoo, целесообразно задействовать ReiserFS: ведь умелое обращение с мелкими файлами — это ее профиль. А в каталоге /home , где возможно появление огромных мультимедийных файлов (и который сам по себе обычно очень велик), ко двору может прийтись XFS (хотя, как показывают измерения, и ReiserFS выглядит тут вполне достойно). Такие меры могут способствовать повышению и надежности хранения данных, и быстродействию файловых операций.

Пользователи BSD-операционок безальтернативно привязаны к файловым система типа FFS. Однако и у них есть пространство для маневра. Во-первых — за счет варьирования размеров блока и фрагмента отдельных файловых систем, способствующего либо производительности дисковых операций, либо экономии дискового пространства. А во-вторых, некоторые ветви файлового древа (такие, как /tmp или /usr/obj , вопреки рекомендациям, можно безбоязненно монтировать в чисто асинхронном режиме, выиграв на этом процент-другой производительности.

LXXIX. Функции POSIX

Этот модель содержит интерфейс к функциям, определённым в документации стандартов IEEE 1003.1 (POSIX.1), доступ к которым не может быть получен иным способом. POSIX.1, например, также определял функции open(), read(), write() и close(), которые традиционно были частью PHP 3 продолжительное время. Некоторые системно специфичные функции до этого не были доступны, и этот модуль делает попытку исправить это, давая простой доступ к данным функциям.

Чувствительные данные могут быть запрошены функциями POSIX ,например, posix_getpwnam() и компанией. Никакие POSIX-функции не выполняют проверку доступа, если safe mode включён. Поэтому настоятельно советуем вообще отключать расширение POSIX (используйте —disable-posix в вашем файле конфигурации), если вы работаете в таком окружении.

Примечание: расширение POSIX недоступно для платформы Windows.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL
Предупреждение!