<<
>>

Общие заголовочные файлы

В каталоге include/ и его подкаталогах содержится набор файлов с общими константами, типами данных и макроопределениями. Большинство этих определений обусловлены стандартом POSIX, который указывает, какое определение в каком файле каталога include/ или его подкаталоге inc lude /sys/ находится.
Файлы, которые присутствуют в этих каталогах, называются заголовочными, или включаемыми, и имеют расширение .h. Подключаются они директивой # inc lude языка С. Благодаря заголовочным файлам упрощается поддержка большой системы.

Для компиляции пользовательских программ требуются по большей части заголовочные файлы из каталога include/, а файлы из каталога include/sys/ традиционно нужны для компиляции системных утилит. Но это — не слишком важное ограничение, и в типичной программе, будь то пользовательская программа или часть системы, задействуются файлы из обоих каталогов. Здесь мы обсудим файлы, необходимые для компиляции MINIX 3, сначала те из них, которые находятся в каталоге include/, а затем — из каталога include/sys/.

В следующем разделе мы рассмотрим файлы в каталогах include/minix/ и include/ ibm/, которые соответственно содержат код, специфичный для системы MINIX 3 и ее реализации на IBM-совместимых компьютерах.

Файлы в каталоге include/ в действительности предназначены для решения общих задач, поэтому в большинство модулей с исходным кодом системы они не включаются. Вместо этого они добавляются в другие заголовочные файлы, например в главный заголовочный файл src/kernel/kernel .h, файлы src/mm/mm.h и src/fs/fs.h, подключаемые при каждой сборке системы. Главные заголовочные файлы каждой из трех составных частей системы служат каждый для своих целей, но начало у всех них одинаковое, оно приведено в листинге 2.12. Главные заголовочные файлы еще будут обсуждаться в этой книге, цель данного обзора — только подчеркнуть, что заголовочные файлы из различных каталогов используются совместно.

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

Листинг 2.12. Часть основного заголовка, подключающая все заголовочные файлы, нужные в коде. Обратите внимание на два файла const.h, один из которых находится в каталоге include/, второй — в локальном каталоге

"const.h"

Начнем с первого из файлов в каталоге include/ — ansi.h (строка 0000). Это — второй заголовочный файл, обрабатываемый при компиляции любой из частей MINIX 3 — лишь файл include/minix/config.h обрабатывается раньше. Назначение ansi.h — удостовериться, что компилятор соответствует требованиям стандарта языка С, который определяется Международной организацией по стандартизации. Этот стандарт иногда называется также ANSI С, поскольку изначально, до международного признания, он разрабатывался Американским национальным институтом стандартов (American National Standard Institute, ANSI). Соответствующий стандарту компилятор должен определять несколько макросов, пригодных к использованию в компилируемых программах. Например, макрос STDC у «правильного» компилятора должен иметь зна

чение 1, как если бы препроцессор С получил строку

#define STDC_ 1

Сейчас поставляемый с MINIX 3 компилятор удовлетворяет стандарту, но более старые версии MINIX были разработаны до его принятия, поэтому MINIX 3 все еще можно скомпилировать классическим компилятором С Кернигана и Ритчи. MINIX создавалась как легко переносимая система, и возможность привлекать старые компиляторы — важная составная часть задуманного. Следующая команда в строках 0023 и 0025 обрабатывается в том случае, если применяется стандартный компилятор:

#define _ANSI

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

Еще один определенный здесь макрос проверки поддерживаемых функций — _POSlx_SOURCE (строка 0065). Его наличие требуется согласно стандарту POSIX. Мы проверяем определение макроса _P0SIX_S0URCE в случае, если определены другие макросы стандарта POSIX.

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

Наиболее важным макросом в файле ansi .h является „PROTOTYPE. Он позволяет записывать прототип функции в таком виде:

_PROTOTYPE(тип-результата, имя-функции, (тип-аргумента аргумент, ...))

Если компилятор соответствует стандарту, препроцессор приводит эту запись к следующему виду:

тип-результата имя-функции (тип-аргумента аргумент, ...)

Если же компилятор более старый, макрос транслируется так:

тип-результата имя-функции ()

Прежде чем переходить от ansi .h к другим файлам, обратим внимание еще на один момент. Все содержимое файла (кроме начальных комментариев) обрамлено следующими строками:

#ifndef _ANSI_H #endif /*_ANSI_H*/

Сразу после строки #ifndef _ANSI_H определяется сам макрос _ANSI_H. Назначение этой конструкции состоит в том, чтобы убедиться, что заголовочный файл будет включен только один раз. При повторном включении все его содержимое игнорируется. Подобная техника используется во всех файлах из каталога include/.

Здесь нужно пояснить два момента. Во-первых, во всех последовательностях #ifndef . . . # de fine в главных заголовочных каталогах имена файлов предваряются подчеркиванием. В каталогах исходных С-кодов может присутствовать другой заголовочный файл с тем же именем; к нему будет применен тот же механизм, однако без подчеркивания. Таким образом, включение файла из главного заголовочного каталога не воспрепятствует обработке одноименного заголовочного файла цз локального каталога.

Во-вторых, обратите внимание на комментарий /*_ANSI_H*/ после #ifndef. Он не является обязательным, а всего лишь помогает отслеживать вложенные конструкции #ifndef . . . #endif и #ifdef. . .#endif. Тем не менее при написании комментариев следует соблюдать осторожность: отсутствие комментариев лучше, чем ошибки в них.

Второй файл из include/, косвенно включаемый в большинство исходных файлов MINIX 3, — это limits.h (строка 0100). В нем объявлены основные размеры, относящиеся как к типам языка (разрядность целого числа), так и к операционной системе (максимальная длина имени файла).

Обратите внимание на то, что номера строк в исходных текстах на сопровождающем книгу компакт-диске начинаются с новой сотни с каждым новым файлом. Другими словами, не следует думать, что файл ansi . h содержит 100 строк, с 00000 по 00099. По этой причине небольшие изменения, вносимые в один файл, скорее всего (хотя и не гарантированно), не подействуют на последующие файлы. Кроме того, когда в листинге появляется новый файл, указывается особый заголовок, включающий ряд символов +, имя файла и еще один ряд символов + (без нумерации строк). Пример такого заголовка вы можете видеть между строками 00068 и 00100.

Большинство главных заголовочных файлов также включает файл errno.h (строка 0200). Здесь содержатся коды ошибок, возвращаемые пользователю в глобальной переменной еггпо после системных вызовов. Эта переменная также индицирует некоторые внутренние ошибки, например попытку переслать сообщение несуществующему процессу. Внутри системы было бы неэффективно проверять значение глобальной переменной после вызова функции, способного привести к ошибке, однако функции зачастую возвращают другие целые числа, например количество байтов, переданных во время операции ввода-вывода. В MINIX 3 коды ошибок отрицательны, благодаря чему система может распознать их. Перед тем как возвращенное функцией значение передается программе пользователя, оно преобразуется в положительное. Такой эффект достигается за счет того, что каждый код ошибки задается строкой вида (строка 0236):

#define EPERM (_SIGN 1)

При компиляции системных файлов в главных заголовочных файлах определяется макрос _SYSTEM, в результате чего _SIGN транслируется в «-», а при компиляции пользовательских программ „SYSTEM никогда не определяется, и _SIGN просто игнорируется.

Следующая рассматриваемая группа файлов не включается в главные заголовочные файлы, хотя и повсеместно используется в коде MINIX 3. Главный из них — unistd. h (строка 0400). В нем перечислены константы, большая часть из которых обязательна в стандарте POSIX. Кроме того, в нем описаны прототипы многих функций, в том числе всех функций для доступа к системным вызовам MINIX 3. Второй файл — string. h (строка 0600); он содержит прототипы многочисленных функций, работающих со строками. Файл signal .h (строка 0700) задает стандартные имена сигналов. В нем же определено несколько специальных сигналов для внутреннего использования операционной системы MINIX 3. Поскольку функции операционной системы выполняются независимыми процессами, а не единым ядром, системным компонентам необходим особый способ взаимодействия друг с другом, подобный обмену сигналами. В файле signal .h определены прототипы некоторых функций для работы с сигналами. Как мы позднее увидим, использование сигналов характерно для всех компонентов MINIX 3.

В файле fcntl .h (строка 0900) указываются различные параметры, используемые при управлении файлами. Например, благодаря задаваемым здесь константам для открытия файла в режиме чтения можно указывать макрос 0_RDONLY вместо того, чтобы напрямую передавать значение 0. Этот файл нужен в основном файловой системе, но он же применяется в нескольких местах внутри ядра и менеджера памяти.

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

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

Эта структура, как и многие макросы и функции в файле termios .h, регламентирована стандартом POSIX.

Тем не менее, каким бы всеобъемлющим ни был стандарт POSIX, он не может включить в себя все, что может понадобиться. Поэтому вторая часть файла, начиная со строки 1140, относится к расширениям POSIX. Некоторые из них вполне очевидны, например определение стандартных скоростей передачи данных (57 600 бод и выше) и поддержка отображения окон терминалами. Подобные расширения не запрещены POSIX, поскольку стандарт не может объять необъятное. Но при разработке в MINIX 3 программ, которые рассчитаны на перенос в другое окружение, следует избегать специфичных для MINIX 3 определений. Сделать это несложно. Если в файле имеются специфичные для MINIX расширения, то их использование контролируется макрокомандой

#ifdef _MINIX

Если макрос _MINIX не определен, расширения игнорируются.

Поддержка «сторожевых» таймеров задается файлом timers.h (строка 1300), включенным в главный заголовочный файл ядра. Этот файл определяет структуру struct timer и прототипы функций, работающих со списками таймеров. В строке 1321 появляется определение типа tmr_func_t. Это — указатель на функцию, применение которого продемонстрировано в строке 1332: в структуре timer, используемой в качестве элемента списка таймеров, один элемент имеет тип tmr_func_t и определяет функцию, вызываемую при истечении таймера.

Упомянем еще четыре файла, находящихся в каталоге include/. Файл stdlib.h определяет типы, макросы и прототипы функций, как правило, необходимые для компиляции С-программ, по сложности превосходящих самые примитивные. Файл stdlib.h — один из наиболее часто применяемых заголовочных файлов при компиляции пользовательских программ, хотя лишь немногие исходные тексты ядра MINIX 3 ссылаются на него. Файл stdio.h знаком любому, кто начинал осваивать язык С с написания известной программы «Hello, World!». Его вряд ли можно встретить в системных файлах, хотя он используется почти в каждой пользовательской программе. Файл а. out. h определяет формат файлов, в которых исполняемые программы хранятся на диске. В нем определена структура ехес, информация которой используется менеджером процессов для загрузки нового образа программы при системном вызове ехес. Файл stddef .h задает несколько употребительных макросов.

Теперь переместимся в подкаталог include/sys/. Как видно из листинга 2.12, все главные заголовочные файлы основных составных частей MINIX 3 включают в себя файл sys/types .h (строка 1400) сразу после ansi .h. В types .h определяются различные типы данных, встречающиеся в MINIX 3. Благодаря ему можно избежать различных ошибок, связанных с неправильным использованием базовых типов. Размеры некоторых типов данных (в битах) для 16- и 32-разрядных систем указаны в табл. 2.4. Кроме того, обратите внимание, что имена всех типов данных заканчиваются символами «_ь». Это — больше чем договоренность, это — требование стандарта Р051Х. Согласно последнему, суффикс «_Ь> является зарезервированным и не должен использоваться в идентификаторах, не являющихся именами типов.

Таблица 2.4. Размеры (в битах) некоторых типов данных на 16- и 32-разрядных системах
Тип 16-разрядная система MINIX 32-разрядная система MINIX
gid_t 8 8
dev_t 16 16
pid_t 16 32
ino_t 16 32

В настоящее время операционная система MINIX 3 полностью поддерживает 32-разрядные микропроцессоры, однако с течением времени 64-разрядные процессоры будут приобретать все большую значимость. При необходимости существует возможность создания типа данных, не поддерживаемого аппаратно. В строке 1471 определен тип u64_t в виде структуры struct {u32_t [2] }. В текущей реализации он используется нечасто, однако иногда может быть полезен — к примеру, все данные о дисках и разделах (смещения и размеры) хранятся в виде 64-разрядных чисел, что позволяет задействовать диски очень большого объема.

В MINIX 3 используются множество определений типов данных, которые, в конечном счете, сводятся компилятором к относительно небольшому числу базовых типов. Это делает код более удобочитаемым; например, переменная, объявленная с типом dev_t, содержит главный и вспомогательный номера, определяющие устройство ввода-вывода. С точки зрения компилятора, ничего бы не изменилось, если бы вы указали тип short в качестве типа переменной. Отметим еще одну особенность: многие типы объявлены «парами», где два имени различаются только регистром первой буквы, к примеру, dev_t и Dev_t. Все типы, начинающиеся с прописной буквы, эквивалентны int. Они предназначены для прототипов функций, которые должны использовать типы данных, совместимые с int, с целью поддержки классических С-компиляторов. Более подробные объяснения вы найдете в комментариях файла types .h.

Внимания заслуживает еще один фрагмент условного кода (строки 1502-1516), начинающийся с директивы

#if _EM_WSIZE = = 2

Как было отмечено ранее, большая часть условного кода удалена из текста, однако мы сохранили этот пример, чтобы продемонстрировать механизм использования условных определений. Макрос _EM_WSIZE — еще один макрос проверки поддерживаемых функций, определенный компилятором. Он задает размер слова данных целевой системы в байтах. Последовательность #if. . . #else. . .#endif создает определения «раз и навсегда», чтобы обеспечить корректную компиляцию кода независимо от разрядности системы — 16 или 32 бит.

В операционной системе MINIX 3 широко используются несколько других файлов из каталога include/sys/. Файл sys/sigcontext .h (строка 1600) определяет структуры для сохранения и восстановления нормального состояния системы до и после исполнения процедуры обработки сигнала и используется ядром и менеджером процессов. Файл sys / stat .h (строка 1700) определяет структуру, представленную в листинге 1.2 и возвращаемую системными вызовами stat и f stat, прототипы функций stat и f stat, а также других функций, работающих со свойствами файлов. Ссылки на файл sys / st at. h имеются в нескольких местах файловой системы и менеджера процессов.

Остальные файлы, которые мы рассмотрим в этом разделе, используются не так широко. Файлы sys/dir .h (строка 1800) определяют структуру записи каталога MINIX 3. Существует лишь одна прямая ссылка на данный файл, однако она включает файл sys/dir .h в другой заголовочный файл, широко используемый в файловой системе. Важным параметром, определяемым в sys/dir .h, является максимальная длина имени файла (60 символов). Файл sys /wait .h (строка 1900) содержит макросы, используемые системными вызовами wait и waitpid, реализованными в диспетчере процессов.

Стоит упомянуть несколько файлов каталога include/sys/. Операционная система MINIX 3 поддерживает трассировку исполняемых файлов и анализ ключевых дампов в программе отладки. Файл sys/ptrace .h определяет различные операции, выполняемые по системному вызову ptrace. Файл sys/svrctl .h включает структуры данных и макросы, используемые в svrctl — неком подобии системного вызова, предназначенным для координирования процессов серверного уровня в ходе запуска системы. Системный вызов select организует ожидание ввода по нескольким каналам, например, для псевдотерминалов, ожидающих сетевых подключений. Определения, необходимые вызову select, находятся в файле sys /select .h.

Мы намеренно отложили обсуждение файла sys/ioctl.h и связанных с ним файлов, поскольку их назначение невозможно полностью понять, не разобравшись с файлом minix/ioctl .h. Системный вызов ioctl используется для управления устройствами. Число устройств, подключаемых к современным компьютерам, неуклонно растет, и каждое из них нуждается в управлении. В данной книге мы описываем операционную систему MINIX 3 с относительно небольшим количеством устройств ввода-вывода. Многие из них, включая сетевые интерфейсы, SCSI-контроллеры и звуковые карты, могут быть добавлены в систему.

Для того чтобы облегчить управление, нужные объявления разбиты на группы и размещены в отдельных небольших файлах. Все эти файлы включены в файл sys /ioctl .h (строка 2000), действующий подобно главному заголовочному файлу в листинге 2.12. На компакт-диске имеется лишь один из файлов, sys/ ioc_disk.h (строка 2100). Он, а также все прочие файлы, включенные в sys/ ioctl.h, расположены в каталоге include/sys/. Они являются частью «открытого интерфейса»; другими словами, программист может использовать их для написания любой программы, предназначенной для работы в среде MINIX 3. Тем не менее все указанные файлы зависят от дополнительных макроопределений из файла minix/ioctl .h (строка 2200), являющегося для них заголовочным. Сам файл minix/ioctl .h не следует использовать при программировании, именно поэтому он расположен в каталоге include/minix/, а не include/sys/.

Макросы, заданные в упомянутых файлах, определяют, каким образом различные элементы, необходимые всем допустимым функциям, представляются 32-раз- рядным целым числом, передаваемым системному вызову ioctl. К примеру, дисковые устройства требуют 5 видов операций, как видно из строк 2110-2114 файла sys/ioc_disk.h. Символьный параметр 1 d1 указывает вызову ioctl, что операция относится к дисковому устройству, число от 3 до 7 задает код операции, а третий параметр операции чтения или записи определяет размер структуры, в которую осуществляется передача данных. В файле minix/ioctl .h строки 2225-2231 показывают, что 8 бит символьного кода сдвигаются на 8 бит влево, 13 бит размера структуры (младших) сдвигаются на 16 бит влево, а затем к полученным значениям применяется логическая операция И, где вторым операндом является код операции. В старших трех разрядах 32-разрядного числа записан код, определяющий тип возвращаемого значения.

Хотя может показаться, что все описанное требует большого объема работы, эта работа выполняется на этапе компиляции и создает более эффективный интерфейс с системным вызовом на этапе выполнения, поскольку фактически переданный параметр — наиболее естественный тип данных для процессора. Тем не менее здесь вспоминается известный комментарий Кена Томпсона, помещенный в исходный код одной из ранних версий UNIX:

/* предполагается, что вы это не поймете */

В строке 2241 файла minix/ioctl .h содержится прототип системного вызова ioctl. Как правило, программисты не совершают этот вызов впрямую, поскольку во многих POSIX-функциях с прототипами в файле include/termios .h устаревшая библиотечная функция ioctl больше не используется для работы с терминалами, консолями и другими подобными устройствами. Тем не менее она по-прежнему нужна. Фактически, библиотека превращает POSIX-функции, управляющие терминальными устройствами, в системные вызовы ioctl.

2.6.4.

<< | >>
Источник: Э. ТАНЕНБАУМ, А. ВУДХАЛЛ. ОПЕРАЦИОННЫЕ СИСТЕМЫ Разработка и реализация 3-е издание. 2007

Еще по теме Общие заголовочные файлы:

  1. § 1. ОБЩИЕ ПОЛОЖЕНИЯ
  2. 1. Общие положения
  3. 3.1. ОБЩИЕ ХАРАКТЕРИСТИКИ
  4. § 1. ОБЩИЕ ПОНЯТИЯ ОБ ОБЯЗАТЕЛЬСТВАХ
  5. Глава 10. Общие положения
  6. Глава 17. Общие положения
  7. Глава 28. Общие положения
  8. РАЗДЕЛ I. ОБЩИЕ ПОЛОЖЕНИЯ
  9. ГЛАВА ПЕРВАЯ ОБЩИЕ В И Д Ы З А Л О Г А
  10. ВВЕДЕНИЕ ОБЩИЕ ПОНЯТИЯ
  11. § 1. Общие правила наследования по закону