Linux
программирование в примерах
Предисловие
Одним из лучших способов научиться программированию является чтение хорошо написанных программ. Данная книга обучает фундаментальному API системных вызовов Linux — тех, которые образуют ядро любой значительной программы — представляя код из программных изделий, которые вы используете каждый день.
Рассматривая конкретные программы, вы не только видите, как использовать Linux API, но можете также исследовать реальные проблемы (производительности, переносимости, устойчивости), которые возникают при написании программного обеспечения.
Хотя книга называется
Данная книга предназначена для лиц, разбирающихся в программировании и знакомых с основами С, по крайней мере на уровне книги Кернигана и Ричи
В частности, вам следует быть знакомыми со всеми операторами С, структурами управления потоком исполнения, использованием объявлений переменных и указателей, функциями работы со строками, использованием exit() и набором функций <stdio.h> для файлового ввода/вывода.
Вы должны понимать базовые концепции cd, cp, date, ln, ls, man (и info, если он у вас имеется), rmdir и rm, с использованием длинных и коротких опций командной строки, переменных окружения и перенаправления ввода/вывода, включая каналы.
Мы предполагаем, что вы хотите писать программы, которые работают не только под GNU/Linux, но и на множестве различных систем Unix. С этой целью мы помечаем каждый интерфейс с точки зрения его доступности (лишь для систем GLIBC или определен в POSIX и т.д.), а в тексте приведены также советы по переносимости.
Программирование, которое здесь приводится, может быть на более низком уровне, чем вы обычно использовали; это нормально. Системные вызовы являются основными строительными блоками для операций более высокого уровня и поэтому они низкоуровневые по своей природе. Это, в свою очередь, определяет использование нами С: функции API были спроектированы для использования из С, и код, связывающий их с языками более высокого уровня, такими как C++ и Java, неизбежно будет на более низком уровне и вероятнее всего, написанным на С. «Низкий уровень» не означает «плохой», это просто значит «более стимулирующий».
Данная книга фокусируется на базовых API, образующих ядро программирования под Linux:
• Управление памятью
• Файловый ввод/вывод
• Метаданные файлов
• Процессы и сигналы
• Пользователи и группы
• Поддержка программирования (сортировка, анализ аргументов и т.д.)
• Интернационализация
• Отладка
Мы намеренно сохранили список тем коротким. Мы считаем, что попытка научить а одной книге «всему, что можно узнать», пугает. Большинство читателей предпочитают книжки поменьше, более сфокусированные, и лучшие книги по Unix написаны таким способом
Поэтому вместо одного гигантского тома мы планируем несколько книг: одну по межпроцессному взаимодействию (IPC) и сетям, другую по разработке программного обеспечения и переносимости кода. Мы также положили глаз а направлении дополнительных томов в серии
Рассматриваемые нами API включают как системные вызовы, так и библиотечные функции. Действительно, на уровне С оба вида выступают в виде простых вызовов функций.
Нашей целью является научить вас использовать Linux API на примерах: в частности, посредством использования, где это возможно, как оригинальных исходных кодов Unix, так и инструментов GNU. К сожалению, самодостаточных примеров не так много, как должно было бы быть. Поэтому мы также написали большое число небольших демонстрационных программ. Был сделан акцент на принципах программирования: особенно на таких аспектах программирования для GNU, как «никаких произвольных ограничений», которые превращают инструменты GNU в незаурядные программы.
Выбор для изучения повседневных программ намеренный. Если вы уже использовали GNU/Linux в течение какого-либо периода времени, вы уже понимаете, что делают такие программы, как ls и cp; после этого просто погрузиться прямо в то, как работают программы, не тратя много времени на изучение того, что они делают.
Иногда мы представляем как высокоуровневый, так и низкоуровневый способы выполнения задачи. Обычно стандарт интерфейса более высокого уровня реализуется посредством более низкоуровневого интерфейса или конструкции. Мы надеемся, что такой взгляд на то, что происходит «под капотом», поможет вам понять, как это работает; для всего кода, который вы пишете сами, нужно всегда использовать более высокоуровневый, стандартный интерфейс.
Таким же образом иногда мы представляем функции, которые предоставляют определенные возможности, а затем рекомендуем (по указанной причине) избегать этих функций! Главной причиной такого подхода является то, что вы получаете возможность узнавать эти функции при встрече и понимать код с их использованием. Всеобъемлющее знание темы требует понимания не только того, что вы можете сделать, но и того, что должны или не должны делать.
Наконец, каждая глава завершается упражнениями. Некоторые из них требуют модификации или написания кода. Другие больше относятся к категориям «Мысленных экспериментов» или «как вы думаете, почему…». Мы рекомендуем выполнить их все — они помогут закрепить понимание материала.
Закон Хоара: «Внутри каждой большой программы есть старающаяся пробиться маленькая программа»
Вначале мы планировали обучать Linux API, используя код инструментов GNU. Однако, современные версии даже простых программ командной строки (подобно mv и cp) большие и многофункциональные. Это особенно верно в отношении GNU вариантов стандартных утилит, которые допускают длинные и короткие опции, делают все, требуемое POSIX и часто имеют также дополнительные, внешне не связанные опции (подобно выделению вывода).
Поэтому возник разумный вопрос: «Как мы можем в этом большом и запутывающем лесу сконцентрироваться на одном или двух важных деревьях?» Другими словами, если мы представим современные полнофункциональные программы, будет ли возможно увидеть лежащую в основе работу программы?
Вот когда
В течение многих лет исходный код Unix был защищен авторскими правами и лицензионными соглашениями коммерческой тайны, что затрудняло его использование для обучения и делало невозможным опубликование. Это до сих пор верно в отношении исходного кода всех коммерческих систем Unix. Однако в 2002 г. Caldera (в настоящее время работающая под именем SCO) сделала оригинальный код Unix (вплоть до V7 и 32V Unix) доступным на условиях лицензии в стиле Open Source (см. приложение В «Лицензия Caldera для старой Unix»). Это дает нам возможность включить в эту книгу код из ранних систем Unix.
По всей книге мы ссылаемся на несколько различных официальных стандартов. Стандарт является документом, описывающим, как что-либо работает. Официальные стандарты существуют для многих вещей, например, форма, размещение и назначение отверстий в электрической розетке на вашей стене определяется официальным стандартом, так что все сетевые шнуры в вашей стране работают со всеми розетками.
Таким же образом официальные стандарты для вычислительных систем определяют, как они должны работать; это дает возможность пользователям и разработчикам знать, чего ожидать от своего программного обеспечения, и дает им возможность жаловаться своему поставщику, когда программное обеспечение не работает.
Здесь интерес для нас представляют:
1.
2.
3.
4.
5.
•
•
•
•
Хотя стандарты языков не являются захватывающим чтением, можно рассмотреть покупку экземпляра стандарта С, он дает окончательное определение языка. Книги можно приобрести в ANSI[2] и в ISO[3]. (PDF-версия стандарта С вполне доступна.)
Стандарт POSIX можно заказать в The Open Group[4]. Исследуя в каталоге их изданий элементы, перечисленные а «Спецификациях CAE» («CAE Specifications»), вы можете найти отдельные страницы для каждой части стандарта (озаглавленные с «C031» по «C034»). Каждая такая страница предоставляет свободный доступ к HTML версии определенного тома
Стандарт POSIX предназначен для реализации как Unix и Unix-подобных систем, так и не-Unix систем. Таким образом, базовые возможности, которые он предоставляет, составляют лишь часть возможностей, которые есть на системах Unix. Однако, стандарт POSIX определяет также
По всей книге мы помечаем каждый API в отношении его доступности: ISO С, POSIX, XSI, только GLIBC или как нестандартный, но широко доступный.
Ограничив себя лишь оригинальным кодом Unix, можно было бы получить интересную историческую книгу, но она была бы не очень полезна в XXI веке. Современные программы не имеют тех же ограничений (памяти, мощности процессора, дискового пространства и скорости), которые были у ранних систем Unix. Более того, они должны работать в многоязычном мире — ASCII и американского английского недостаточно.
Что еще важнее, одной из главных свобод, выдвинутых явным образом Фондом бесплатных программ (Free Software Foundation) и проектом GNU[5], является «свобода обучения». Программы GNU предназначены для обеспечения большого собрания хорошо написанных программ, которые программисты среднего уровня могут использовать а качестве источника для своего обучения.
Используя программы GNU, мы преследуем две цели: показать вам хорошо написанный современный код, на котором вы будете учиться писать хорошие программы, а также использовать API.
Мы считаем, что программное обеспечение GNU лучше, поскольку оно свободно (в смысле «свободы», а не «бесплатного пива»)[6]. Но признается также, что программное обеспечение GNU часто также
Часть примеров кода GNU происходит из gawk (GNU awk). Главной причиной этого является то, что это программа, с которой мы очень знакомы, поэтому было просто отобрать оттуда примеры. У нас нет относительно нее других притязаний.
Вождение автомобиля целостный процесс, включающий множество одновременных задач. Во многих отношениях программирование по Linux похоже на это, требуя понимания многих аспектов API, таких, как файловый ввод/вывод, метаданные файлов, каталоги, хранение сведений о времени и т.д.
В первой части книги рассматривается достаточное количество этих отдельных элементов, чтобы дать возможность изучить первую значительную программу, V7 ls. Затем мы завершим наше обсуждение файлов и пользователей, рассмотрев иерархии файлов и принципы работы файловых систем и их использование.
описывает модели файлов и процессов Unix и Linux, рассматривает отличия оригинального С от стандартного С 1990 г., а также предоставляет обзор принципов, которые делают программы GNU в целом лучшими по сравнению со стандартными программами Unix.
описывает, как С программа получает доступ к аргументам и опциям командной строки и обрабатывает их, а также объясняет, как работать с переменными окружения.
предоставляет обзор различных видов используемой памяти, доступной работающему процессу. Управление памятью на уровне пользователя является основным для каждого нетривиального приложения, поэтому важно понять это с самого начала.
обсуждает базовый файловый ввод/вывод, показывая, как создавать и использовать файлы. Понимание этого важно для всего последующего материала.
описывает, как работают каталоги, прямые и символические ссылки. Затем описываются служебные данные файлов, такие, как владелец, права доступа и т.д., а также освещается работа с каталогами
рассматривает первый набор интерфейсов общего программирования, которые будут нам нужны для эффективного использования служебных данных файлов.
связывает воедино все рассмотренное до сих пор, рассматривая программу V7 ls.
описывает, как монтируются и демонтируются файловые системы и как программа может получить сведения о том, что смонтировано в системе. В главе описывается также, как программа может легко «обойти» всю иерархию файлов, предпринимая а отношении каждого встреченного объекта необходимые действия.
Вторая часть книги имеет дело с созданием и управлением процессами, межпроцессным взаимодействием посредством каналов и сигнала, ID пользователей и групп и дополнительными интерфейсами общего программирования. Далее в книге сначала описываются интернационализация с использованием GNU gettext, а затем несколько расширенных API.
рассматривает создание процесса, исполнение программы, межпроцессное взаимодействие (IPC) с использованием каналов и управление дескрипторами файлов, включая неблокирующий ввод/вывод.
обсуждает сигналы, упрощенную форму межпроцессного взаимодействия. Сигналы играют также важную роль а управлении родительским процессом порожденных процессов.
рассматривает, как идентифицируются процессы и файлы, как работает проверка прав доступа и как работают механизмы setuid и setgid.
рассматривает оставшуюся часть общих API; многие из них более специализированы, чем первый общий набор API.
объясняет, как почти без усилий обеспечить работу ваших программ на нескольких языках.
описывает несколько расширенных версий интерфейсов, освещенных в предыдущих главах, а также более подробно освещает блокировку файлов.
Мы завершаем книгу главой об отладке, поскольку (почти) никто не может сделать все правильно с первого раза, и рекомендуем финальный проект для закрепления ваших знаний по рассмотренным в данной книге API.
описывает основы отладчика GDB, передает как можно больше нашего опыта в этой области и рассматривает несколько полезных инструментов для осуществления различных видов отладки.
представляет значительный проект по программированию, который использует почти все, рассмотренное в книге.
Несколько приложений освещают представляющие интерес темы, включая лицензии для использованного в данной книге исходного коде.
ссылается на знаменитое высказывание: «Москва не сразу строилась»[7]. Также и квалификация в Linux/Unix и понимание этих систем приходит лишь со временем и практикой. С этой целью мы включили это эссе Петера Норвига, которое мы горячо рекомендуем.
охватывает исходный код Unix, использованный в данной книге.