Разработка приложений в среде Linux. Второе издание
Шрифт:
Пример может служить
Кроме
Глава 9
Системное окружение Linux
В этой главе рассматривается процесс запроса системных служб, включая низкоуровневые средства ядра и высокоуровневые возможности библиотек.
9.1. Окружение процесса
Как подробно описано в главе 10, в каждом выполняющемся процессе есть переменные окружения. Переменные окружения представляют собой пары "имя-значение", и некоторые из них представляют ценность для программистов на языке С. (Многие переменные в первую очередь используются при программировании оболочки, как ускоренные альтернативы запуску программ, вызывающих функции библиотек; в этой книге они описываться не будут.)
EDITOR или VISUAL | При установке EDITOR или VISUAL у пользователя появляется возможность выбирать текстовый редактор для редактирования текстового файла. Использование двух различных переменных объясняется тем, что когда-то EDITOR применялась для телетайпной машины, a VISUAL — для полноэкранного терминала. |
LD_LIBRARY_PATH | Обеспечивает разделенные двоеточиями пути к каталогам, в которых следует искать библиотеки. Обычно эту переменную устанавливать не нужно, поскольку в системном файле /etc/ld.so.conf есть вся необходимая информация. Модифицировать его в своих программах вряд ли придется; эта переменная предоставляет информацию для системного компоновщика времени выполнения, ld.so . Однако, как было описано в главе 8, LD_LIBRARY_PATH может оказаться полезной при разработке совместно используемых библиотек. |
LD_PRELOAD | Перечисляет библиотеки, которые должны быть загружены для переопределения символов в системных библиотеках. LD_PRELOAD , как и LD_LIBRARY_PATH , более подробно описана в главе 8. |
PATH | Предоставляет разделенный двоеточиями путь к каталогам, где следует искать исполняемые программы для запуска. Следует отметить, что в Linux (как и во всех вариантах Unix), в отличие от некоторых операционных систем, не принят автоматический поиск исполняемого файла в текущем каталоге. Для этого путь должен включать каталог . (точка). Более подробно о работе с этой переменной рассказывается в главе 10. |
TERM | Предоставляет информацию о типе терминала, установленного у пользователя; это определяет способ позиционирования символов на экране. Более подробно об этом читайте в главах 24 и 21. |
9.2. Системные вызовы
В этой книге практически повсеместно упоминаются системные вызовы, которые являются фундаментальными для программного окружения. На первый взгляд, они выглядят как обычные вызовы функций С. И это не случайно; они представляют собой специальную разновидность вызовов функций. Чтобы понять различия, нужно иметь общее представление о структуре операционной системы. Несмотря на то что операционная система Linux состоит из множества фрагментов кода (утилиты, библиотеки, приложения, программные библиотеки, драйверы устройств, файловые системы, управление памятью и так далее), все эти кодовые фрагменты работают в одном из двух контекстов: режиме пользователя или режиме ядра.
При разработку программы написанный код работает в режиме пользователя (user mode). Драйверы устройств и файловые системы, наоборот, работают в режиме ядра (kernel mode). В пользовательском режиме программы тщательно защищены от повреждений, вызванных их взаимодействием друг с другом или остальной частью системы. Код, работающий в режиме ядра, имеет полный доступ к компьютеру и может делать или же разрушать все, что угодно.
Драйвер, который разработан для управления и контроля аппаратного устройства, должен иметь полный доступ к нему. Устройство нужно защитить от случайных программ, чтобы они не смогли нарушить свою работу или работу друг друга, повредив само устройство. Память, с которой работает устройство, также защищена от случайных программ. Весь код, работающий в режиме ядра, существует исключительно для обслуживания кода, который работает в режиме пользователя. Системный вызов — это то, посредством чего код приложения, выполняющегося в пользовательском режиме, запрашивает службу, предоставляемую кодом, который выполняется в режиме ядра.
Возьмем, к примеру, процесс выделения памяти. Пользовательский процесс запрашивает память у защищенного кода, выполняющегося в режиме ядра, который и производит выделение физической памяти для процесса. В качестве следующего примера возьмем файловые системы, которые нуждаются в защите для поддержания целостности данных на диске (или в сети), но вашим повседневным, обычным процессам требуется считывать файлы из файловой системы.
Неприятные детали вызова через барьер пространств пользователь/ядро часто скрыты в библиотеке С. Процесс вызова через этот барьер не использует обычные вызовы функций; он использует неуклюжий интерфейс, оптимизированный по скорости и обладающий существенными ограничениями. Библиотека С скрывает большинство интерфейсов от глаз пользователя, предлагая взамен обычные функции С, которые являются оболочками для системных вызовов. Применение этих функций станет понятнее, если получить некоторое представление о том, что происходит внутри них.
9.2.1. Ограничения системных вызовов
Режим ядра защищен от влияния режима пользователя. Одна из таких защит состоит в том, что тип данных, передаваемых между режимами ядра и пользователя, ограничен, легко верифицируется и следует строгим соглашениям.
• Длина каждого аргумента, передаваемого из режима пользователя в режим ядра, практически всегда соответствует размеру слов, используемых машиной для представления указателей. Этого размера достаточно как для передачи указателей, так и длинных целых. Переменные типа
• Тип возвращаемого значения ограничен размером слова со знаком. Первые несколько сотен небольших отрицательных целых чисел зарезервированы в качестве кодов ошибок, и в пределах системных вызовов имеют одинаковое значение. Это значит, что системные вызовы, возвращающие указатель, не могут вернуть некоторые указатели, соответствующие адресам в верхней области доступной виртуальной памяти. К счастью, эти адреса находятся в зарезервированном пространстве и в любом случае никогда не возвращаются, потому возвращаемые слова со знаком могут быть без проблем преобразованы в указатели.
В отличие от соглашений С о вызовах, в котором структуры С могут передаваться по значению в стеке, нельзя передавать структуры по значению из пользовательского режима в режим ядра; точно также ядро не может вернуть структуру в пользовательский режим. Большие элементы данных можно передавать только по ссылке. Передавайте указатели на структуры, как всегда поступаете, когда их необходимо модифицировать.
9.2.2. Коды возврата системных вызов
Коды возврата, зарезервированные для всех системных вызовов — это универсальные коды возврата ошибок, представленные небольшими отрицательными числами. Библиотека С проверяет наличие ошибок каждый раз, когда происходит системный вызов. При возникновении ошибки библиотека помещает значение ошибки в глобальную переменную
15
Для многопоточных приложений библиотека хранит код ошибки там, где функция