Разработка ядра Linux
Шрифт:
Системные вызовы являются прослойкой между аппаратурой и процессами, работающими в пространстве пользователя. Эта прослойка служит для трех главных целей. Во-первых, она обеспечивает абстрактный интерфейс между аппаратурой и пространством пользователя. Например, при записи или чтении данных из файла прикладным программам нет дела до типа жесткого диска, до среды, носителя информации, и даже до типа файловой системы, на которой находится файл. Во-вторых, системные вызовы гарантируют безопасность и стабильность системы. Так как ядро работает посредником между ресурсами системы и пространством пользователя, оно может принимать решение о предоставлении доступа в соответствии с правами пользователей и другими критериями. Например, это позволяет предотвратить возможность неправильного использования аппаратных ресурсов программами, воровство каких-либо ресурсов у других программ, а также возможность нанесения вреда системе. И наконец, один общий слой между пространством пользователя и остальной системой позволяет осуществить виртуальное представление процессов, как обсуждается в главе 3, "Управление процессами".
Если бы приложения имели свободный доступ ко всем ресурсам системы без помощи ядра,
Интересно, что в ОС Linux реализовано значительно меньше системных вызовов, чем во многих других операционных системах [23] .
В этой главе рассказывается о роли и реализации системных вызовов в операционной системе Linux.
API, POSIX и библиотека С
Обычно прикладные программы не разрабатываются с непосредственным использованием системных вызовов, при этом используются программные интерфейсы приложений (Application Programing Interface, API). Это является важным, так как в таком случае нет необходимости в корреляции между интерфейсами, которые используют приложения, и интерфейсами, которые предоставляет ядро. Различные API определяют набор программных интерфейсов, которые используются приложениями. Эти интерфейсы могут быть реализованы с помощью одного системного вызова, нескольких системных вызовов, а также вообще без использования системных вызовов. В действительности, может существовать один и тот же программный интерфейс приложений для различных операционных систем, в то время как реализация этих API может для разных ОС существенно отличаться.
23
Для аппаратной платформы x86 существует около 250 системных вызовов (для каждой аппаратной платформы разрешается определять свои уникальные системные вызовы). Хотя не для всех операционных систем опубликованы действительные системные вызовы, но по оценкам для некоторых операционных систем таких вызовов более тысячи.
Один из наиболее популярных программных интерфейсов приложений в мире Unix-подобных систем базируется на стандарте POSIX. Технически стандарт POSIX включает в себя набор стандартов IEEE [24] , целью которого является обеспечение переносимого стандарта операционной системы, приблизительно базирующегося на ОС Unix. ОС Linux соответствует стандарту POSIX.
Стандарт POSIX является хорошим примером соотношения между интерфейсом API и системными вызовами. Для большинства Unix-подобных операционных систем вызовы интерфейса API, определенные в стандарте POSIX, сильно коррелируют с системными вызовами. Конечно, стандарт POSIX создавался для того, чтобы сделать те интерфейсы, которые предоставляли ранние версии ОС Unix, похожими между собой. С другой стороны, некоторые операционные системы, далекие от OS Unix, такие как Windows NT, предоставляют библиотеки, совместимые со стандартом POSIX.
24
IEEE, eye-triple-E (Институт инженеров по электротехнике и радиоэлектронике, Institute of Electrical and Electronics Engineers) является бесприбыльной профессиональной ассоциацией, действующей во многих технических областях и отвечающей за многие важные стандарты, такие как стандарт POSIX. Больше информации доступно по адресу:
Частично интерфейс к системным вызовам в операционной системе Linux, так же как и в большинстве Unix-систем, обеспечивается библиотекой функций на языке С. Библиотека С реализует главный программный интерфейс приложений для Unix-систем, что включает стандартную библиотеку языка программирования С и интерфейс системных вызовов. Библиотека С используется всеми программами, написанными на языке программирования С, а также, в связи со свойствами языка С, может быть легко использована для программ, написанных на других языках программирования.
Рис. 5.1. Взаимоотношения между приложением, библиотекой С и ядром на примере вызова функции
Дополнительно библиотека функций С также представляет большую часть API-стандарта POSIX.
С точки зрения прикладного программиста, системные вызовы не существенны: все, с чем работает программист, — это интерфейс API. С другой стороны, ядро имеет отношение только к системным вызовам: все, что делают библиотечные вызовы и пользовательские программы с системными вызовами, — это для ядра не существенно. Тем не менее с точки зрения ядра все-таки важно помнить о потенциальных возможностях использования системного вызова для того, чтобы по возможности поддерживать универсальность и гибкость системных вызовов.
Общий девиз для интерфейсов ОС Unix — это "предоставлять механизм, а не стратегию". Другими словами, системные вызовы существуют для того, чтобы обеспечить определенную функцию в наиболее абстрактном смысле. А то, каким образом используется эта функция, ядра не касается.
Вызовы syscall
Системные вызовы (часто называемые syscall в ОС Linux) обычно реализуются в виде вызова функции. Для них могут быть определены один или более аргументов (inputs), которые могут приводить к тем или иным побочным эффектам [25] ,
например к записи данных в файл или к копированию некоторых данных в область памяти, на которую указывает переданный указатель. Системные вызовы также имеют возвращаемое значение типа25
Следует обратить внимание на слово "могут". Хотя почти все вызовы создают различные побочные эффекты (т.е. приводят к каким-либо изменениям в состоянии системы), тем не менее небольшое количество вызовов, как, например, вызов
26
Тип
Системные вызовы, конечно, имеют определенное поведение. Например, системный вызов
Следует заметить, что в определении ничего не говорится о способе реализации. Ядро должно обеспечить необходимую функциональность системного вызова, но реализация может быть абсолютно свободной, главное, чтобы результат был правильный. Конечно, рассматриваемый системный вызов в действительности является таким же простым, как и показано, и существует не так уж много различных вариантов для его реализации (на самом деле более простого метода не существует) [27] .
27
Может быть, интересно, почему вызов
Даже из такого примера можно сделать пару наблюдений, которые касаются системных вызовов. Во-первых, следует обратить внимание на модификатор
Номера системных вызовов
Каждому системному вызову операционной системы Linux присваивается номер системного вызова (syscall number). Этот уникальный номер используется для обращения к определенному системному вызову. Когда процесс выполняет системный вызов из пространства пользователя, процесс не обращается к системному вызову по имени.
Номер системного вызова является важным атрибутом. Однажды назначенный номер не должен меняться никогда, иначе это нарушит работу уже скомпилированных прикладных программ. Если системный вызов удаляется, то соответствующий номер не может использоваться повторно. В операционной системе Linux предусмотрен так называемый "не реализованный" ("not implemented") системный вызов — функция
Ядро поддерживает список зарегистрированных системных вызовов в таблице системных вызовов. Эта таблица хранится в памяти, на которую указывает переменная
Производительность системных вызовов
Системные вызовы в операционной системе Linux работают быстрее, чем во многих других операционных системах. Это отчасти связано с невероятно малым временем переключения контекста. Переход в режим ядра и выход из него являются хорошо отлаженным процессом и простым делом. Другой фактор — это простота как механизма обработки системных вызовов, так и самих системных вызовов.