Системные вызовы
Теперь нужно выбрать между неопределенной обобщенностью («у операционных систем есть системные вызовы для чтения файлов») и какой-нибудь конкретной системой («в UNIX есть системный вызов чтения, имеющий три параметра: в одном из них определяется файл, в другом — куда поместить данные, а в третьем — сколько байтов следует считать»).
Мы выбрали второй подход. Пусть он сложнее, зато позволяет лучше понять, как на самом деле операционная система выполняет свою работу. Хотя все, что будет рассматриваться, имеет непосредственное отношение к стандарту POSIX (Международный стандарт 9945-1), а следовательно к UNIX, System V, BSD, Linux, MINIX 3 и т. д., у большинства других современных операционных систем имеются системные вызовы, выполняющие аналогичные функции, при некоторых отличиях в деталях. Поскольку фактический механизм выполнения системного вызова существенно зависит от конкретной машины и зачастую должен быть реализован на ассемблере, разработаны библиотеки процедур, осуществляющие системные вызовы из программ, написанных, например, на языке C.
Очень полезно всегда помнить следующее. Любой однопроцессорный компьютер одномоментно может выполнить только одну команду. Когда процесс выполняет пользовательскую программу в режиме пользователя и нуждается в какой-нибудь услуге операционной системы, например в чтении данных из файла, он должен выполнить команду системного прерывания, чтобы передать управление операционной системе. Затем операционная система по параметрам вызова определяет, что именно требуется вызывающему процессу. После этого она обрабатывает системный вызов и возвращает управление той команде, которая следует за системным вызовом. В некотором смысле выполнение системного вызова похоже на выполнение особой разновидности вызова процедуры, с той лишь разницей, что системные вызовы входят в ядро, а процедурные — нет.
Для того чтобы прояснить механизм системных вызовов, рассмотрим системный вызов чтения — read. Как уже упоминалось, он имеет три параметра: первый служит для задания файла, второй указывает на буфер, а третий задает количество байтов, которое нужно прочитать. Как практически все системные вызовы, он осуществляется из программы на языке C с помощью вызова библиотечной процедуры, имя которой совпадает с именем системного вызова: read. Вызов из программы на C может иметь следующий вид:
count = read(fd, buffer, nbytes);
Системный вызов (и библиотечная процедура) возвращает количество фактически считанных байтов, которое сохраняется в переменной count. Обычно это значение совпадает со значением параметра nbytes, но может быть и меньше, если, например, в процессе чтения будет достигнут конец файла.
Если системный вызов не может быть выполнен из-за неправильных параметров или ошибки диска, значение переменной count устанавливается в -1, а номер ошибки помещается в глобальную переменную errno. Программам обязательно нужно проверять результаты системного вызова, чтобы отслеживать возникновение ошибки.
Выполнение системного вызова состоит из нескольких шагов. Для прояснения ситуации вернемся к упоминавшемуся ранее примеру вызова read.
Сначала, при подготовке вызова библиотечной процедуры read, которая фактически и осуществляет системный вызов read, вызывающая программа помещает параметры в стек (рис. 1.17, шаги 1-3). Рис. 1.17. 11 этапов выполнения системного вызова read(fd, buffer, nbytes) |
Компиляторы C и C++ помещают параметры в стек в обратном порядке, следуя исторически сложившейся традиции (чтобы на вершине стека оказался первый параметр функции printf — строка формата вывода данных). Первый и третий параметры передаются по значению, а второй параметр передается по ссылке, поскольку это адрес буфера (о чем свидетельствует знак &), а не его содержимое. Затем осуществляется фактический вызов библиотечной процедуры (шаг 4). Эта команда представляет собой обычную команду вызова процедуры и используется для вызова любых процедур.
Библиотечная процедура, возможно, написанная на ассемблере, обычно помещает номер системного вызова туда, где его ожидает операционная система, например в регистр (шаг 5). Затем она выполняет команду TRAP для переключения из пользовательского режима в режим ядра, и выполнение продолжается с фиксированного адреса, находящегося внутри ядра операционной системы (шаг 6). Фактически команда TRAP очень похожа на команду вызова процедуры в том смысле, что следующая за ней команда берется из удаленного места, а адрес возврата сохраняется в стеке для последующего использования.
Тем не менее у команды TRAP и команды вызова процедуры есть два основных различия. Во-первых, побочный эффект, заключающийся в переключении в режим ядра. Команда вызова процедуры не меняет используемый режим. А во-вторых, команда TRAP не может получить относительный или абсолютный адрес местонахождения процедуры, поскольку не может осуществить переход на произвольный адрес.
Начавшая работу после команды TRAP часть ядра (диспетчер на рис.
1. 17) проверяет номер системного вызова, а затем передает управление нужному обработчику. Обычно передача управления осуществляется посредством таблицы указателей на обработчики системных вызовов, которая индексирована по номерам этих вызовов (шаг 7). После этого вступает в действие обработчик конкретного системного вызова (шаг 8). Как только обработчик закончит работу, управление может быть возвращено библиотечной процедуре, находящейся в пользовательской области памяти, той самой команде, которая следует за командой TRAP (шаг 9). В свою очередь эта процедура вернет управление пользовательской программе по обычной схеме возврата из процедуры (шаг 10).Чтобы завершить работу с процедурой read, пользовательская программа должна очистить стек, точно так же, как она это делает после любого вызова процедуры (шаг 11). Если в нашем примере стек растет вниз (как это чаще всего и бывает), пользовательская программа в скомпилированном виде должна содержать команды увеличения указателя стека ровно настолько, чтобы были удалены параметры, помещенные в стек перед вызовом процедуры read. Теперь программа может продолжить свою работу.
При рассмотрении шага 9 было специально отмечено, что «управление может быть возвращено библиотечной процедуре, находящейся в пользовательской области памяти». Системный вызов может заблокировать вызывающую программу, препятствуя продолжению ее работы. Например, вызывающая программа должна быть заблокирована при попытке чтения с клавиатуры, когда на ней еще ничего не набрано. В этом случае операционная система ищет другой процесс, который может быть запущен. Позже, когда станут доступны требуемые входные данные, система вспомнит о заблокированном процессе и будут выполнены шаги с 9-го по 11-й.
В следующих разделах мы рассмотрим некоторые из наиболее востребованных системных вызовов стандарта POSIX, или, точнее, библиотечных процедур, осуществляющих эти системные вызовы. В стандарте POSIX определено более 100 процедур, обеспечивающих обращение к системным вызовам.
Некоторые наиболее важные из них и сгруппированные для удобства по категориям перечислены в табл. 1.1. Далее мы кратко опишем каждый вызов и его назначение.Услуги, предоставляемые этими системными вызовами, в значительной степени определяют большую часть возможностей операционной системы, поскольку управление ресурсами на персональных компьютерах осуществляется в минимальном объеме (во всяком случае, по сравнению с большими машинами, обслуживающими множество пользователей). Они включают в себя такие виды обслуживания, как создание и прерывание процессов, создание, удаление, чтение и запись файлов, управление каталогами, ввод и вывод данных.
Отдельно стоит упомянуть, что отображение процедурных вызовов POSIX на системные вызовы не является взаимно однозначным. В стандарте POSIX определены процедуры, которые должна предоставить совместимая с ним система, но он не указывает, чем именно они должны быть реализованы: системными, библиотечными вызовами или чем-нибудь еще. Если процедура может быть выполнена без системного вызова (то есть без переключения в режим ядра), то она из соображений производительности обычно выполняется в пользовательском пространстве. Однако большинство процедур POSIX осуществляют системные вызовы, при этом обычно одна процедура непосредственно отображается на один системный вызов. В некоторых случаях, особенно когда несколько необходимых системе процедур мало чем отличаются друг от друга, один системный вызов обрабатывает более одного вызова библиотечных процедур. [7]
Таблица 1.1. Некоторые важнейшие системные вызовы POSIX1 |
1.6.1.
Еще по теме Системные вызовы:
- I. 1. СИСТЕМНЫЙ ПОДХОД КАК ИНСТРУМЕНТ ДЛЯ ПОСТРОЕНИЯ СИСТЕМНЫХ ОПИСАНИЙ
- I. СИСТЕМНЫЕ ОПИСАНИЯ - ГЛАВНЫЙ РЕЗУЛЬТАТ СИСТЕМНОГО ПОДХОДА В ПСИХОЛОГИИ
- Психология личности целостна, системна (принцип целостности, системности).
- ВЫЗОВ ВРАЧА
- СПТ откликается на вызов обстоятельств
- Быстрый вызов желаемого
- Бросьте себе вызов
- Самоубийство как вызов обществу
- § 14 Условное соглашение. – Предложение и вызов. – Договор посредством публичного торга или состязания. – Одностороннее обещание.
- § 46 Принятие наследства. – Значение вызова кредиторов и некоторых публикаций. – Отзыв о принятии и действия, служащие признаком принятия.
- ПРИНЦИП СИСТЕМНОСТИ
- § 2.4. Системный подход
- принцип системности
- Тема 1. Основные принципы системного анализа
- ГОЛОВОКРУЖЕНИЕ СИСТЕМНОЕ
- 2.1.4. Системный анализ
- принцип целостности, системности