Системные вызовы и библиотечные подпрограммы
В предисловии уже упомянули, что книга сконцентрирована на интерфейсе системных вызовов (system call interface). Тем не менее понятие системного вызова требует дополнительного определения.
Системные вызовы фактически дают разработчику программного обеспечения доступ к ядру (kernel). Ядро, с которым познакомились в предисловии, – это блок кода, который постоянно находится в памяти и занимается планированием системных процессов UNIX и управлением вводом/выводом. По существу ядро является частью UNIX, которое и делает ее операционной системой. Все управление, выделение ресурсов и контроль над пользовательскими процессами, а также весь доступ к файловой системе осуществляется через ядро.
Системные вызовы осуществляются так же, как и вызовы обычных подпрограмм и функций Паскаля. Например, можно считать данные из файла, используя библиотечную подпрограмму Паскаля read:
read(fileptr, inputbuf);
или при помощи низкоуровневого системного вызова fdread:
nread := fdread(filedes, inputbuf, BUFSIZE);
Основное различие между подпрограммой и системным вызовом состоит в том, что при вызове подпрограммы исполняемый код является частью объектного кода программы, даже если он был скомпонован из библиотеки; при системном вызове основная часть исполняемого кода в действительности является частью ядра, а не вызывающей программы. Другими словами, вызывающая программа напрямую вызывает средства, предоставляемые ядром. Переключение между ядром и пользовательским процессом обычно осуществляется при помощи механизма программных прерываний.
Неудивительно, что большинство системных вызовов выполняет операции либо над файлами, либо над процессами. Фактически системные вызовы составляют основные примитивы, связанные с обоими типами объектов.
При работе с файлами эти операции могут включать передачу данных в файл и из файла, произвольный поиск в файле или изменение связанных с файлом прав доступа.
| |||||||||||
Код программы | Адресное пространство пользователя (данные и программы пользователя) | ||||||||||
Библиотечная процедура read | |||||||||||
Пользовательский код для fdread | Адресное пространство ядра (системные ресурсы) | ||||||||||
Код ядра для вызова fdread | |||||||||||
Рис. 1.2. Связь между кодом программы, библиотечной подпрограммой и системным вызовом
При выполнении действий с процессами системные вызовы могут создавать новые процессы, завершать существующие, получать информацию о состоянии процесса и создавать канал взаимодействия между двумя процессами.
Небольшое число системных вызовов не связано ни с файлами, ни с процессами. Обычно системные процессы, входящие в эту категорию, относятся к управлению системой в целом или запросам информации о ней. Например, один из системных вызовов позволяет запросить у ядра текущее время и дату, а другой позволяет переустановить их.
Кроме интерфейса системных вызовов, системы UNIX также предоставляют обширную библиотеку стандартных процедур, одним из важных примеров которых является Стандартная библиотека ввода/вывода
(Standard I/O Library). Подпрограммы этой библиотеки обеспечивают средства преобразования форматов данных и автоматическую буферизацию, которые отсутствуют в системных вызовах доступа к файлам. Хотя процедуры стандартной библиотеки ввода/вывода гарантируют эффективность, они сами, в конечном счете, используют интерфейс системных вызовов. Их можно представить, как дополнительный уровень средств доступа к файлам, основанный на системных примитивах доступа к файлам, а не отдельную подсистему. Таким образом, любой процесс, взаимодействующий со своим окружением, каким бы незначительным не было это взаимодействие, должен использовать системные вызовы.
На рис. 1.2 показана связь между кодом программы и библиотечной процедурой, а также связь между библиотечной процедурой и системным вызовом. Из рисунка видно, что библиотечная процедура read в конечном итоге является интерфейсом к лежащему в его основе системному вызову fdread.
Упражнение 1.1. Объясните значение следующих терминов: ядро, системный вызов, подпрограмма Pascal, процесс, каталог, путь.