Linux API. Исчерпывающее руководство
От автора
Приветствую вас, читателей русскоязычного издания моей книги The Linux Programming Interface.
В этой книге представлено практически полное описание API системного программирования под управлением Linux. Ее содержимое применимо к широкому диапазону Linux-платформ, начиная с обычных серверов, универсальных компьютеров и настольных систем и заканчивая большим разнообразием встроенных устройств (в том числе работающих под управлением операционной системы Android), на которых в настоящее время запускается ОС Linux.
Англоязычное издание этой книги вышло в конце 2010 года. С того времени было выпущено несколько обновлений ядра Linux (их было примерно по пять за год). Несмотря на это, содержимое оригинала книги, а следовательно, и данного перевода, не утратило актуальности и сохранит ее еще на долгие годы. Тому есть две причины.
Во-первых, несмотря на стремительность разработки ядра Linux, API, связанный с пользовательским пространством ядра, изменяется гораздо медленнее. Такая консервативность — естественное следствие того факта, что ядро разработано с целью обеспечить
Во-вторых, изменения вносятся в виде
В заключение хочу отметить: я очень горжусь тем, что моя книга будет переведена на другой язык. Перевод на русский стал для меня особенно приятным сюрпризом, поскольку в детстве я пытался выучить этот язык по книгам. (К сожалению, отсутствие педагога или русскоговорящего окружения не позволили мне существенно преуспеть в этом начинании.) Перевод текста объемом 1250 страниц — задача не из легких, и я благодарен издателю и команде переводчиков. Надеюсь, что результаты нашей усердной работы и усилий множества других людей, помогавших выпустить в свет оригинал, окажутся весьма полезными для sвас, читателей этого русскоязычного издания.
Предисловие
Цель книги
В этой книге я описываю программный интерфейс операционной системы Linux: системные вызовы, библиотечные функции и другие низкоуровневые интерфейсы, которые есть в Linux — свободно распространяемой реализации UNIX. Эти интерфейсы прямо или косвенно используются каждой программой, работающей в Linux. Они позволяют приложениям выполнять следующие операции:
• файловый ввод/вывод;
• создание и удаление файлов и каталогов;
• создание новых процессов;
• запуск программ;
• установку таймеров;
• взаимодействие между процессами и потоками на одном компьютере;
• взаимодействие между процессами, запущенными на разных компьютерах, объединенных посредством сети.
Такой набор низкоуровневых интерфейсов иногда называют
Несмотря на то что основное внимание уделяется операционной системе Linux, подробно рассмотрены также стандарты и вопросы, связанные с портируемостью. Я четко разграничиваю темы, специфичные для Linux, и функциональные возможности, типичные для большинства реализаций UNIX и описанные в стандарте POSIX, а также в спецификации Single UNIX Specification. Таким образом, эта книга предлагает всеобъемлющее рассмотрение программного интерфейса UNIX/POSIX и ее могут использовать программисты, которые создают приложения, предназначенные для других UNIX-систем, или портируемые программы.
Для кого эта книга
Книга предназначена главным образом для такой аудитории, как:
• программисты и разработчики программного обеспечения, создающие приложения для Linux, других UNIX-систем или иных систем, совместимых со стандартом POSIX;
• программисты, выполняющие портирование приложений из Linux в другие реализации UNIX или из Linux в другие операционные системы (ОС);
• преподаватели и заинтересованные студенты, которые преподают или изучают программирование для Linux или для других UNIX-систем;
• системные администраторы и «продвинутые» пользователи, которые желают тщательнее изучить программный интерфейс Linux/UNIX и понять, каким образом реализованы различные части системного ПО.
Предполагается, что у вас есть какой-либо опыт программирования, при этом опыт системного программирования необязателен. Я также рассчитываю на то, что вы разбираетесь в языке программирования C и знаете, как работать в оболочке и пользоваться основными командами Linux или UNIX. Если вы впервые сталкиваетесь с Linux или UNIX, вам будет полезно прочесть главу 2 — в ней приводится обзор основных понятий Linux и UNIX, ориентированный на программиста.
Стандартным справочным руководством по языку C является книга [Kernighan & Ritchie, 1988]. В книге [Harbison & Steele, 2002] этот язык рассмотрен более подробно, а также приведены изменения, появившиеся в стандарте C99. Издание [van der Linden, 1994] дает альтернативное рассмотрение языка С, очень увлекательное и толковое. В книге [Peek et al., 2001] содержится хорошее краткое введение в работу с системой UNIX.
Linux и UNIX
Эта книга могла бы быть полностью посвящена системному программированию в стандарте UNIX (то есть POSIX), поскольку многие функции, которые можно найти в других реализациях UNIX, присутствуют также в Linux и наоборот. Тем не менее, поскольку создание портируемых приложений — одна из основных задач, важно также описать и особенности Linux, которые расширяют стандартный программный интерфейс UNIX. Одной из причин является популярность Linux. Другая причина состоит в том, что иногда необходимо применять нестандартные расширения: либо из соображений производительности, либо для достижения функциональности, недоступной в стандартном программном интерфейсе UNIX. (По этим причинам все реализации UNIX снабжены нестандартными расширениями.)
Таким образом, хотя я задумал эту книгу так, чтобы она была полезна для программистов, работающих со всеми реализациями UNIX, я также привожу полное описание программных средств, характерных для Linux. К ним относятся следующие:
• интерфейс epoll, который позволяет получать уведомления о событиях файлового ввода-вывода;
• интерфейс inotify, позволяющий отслеживать изменения файлов и каталогов;
• мандаты (возможности) (capabilities) — позволяют предоставить какому-либо процессу часть прав суперпользователя;
• расширенные атрибуты;
• флаги индексного дескриптора;
• системный вызов clone();
• файловая система /proc;
• характерные для Linux особенности реализации файлового ввода-вывода, сигналов, таймеров, потоков, совместно используемых (общих) библиотек, межпроцессного взаимодействия и сокетов.
Структура книги
Вы можете использовать эту книгу по меньшей мере двумя способами.
• В качестве вводного руководства в программный интерфейс Linux/UNIX. Можно читать книгу от начала до конца. Главы из второй половины издания основаны на материале, изложенном в главах первой половины; ссылки на более поздний материал по возможности сведены к минимуму.
• В качестве всеобъемлющего справочника по программному интерфейсу Linux/UNIX. Расширенное оглавление и обилие перекрестных ссылок позволяют читать книгу в произвольном порядке.
Главы этой книги сгруппированы следующим образом.
1.
2.
3.
4.
5.
6.
7.
8.
Примеры программ
Как использовать большинство интерфейсов, описанных в этой книге, я поясняю с помощью коротких готовых программ. Многие из них позволят вам с легкостью поэкспериментировать с командной строкой, чтобы увидеть, как работают различные системные вызовы и библиотечные функции. Таким образом, книга содержит довольно много программного кода с примерами — около 15 000 строк на языке C — а также фрагменты сессий работы в оболочке.
Для начала неплохо разобрать примеры программ и поэкспериментировать с ними, но усвоить понятия, изложенные в этой книге, эффективнее всего можно, написав код. Либо изменяя предложенные примеры для реализации ваших идей, либо создавая новые программы.
Весь исходный код доступен для загрузки с сайта англоязычного издания этой книги: http://www.man7.org/tlpi.
В архив включены также дополнительные программы, которых нет в издании. Назначение и подробности работы этих программ описаны в комментариях к программному коду. Для сборки программ приложены файлы makefiles, а в сопроводительных файлах README приводятся дополнительные подробности о программах.
Предоставляемый исходный код является свободно распространяемым, и его можно изменять при условии соблюдения лицензии GNU Affero General Public License (Affero GPL) version 3 («Общедоступная лицензия GNU Affero, 3-я версия»), текст которой присутствует в архиве с исходным кодом.
На указанном сайте вы также найдете дополнительную информацию об этой книге.
Упражнения
Большинство глав завершаются набором упражнений. В одних из них мы предлагаем вам поэкспериментировать с приведенными примерами программ. Другие упражнения — это вопросы по темам, рассматриваемым в главе. Среди них также есть указания по написанию программ, которые могли бы помочь вам в усвоении материала.
Стандарты и портируемость
В этой книге особое внимание я уделил вопросам портируемости. Вы обнаружите немало ссылок на соответствующие стандарты, в особенности на объединенный стандарт POSIX.1-2001 и Single UNIX Specification version 3 (SUSv3). Кроме того, я привожу подробные сведения об изменениях в недавней версии — объединенном стандарте POSIX.1-2008 и SUSv4. (Поскольку стандарт SUSv3 гораздо обширнее и является стандартом UNIX с наибольшим влиянием (на момент написания книги), в этом руководстве при рассмотрении стандартов я, как правило, опираюсь на SUSv3, добавляя примечания об отличиях в SUSv4. Тем не менее можно рассчитывать на то, что, за исключением указанных случаев, утверждения о спецификациях в стандарте SUSv3 действуют и в SUSv4.)
Рассказывая о функциях, которые не стандартизированы, я привожу перечень отличий для других реализаций UNIX. Я также особо отмечаю те главные функции Linux, которые характерны именно для этой ОС, а заодно выделяю небольшие различия в реализации системных вызовов и библиотечных функций Linux и других UNIX-систем. В тех случаях, когда о какой-либо функции не говорится как о специфичной для Linux, можете считать, что она является стандартной и присутствует в большинстве или во всех реализациях UNIX.
Я протестировал работу большинства программ, приведенных в этой книге (за исключением тех, что используют функции, отмеченные как специфичные для Linux), в некоторых или во всех этих системах: Solaris, FreeBSD, Mac OS X, Tru64 UNIX и HP-UX. Для улучшения портируемости программ в некоторые из этих систем на сайте http://www.man7.org/tlpi приводятся альтернативные версии большинства примеров с дополнительным кодом, которого нет в этой книге.
Ядро Linux и версии библиотеки C
Основной акцент этой книги сделан на версии Linux 2.6.x, ядро которой было наиболее популярно на момент написания книги. Подробности версии 2.4 также описаны, причем отмечено, чем различаются функции в версиях Linux 2.4 и 2.6. В тех случаях, когда новые функции введены в серии релизов Linux 2.6.x, указана точная версия ядра, в которой они появились (например, 2.6.34).
Что касается библиотеки C, основное внимание уделено GNU-библиотеке C (glibc) 2-й версии. Там, где это важно, приведены различия между версиями glibc 2.x.
Когда это издание готовилось к печати, было выпущено ядро Linux версии 2.6.35, а версия glibc 2.12 уже появилась. Книга написана применительно к этим версиям программного обеспечения. Изменения, которые появились в интерфейсах Linux и в библиотеке glibc после публикации этой книги, будут отмечены на сайте.
Использование программного интерфейса других языков программирования
Хотя примеры программ написаны на языке C, вы можете применять рассмотренные в этой книге интерфейсы из других языков программирования, в частности компилируемых языков C++, Pascal, Modula, Ada, FORTRAN, D, а также таких языков сценариев, как Perl, Python и Ruby. (Для Java необходим другой подход; см., например, работу [Rochkind, 2004].) Понадобятся иные приемы для того, чтобы добиться необходимых определений констант и объявлений функций (за исключением языка C++). Может также потребоваться дополнительная работа для передачи аргументов функции в том стиле, которого требуют соглашения о связях в языке С. Но, несмотря на эти различия, основные понятия не меняются, и вы обнаружите, что информация из этого руководства применима даже при работе с другим языком программирования.
Об авторе
Я начал работать в UNIX и на языке С в 1987 году, когда провел несколько недель за рабочей станцией HP Bobcat, имея при себе первое издание книги Марка Рохкинда «Эффективное UNIX-программирование» (
Сначала я работал инженером-программистом и разработчиком ПО. В то же время мне очень нравится преподавание, и несколько лет я занимался им как в академической, так и в бизнес-среде. Я провел множество недельных курсов по обучению системному программированию UNIX, и этот опыт вдохновил меня на написание книги.
Я проработал в Linux почти в два раза меньше, чем в UNIX, но за это время мои интересы еще больше сфокусировались на границе между ядром и пространством пользователя — на программном интерфейсе Linux. Это вовлекло меня в несколько взаимосвязанных видов деятельности. Время от времени я предоставлял исходную информацию и отчеты об ошибках для стандарта POSIX/SUS, выполнял тестирование и экспертную оценку новых интерфейсов пространства пользователя, добавленных к ядру Linux (и помог обнаружить и исправить множество ошибок в коде и дизайне этих интерфейсов). Я также регулярно выступал на конференциях, посвященных интерфейсам и связанной с ними документации. Меня приглашали на несколько ежегодных совещаний Linux Kernel Developers Summit (саммит разработчиков ядра Linux). Общей нитью, которая связывает все эти виды деятельности воедино, является мой наиболее заметный вклад в мир Linux — работа над проектом man-pages (http://www.kernel.org/doc/man-pages/).
Названный проект лежит в основе страниц руководства Linux в разделах 2–5 и 7. Эти страницы описывают программные интерфейсы, которые предоставляются ядром Linux и GNU-библиотекой C, — тот же охват тем, что и в этой книге. Я занимался проектом man-pages более десяти лет. Начиная с 2004 года я сопровождаю его. В эту задачу входят приблизительно в равных долях написание документации, изучение исходного кода ядра и библиотеки, а также создание программ для проверки деталей. (Документирование интерфейса — прекрасный способ обнаружить ошибки в этом интерфейсе.) Кроме того, я самый продуктивный участник проекта man-pages: он содержит около 900 страниц, 140 из них написал я один и 125 — в соавторстве. Поэтому весьма вероятно, что вы уже читали что-либо из моих публикаций еще до того, как приобрели эту книгу. Надеюсь, что информация вам пригодилась, и также надеюсь, что эта книга окажется еще более полезной.
Благодарности
Без поддержки огромного количества людей эта книга не стала бы такой, какая она есть. С великим удовольствием благодарю их.
Научные редакторы из разных стран, как одна большая команда, читали черновые варианты, отыскивали ошибки, указывали на нечеткие объяснения, предлагали перефразированные варианты и схемы, тестировали программы, снабжали упражнениями, выявляли особенности работы Linux и других реализаций UNIX, которые были мне неизвестны, а также поддерживали и воодушевляли меня. Многие эксперты щедро поделились ценной информацией, которую мне удалось включить в эту книгу. Благодаря этим дополнениям возникает впечатление о моей большой осведомленности, хотя на самом деле это не так. Все ошибки, которые присутствуют в книге, конечно же, остаются на моей совести.
Особо благодарю следующих специалистов, которые развернуто прокомментировали различные фрагменты рукописи.
• Кристоф Блэсс (Christophe Blaess) является программистом-консультантом и профессиональным преподавателем. Специализируется на производственных (в реальном времени и встраиваемых) приложениях на основе Linux. Он автор замечательной книги на французском языке
• Дэвид Бутенхоф (David Butenhof) из компании Hewlett-Packard — участник самой первой рабочей группы по потокам POSIX, а также по расширениям стандарта Single UNIX Specification для потоков. Он автор книги
• Джеф Клэр (Geoff Clare) занят в проекте The Open Group разработкой комплексов тестирования на соответствие стандартам UNIX. Он уже более 20 лет принимает участие в разработке стандартов UNIX и является одним из немногих ключевых участников группы Austin Group, которая создает объединенный стандарт, образующий спецификацию POSIX.1 и основные части спецификации Single UNIX Specification. Джеф тщательно проверил части рукописи, относящиеся к стандартным интерфейсам UNIX, терпеливо и вежливо предлагая свои исправления и улучшения. Он выявил малозаметные ошибки в коде и помог сосредоточиться на важности следования стандартам при создании портируемых программ.
• Лоик Домэнье (Loic Domaigne), работавший в немецкой авиадиспетчерской службе, — разработчик программных комплексов, который проектирует и создает распределенные параллельные отказоустойчивые встроенные системы с жесткими требованиями работы в реальном времени. Он предоставил обзорный вводный материал для спецификации потоков в стандарте SUSv3. Лоик — замечательный преподаватель и эрудированный участник различных технических онлайн-форумов. Он тщательно проверил главы о потоках, а также многие другие разделы книги. Он также написал несколько хитроумных программ для проверки особенностей реализации потоков в Linux, а также предложил множество идей по улучшению общей подачи материала.
• Герт Деринг (Gert Doring) написал программы mgetty и sendfax — наиболее популярные свободно распространяемые пакеты для работы с факсами в UNIX и Linux. В настоящее время он главным образом работает над созданием обширных сетей на основе протоколов IPv4 и IPv6 и управлением ими. Эта деятельность включает в себя сотрудничество с коллегами по всей Европе с целью определения рабочих политик, которые обеспечивают надежную работу инфраструктуры Интернета. Герт дал немало ценных советов по главам, описывающим терминалы, учетные записи, группы процессов, сессии и управление задачами.
• Вольфрам Глоджер (Wolfram Gloger) — ИТ-консультант, который последние 15 лет нередко участвовал в различных проектах FOSS (Free and Open Source Software, свободно распространяемое ПО и ПО с открытым исходным кодом). Среди прочего, Вольфрам является разработчиком пакета malloc, который используется в GNU-библиотеке C. В настоящее время он разрабатывает веб-сервисы для дистанционного обучения, но иногда все так же занимается ядром и системными библиотеками. Вольфрам проверил несколько глав и особенно помог мне при рассмотрении вопросов, относящихся к памяти.
• Фернандо Гонт (Fernando Gont) — сотрудник центра CEDI (Centro de Estudios de Informatica) при аргентинском университете Universidad Tecnologica Nacional. Он занимается в основном интернет-разработками и активно участвует в работе сообщества IETF (Internet Engineering Task Force, Инженерный совет Интернета), для которого он написал несколько рабочих предложений. Фернандо также занят оценкой безопасности коммуникационных протоколов в британском центре CPNI (Centre for the Protection of National Infrastructure, Центр защиты государственной инфраструктуры). Он впервые выполнил всестороннюю оценку безопасности протоколов TCP и IP. Фернандо очень тщательно проверил главы, посвященные сетевому программированию, объяснил множество особенностей протоколов TCP/IP, а также предложил немало улучшений.
• Андреас Грюнбахер (Andreas Grunbacher) — специалист по ядру и автор реализации расширенных атрибутов в Linux, а также списков контроля доступа в стандарте POSIX. Андреас тщательно проверил многие главы, оказал существенную поддержку, а один из его комментариев значительно повлиял на структуру книги.
• Кристоф Хельвиг (Christoph Hellwig) является консультантом по хранению данных в Linux и по файловым системам, а также экспертом по ядру, многие части которого он сам и разрабатывал. Кристоф любезно согласился на некоторое время отвлечься от написания и проверки обновлений для ядра Linux, чтобы просмотреть пару глав этой книги и дать много ценных советов.
• Андреас Егер (Andreas Jaeger) руководил портированием Linux в архитектуру x86-64. Будучи разработчиком GNU-библиотеки C, он портировал эту библиотеку и сумел добиться ее соответствия стандартам в различных областях, особенно в ее математической части. В настоящее время он является менеджером проектов openSUSE в компании Novell. Андреас проверил намного больше глав, чем я рассчитывал, предложил множество улучшений и воодушевил на дальнейшую работу с книгой.
• Рик Джонс (Rick Jones), который известен также как «Мистер Netperf» (Networked Systems Performance Curmudgeon (буквально — «старый ворчун на тему производительности сетевых систем») в компании Hewlett-Packard), дотошно проверил главы о сетевом программировании.